using System.Collections; using System.Collections.Generic; using UnityEngine; public class Seeker : MonoBehaviour { public LayerMask collisionMask; public LayerMask groundingMask; public float range = 3.0f; public float maxSpeed = 3.0f; // units per second public float acceleration = 3.0f; // increase in velocity per second public float drag = 1.0f; public Vector3 bottomLeft; public Vector3 bottomRight; private float skinDepth = 0.01f; private Vector3 pivot; private Vector3 leftMax; private Vector3 rightMax; private Vector3 velocity; void Start() { velocity = Vector3.zero; } // Start is called before the first frame update void Awake() { pivot = transform.position; leftMax = pivot + Vector3.left * range; rightMax = pivot + Vector3.right * range; } // Update is called once per frame void Update() { detectPlayer(); velocity.x = Mathf.Clamp(velocity.x, -maxSpeed, maxSpeed); Vector3 next = transform.position + velocity * Time.deltaTime; Debug.LogFormat("{0} x velocity: {1}", gameObject.name, velocity.x); if (velocity.x >= 0) { // if we're moving right, cast a ray from the bottom right corner to make sure we don't go over the ledge RaycastHit2D hitRight = Physics2D.Raycast(transform.position + bottomRight + skinDepth * Vector3.up, Vector2.down, Mathf.Infinity, groundingMask); RaycastHit2D nextHitRight = Physics2D.Raycast(next + bottomRight + skinDepth * Vector3.up, Vector2.down, Mathf.Infinity, groundingMask); hitRight.distance -= skinDepth; nextHitRight.distance -= skinDepth; Debug.LogFormat("Hit Right: {0} Dist: {1} Next Hit Right: {2} Dist: {3}", hitRight, hitRight.distance, nextHitRight, nextHitRight.distance); // this is gross but because we're only placing blocks in unit // increments we can avoid rounding comparison errors by just // checking if the difference between this height and the next // height is less than half our minimum delta that we use in the // levels. This is faster than figuring out a real solution. if (Mathf.Abs(nextHitRight.distance - hitRight.distance) < 0.5f) { transform.position = next; } else { velocity.x = 0; } } else { // if we're moving left, cast a ray from the bottom left corner to make sure we don't go over the ledge RaycastHit2D hitLeft = Physics2D.Raycast(transform.position + bottomLeft + skinDepth * Vector3.up, Vector2.down, Mathf.Infinity, groundingMask); RaycastHit2D nextHitLeft = Physics2D.Raycast(next + bottomLeft + skinDepth * Vector3.up, Vector2.down, Mathf.Infinity, groundingMask); hitLeft.distance -= skinDepth; nextHitLeft.distance -= skinDepth; if (Mathf.Abs(hitLeft.distance - nextHitLeft.distance) < 0.5f) { transform.position = next; } else { velocity.x = 0; } } } private void detectPlayer() { if (detectPlayer(transform.position + Vector3.left * 1.22f, Vector3.left)) { velocity += Vector3.left * acceleration * Time.deltaTime; } if (detectPlayer(transform.position + Vector3.right * 1.22f, Vector3.right)) { velocity += Vector3.right * acceleration * Time.deltaTime; } } private bool detectPlayer(Vector3 start, Vector3 dir) { Debug.LogFormat("start: {0} dir: {1}", start, dir); RaycastHit2D hit = Physics2D.Raycast(start, dir, range, collisionMask); if (hit) { if (hit.collider.gameObject.layer == LayerMask.NameToLayer("Player")) { Debug.DrawRay(start, dir * hit.distance, Color.red); return true; } else { Debug.DrawRay(start, dir * hit.distance, Color.green); if (dir.x >= 0) { velocity.x = Mathf.Clamp(velocity.x, -Mathf.Infinity, hit.distance / Time.deltaTime); } else { velocity.x = Mathf.Clamp(velocity.x, -hit.distance / Time.deltaTime, Mathf.Infinity); } } } else { Debug.DrawRay(start, dir * range, Color.gray); } return false; } }