Created
July 4, 2019 20:55
-
-
Save beardordie/b04560c3ab5eefc39c5a99e9ea18fb77 to your computer and use it in GitHub Desktop.
Unity - simple 2D patrol component, upgraded from Unity Playground with gizmos and new functionality, follows Open/Closed principle, uses NaughtyAttributes for reorderable list of waypoints (remove line 4 and line 28 if unwanted)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using UnityEngine; | |
using NaughtyAttributes; | |
[RequireComponent(typeof(Rigidbody2D))] | |
public class Patrol2D : MonoBehaviour | |
{ | |
public enum FacingType | |
{ | |
DontChange, | |
Snap, | |
Smooth | |
} | |
public bool isPatrolling = true; | |
[Header("Movement")] | |
public float speed = 5f; | |
[Header("Orientation")] | |
public FacingType facingType = FacingType.DontChange; | |
public float smoothFaceSpeed = 7f; | |
public Directions lookAxis = Directions.Right; | |
[Header("Stops")] | |
[ReorderableList] | |
public List<Vector2> waypoints; | |
public bool waitAtEachStop = false; | |
public float waitTime = 1f; | |
protected float currentWaitTime = 0f; | |
protected bool isWaiting = false; | |
[Header("Preview")] | |
public bool previewWaypoints = true; | |
public float gizmoScale = 0.3f; | |
public static event Action<GameObject> OnReachedWaypoint = delegate { }; | |
protected new Rigidbody2D rigidbody2D; | |
protected Vector2[] newWaypoints; | |
protected int currentTargetIndex; | |
protected virtual void Awake() | |
{ | |
rigidbody2D = GetComponent<Rigidbody2D>(); | |
} | |
protected virtual void Start() | |
{ | |
if(isPatrolling) InitializePatrol(); | |
} | |
protected virtual void InitializePatrol() | |
{ | |
currentTargetIndex = 0; | |
newWaypoints = new Vector2[waypoints.Count + 1]; | |
int w = 0; | |
for (int i = 0; i < waypoints.Count; i++) | |
{ | |
newWaypoints[i] = waypoints[i]; | |
w = i; | |
} | |
//Add the starting position at the end, only if there is at least another point in the queue - otherwise it's on index 0 | |
int v = (newWaypoints.Length > 1) ? w + 1 : 0; | |
newWaypoints[v] = transform.position; | |
if (facingType== FacingType.Snap || facingType == FacingType.Smooth) | |
{ | |
SetAxisTowards(lookAxis, transform, ((Vector3)newWaypoints[1] - transform.position).normalized); | |
} | |
} | |
protected virtual void Update() | |
{ | |
if (isPatrolling && waitAtEachStop && currentWaitTime > 0f) | |
{ | |
currentWaitTime -= Time.deltaTime; | |
isWaiting = true; | |
return; | |
} | |
else | |
{ | |
isWaiting = false; | |
} | |
} | |
public virtual void FixedUpdate() | |
{ | |
if (!isPatrolling) return; | |
Vector2 currentTarget = newWaypoints[currentTargetIndex]; | |
Vector2 currentDirection; | |
// Snap Rotate | |
if (facingType == FacingType.Snap && facingType != FacingType.Smooth) | |
{ | |
currentDirection = UpdateDirection(currentTarget); | |
SetAxisTowards(lookAxis, transform, currentDirection); | |
} | |
// Smooth Rotate | |
if (facingType == FacingType.Smooth) | |
{ | |
currentDirection = UpdateDirection(currentTarget); | |
float angle = Mathf.Atan2(currentDirection.y, currentDirection.x) * Mathf.Rad2Deg; | |
transform.rotation = Quaternion.Lerp(transform.rotation, Quaternion.AngleAxis(angle, Vector3.forward), Time.deltaTime * smoothFaceSpeed); | |
} | |
if (isWaiting) return; | |
// Move toward waypoint | |
rigidbody2D.MovePosition(transform.position + ((Vector3)currentTarget - transform.position).normalized * speed * Time.fixedDeltaTime); | |
if (Vector2.Distance(transform.position, currentTarget) <= .05f) | |
{ | |
rigidbody2D.MovePosition((Vector3)currentTarget); | |
//new waypoint has been reached | |
OnReachedWaypoint.Invoke(gameObject); | |
if(waitAtEachStop) currentWaitTime = waitTime; | |
currentTargetIndex = (currentTargetIndex < newWaypoints.Length - 1) ? currentTargetIndex + 1 : 0; | |
} | |
} | |
protected virtual Vector2 UpdateDirection(Vector2 currentTarget) | |
{ | |
return ((Vector3)currentTarget - transform.position).normalized; | |
} | |
public virtual void Reset() | |
{ | |
waypoints.Clear(); | |
Vector2 thisPosition = transform.position; | |
waypoints[0] = new Vector2(2f, .5f) + thisPosition; | |
} | |
protected void SetAxisTowards(Directions axis, Transform t, Vector2 direction) | |
{ | |
switch (axis) | |
{ | |
case Directions.Up: | |
t.up = direction; | |
break; | |
case Directions.Down: | |
t.up = -direction; | |
break; | |
case Directions.Right: | |
t.right = direction; | |
break; | |
case Directions.Left: | |
t.right = -direction; | |
break; | |
} | |
} | |
public enum Directions | |
{ | |
Up, | |
Right, | |
Down, | |
Left, | |
} | |
protected virtual void OnDrawGizmosSelected() | |
{ | |
if (!previewWaypoints) return; | |
for (int i = 0; i < waypoints.Count; i++) | |
{ | |
Gizmos.color = Color.yellow; | |
Gizmos.DrawSphere(new Vector3(waypoints[i].x, waypoints[i].y, transform.position.z), gizmoScale); | |
Gizmos.color = Color.green; | |
if (i == 0) | |
{ | |
Gizmos.DrawLine(waypoints[i], transform.position); | |
} | |
else | |
{ | |
Gizmos.DrawLine(waypoints[i], waypoints[i-1]); | |
} | |
} | |
} | |
[ContextMenu("Start Patrolling")] | |
public virtual void StartPatrolling() | |
{ | |
isPatrolling = true; | |
InitializePatrol(); | |
} | |
[ContextMenu("Stop Patrolling")] | |
public virtual void StopPatrolling() | |
{ | |
isPatrolling = false; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment