Unity navmesh. follow + stop certain distance away from target - unity3d

Hi so what im trying to create is the player can right click on an enemy and he will follow at a certain distance. which is working fine. but what i want it to also do is stop at that distance too. currently if the enemy stops he will try and go to its exact position instead of stopping a little bit away this is what i have currently.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;
public class Attacking : MonoBehaviour {
NavMeshAgent agent;
Transform target;
public float distance;
public float followDistance;
// Use this for initialization
void Start () {
agent = GetComponent<NavMeshAgent>();
}
// Update is called once per frame
void Update () {
if (Input.GetMouseButton(0))
{
target = null;
}
if (Input.GetMouseButton(1))
{
RaycastHit hit;
if (Physics.Raycast(Camera.main.ScreenPointToRay(Input.mousePosition), out hit, 100))
{
if (hit.collider.gameObject.tag == "enemy" || hit.collider.gameObject.tag == "Player")
{
target = hit.collider.transform;
}
}
}
if(target != null)
{
distance = Vector3.Distance(transform.position, target.position);
if (followDistance <= distance)
agent.destination = target.position;
}
}
}

Attach a script to your enemy and give it a certain radius ,say 3f.
public float radius=3f;
for better understanding and visual aid use OnDrawGizmosSelected() (on your enemy script).
void OnDrawGizmosSelected ()
{
Gizmos.color=Color.yellow;
Gizmos.DrawWireSphere(transform.position,radius);
}
Now,on your player script use agent.StoppingDistance() as:
if(target != null)
{
distance = Vector3.Distance(transform.position, target.position);
if (followDistance <= distance){
agent.destination = target.position;
agent.StoppingDistance=target.radius;
}
}
Note:you will have to change target from transform to an instance of your player

Related

Difficulties flipping a collider in Unity when attacking

