Skip to content

Instantly share code, notes, and snippets.

@PappaBjorn
Last active February 4, 2020 12:33
Show Gist options
  • Save PappaBjorn/3ed04dcda225c8b6b457e6b61664bab5 to your computer and use it in GitHub Desktop.
Save PappaBjorn/3ed04dcda225c8b6b457e6b61664bab5 to your computer and use it in GitHub Desktop.
AI for Owlchemist
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Assertions;
public class AIPerception : MonoBehaviour
{
public float perceptionRadius = 1f;
[Range(0, 360f)] public float fovAngle = 100f;
public LayerMask perceptionMask;
public Material seenMaterial;
public Material unseenMaterial;
public List<Collider> previouslySeen = new List<Collider>();
public ChickenStateMachine chickenStateMachine;
private void Awake()
{
chickenStateMachine = GetComponent<ChickenStateMachine>();
Assert.IsNotNull(chickenStateMachine, "<b>AIPerception</b> failed to locate <b>ChickenStateMachine</b> on Enemy GameObject");
}
private void Update()
{
for (int i = 0; i < previouslySeen.Count; i++)
{
// material renderer for sphere above player. Debug reasons only!
previouslySeen[i].GetComponent<Renderer>().sharedMaterial = unseenMaterial;
chickenStateMachine.seeingPlayer = false;
}
previouslySeen.Clear();
previouslySeen.AddRange(Physics.OverlapSphere(transform.position, perceptionRadius, perceptionMask, QueryTriggerInteraction.Ignore));
for (int i = 0; i < previouslySeen.Count; i++)
{
Vector3 direction = previouslySeen[i].transform.position - transform.position;
float angle = Vector3.Angle(direction, transform.forward);
if (angle <= fovAngle * 0.5f)
{
// material renderer for sphere above player. Debug reasons only!
previouslySeen[i].GetComponent<Renderer>().sharedMaterial = seenMaterial;
chickenStateMachine.seeingPlayer = true;
}
}
}
#if UNITY_EDITOR
private void OnDrawGizmosSelected()
{
if (!Application.isPlaying)
{
Color oldColor = Gizmos.color;
float halfFOV = fovAngle * 0.5f;
float coneDir = -90f;
Quaternion leftRayRotation = Quaternion.AngleAxis(-halfFOV + coneDir, Vector3.up);
Quaternion rightRayRotation = Quaternion.AngleAxis(halfFOV + coneDir, Vector3.up);
Vector3 leftDir = leftRayRotation * transform.right * perceptionRadius;
Vector3 rightDir = rightRayRotation * transform.right * perceptionRadius;
UnityEditor.Handles.color = new Color(0f, 1f, 0f, 0.15f);
UnityEditor.Handles.DrawSolidArc(transform.position, Vector3.up, leftDir, fovAngle, perceptionRadius);
UnityEditor.Handles.color = new Color(1f, 0f, 0f, 0.15f);
UnityEditor.Handles.DrawSolidArc(transform.position, Vector3.up, rightDir, 360f - fovAngle, perceptionRadius);
Gizmos.color = oldColor;
}
}
#endif
}
using UnityEngine;
public class AIWaypointSystem : MonoBehaviour
{
public Vector3[] wayPoints;
}
using UnityEngine;
using UnityEngine.AI;
using UnityEngine.Assertions;
[RequireComponent(typeof(NavMeshAgent))]
public class ChickenAIComponent : MonoBehaviour
{
public AIWaypointSystem waypoints;
public ChickenStateMachine stateMachine;
private NavMeshAgent agent;
private AIPerception perceptionScript;
private GameObject player;
private LightComponent playerLightComponent;
private CombatComponent playerCombatComponent;
private GameObject[] lightBombs;
private GameManagerComponent gameManager;
private GameObject world;
private World worldSystem;
public float waitTime = 0f;
public float huntTime = 0f;
public float stunTime = 5f;
public float chargeTime = 1f;
public float stalkingSpeed = 3.5f;
public float chargeDistance = 3f;
public float attackDistance = 1f;
public float attackCooldown = 2f;
public float fleeTorchDistance = 5f;
public float chargeSpeed = 3.5f;
public float playerSwipeDistance = 4f;
private float nextPointDistance = 0.5f;
private float originalWaitTime;
private float originalHuntTime;
private float remainingStunTime = 0f;
[SerializeField]
private float startChargeTime = 0f;
private float originalWalkSpeed;
private float originalAttackCooldown;
private float originalChargeSpeed;
public int damage = 1;
private int currentWaypointIndex = 0;
#region Animation Events
public delegate void SetRunningDelegate();
public SetRunningDelegate OnSetRunning;
public delegate void SetWalkingDelegate();
public SetWalkingDelegate OnSetWalking;
public delegate void SetStalkingDelegate();
public SetStalkingDelegate OnSetStalking;
public delegate void TriggerLookAroundDelegate();
public TriggerLookAroundDelegate OnTriggerLook;
public delegate void TriggerStaggerDelegate();
public TriggerStaggerDelegate OnTriggerStagger;
public delegate void TriggerStompDelegate();
public TriggerStompDelegate OnTriggerStomp;
#endregion
private void Awake()
{
player = GameObject.FindGameObjectWithTag("Player");
Assert.IsNotNull(player, "Failed to locate GameObject with tag <b>Player</b>");
agent = GetComponent<NavMeshAgent>();
Assert.IsNotNull(agent, "Failed to locate <b>NavMeshAgent</b> on Enemy GameObject");
stateMachine = GetComponent<ChickenStateMachine>();
Assert.IsNotNull(stateMachine, "Failed to locate <b>ChickenStateMachine.cs</b> on Enemy GameObject");
perceptionScript = GetComponent<AIPerception>();
Assert.IsNotNull(perceptionScript, "Failed to locate <b>AIPerception.cs</b> on Enemy GameObject");
playerLightComponent = player.GetComponent<LightComponent>();
Assert.IsNotNull(playerLightComponent, "Failed to locate <b>LightComponent.cs</b> on Player GameObject");
playerCombatComponent = player.GetComponent<CombatComponent>();
Assert.IsNotNull(playerCombatComponent, "Failed to locate <b>CombatComponent.cs</b> on Player GameObject");
lightBombs = GameObject.FindGameObjectsWithTag("LightBomb");
Assert.IsNotNull(lightBombs, "Failed to locate <b>LightBombs</b> in array check in Enemy GameObject");
gameManager = player.GetComponent<GameManagerComponent>();
Assert.IsNotNull(gameManager, "Failed to locate <b>GameManagerComponent.cs</b> on Player GameObject");
world = GameObject.FindGameObjectWithTag("World");
Assert.IsNotNull(world, "Failed to locate <b>World</b> under Main scene in hierarchy");
worldSystem = world.GetComponent<World>();
Assert.IsNotNull(worldSystem, "Failed to locate <b>World.cs</b> on World GameObject");
originalWaitTime = waitTime;
originalHuntTime = huntTime;
originalAttackCooldown = attackCooldown;
originalWalkSpeed = agent.speed;
originalChargeSpeed = chargeSpeed;
}
void Start()
{
Assert.IsNotNull(waypoints, "Failed to locate <b>AIWayPointSystem</b> on Enemy GameObject");
agent.SetDestination(waypoints.wayPoints[currentWaypointIndex]);
Mathf.Clamp(huntTime, 0f, originalHuntTime);
}
void Update()
{
if (worldSystem.shouldTick)
{
agent.enabled = true;
float playerDistance = Vector3.Distance(transform.position, player.transform.position);
remainingStunTime -= Time.deltaTime;
if (startChargeTime >= chargeTime)
{
startChargeTime = 0f;
stateMachine.SetPreviousStateFalse();
}
if (remainingStunTime <= 0)
{
stateMachine.stunned = false;
}
if (!stateMachine.seeingPlayer)
{
attackCooldown -= Time.deltaTime;
huntTime -= Time.deltaTime;
if (huntTime <= 0f)
{
if (!agent.pathPending && !stateMachine.stunned && agent.remainingDistance <= nextPointDistance)
{
if (waitTime == originalWaitTime)
{
IdleOnSpot();
}
waitTime -= Time.deltaTime;
if (waitTime < 0)
{
GoToNextWaypoint();
stateMachine.SetPreviousStateFalse();
stateMachine.patrolling = true;
waitTime = originalWaitTime;
}
}
}
if (playerLightComponent.lightSource.enabled && !stateMachine.stunned && playerDistance <= fleeTorchDistance)
{
Retreating();
if (agent.remainingDistance <= 0.5)
{
waitTime = originalWaitTime;
}
}
for (int i = 0; i < lightBombs.Length; i++)
{
if (lightBombs[i].activeInHierarchy)
{
Retreating();
if (agent.remainingDistance <= 0.5)
{
waitTime = originalWaitTime;
}
}
}
}
if (stateMachine.seeingPlayer && !stateMachine.stunned)
{
attackCooldown -= Time.deltaTime;
agent.SetDestination(player.transform.position);
if (attackCooldown <= 0f)
{
if (agent.remainingDistance > chargeDistance)
{
StalkPlayer();
}
if (agent.remainingDistance <= chargeDistance && agent.remainingDistance > attackDistance && stateMachine.stalking && startChargeTime < chargeTime ||
agent.remainingDistance <= chargeDistance && agent.remainingDistance > attackDistance && stateMachine.attacking && startChargeTime < chargeTime)
{
ChargePlayer();
startChargeTime += Time.deltaTime;
}
if (agent.remainingDistance <= attackDistance && stateMachine.charging)
{
AttackPlayer();
}
}
if (playerLightComponent.lightSource.enabled && playerDistance <= fleeTorchDistance)
{
agent.speed = originalWalkSpeed;
Retreating();
if (agent.remainingDistance <= 0.5)
{
waitTime = originalWaitTime;
}
}
for (int i = 0; i < lightBombs.Length; i++)
{
if (lightBombs[i].activeInHierarchy)
{
Retreating();
if (agent.remainingDistance <= 0.5)
{
waitTime = originalWaitTime;
}
}
}
}
if (playerCombatComponent.isSwiping && playerSwipeDistance >= playerDistance)
{
Stunned();
}
}
if (!worldSystem.shouldTick)
{
agent.enabled = false;
}
}
public void GoToNextWaypoint()
{
OnSetWalking();
currentWaypointIndex = (currentWaypointIndex + 1) % waypoints.wayPoints.Length;
agent.SetDestination(waypoints.wayPoints[currentWaypointIndex]);
agent.speed = originalWalkSpeed;
}
public void ChargePlayer()
{
stateMachine.SetPreviousStateFalse();
stateMachine.charging = true;
agent.speed = chargeSpeed;
}
public void AttackPlayer()
{
OnTriggerStomp();
stateMachine.SetPreviousStateFalse();
stateMachine.attacking = true;
player.GetComponent<HealthComponent>().TakeGranularDamageOverTime(100f, 0.3f, true);
agent.speed = originalWalkSpeed;
attackCooldown = originalAttackCooldown;
}
public void StalkPlayer()
{
OnSetStalking();
stateMachine.SetPreviousStateFalse();
stateMachine.stalking = true;
huntTime = originalHuntTime;
agent.speed = stalkingSpeed;
}
public void IdleOnSpot()
{
OnTriggerLook();
stateMachine.SetPreviousStateFalse();
stateMachine.idle = true;
}
public void Retreating()
{
OnSetRunning();
stateMachine.SetPreviousStateFalse();
stateMachine.retreating = true;
Vector3 dirToPlayer = transform.position - player.transform.position;
Vector3 newPos = transform.position + dirToPlayer;
agent.SetDestination(newPos);
}
public void Stunned()
{
OnTriggerStagger();
stateMachine.SetPreviousStateFalse();
stateMachine.stunned = true;
remainingStunTime = stunTime;
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Assertions;
public class ChickenAnimations : MonoBehaviour
{
private Animator anim;
private ChickenAIComponent chickAI;
private void Awake()
{
anim = GetComponentInChildren<Animator>();
chickAI = GetComponent<ChickenAIComponent>();
chickAI.OnTriggerLook += TriggerLookAroundAnim;
chickAI.OnTriggerStagger += TriggerStaggerAnim;
chickAI.OnTriggerStomp += TriggerStompAnim;
chickAI.OnSetRunning += SetIsRunning;
chickAI.OnSetWalking += SetIsWalking;
chickAI.OnSetStalking += SetIsStalking;
Assert.IsNotNull(anim, "Animator is missing on Enemy");
}
private void TriggerLookAroundAnim()
{
anim.SetTrigger("LookAround");
}
private void TriggerStaggerAnim()
{
anim.SetTrigger("Stagger");
}
private void TriggerStompAnim()
{
anim.SetTrigger("Stomp");
}
private void SetIsRunning()
{
anim.SetBool("IsRunning", true);
anim.SetBool("IsWalking", false);
anim.SetBool("IsStalking", false);
}
private void SetIsWalking()
{
anim.SetBool("IsRunning", false);
anim.SetBool("IsWalking", true);
anim.SetBool("IsStalking", false);
}
private void SetIsStalking()
{
anim.SetBool("IsRunning", false);
anim.SetBool("IsWalking", false);
anim.SetBool("IsStalking", true);
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ChickenStateMachine : MonoBehaviour
{
public bool patrolling = false;
public bool stalking = false;
public bool retreating = false;
public bool idle = false;
public bool attacking = false;
public bool charging = false;
public bool seeingPlayer = false;
public bool stunned = false;
public void SetPreviousStateFalse()
{
patrolling = false;
stalking = false;
retreating = false;
idle = false;
attacking = false;
charging = false;
}
}
using UnityEngine;
using UnityEngine.AI;
[RequireComponent(typeof(NavMeshAgent))]
public class EnemyComponent : BaseComponent
{
[HideInInspector]
public NavMeshAgent agent;
public float moveSpeed = 1.5f;
public float aggroRange = 20f;
public bool isPursuingTransportable { get; set; }
public bool isActive { get; set; }
private void Awake()
{
agent = GetComponent<NavMeshAgent>();
agent.speed = moveSpeed;
isActive = true;
}
public void SetIsActive(bool value)
{
isActive = value;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment