Unity 2018 - NPC smoothly turning to face the Player while within range - c#

With the current code the NPC will detect the and turn towards the player with the desired animation playing. However, the NPC only snaps to face the player and does not continue to rotate as the player walks around the NPC while they are in range.
I would like to modify this so that the NPC consistently and smoothly turns to face the character while the player is in range of the collider. I figured it would have something to do within void update, but since the function to turn is currently in onTriggerEnter it's a little confusing to a beginner such as myself.
public class helloTrigger : MonoBehaviour
{
public Animator anim;
public CharacterController player;
public Transform Player;
void Start()
{
player = GameObject.FindObjectOfType<CharacterController>();
anim = GetComponent<Animator>();
}
void OnTriggerEnter(Collider other)
{
if (other.tag != "Player") return;
anim.Play("Hello");
Vector3 lookAt = Player.position;
lookAt.y = transform.position.y;
transform.LookAt(lookAt);
}
void OnTriggerExit(Collider other)
{
if (other.tag == "Player")
{
anim.Play("Idle");
}
}
}

If you want a GameObject to face another GameObject smoothly, use Quaternion.LookRotation to calculate the destination rotation then use Quaternion.Lerp to lerp between the current rotation and the destination rotation calculated with Quaternion.LookRotation. Finally, do the lerping over time. See this post to understand how lerping over rotation work. You can do this in the Update function but I will use it in a coroutine function since it gives you more control.
A simple Look-at smoothly function:
IEnumerator LookAtSmoothly(Transform objectToMove, Vector3 worldPosition, float duration)
{
Quaternion currentRot = objectToMove.rotation;
Quaternion newRot = Quaternion.LookRotation(worldPosition -
objectToMove.position, objectToMove.TransformDirection(Vector3.up));
float counter = 0;
while (counter < duration)
{
counter += Time.deltaTime;
objectToMove.rotation =
Quaternion.Lerp(currentRot, newRot, counter / duration);
yield return null;
}
}
Your new script:
public class helloTrigger : MonoBehaviour
{
public Animator anim;
public CharacterController player;
public Transform Player;
Coroutine smoothMove = null;
// Use this for initialization
void Start()
{
player = GameObject.FindObjectOfType<CharacterController>();
anim = GetComponent<Animator>();
}
void OnTriggerEnter(Collider other)
{
if (other.tag == "Player")
{
anim.Play("Hello");
LookSmoothly();
}
}
void OnTriggerExit(Collider other)
{
if (other.tag == "Player")
{
anim.Play("Idle");
}
}
void LookSmoothly()
{
float time = 1f;
Vector3 lookAt = Player.position;
lookAt.y = transform.position.y;
//Start new look-at coroutine
if (smoothMove == null)
smoothMove = StartCoroutine(LookAtSmoothly(transform, lookAt, time));
else
{
//Stop old one then start new one
StopCoroutine(smoothMove);
smoothMove = StartCoroutine(LookAtSmoothly(transform, lookAt, time));
}
}
IEnumerator LookAtSmoothly(Transform objectToMove, Vector3 worldPosition, float duration)
{
Quaternion currentRot = objectToMove.rotation;
Quaternion newRot = Quaternion.LookRotation(worldPosition -
objectToMove.position, objectToMove.TransformDirection(Vector3.up));
float counter = 0;
while (counter < duration)
{
counter += Time.deltaTime;
objectToMove.rotation =
Quaternion.Lerp(currentRot, newRot, counter / duration);
yield return null;
}
}
}

Related

Why doesn't my coin disappear when my player collides with it?

I just started coding so i still have so much to learn. Unity doesn't give any error when I play the game but also nothing happenes when the player touches the gold. I want the gold to dissapear when the playes touches it but I don't know why it doesn't work. (collision part is the last part)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Player : MonoBehaviour
{
private Rigidbody2D myRigidbody;
private Animator anim;
[SerializeField]
private int speed;
private bool lookright;
void Start()
{
myRigidbody = GetComponent<Rigidbody2D>();
anim = GetComponent<Animator>();
lookright = true;
}
void Update()
{
float horizontal = Input.GetAxis("Horizontal");
movements(horizontal);
changedirection(horizontal);
}
private void movements (float horizontal)
{
anim.SetFloat ("Walk", Mathf.Abs(horizontal));
myRigidbody.velocity = new Vector2 (horizontal*speed, myRigidbody.velocity.y);
}
private void changedirection(float horizontal)
{
if (horizontal > 0 && !lookright || horizontal < 0 && lookright)
{
lookright = !lookright;
Vector3 direction = transform.localScale;
direction.x *= -1;
transform.localScale = direction;
}
}
void OnCollisionEnter2D(Collision2D other)
{
if (other.gameObject.tag == "gold")
{
other.gameObject.SetActive(false);
}
} }
This is the Unity inspector of the gold
OnCollisionEnter2D(Collision collision) won't fire when you have its IsTrigger set to true. Change it to OnTriggerEnter2D(Collider2D collider) and it will work.
Take a look at the documentation for colliders.

Cannot implicitly convert type 'UnityEngine.GameObject' to 'GameObject'

Hello everyone and thanks in advance for your answers!
I am a beginner in unity, coding my second game after i finshed a couple of tutorials.
Since today, i noticed that suddenly all my "GameObjects" have a "UnityEngine." in front of them.
I have no idea how that happened, and it is also not just in one script, but all of them. Heres an example of it:
UnityEngine.GameObject item = (UnityEngine.GameObject)Instantiate(itemGOList[i], spawnTransform, spawnRotation);
This worked fine before without the "UnityEngine.", but now it only works when its written this way.
Do you know how this could have happened and how to revert it?
This is one of the scripts:
using UnityEngine;
using UnityEngine.EventSystems;
public class Turret : MonoBehaviour
{
private Transform target;
private Enemy targetEnemy;
[Header("General")]
public float range = 10f;
[Header("Use Bullets/missiles (default)")]
public UnityEngine.GameObject bulletPrefab;
public float fireRate = 1f;
private float fireCountdown = 0f;
public AudioClip shootingSound;
private AudioSource audioSource;
[Header("Use Laser (default)")]
public bool useLaser = false;
public int damageOverTime = 20;
public float slowAmount = .5f;
public LineRenderer lineRenderer;
public ParticleSystem impactEffect;
public Light impactLight;
[Header("Unity Setup Fields")]
public string enemyTag = "Enemy";
public Transform partToRotate;
public float turnSpeed = 10f;
public Transform firePoint;
void Start()
{
InvokeRepeating(nameof(UpdateTarget), 0f, 0.3f); // Call the UpdateTarget Method after 0 seconds, then repeat every 0.3 seconds.
audioSource = GetComponent<AudioSource>(); //Puts the AudioSource of this GO (the turret) into the variable audioSource.
}
void UpdateTarget ()
{
UnityEngine.GameObject[] enemies = UnityEngine.GameObject.FindGameObjectsWithTag(enemyTag); // Find all enemies (and store in a list?).
float shortestDistance = Mathf.Infinity; // Create a float for the shortest distance to an enemy.
UnityEngine.GameObject nearestEnemy = null; // Create a variable which stores the nearest enemy as a gameobject.
foreach (UnityEngine.GameObject enemy in enemies) // Loop through the enemies array.
{
float distanceToEnemy = Vector3.Distance(transform.position, enemy.transform.position); //Get the distance to each enemy and stores it.
if (distanceToEnemy < shortestDistance) // If any of the enemies is closer than the original, make this distance the new shortestDistance and the new enemy to the nearestEnemy.
{
shortestDistance = distanceToEnemy;
nearestEnemy = enemy;
}
}
if (nearestEnemy != null && shortestDistance <= range) // Sets the target to the nearestEnemy (only if its in range and not null).
{
target = nearestEnemy.transform;
targetEnemy = nearestEnemy.GetComponent<Enemy>();
}
else
{
target = null;
}
}
void Update()
{
if (target == null) // If there is no target, do nothing.
{
if (useLaser)
{
if (lineRenderer.enabled)
{
lineRenderer.enabled = false;
impactEffect.Stop();
impactLight.enabled = false;
audioSource.Stop();
}
}
return;
}
LockOnTarget();
if (useLaser)
{
Laser();
}
else
{
if (fireCountdown <= 0f)
{
Shoot();
fireCountdown = 1f / fireRate;
}
fireCountdown -= Time.deltaTime;
}
}
void Laser()
{
targetEnemy.TakeDamage(damageOverTime * Time.deltaTime);
targetEnemy.Slow(slowAmount);
if (!lineRenderer.enabled)
{
lineRenderer.enabled = true;
impactEffect.Play();
impactLight.enabled = true;
if (audioSource.isPlaying == false)
{
audioSource.Play();
}
}
lineRenderer.SetPosition(0, firePoint.position);
lineRenderer.SetPosition(1, target.position);
Vector3 dir = firePoint.position - target.position;
impactEffect.transform.position = target.position + dir.normalized * 1f;
impactEffect.transform.rotation = Quaternion.LookRotation(dir);
}
void LockOnTarget()
{
Vector3 dir = target.position - transform.position; // Store the direction from turret to target.
Quaternion lookRotation = Quaternion.LookRotation(dir); // I have no idea how this works...
Vector3 rotation = Quaternion.Lerp(partToRotate.rotation, lookRotation, Time.deltaTime * turnSpeed).eulerAngles; // Convert quaternion angles to euler angles and Lerp it (smoothing out the transition).
partToRotate.rotation = Quaternion.Euler(0f, rotation.y, 0f); // Only rotate around the y-axis.
}
void Shoot()
{
UnityEngine.GameObject bulletGO = (UnityEngine.GameObject)Instantiate(bulletPrefab, firePoint.position, firePoint.rotation); // Spawn a bullet at the location and rotation of firePoint. (Also makes this a GO to reference it.
Bullet bullet = bulletGO.GetComponent<Bullet>(); // I have no idea how this works...
audioSource.PlayOneShot(shootingSound, 0.2f);
if (bullet != null) // If there is a bullet, use the seek method from the bullet script.
{
bullet.Seek(target);
}
}
void OnDrawGizmosSelected() // Do this method if gizmo is selected.
{
Gizmos.color = Color.red;
Gizmos.DrawWireSphere(transform.position, range); // Draw a wire sphere on the selected point (selected turret f. e.) and give it the towers range.
}
}
The problem was that i had a script called "GameObject" in my Unity Project.
I was also able to rename all "UnityEngine.GameObject" to "Gameobject" by using the Quick Action in Visual Studio.

Why is my Turret not updating the current enemy?

My problem is that, my turret locates the first enemy, but when it dies, OR, another enemy gets closer, the turret continues to focus on the last position of the first enemy.
The goal is to update the turrets target, to the enemy that is closest to its own transform . At some point I would like to extend that transform, to a player designated area.
private Transform target;
float range = 5f;
float distanceToAttackEnemy;
float shortestDistance = Mathf.Infinity;
[SerializeField] GameObject turretTower;
[SerializeField] GameObject turretProjektile;
[SerializeField] Transform bulletSpawn;
List<GameObject> listOfEnemies = new List<GameObject>();
GameObject nearestEnemy = null;
// Update is called once per frame
void Update()
{
CheckingForClosestEnemyInList();
}
private void OnTriggerEnter2D(Collider2D other)
{
if (other.tag == ("Enemy"))
{
listOfEnemies.Add(other.gameObject);
}
}
private void OnTriggerExit2D(Collider2D other)
{
if (other.tag == ("Enemy"))
{
listOfEnemies.Remove(other.gameObject);
}
}
void CheckingForClosestEnemyInList()
{
foreach(GameObject enemy in listOfEnemies)
{
float distBetweenPlayerAndEnemy = Vector3.Distance(transform.position, enemy.transform.position);
if (distanceToAttackEnemy < shortestDistance)
{
shortestDistance = distanceToAttackEnemy;
nearestEnemy = enemy;
}
}
if (nearestEnemy != null && shortestDistance <= range)
{
var dir = nearestEnemy.transform.position - transform.position;
var angle = Mathf.Atan2(dir.y, dir.x) * Mathf.Rad2Deg - 90;
turretTower.transform.rotation = Quaternion.AngleAxis(angle, Vector3.forward);
}
else
{
nearestEnemy = null;
}
}
}
Any and all help would be greatly appreciated.

Point deduction from enemy once target is reached?

