Skip to content

Instantly share code, notes, and snippets.

@beardordie
Created July 4, 2019 20:55
Show Gist options
  • Save beardordie/b04560c3ab5eefc39c5a99e9ea18fb77 to your computer and use it in GitHub Desktop.
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)
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