using System.Collections; using System.Collections.Generic; using UnityEngine; public class NpcMovementController : MonoBehaviour { [SerializeField] private SpriteRenderer spriteRenderer; [SerializeField] private Animator animator; [SerializeField] private Rigidbody2D rigidBody; [SerializeField] private float walkSpeed; [Range(0f, 1f)] [SerializeField] private float walkProbability; [Range(0f, 3f)] [SerializeField] private float smoothTime; [SerializeField] private float minTimeIdle; [SerializeField] private float maxTimeIdle; [SerializeField] private float minTimeWalk; [SerializeField] private float maxTimeWalk; public Vector2 boundsX; public bool isFacingRight; public bool isWalking; private Vector2 targetVelocity; private Vector2 referenceVelocity = Vector2.zero; private Vector2 myBounds; // Start is called before the first frame update void Awake() { animator = gameObject.GetComponent(); rigidBody = gameObject.GetComponent(); isFacingRight = true; isWalking = false; StartCoroutine("IdleWalkCoroutine"); myBounds = new Vector2(transform.position.x - boundsX.x, transform.position.x + boundsX.y); } private void FixedUpdate() { // If you're about to walk off a ledge, don't. bool isMoving = Mathf.Abs(rigidBody.velocity.x) > 0.001; bool isMovingRight = rigidBody.velocity.x > 0; if (isMoving && !CheckBounds(isMovingRight)) { rigidBody.velocity = new Vector2(0f, rigidBody.velocity.y); targetVelocity = new Vector2(0f, rigidBody.velocity.y); } else if (Mathf.Abs(rigidBody.velocity.y) < 0.01f) { Vector2 newVelocity = new Vector2(Vector2.SmoothDamp(rigidBody.velocity, targetVelocity, ref referenceVelocity, smoothTime).x, rigidBody.velocity.y); rigidBody.velocity = newVelocity; } animator.SetFloat("Speed", Mathf.Abs(rigidBody.velocity.x)); } private bool TryToWalk(bool right, bool chase = false) { if (CheckBounds(right)) { if (right) { targetVelocity = new Vector3(walkSpeed, 0, 0); } else { targetVelocity = new Vector3(-1 * walkSpeed, 0, 0); } if (isFacingRight != right) { Flip(); } return true; } return false; } private bool CheckBounds(bool right) { if ((right && transform.position.x > myBounds.y) || (!right && transform.position.x < myBounds.x)) { return false; } return true; } private IEnumerator IdleWalkCoroutine() { while (true) { if (ShouldWalk() && !isWalking) { // Walk in a random direction. isWalking = true; if (Random.Range(0f, 1f) < 0.5f) { // Walk left. if (!TryToWalk(false)) { TryToWalk(true); } } else { // Walk right. if (!TryToWalk(true)) { TryToWalk(false); } } yield return new WaitForSeconds(RandomWalkSeconds()); } else { // Idle. isWalking = false; targetVelocity = Vector3.zero; yield return new WaitForSeconds(RandomIdleSeconds()); } } } private bool ShouldWalk() { return Random.Range(0f, 1f) < walkProbability; } private float RandomIdleSeconds() { return Random.Range(0f, 1f) * (maxTimeIdle - minTimeIdle) + minTimeIdle; } private float RandomWalkSeconds() { return Random.Range(0f, 1f) * (maxTimeWalk - minTimeWalk) + minTimeWalk; } private void Flip() { isFacingRight = !isFacingRight; Vector3 scale = transform.localScale; scale.x *= -1; transform.localScale = scale; } }