I made an AI Enemy system that has the enemy walk towards a target that I want to attack.The target (who is the player collects points). What I want is when the enemy reaches its target it takes away -1 point from the player. What I want to know is how do I deduct the point from my game manager. I already created a public game object that carries the script for my points. When I try this out the enemy does not take a point away once it reaches the player. Should I create a function within my Enemy AI script called TargetIsReached()?
public class aiEnemy : MonoBehaviour, IPooledObject {
public float lookRadius = 40f;
Transform target;
UnityEngine.AI.NavMeshAgent agent;
Rigidbody theRigidBody;
public Casting castingScript;
void Start(){
target = PlayerManager.instance.player.transform;
agent = GetComponent<UnityEngine.AI.NavMeshAgent>();
}
void Update(){
float distance = Vector3.Distance (target.position, transform.position);
if (distance <= lookRadius)
{
agent.SetDestination (target.position);
if (distance <= agent.stoppingDistance)
{
FaceTarget ();
castingScript.RemovePoint ();
}
}
}
void FaceTarget()
{
Vector3 direction = (target.position - transform.position).normalized;
Quaternion lookRotation = Quaternion.LookRotation (new Vector3 (direction.x, 0, direction.z));
transform.rotation = Quaternion.Slerp (transform.rotation, lookRotation, Time.deltaTime * 5f);
}
// Use this for initialization
public void OnObjectSpawn () {
//myRender = GetComponent<Renderer> ();
theRigidBody = GetComponent<Rigidbody>();
}
void OnDrawGizmosSelected()
{
Gizmos.color = Color.red;
Gizmos.DrawWireSphere (transform.position, lookRadius);
}
}
//Casting Script//
public GameObject Float;
public Transform target;
public GameObject disableFish;
public float waitTime= 3f;
public int scoreValue = 1;
int waterMask;
public float maxCastDist = 1000f;
public float fishDist = 1f;
public bool hitObject;
void Awake ()
{
waterMask = LayerMask.GetMask ("Water"); // will only cast in water
}
void Update ()
{
if (EventSystem.current.IsPointerOverGameObject ()) {
return;
} else {
if (Input.GetMouseButtonDown (0)) {
RaycastHit hit;
Ray ray = Camera.main.ScreenPointToRay (Input.mousePosition);
if (Physics.Raycast (ray, out hit, maxCastDist, waterMask))
if (hit.collider != null) {
float dist = Vector3.Distance (hit.point, target.position);
Debug.Log (dist);
Destroy (GameObject.FindWithTag ("Float"));
Instantiate (Float, hit.point, Quaternion.identity);
if (dist < fishDist) {
disableFish.SetActive(false);
StartCoroutine (fishWaiter ());
StartCoroutine (DestroyOb(waitTime));
}
}
}
}
}
IEnumerator fishWaiter ()
{
FishLoader.SetActive (true);
FishSlider.value = waitTime;
Debug.Log ("Timer Started");
yield return new WaitForSeconds (waitTime);
print ("I waited" + waitTime + "Sec");
FishSlider.value = 0f;
print ("You Caught The Fish!");
ScoreManager.score += scoreValue;
}
IEnumerator DestroyOb (float waitTime){
yield return new WaitForSeconds (waitTime);
Destroy (GameObject.FindWithTag ("Float"));
}
}

Npc animation following player unity 2d top down

Hey I am making a top down game in unity. The problem I ma having is making npc players change the way they are facing while following the player. So if the player turns left the npc follows them but doesn't turn to face the direction the npc is going. I can get the npc to look like its walking just not change the direction it is looking. This is a 2d top down game please any help will be nice. here is my npc code.
using UnityEngine;
using System.Collections;
public class SlimeController : MonoBehaviour
{
public Transform Character; // Target Object to follow
public float speed = 0.1F; // Enemy speed
public float maxDist = 10.0f;
public float attackdistance = 3;
public float farenough;
private Vector3 directionOfCharacter;
private bool challenged = false;// If the enemy is Challenged to follow by the player
public Transform StartMarker;
private Vector3 goback;
public Transform EndMarker;
public Rigidbody2D rb;
Animator anim;
float oldx;
bool left;
bool right;
private void Start()
{
rb = GetComponent<Rigidbody2D>();
anim= GetComponent<Animator>();
oldx = transform.position.x;
}
void Update()
{
anim.SetBool("left", false);
anim.SetBool("right", false);
var distanceFromPlayer = Vector3.Distance(Character.position, transform.position);
if(oldx>transform.position.x)
{
left = false;
right = true;
}
if(oldx<transform.position.x)
{
left = true;
right = false;
}
if (oldx == transform.position.x)
{
left = false;
right = false;
}
if (challenged)
{
directionOfCharacter = Character.transform.position - transform.position;
directionOfCharacter = directionOfCharacter.normalized; // Get Direction to Move Towardsss
transform.Translate(directionOfCharacter * speed, Space.World);
enabled = true;
if (distanceFromPlayer < attackdistance)
{
attack();
}
if (distanceFromPlayer > attackdistance)
{
speed = 0.03f;
}
}
if (!challenged)
{
goback = StartMarker.transform.position - transform.position;
goback = goback.normalized;
transform.Translate(goback * speed, Space.World);
}
}
// Will be triggered as soon as player would touch the Enemy Object
void OnTriggerEnter2D(Collider2D other)
{
if (other.gameObject.name == ("Player"))
{
challenged = true;
}
}
private void OnTriggerExit2D(Collider2D other)
{
if (other.gameObject.name == ("Player"))
{
speed = 0.03f;
challenged = false;
}
}
void attack()
{
speed = 0;
transform.Translate(directionOfCharacter * speed, Space.World);
}
}
This is because you are just moving the object towards your target. But to have it look at your target you need to also rotate it in the direction of your target.
The Transform Component has a function called LookAt. You supply it with your Target and the Axis your object should rotate around. So in your case:
this.transform.LookAt(Character, Vector3.up);
See here for more info on LookAt.

Categories

Resources