Skip to content

Instantly share code, notes, and snippets.

@IneonInoodle
Created October 2, 2019 15:53
Show Gist options
  • Save IneonInoodle/81d5c9ead1d292869916f5bd8036d246 to your computer and use it in GitHub Desktop.
Save IneonInoodle/81d5c9ead1d292869916f5bd8036d246 to your computer and use it in GitHub Desktop.
Enemy AI handler for shaman saga
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