Gameplay.EnemyPatrol - robblofield/TomeboundDocs GitHub Wiki
Gameplay.EnemyPatrol
Author: Shahrzad Beevis
Last Updated: 04/12/2023
Overview
A script that controls a patrolling enemy, that moves between two points and quickly moves to block the player when they are within a certain radius of the enemy. It also changes it's appearance when it is in blocking mode - and can be frozen for a specified number of seconds when another game object (e.g. water) is dropped on to it.
Dependencies
N/A
Contents
- Use case
- Breakdown of code
- Using Statements
- Class Declaration
- Variables
- Method/Start()
- Method/Update()
- Method/SetObjectStates()
- Method/Patrol()
- Method/BlockPlayer()
- Method/ToggleObjectStates()
- Method/OnCollisionEnter(Collision collision)
- Method/FreezeEnemy(GameObject waterObject)
- Method/CheckFrozenDuration()
- Future Expansion
- Full Code Reference
Use case
This script can be employed to create a challenging enemy that blocks the path of a player when within a set radius.
Breakdown of Code
Using Statements
using UnityEngine;
Class Declaration
public class EnemyPatrol : MonoBehaviour
Variables
public Transform startPoint;
public Transform endPoint;
public float patrolSpeed = 2f;
public float blockingSpeed = 60f;
public GameObject detectionRadiusObject;
public float detectionRadiusX = 2f;
public float detectionRadiusY = 1f;
public GameObject blockingObject;
public GameObject patrollingObject;
private Transform target;
private bool isPatrolling = true;
private bool blockingObjectActiveState = false;
public float freezeDuration = 5f;
private bool isFrozen = false;
private float timeFrozen;
public GameManager gameManager;
public Transform startPoint:
Represents the start point of the enemy's patrol path.
public Transform endPoint:
Represents the end point of the enemy's patrol path.
public float patrolSpeed = 2f:
Defines the speed of the enemy during it's patrolling state.
public float blockingSpeed = 60f:
Defines the speed of the enemy during it's patrolling state.
public GameObject detectionRadiusObject:
This variable represents the object used to calculate the detection radius for player detection. The radius itself is determined by the next two variables.
public float detectionRadiusX = 2f:
Defines the detection radius on the X-axis.
public float detectionRadiusY = 1f:
Defines the detection radius on the Y-axis.
public GameObject blockingObject:
This variable represents the GameObject assigned to the enemy's blocking state.
public GameObject patrollingObject:
This variable represents the GameObject assigned to the enemy's patrolling state.
private Transform target:
This variable stores the target destination for the enemy.
private bool isPatrolling = true:
This variable is used as a flag to determine whether the enemy is currently patrolling and is set to true as default.
private bool blockingObjectActiveState = false:
Variable that indicates whether the blocking object is currently active - this is used to avoid unnecessary toggling of objects.
public float freezeDuration = 5f:
Defines the duration for which the enemy remains frozen when in contact with 'water'.
private bool isFrozen = false:
Variable indicating whether the enemy is currently frozen.
private float timeFrozen:
Variable noting the time at which the enemy was frozen.
public GameManager gameManager:
Variable referencing the GameManager script
Start()
private void Start()
{
target = endPoint;
SetObjectStates();
}
This method sets the target variable to the endPoint & calls the SetObjectStates() method to set the initial object states.
Update()
private void Update()
{
if (isFrozen)
{
CheckFrozenDuration();
}
else if (isPatrolling)
{
Patrol();
}
else
{
BlockPlayer();
}
}
SetObjectStates()
public void SetObjectStates()
{
blockingObject.SetActive(false);
patrollingObject.SetActive(true);
}
Sets the initial state of the blocking and patrolling objects. It deactivates the blocking object (blockingObject) & activates the patrolling object (patrollingObject).
Patrol()
private void Patrol()
{
float pingPongValue = Mathf.PingPong(Time.time * patrolSpeed, 1f);
Vector3 newPosition = new Vector3(Mathf.Lerp(startPoint.position.x, endPoint.position.x, pingPongValue), transform.position.y, transform.position.z);
transform.position = newPosition;
Collider[] colliders = Physics.OverlapBox(detectionRadiusObject.transform.position, new Vector3(detectionRadiusX, detectionRadiusY, 1f));
foreach (Collider collider in colliders)
{
if (collider.CompareTag("Player"))
{
isPatrolling = false;
if (!blockingObjectActiveState)
{
ToggleObjectStates();
blockingObjectActiveState = true;
}
return;
}
}
}
This method handles the logic for enemy patrolling behaviour. It calculates a position using a ping-pong value based on time, creating a back-and-forth movement between startPoint and endPoint. It updates the enemy's position based on the calculated position. It also checks for players within the detection radius and switches to blocking mode if a player is detected.
BlockPlayer()
private void BlockPlayer()
{
Vector3 playerPosition = gameManager.controllingObject.transform.position;
Vector3 newPosition = new Vector3(playerPosition.x, transform.position.y, transform.position.z);
transform.position = Vector3.MoveTowards(transform.position, newPosition, blockingSpeed * Time.deltaTime);
float distanceToPlayer = Vector3.Distance(transform.position, playerPosition);
if (distanceToPlayer > Mathf.Max(detectionRadiusX, detectionRadiusY))
{
isPatrolling = true;
if (blockingObjectActiveState)
{
ToggleObjectStates();
blockingObjectActiveState = false;
}
}
}
This method handles the logic for blocking the player. It works to move the enemy to be in line with the player on the x-axis, it calls the variable controllingObject from the GameManager script and uses this as reference for the player position. It also checks if the player is still within the detection radius; if not, it resumes patrolling.
ToggleObjectStates()
public void ToggleObjectStates()
{
blockingObject.SetActive(!isPatrolling);
patrollingObject.SetActive(isPatrolling);
}
Toggles the active state of blocking and patrolling objects based on the patrolling state. The blocking object is activated if patrolling is false, and deactivates it if patrolling is true. It also activates the patrolling object if patrolling is true, and deactivates it if patrolling is false.
OnCollisionEnter(Collision collision)
private void OnCollisionEnter(Collision collision)
{
if (collision.gameObject.CompareTag("Water"))
{
FreezeEnemy(collision.gameObject);
}
}
This method handles the logic for when the enemy collides with an object. In this case if the collision is with an object tagged as "Water," it calls the FreezeEnemy() method.
FreezeEnemy(GameObject waterObject)
private void FreezeEnemy(GameObject waterObject)
{
if (!isFrozen)
{
isFrozen = true;
timeFrozen = Time.time;
isPatrolling = false;
ToggleObjectStates();
blockingObjectActiveState = true;
transform.position = transform.position;
Debug.Log("Enemy is frozen!");
waterObject.SetActive(false);
}
}
Freezes the enemy when in contact with water. It checks if the enemy is not already frozen before proceeding - then freezes the enemy by deactivating patrolling and blocking, stopping the enemy's movement, and logging a message. The water object is deactivated at the end of this interaction.
CheckFrozenDuration()
private void CheckFrozenDuration()
{
if (Time.time - timeFrozen >= freezeDuration)
{
Vector3 playerPosition = gameManager.controllingObject.transform.position;
float distanceToPlayer = Vector3.Distance(transform.position, playerPosition);
if (distanceToPlayer <= Mathf.Max(detectionRadiusX, detectionRadiusY))
{
isPatrolling = false;
ToggleObjectStates();
blockingObjectActiveState = true;
}
else
{
isPatrolling = true;
ToggleObjectStates();
blockingObjectActiveState = false;
}
isFrozen = false;
Debug.Log("Enemy is no longer frozen!");
}
}
This method checks the duration of the frozen state and resumes patrolling or blocking accordingly. It first checks if the frozen duration has passed and decides whether to resume blocking or patrolling based on the player's position. Following this it updates the patrolling state and toggles object states accordingly. A message is logged to the console indicating that the enemy is no longer frozen.
Future Expansion
N/A
Full Code Reference
using UnityEngine;
public class EnemyPatrol : MonoBehaviour
{
public Transform startPoint;
public Transform endPoint;
public float patrolSpeed = 2f;
public float blockingSpeed = 60f;
public GameObject detectionRadiusObject;
public float detectionRadiusX = 2f;
public float detectionRadiusY = 1f;
public GameObject blockingObject;
public GameObject patrollingObject;
private Transform target;
private bool isPatrolling = true;
private bool blockingObjectActiveState = false;
public float freezeDuration = 5f;
private bool isFrozen = false;
private float timeFrozen;
public GameManager gameManager;
private void Start()
{
target = endPoint;
SetObjectStates();
}
private void Update()
{
if (isFrozen)
{
CheckFrozenDuration();
}
else if (isPatrolling)
{
Patrol();
}
else
{
BlockPlayer();
}
}
public void SetObjectStates()
{
blockingObject.SetActive(false);
patrollingObject.SetActive(true);
}
private void Patrol()
{
float pingPongValue = Mathf.PingPong(Time.time * patrolSpeed, 1f);
Vector3 newPosition = new Vector3(Mathf.Lerp(startPoint.position.x, endPoint.position.x, pingPongValue), transform.position.y, transform.position.z);
transform.position = newPosition;
Collider[] colliders = Physics.OverlapBox(detectionRadiusObject.transform.position, new Vector3(detectionRadiusX, detectionRadiusY, 1f));
foreach (Collider collider in colliders)
{
if (collider.CompareTag("Player2"))
{
isPatrolling = false;
if (!blockingObjectActiveState)
{
ToggleObjectStates();
blockingObjectActiveState = true;
}
return;
}
}
}
private void BlockPlayer()
{
Vector3 playerPosition = gameManager.controllingObject.transform.position;
Vector3 newPosition = new Vector3(playerPosition.x, transform.position.y, transform.position.z);
transform.position = Vector3.MoveTowards(transform.position, newPosition, blockingSpeed * Time.deltaTime);
float distanceToPlayer = Vector3.Distance(transform.position, playerPosition);
if (distanceToPlayer > Mathf.Max(detectionRadiusX, detectionRadiusY))
{
isPatrolling = true;
if (blockingObjectActiveState)
{
ToggleObjectStates();
blockingObjectActiveState = false;
}
}
}
public void ToggleObjectStates()
{
blockingObject.SetActive(!isPatrolling);
patrollingObject.SetActive(isPatrolling);
}
private void OnCollisionEnter(Collision collision)
{
if (collision.gameObject.CompareTag("Water"))
{
FreezeEnemy(collision.gameObject);
}
}
private void FreezeEnemy(GameObject waterObject)
{
if (!isFrozen)
{
isFrozen = true;
timeFrozen = Time.time;
isPatrolling = false;
ToggleObjectStates();
blockingObjectActiveState = true;
transform.position = transform.position;
Debug.Log("Enemy is frozen!");
waterObject.SetActive(false);
}
}
private void CheckFrozenDuration()
{
if (Time.time - timeFrozen >= freezeDuration)
{
Vector3 playerPosition = gameManager.controllingObject.transform.position;
float distanceToPlayer = Vector3.Distance(transform.position, playerPosition);
if (distanceToPlayer <= Mathf.Max(detectionRadiusX, detectionRadiusY))
{
isPatrolling = false;
ToggleObjectStates();
blockingObjectActiveState = true;
}
else
{
isPatrolling = true;
ToggleObjectStates();
blockingObjectActiveState = false;
}
isFrozen = false;
Debug.Log("Enemy is no longer frozen!");
}
}
}