This repository has been archived on 2023-09-13. You can view files and clone it, but cannot push or open issues or pull requests.
station_obscurum_unity/Assets/Scripts/Enemies/AI/SkinlessMonsterComponent.cs

397 lines
12 KiB
C#
Raw Permalink Normal View History

2023-04-18 06:29:51 +02:00
using UnityEngine;
using UnityEngine.AI;
public class SkinlessMonsterComponent : MonoBehaviour
{
2023-06-01 17:03:48 +02:00
[SerializeField] private NavMeshAgent agent;
2023-04-18 06:29:51 +02:00
2023-06-01 20:25:46 +02:00
[SerializeField] private Enemy.SkinlessMonsterAnimator animator;
2023-04-21 09:30:43 +02:00
2023-06-01 17:03:48 +02:00
[SerializeField] private float atTargetDistance = 2;
2023-04-18 06:29:51 +02:00
2023-06-01 17:03:48 +02:00
[SerializeField] private float health = 2f;
2023-04-21 09:30:43 +02:00
2023-06-01 17:03:48 +02:00
[SerializeField] [Tooltip("This is the angle of visibility the enemy has")]
private float visibilityConeLimit = 90f;
2023-04-21 09:30:43 +02:00
2023-06-01 17:03:48 +02:00
[SerializeField] private float bulletSoundRange = 15f;
2023-04-21 09:30:43 +02:00
2023-06-01 17:03:48 +02:00
[SerializeField] private float newTargetCooldown = 5f;
//private bool prePauseStoppedState = false;
2023-04-21 09:30:43 +02:00
2023-06-01 17:03:48 +02:00
[SerializeField] private BoxCollider leftHandDamage;
[SerializeField] private BoxCollider rightHandDamage;
private bool atTarget;
2023-04-21 09:30:43 +02:00
private float distanceToPlayer;
2023-06-02 06:30:58 +02:00
private Item.FlareRegister flareRegister;
2023-06-01 17:03:48 +02:00
private bool inDamageMargin;
private bool inDamageRange;
2023-04-21 09:30:43 +02:00
2023-06-01 17:03:48 +02:00
private bool isAlive = true;
2023-04-21 09:30:43 +02:00
2023-06-02 06:30:58 +02:00
2023-04-21 09:30:43 +02:00
2023-06-01 17:03:48 +02:00
private Vector3 oppositeVector;
2023-06-01 20:25:46 +02:00
private Player.PlayerComponent player;
2023-06-01 17:03:48 +02:00
private TargetInformation target;
private GameObject targetObject;
private float timeSinceTarget;
2023-04-21 09:30:43 +02:00
private void Awake()
2023-04-18 06:29:51 +02:00
{
2023-04-21 09:30:43 +02:00
//Find active player rn.
2023-06-01 20:25:46 +02:00
var players = FindObjectsOfType<Player.PlayerComponent>();
2023-06-01 17:03:48 +02:00
foreach (var p in players)
2023-04-21 09:30:43 +02:00
if (p.isActiveAndEnabled)
player = p;
2023-06-01 17:03:48 +02:00
2023-06-02 06:30:58 +02:00
2023-04-21 09:30:43 +02:00
}
private void Start()
{
2023-06-02 06:30:58 +02:00
flareRegister = FindObjectOfType<Item.FlareRegister>();
2023-04-21 09:30:43 +02:00
if (targetObject == null)
targetObject = new GameObject();
targetObject.name = "Enemy Target";
2023-06-01 17:03:48 +02:00
2023-06-01 20:25:46 +02:00
if (player == null) player = FindObjectOfType<Player.PlayerComponent>();
2023-06-01 17:03:48 +02:00
}
private void Update()
{
if (!player.IsAlive) Stop();
if (target != null) HandleTargetOperations();
CheckForAOERanges();
if (isAlive && player.IsAlive)
SetLiveTargeting();
timeSinceTarget += Time.deltaTime;
if (isAlive)
{
leftHandDamage.enabled = true;
rightHandDamage.enabled = true;
}
else
{
2023-06-01 17:03:48 +02:00
leftHandDamage.enabled = false;
rightHandDamage.enabled = false;
}
2023-06-01 17:03:48 +02:00
/*
AI Behavior:
A. If not targeting player
1. Get distance to player
- if distance < player.NoiseDistance then target player.
2. Raycast to player to test line of sight
- if in line of sight and angle between LOS vector and forward vector is < LOF margin, then
target player.
B. If targeting player
1. If out of sound range and no line of sight then lose target.
- stop()
2. If beacon placed down and in range
- remove agent
- kill()
3. If beacon placed down and in margin:
- stop() and wait for player to leave AOE (enforced by navigation).
*/
2023-04-21 09:30:43 +02:00
}
2023-06-01 17:03:48 +02:00
private void OnTriggerEnter(Collider other)
{
2023-06-02 06:30:58 +02:00
if (other.gameObject.GetComponent<Item.BulletComponent>() != null)
health -= other.gameObject.GetComponent<Item.BulletComponent>().DamageMagnitude;
2023-06-01 17:03:48 +02:00
}
private void HandleTargetOperations()
2023-04-21 09:30:43 +02:00
{
//Update booleans for movement
2023-06-01 17:03:48 +02:00
var distToTarget = Vector3.Distance(target.target.transform.position, agent.transform.position);
2023-04-21 09:30:43 +02:00
atTarget = atTargetDistance >= distToTarget;
if (target.hasDamageRange)
{
inDamageRange = target.damageRange <= distToTarget;
inDamageMargin = target.damageMargin + target.damageRange <= distToTarget;
}
else
2023-04-18 06:29:51 +02:00
{
2023-04-21 09:30:43 +02:00
inDamageRange = false;
inDamageMargin = false;
}
2023-06-01 17:03:48 +02:00
2023-04-21 09:30:43 +02:00
//Perform actions.
if (target.runTowards)
{
if (inDamageRange)
2023-04-18 06:29:51 +02:00
{
2023-04-21 09:30:43 +02:00
//Damage is being dealt
animator.InLight();
//Deal Damage
health -= Time.deltaTime;
2023-06-01 17:03:48 +02:00
if (health < 0 && isAlive) Kill();
2023-04-21 09:30:43 +02:00
}
else if (inDamageMargin)
{
//Effective stop
agent.SetDestination(transform.position);
animator.StopMoving();
2023-04-18 06:29:51 +02:00
}
else
{
2023-04-21 09:30:43 +02:00
if (atTarget)
{
//Effective stop
agent.SetDestination(transform.position);
animator.StopMoving();
if (target.isHostile)
{
animator.Attack();
animator.SetAttackType(Random.Range(0, 0));
}
}
else
{
agent.SetDestination(target.target.transform.position);
animator.StartMoving();
animator.NotInLight();
}
}
}
else
{
//Run away logic
if (atTarget || distToTarget < 3)
{
animator.StopMoving();
2023-04-18 06:29:51 +02:00
}
2023-04-21 09:30:43 +02:00
else
{
2023-06-01 17:03:48 +02:00
var r = new Ray();
2023-04-21 09:30:43 +02:00
r.origin = transform.position;
r.direction = oppositeVector;
RaycastHit hit;
if (Physics.Raycast(r, out hit))
{
animator.StartMoving();
agent.SetDestination(hit.point);
}
else
{
agent.SetDestination(transform.position + oppositeVector * 100);
agent.isStopped = false;
}
}
2023-04-18 06:29:51 +02:00
}
2023-04-21 09:30:43 +02:00
2023-06-01 17:03:48 +02:00
if (atTarget && target.isHostile) SlowLookAt(target.target.transform.position - transform.position);
2023-04-21 09:30:43 +02:00
}
2023-06-01 17:03:48 +02:00
private void SlowLookAt(Vector3 targetVector, float animatedRotationSpeed = 1f)
2023-04-21 09:30:43 +02:00
{
2023-06-01 17:03:48 +02:00
var relativePos = targetVector;
var toRotation = Quaternion.LookRotation(relativePos);
2023-04-21 09:30:43 +02:00
transform.rotation = Quaternion.Lerp(transform.rotation, toRotation, animatedRotationSpeed * Time.deltaTime);
}
2023-06-01 17:03:48 +02:00
private void CheckForAOERanges()
2023-04-21 09:30:43 +02:00
{
2023-06-01 17:03:48 +02:00
foreach (var bullet in flareRegister.bullets)
2023-04-21 09:30:43 +02:00
{
2023-06-01 17:03:48 +02:00
var dist = Vector3.Distance(bullet.transform.position, transform.position);
2023-04-21 09:30:43 +02:00
if (dist <= bullet.DamageRange)
{
2023-06-01 17:03:48 +02:00
health = 0;
Kill(true);
2023-04-21 09:30:43 +02:00
}
}
2023-06-01 17:03:48 +02:00
foreach (var beacon in flareRegister.beacons)
2023-04-21 09:30:43 +02:00
{
2023-06-01 17:03:48 +02:00
var dist = Vector3.Distance(beacon.transform.position, transform.position);
if (dist <= beacon.Range) health = 0f;
2023-04-21 09:30:43 +02:00
}
2023-06-01 17:03:48 +02:00
if (health <= 0) Kill();
2023-04-21 09:30:43 +02:00
}
public bool isPlayerVisible(bool withAngle)
{
2023-06-01 17:03:48 +02:00
var r = new Ray();
2023-04-21 09:30:43 +02:00
r.origin = transform.position;
r.direction = player.transform.position - transform.position;
2023-06-01 17:03:48 +02:00
var toPosition = (player.transform.position - transform.position).normalized;
var angleToPosition = Vector3.Angle(transform.forward, toPosition);
2023-04-21 09:30:43 +02:00
RaycastHit hit;
2023-06-01 17:03:48 +02:00
if (Physics.Raycast(r, out hit))
2023-04-21 09:30:43 +02:00
{
2023-06-01 17:03:48 +02:00
var hitObject = hit.transform.gameObject;
2023-06-01 20:25:46 +02:00
if (hitObject.GetComponent<Player.PlayerComponent>() != null)
2023-04-21 09:30:43 +02:00
//hit player
2023-06-01 17:03:48 +02:00
return angleToPosition <= visibilityConeLimit || !withAngle;
2023-06-01 20:25:46 +02:00
if (hitObject.GetComponentInParent<Player.PlayerComponent>() != null)
2023-04-21 09:30:43 +02:00
//also hit player
2023-06-01 17:03:48 +02:00
return angleToPosition <= visibilityConeLimit || !withAngle;
2023-04-21 09:30:43 +02:00
}
2023-06-01 17:03:48 +02:00
2023-04-21 09:30:43 +02:00
return false;
}
2023-06-01 17:03:48 +02:00
private void SetLiveTargeting()
2023-04-21 09:30:43 +02:00
{
if (!isAlive)
//this.targetObject.transform.position = transform.position;
2023-06-01 17:03:48 +02:00
Stop();
2023-04-21 09:30:43 +02:00
2023-06-01 17:03:48 +02:00
distanceToPlayer = Vector3.Distance(transform.position, player.transform.position);
2023-04-21 09:30:43 +02:00
//print("Dist Comparison "+distanceToPlayer.ToString()+" Noise:"+player.NoiseManager.NoiseDistance);
2023-06-01 17:03:48 +02:00
var isPlayerVisible = this.isPlayerVisible(true);
2023-04-21 09:30:43 +02:00
//check if player is heard or player line of sight
2023-06-01 17:03:48 +02:00
if (distanceToPlayer <= player.NoiseManager.NoiseDistance || isPlayerVisible)
2023-04-21 09:30:43 +02:00
{
//check that nothing in between
2023-06-01 17:03:48 +02:00
if (this.isPlayerVisible(false)) Target(player.gameObject, true);
2023-04-21 09:30:43 +02:00
}
else
{
2023-06-01 17:03:48 +02:00
if (timeSinceTarget < newTargetCooldown)
2023-04-21 09:30:43 +02:00
//no further targeting
//Stop();
return;
2023-06-01 17:03:48 +02:00
2023-06-02 06:30:58 +02:00
Item.BulletComponent closestBullet = null;
2023-06-01 17:03:48 +02:00
var closestDistance = Mathf.Infinity;
foreach (var bullet in flareRegister.bullets)
2023-04-21 09:30:43 +02:00
{
2023-06-01 17:03:48 +02:00
var bDist = Vector3.Distance(bullet.transform.position, transform.position);
if (closestBullet == null || (bDist < bulletSoundRange && bDist < closestDistance))
2023-04-21 09:30:43 +02:00
{
closestBullet = bullet;
closestDistance = bDist;
}
}
2023-06-01 17:03:48 +02:00
if (closestBullet != null && closestBullet.DamageRange == 0)
2023-04-21 09:30:43 +02:00
{
2023-06-01 17:03:48 +02:00
targetObject.transform.position = closestBullet.transform.position;
Target(targetObject);
2023-04-21 09:30:43 +02:00
}
else
{
targetObject.transform.position = transform.position;
2023-06-01 17:03:48 +02:00
Target(targetObject);
Stop();
2023-04-21 09:30:43 +02:00
}
}
}
2023-04-18 06:29:51 +02:00
/*
STANDARD BEHAVIOR:
- OnSeeing/Hearing Player:
- Run towards Player
- OnHearing Bolt:
- Run towards bolt
- OnSeeing Flare:
- Run towards flare if not within (flareRange + flareMargin).
- This acts like if you are far enough away that you don't feel the pain of the flare
- OnSeeing FlareBeacon:
- Run away if within (flareBeaconRange + flareBeaconMargin).
*/
//Runs towards target. If its meant to be hostile, it will melee attack once in range.
2023-06-01 17:03:48 +02:00
public void Target(GameObject obj, bool hostile = false)
2023-04-18 06:29:51 +02:00
{
2023-06-01 17:03:48 +02:00
if (target == null || target.target != obj)
2023-04-18 06:29:51 +02:00
{
//target = new TargetInformation(obj,hostile,false,runTowards:true);
2023-04-21 09:30:43 +02:00
target = new TargetInformation();
target.target = obj;
target.isHostile = hostile;
target.hasDamageRange = false;
target.runTowards = true;
timeSinceTarget = 0;
2023-04-18 06:29:51 +02:00
}
}
2023-06-01 17:03:48 +02:00
2023-04-18 06:29:51 +02:00
//Runs towards flare such that it stops randomly within margin before range.
2023-06-01 17:03:48 +02:00
public void TargetFlare(GameObject obj, float range, float margin)
2023-04-18 06:29:51 +02:00
{
2023-06-01 17:03:48 +02:00
if (target == null || target.target != obj)
2023-04-18 06:29:51 +02:00
{
//target = new TargetInformation(obj, false, true, range, margin,runTowards:true);
2023-04-21 09:30:43 +02:00
target = new TargetInformation();
target.target = obj;
target.isHostile = false;
target.hasDamageRange = true;
target.damageRange = range;
target.damageMargin = margin;
target.runTowards = true;
timeSinceTarget = 0;
2023-04-18 06:29:51 +02:00
}
}
2023-06-01 17:03:48 +02:00
2023-04-18 06:29:51 +02:00
/// <summary>
2023-06-01 17:03:48 +02:00
/// Runs away from object. A minimum range is specified where it no longer takes damage, and a minimum margin is
/// speicifed where it can stop running.
2023-04-18 06:29:51 +02:00
/// </summary>
/// <param name="obj"></param>
/// <param name="minRange"></param>
/// <param name="minMargin"></param>
2023-06-01 17:03:48 +02:00
public void RunAwayFrom(GameObject obj, float minRange, float minMargin)
{
if (target == null || target.target != obj)
2023-04-18 06:29:51 +02:00
{
//target = new TargetInformation(obj, false, true, minRange, minMargin, false);
2023-04-21 09:30:43 +02:00
target = new TargetInformation();
target.target = obj;
target.isHostile = false;
target.hasDamageRange = true;
target.damageRange = minRange;
target.damageMargin = minMargin;
target.runTowards = false;
oppositeVector = -(target.target.transform.position - transform.position).normalized;
timeSinceTarget = 0;
2023-04-18 06:29:51 +02:00
}
}
2023-04-21 09:30:43 +02:00
public void Stop()
{
target = new TargetInformation();
2023-06-01 17:03:48 +02:00
target.target = gameObject;
2023-04-21 09:30:43 +02:00
target.isHostile = false;
target.hasDamageRange = false;
target.runTowards = true;
}
2023-06-01 17:03:48 +02:00
public void Kill(bool fast = false)
2023-04-21 09:30:43 +02:00
{
2023-06-01 17:03:48 +02:00
if (isAlive)
2023-04-21 09:30:43 +02:00
{
2023-06-01 17:03:48 +02:00
animator.Kill(fast);
isAlive = false;
2023-04-21 09:30:43 +02:00
agent.isStopped = true;
}
}
2023-04-18 06:29:51 +02:00
}
2023-06-01 17:03:48 +02:00
internal class TargetInformation
{
2023-04-18 06:29:51 +02:00
public float damageMargin;
2023-06-01 17:03:48 +02:00
public float damageRange;
public bool hasDamageRange;
public bool isHostile;
2023-04-18 06:29:51 +02:00
public bool runTowards;
2023-06-01 17:03:48 +02:00
public GameObject target;
2023-04-18 06:29:51 +02:00
}