I am working on a small game for a school project, in which my player needs to attack enemies in a level. My plan is to have a collider that is enabled in an attached script, and then disabled when the attack is done. My current problem is that that the collider does not flip the way it is supposed to, it seems to flip directly on the overall x axis instead of flipping in the x axis related to the player. It is a child of the player so I am clueless as to why it is doing this. Any solutions or other approaches would be greatly appreciated. I will attach the current script that controls the collider below.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class VerticalSword : MonoBehaviour
{
//Variables use to active the colliders
Collider2D AttackColliderVertical;
//Variables for the location of the collider
Vector2 attackOffset;
private void Start()
{
AttackColliderVertical = GetComponent<Collider2D>();
attackOffset = transform.position;
AttackColliderVertical.enabled = false;
}
public void FixedUpdate()
{
attackOffset = transform.position;
}
public void AttackUp()
{
AttackColliderVertical.enabled = true;
if (attackOffset.y > 0)
{
transform.position = attackOffset;
}
else if (attackOffset.y < 0)
{
transform.position = new Vector2(attackOffset.x, (attackOffset.y * -1)); //I think the problem is somewhere in this if and else if statement
}
print("Attack up successful"); //Error checking (This works when run)
}
public void AttackDown()
{
AttackColliderVertical.enabled = true;
if (attackOffset.y > 0)
{
transform.position = new Vector2(attackOffset.x, (attackOffset.y * -1));
}
else if (attackOffset.y < 0)
{
transform.position = attackOffset; //I think the problem is somewhere in this if and else if statement
}
print("Attack down successful"); //Error checking (This works when run)
}
public void StopAttack()
{
AttackColliderVertical.enabled = false;
}
}
Use transform.localPosition, not transform.position (that's its world space position). You need to change it everywhere in this script; the Start() function and the two attack functions

AI path changing when OnTriggerEnter with UNITY

Here I share my AiPathfinding script. I want my AI walks to a destination (target) when game starts. however, when AI collide an certain object (finding with tag="bullet") then I want to set new destination. Although code does not give error, when playing AI goes to first destination then stops there even though it collides with certain object on the way.
Can someone have a look please?
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Pathfinding;
public class AIPathfinding : MonoBehaviour
{
[SerializeField] public Transform target;
[SerializeField] public Transform target2;
[SerializeField] public float speed = 200f;
[SerializeField] public float nextWayPointDistance = 3f;
[SerializeField] Path path;
[SerializeField] int currentWaypoint = 0;
[SerializeField] bool reachedEndOfPath = false;
[SerializeField] Seeker seeker;
[SerializeField] Rigidbody2D rb;
// Start is called before the first frame update
void Start()
{
seeker = GetComponent<Seeker>();
rb = GetComponent<Rigidbody2D>();
InvokeRepeating("UpdatePath", 0f, 0.5f);
}
private void OnTriggerEnter2D(Collider2D collision)
{
if (collision.gameObject.tag == "bullet")
{
UpdatePath(target2);
}
}
public void UpdatePath(Transform target2)
{
if (seeker.IsDone())
{
seeker.StartPath(rb.position, target.position, OnPathComplete);
}
}
void OnPathComplete(Path p)
{
if (!p.error)
{
path = p;
currentWaypoint = 0;
}
}
void Update()
{
if (path == null)
{
return;
}
if (currentWaypoint >= path.vectorPath.Count)
{
reachedEndOfPath = true;
return;
}
else
{
reachedEndOfPath = false;
}
Vector2 direction = ((Vector2)path.vectorPath[currentWaypoint] - rb.position).normalized;
Vector2 force = direction * speed * Time.deltaTime;
rb.AddForce(force);
float distance = Vector2.Distance(rb.position, path.vectorPath[currentWaypoint]);
if (distance < nextWayPointDistance)
{
currentWaypoint++;
}
}
}
My guess is that you never stopped the Coroutine InvokeRepeating("UpdatePath", 0f, 0.5f). So do call CancelInvoke() when the bullet hits and change the target of the seeker (Start InvokeRepeating(...) again).
I assume NavMeshAgent.SetDestination(Vector3 position) is called in the function seeker.SetTarget(). And somehow Path gets calculated and set.
You could also just move the body of Update Path into the Update function or call it from there. Nevermind the Interval. Or do call it in your custom interval. Just calculate the next time you want to call it so if the nextUpdatePathTime (Time.time plus your cooldown) is smaller than Time.time then call it and calculate the next nextUpdatePathTime.

How to stop the enemy rotate when the player collide with it

I have been try for a while to stop the enemy rotate wen my player collide with it. I have insert a rigid body, and I have try to freeze position and also the freeze rotation but he keep doing the same, I can't find any were any info to learn about this problem.
I have google it a few times and could not find any thing, close to my problem.
This is the screenshot of the problem
https://imgur.com/fyryuqY
Also I have tried to set Angular drag to 0 but no success it keep doing the same.
It sounds like I am missing something, but I can't find any solution for it yet.
I have also tried to see all answers on unity forum but I can't find anywhere a solution or the way to learn about this problem.
I have edit to insert the enemy script
This is my enemy script
using UnityEngine;
using System.Collections;
public class target : MonoBehaviour {
public float health = 100f;
public Animator animx2;
public AudioSource audiovfx2;
public void TakeDamage (float amount)
{
health -= amount;
if (health <= 0f)
{
Die();
}
}
void Die()
{
animx2.SetBool("isdie",true);
audiovfx2.Play();
healthcontroller.score += 10;
health -= 10;
Destroy (gameObject, 1.5f);
}
void Update()
{
animx2 = GetComponent<Animator>();
GetComponent<AudioSource>().Play();
}
void OnTriggerEnter(Collider other) {
if (other.tag == "Player") {
Rigidbody rb = GetComponent<Rigidbody>();
rb.angularVelocity = Vector3.zero
//Stop Moving/Translating
//rbdy.velocity = Vector3.zero;
//Stop rotating
//rbdy.angularVelocity = Vector3.zero;
}
}
void OnTriggerExit(Collider other) {
if (other.tag == "Player") {
//here i want to return to normal
}
}
}
Thank you in advance
Did you try to nullify angularVelocity of the Rigidbody component, for example
rb.angularVelocity = Vector3.zero;

Unity3D Collision with character controller

i'm new to unity and i'm trying to make an endless runner. When my player (a ball) hits one of the walls the game needs to go the scene: LostMenu. My problem is that the collision doesn't work. Nothing happens when they collide... Here are the inspectors on both the player and the walls: Walls http://prntscr.com/a2pgzm Player http://prntscr.com/a2ph66 .
The collision script on the walls:
using UnityEngine;
using System.Collections;
public class LostByWallCSR : MonoBehaviour
{
void OnCollisionEnter (Collision col)
{
if(col.gameObject.name == "Player")
{
Application.LoadLevel("LostMenu");
Debug.Log ("WORKS!");
}
}
}
The movement script on the player:
using UnityEngine;
using System.Collections;
public class CharacterControllerz : MonoBehaviour {
public float speed = 5f;
public float gravity = 20f;
private Vector3 moveDirections = new Vector3();
private Vector3 inputs = new Vector3();
void FixedUpdate()
{
CharacterController cc = GetComponent<CharacterController>();
if (cc.isGrounded)
{
if (Input.GetKey("left"))
inputs.z = 3;
else if (Input.GetKey("right"))
inputs.z = -3;
else
inputs.z = 0;
moveDirections = transform.TransformDirection(inputs.x, 0, inputs.z) * speed;
}
inputs.x = 5;
moveDirections.y = inputs.y - gravity;
cc.Move(moveDirections * Time.deltaTime);
}
}
Any idea on how to setup the collision properly? Attaching a rigibody is not helping it. Unless i'm missing something ofcourse.
Make sure both objects have a collider component.
You need to attach a rigidbody component to one of the objects.(if you don't attach rigid body it will not work)
your Sphere collider is Trigger , remove that or change your function to
public void OnTriggerEnter(Collider other)
{
}
You need to use OnControllerColliderHit method
private void OnControllerColliderHit(ControllerColliderHit hit)
{
if (hit.gameObject.CompareTag("Trap"))
{
Debug.Log("colisiono with trap");
playerDentro = true;
}
else
{
if (!hit.gameObject.CompareTag("Trap"))
{
Debug.Log("player salio de la trampa");
playerDentro = false;
}
}
}e

how to stop continous firing of automatic turrets after the enemies cross the collider?

i have a turret,as a game object when a enemy enters it's collison box,the turret starts firing towards it,the logic is when the enemy exits the collider,it should stop its firing ,and other problem is that when again an enemy enters the collison box i.e the second enemy,it gives me an exception ,"MissingReferenceException :the object of type 'transform' has been destroyed but you are still trying to access it.Your script should eihter be check if it is null or you should not destroy it",but i am checking if the list in not null in my code.here is my code
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class TurretScript : MonoBehaviour {
public float shotInterval = 0.2f; // interval between shots
public GameObject bulletPrefab; // drag the bullet prefab here
public float bulletSpeed;
private float shootTime = 0.0f;
private List<Transform> targets;
private Transform selectedTarget;
private Transform myTransform;
private Transform bulletSpawn;
void Start(){
targets = new List<Transform>();
selectedTarget = null;
myTransform = transform;
bulletSpawn = transform.Find ("bulletSpawn"); // only works if bulletSpawn is a turret child!
}
void OnTriggerEnter2D(Collider2D other){
if (other.tag == "enemy"){ // only enemies are added to the target list!
targets.Add(other.transform);
}
}
void OnTriggerExit2D(Collider2D other){
if (other.tag == "enemy"){
targets.Remove(other.transform);
Debug.Log("gone out");
}
}
void TargetEnemy(){
if (selectedTarget == null){ // if target destroyed or not selected yet...
SortTargetsByDistance(); // select the closest one
if (targets.Count > 0) selectedTarget = targets[0];
}
}
void SortTargetsByDistance(){
targets.Sort(delegate(Transform t1, Transform t2){
return Vector3.Distance(t1.position, myTransform.position).CompareTo(Vector3.Distance(t2.position, myTransform.position));
});
}
void Update(){
TargetEnemy(); // update the selected target and look at it
if (selectedTarget)
{
// if there's any target in the range...
Vector3 dir = selectedTarget.position - transform.position;
float angle = Mathf.Atan2(dir.y,dir.x) * Mathf.Rad2Deg;
transform.rotation = Quaternion.AngleAxis(angle, Vector3.forward);// aim at it
if (Time.time >= shootTime){// if it's time to shoot...
// shoot in the target direction
Vector3 lookPos = new Vector3(bulletSpawn.position.x,bulletSpawn.position.y,0);
lookPos = lookPos - transform.position;
float ang = Mathf.Atan2(lookPos.y,lookPos.x)*Mathf.Rad2Deg;
GameObject b1 = Instantiate(bulletPrefab,new Vector3(transform.position.x,transform.position.y,5),transform.rotation)as GameObject;
b1.rigidbody2D.velocity = new Vector3(Mathf.Cos(ang*Mathf.Deg2Rad),Mathf.Sin(ang*Mathf.Deg2Rad),0)*bulletSpeed;
shootTime = Time.time + shotInterval; // set time for next shot
}
}
}
}
here is my enemy script
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class EnemyScript : MonoBehaviour {
public Transform target;
public float speed = 2f;
public int Health;
public float GetHealth()
{
return Health;
}
void Update ()
{
transform.position = Vector2.MoveTowards(transform.position, target.position, speed * Time.deltaTime);
}
void TakeDamage(int damage){
Health -= damage;
if (Health <= 0)
Destroy(gameObject);
}
void OnTriggerEnter2D(Collider2D otherCollider)
{
PlayerControl shot = otherCollider.gameObject.GetComponent<PlayerControl>();
if (shot != null)
{
SpecialEffectsHelper.Instance.Explosion(transform.position);
Destroy(shot.gameObject);
}
}
}
you need to check if the selected target is the target leaving the collider. You remove the target from the targets list but the selectedTarget var is still populated.
For the null ref exception. Are you using Destroy() to kill the target? Destroy() doesn't cause an OnTriggerExit() or OnCollisionExit() event call, the object is just gone.
edit: you can get around the lack of a call by adding an OnDestroy() function to the dying object that sets it's position to something well outside the level/view of the player. This way the target leaves the collider and then disappears rather than just disappearing in place.