-
-
Save PappaBjorn/3ed04dcda225c8b6b457e6b61664bab5 to your computer and use it in GitHub Desktop.
AI for Owlchemist
This file contains 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.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 | |
} |
This file contains 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 UnityEngine; | |
public class AIWaypointSystem : MonoBehaviour | |
{ | |
public Vector3[] wayPoints; | |
} |
This file contains 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 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; | |
} | |
} |
This file contains 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.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); | |
} | |
} |
This file contains 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.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; | |
} | |
} |
This file contains 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 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