-
-
Save IneonInoodle/81d5c9ead1d292869916f5bd8036d246 to your computer and use it in GitHub Desktop.
Enemy AI handler for shaman saga
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.AI; | |
public enum EnemyType | |
{ | |
Meele, | |
Ranged, | |
Turret | |
} | |
public class AIHandler : MonoBehaviour | |
{ | |
public LayerMask layerMask; | |
public bool autoSetTarget = true; | |
public EnemyType enemyType; | |
public AIstate aiState; | |
public enum AIstate | |
{ | |
far, close, inSight, attacking, dodging | |
} | |
[SerializeField] | |
public float InSightSpeed = 2f; | |
public AIAttacks[] ai_attacks; | |
public Enemy enemy; | |
public Transform target; | |
public bool isAngry = false; | |
public float sight; | |
public float fov_angle; | |
public float patrolSpeed = 1f; | |
public float wanderRadius = 20f; | |
public int closeCount = 10; | |
int _close; | |
public int frameCount = 30; | |
int _frame; | |
public int attackCount = 30; | |
int _attack; | |
float dis; | |
float angle; | |
float anglePlayer; //used to check if player is looking at enemy | |
float delta; | |
public bool isBeingAimedAt = false; | |
Vector3 dirToTarget; | |
Vector3 dirFromTarget; | |
Vector3 patrolVector; | |
private GameObject currentRangedObject; | |
public Vector3 RandomWanderPoint() | |
{ | |
NavMeshHit navHit; | |
if (!NavMesh.SamplePosition(this.transform.position, out navHit, 4, -1)) | |
return transform.position; | |
if ((navHit.position - transform.position).magnitude > 0.01f) | |
return navHit.position; | |
while (true) | |
{ | |
bool bSuccess = true; | |
Vector3 lastPoint = transform.position; | |
Vector3 randomDirection = Random.insideUnitSphere; | |
float fStep = 0.5f; | |
for (float f = 0; f < wanderRadius; f += fStep) | |
{ | |
Vector3 stepPoint = transform.position + f * randomDirection; | |
if (!NavMesh.SamplePosition(stepPoint, out navHit, fStep / 2, -1)) | |
{ | |
bSuccess = false; | |
break; | |
} | |
lastPoint = navHit.position; | |
} | |
if (bSuccess) return lastPoint; | |
} | |
} | |
public float distanceFromTarget() | |
{ | |
if (target == null) | |
return 100; | |
return Vector3.Distance(target.position, transform.position); | |
} | |
public float angleToTarget() | |
{ | |
float a = 180; | |
if (target) | |
{ | |
Vector3 d = dirToTarget; | |
a = Vector3.Angle(d, transform.forward); | |
} | |
return a; | |
} | |
public float angleToPlayer() | |
{ | |
float a = 180; | |
if (target) | |
{ | |
Vector3 d = dirFromTarget; | |
a = Vector3.Angle(d, target.transform.forward); | |
} | |
return a; | |
} | |
public void isInSightofPlayer() | |
{ | |
} | |
public void shootInvoke() | |
{ | |
var bulletGo = Instantiate(currentRangedObject, enemy.shootpoint.position, enemy.shootpoint.transform.rotation); | |
} | |
public void Shoot(GameObject o, float shootDelay = 0.2f) | |
{ | |
currentRangedObject = o; | |
Invoke("shootInvoke", shootDelay); | |
} | |
private void Start() | |
{ | |
if (enemy == null) | |
{ | |
enemy = GetComponent<Enemy>(); | |
} | |
enemy.Init(); | |
} | |
public void SetTarget() | |
{ | |
if (autoSetTarget) | |
{ | |
Transform closestPlayer = null; | |
foreach (Player p in GameManager.Instance.players) | |
{ | |
if (closestPlayer == null) | |
closestPlayer = p.transform; | |
else if (Vector3.Distance(this.transform.position, p.transform.position) < Vector3.Distance(this.transform.position, closestPlayer.position)) | |
{ | |
closestPlayer = p.transform; | |
} | |
} | |
target = closestPlayer; | |
} | |
} | |
private void Update() | |
{ | |
SetTarget(); | |
delta = Time.deltaTime; | |
dis = distanceFromTarget(); | |
angle = angleToTarget(); | |
anglePlayer = angleToPlayer(); | |
if (target) | |
{ | |
dirToTarget = target.position - transform.position; | |
dirFromTarget = transform.position - target.position; | |
} | |
switch (aiState) | |
{ | |
case AIstate.far: | |
HandleFarSight(); | |
break; | |
case AIstate.close: | |
HandleCloseSight(); | |
break; | |
case AIstate.inSight: | |
InSight(); | |
break; | |
case AIstate.attacking: | |
if (enemy.canMove) | |
{ | |
aiState = AIstate.inSight; | |
//enemy.agent.enabled = true; | |
} | |
break; | |
case AIstate.dodging: | |
if (enemy.canMove) | |
{ | |
aiState = AIstate.inSight; | |
//enemy.agent.enabled = true; | |
} | |
break; | |
} | |
enemy.Tick(delta); | |
} | |
void HandleCloseSight() | |
{ | |
enemy.anim.SetBool("FlyForward", true); | |
_close++; | |
if (_close > closeCount) | |
{ | |
_close = 0; | |
if (dis > sight || angle > fov_angle) | |
{ | |
aiState = AIstate.far; | |
return; | |
} | |
} | |
if (patrolVector == Vector3.zero || Vector3.Distance(patrolVector, this.transform.position) < 1f) | |
{ //setting wander point | |
patrolVector = (RandomWanderPoint()); | |
GoToTargetPatrol(); | |
} | |
TurnToMoveDir(); | |
RaycastToTarget(); | |
} | |
void HandleCooldowns() | |
{ | |
for (int i = 0; i < ai_attacks.Length; i++) | |
{ | |
AIAttacks a = ai_attacks[i]; | |
if (a._cool > 0) | |
{ | |
a._cool -= delta; | |
if (a.cooldown < 0) | |
{ | |
a._cool = 0; | |
} | |
} | |
} | |
} | |
void InSight() | |
{ | |
LookTowardsTarget(); | |
HandleCooldowns(); | |
enemy.agent.speed = InSightSpeed; | |
float d2 = Vector3.Distance(enemy.targetDestination, target.position); | |
Vector3 origin = transform.position; | |
RaycastHit hit; | |
dirToTarget = target.position - transform.position; | |
if (Physics.Raycast(origin, dirToTarget, out hit, sight, layerMask)) | |
{ | |
PlayerStateManager st = hit.transform.GetComponentInParent<PlayerStateManager>(); | |
if (st == null) // we have player in sight | |
{ | |
GoToTarget(); | |
return; | |
} | |
} | |
if (d2 > 0.1 || dis > sight * .5) | |
{ | |
GoToTarget(); | |
} | |
if (dis < 0.1)// 2 should load from attack distance. but whatever | |
{ | |
enemy.agent.isStopped = true; | |
} | |
if (_attack > 0) | |
{ | |
_attack--; | |
return; | |
} | |
_attack = attackCount; | |
if (enemy.canMove) | |
{ | |
AIAttacks attack = WillAttack(); | |
if (attack != null) | |
{ | |
aiState = AIstate.attacking; | |
enemy.anim.Play(attack.targetAnim); | |
//Play Sound based on if dodging or not | |
if (attack.targetAnim.Contains("Dodge")) | |
{ | |
GetComponent<EnemySound>().ChangeSound(EnemySound.EnemyAudioState.dodge); | |
} | |
if (attack.targetAnim.Contains("Attack")) | |
{ GetComponent<EnemySound>().ChangeSound(EnemySound.EnemyAudioState.attack); } | |
if (attack.isRanged) | |
{ | |
if (attack.bullet != null)//extra check | |
{ | |
ProjectileMoveScript[] pms = attack.bullet.GetComponentsInChildren<ProjectileMoveScript>(); | |
foreach (ProjectileMoveScript p in pms) | |
{ | |
p.damage = attack.bulletDamage; | |
p.speed = attack.bulletSpeed; | |
p.lightMode = attack.lightMode; //how now brown cow. | |
} | |
Shoot(attack.bullet, attack.shootDelay); | |
} | |
} else | |
{ | |
foreach (GameObject g in enemy.w_hook.damageCollider) | |
{ | |
g.GetComponent<EnemyDamageCollider>().damage = attack.damage; | |
} | |
} | |
attack._cool = attack.cooldown; | |
if (attack.applyRootMotion) | |
{ | |
enemy.anim.applyRootMotion = true; | |
//enemy.agent.isStopped = f; | |
enemy.anim.SetBool("canMove", false); | |
enemy.canMove = false; | |
} | |
// enemy.agent.enabled = false; | |
return; | |
} | |
} | |
} | |
void GoToTarget() | |
{ | |
enemy.hasDestination = false; | |
enemy.SetDestination(target.position); | |
} | |
void GoToTargetPatrol() | |
{ | |
enemy.hasDestination = false; | |
enemy.agent.speed = patrolSpeed; | |
enemy.SetDestination(patrolVector); | |
// This makes us rotate to the direction we are walking toward (normal movement) | |
} | |
void TurnToMoveDir() | |
{ | |
Vector3 targetDir = enemy.agent.desiredVelocity; | |
targetDir.y = 0; | |
if (targetDir == Vector3.zero) | |
targetDir = transform.forward; | |
Quaternion tr = Quaternion.LookRotation(targetDir); | |
Quaternion targetRotation = Quaternion.Slerp(transform.rotation, tr, delta * 3); | |
transform.rotation = targetRotation; | |
} | |
public AIAttacks WillAttack() | |
{ | |
int w = 0; | |
List<AIAttacks> l = new List<AIAttacks>(); | |
for (int i = 0; i < ai_attacks.Length; i++) | |
{ | |
AIAttacks a = ai_attacks[i]; | |
if (a._cool > 0) | |
{ | |
continue; | |
} | |
/* | |
Debug.Log(dis + " " + a.minDistance); | |
Debug.Log(angle + " " + a.minAngle); | |
Debug.Log(angle + " " + a.maxAngle); | |
*/ | |
if (dis > a.minDistance) | |
continue; | |
if (dis < a.maxDistance) | |
continue; | |
if (angle < a.minAngle) | |
continue; | |
if (angle > a.maxAngle) | |
continue; | |
if (anglePlayer > a.maxAnglePlayer) | |
continue; | |
if (anglePlayer < a.minAnglePlayer) | |
continue; | |
if (a.requiredLightLevel != enemy.lightLevel) | |
continue; | |
if (a.weight == 0) | |
continue; | |
w += a.weight; | |
l.Add(a); | |
} | |
if (l.Count == 0) | |
return null; | |
int ran = Random.Range(0, w + 1); | |
int c_w = 0; | |
for (int i = 0; i < l.Count; i++) | |
{ | |
c_w += l[i].weight; | |
if (c_w > ran) | |
{ | |
return l[i]; | |
// attack | |
} | |
} | |
return null; | |
} | |
void LookTowardsTarget() | |
{ | |
Vector3 dir = dirToTarget; | |
dir.y = 0; | |
if (dir == Vector3.zero) | |
dir = transform.forward; | |
Quaternion targetRotation = Quaternion.LookRotation(dir); | |
transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, delta * 5); | |
} | |
void RaycastToTarget() | |
{ | |
RaycastHit hit; | |
Vector3 origin = transform.position; | |
//origin.y += 0.5f; | |
dirToTarget = target.position - transform.position; | |
//dirToTarget.y += 0.5f; | |
if (Physics.Raycast(origin, dirToTarget, out hit, sight, layerMask)) | |
{ | |
PlayerStateManager st = hit.transform.GetComponentInParent<PlayerStateManager>(); | |
if (st != null) // we have player in sight | |
{ | |
aiState = AIstate.inSight; | |
enemy.SetDestination(target.position); | |
enemy.anim.SetBool("FlyForward", true); | |
} | |
} | |
} | |
void HandleFarSight() | |
{ | |
//patrol | |
if (target == null) | |
return; | |
_frame++; | |
if (_frame > frameCount) | |
{ | |
_frame = 0; | |
if (dis < sight) | |
{ | |
if (angle < fov_angle) | |
{ | |
aiState = AIstate.close; | |
} | |
} | |
} | |
if (patrolVector == Vector3.zero || Vector3.Distance(patrolVector, this.transform.position) < 1f) | |
{ //setting wander point | |
patrolVector = (RandomWanderPoint()); | |
GoToTargetPatrol(); | |
} | |
TurnToMoveDir(); | |
} | |
} | |
[System.Serializable] | |
public class AIAttacks | |
{ | |
[Header("General Attack")] | |
public float damage; | |
public float requiredLightLevel; // 0 min, 1 middle, 2 max | |
public int weight; // 0-100, 100 means will always be played | |
public LightMode lightMode; | |
public float minDistance; | |
public float maxDistance = 100; | |
public float minAngle; | |
public float maxAngle; | |
public AudioSource[] audio; | |
public string targetAnim; | |
public float cooldown = 2; | |
public float _cool; | |
[Header("Dodges")] | |
public float minAnglePlayer = 0; // to init dodges // should be 0 for all the time | |
public float maxAnglePlayer = 180; // 180 for all the time | |
[Header("Ranged Attack")] | |
public bool isRanged = false; | |
public GameObject bullet; | |
public float bulletSpeed; | |
public float bulletDamage; | |
public float shootDelay = 0.2f; | |
public bool applyRootMotion = true; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment