initial work on enemy behavior
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
This commit is contained in:
parent
ad725ee518
commit
db7559cd16
@ -1,11 +1,187 @@
|
||||
using RuntimeSet;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Assertions;
|
||||
|
||||
namespace DefaultNamespace
|
||||
{
|
||||
// TODO (Michael): Empty behavior until we decide more on how enemies should be structured. Mainly being used for
|
||||
// other systems to have something to reference.
|
||||
public class Enemy : MonoBehaviour
|
||||
{
|
||||
|
||||
private enum BehaviorState
|
||||
{
|
||||
Create,
|
||||
Spawning,
|
||||
Chasing,
|
||||
Attacking,
|
||||
Feared,
|
||||
Dead,
|
||||
}
|
||||
|
||||
[SerializeField] private EnemyAttributesSO attributes;
|
||||
[SerializeField] private EnemyRuntimeSetSO enemySet;
|
||||
[SerializeField] private HeroUnitRuntimeSetSO heroSet;
|
||||
[SerializeField] private GameObject target;
|
||||
// TODO(zeph): Having behavior as a serialized member is convenient for
|
||||
// experimentation early on so we can change it mid-play through the
|
||||
// inspector, but later we'll more likely want it to be a property.
|
||||
[SerializeField] private BehaviorState _behavior = BehaviorState.Spawning;
|
||||
[SerializeField] private float _behaviorTime = 0;
|
||||
[SerializeField] private float _fear = 0;
|
||||
[SerializeField] private long _hp;
|
||||
|
||||
private void Update()
|
||||
{
|
||||
// When the current behavior indicates movement, we do it in Update
|
||||
// to ensure it is smooth at all framerates.
|
||||
if (_behavior != BehaviorState.Chasing && _behavior != BehaviorState.Feared)
|
||||
{
|
||||
// No movement.
|
||||
return;
|
||||
}
|
||||
if (target == null)
|
||||
{
|
||||
// No target. Find a new one.
|
||||
Retarget();
|
||||
return;
|
||||
}
|
||||
// TODO(zeph): how do we actually,, get SPD attribute
|
||||
var spd = SpaceSpeed(1000, Time.deltaTime);
|
||||
var to = Vector3.MoveTowards(transform.position, target.transform.position, spd);
|
||||
if (_behavior == BehaviorState.Feared)
|
||||
{
|
||||
// Move away rather than toward.
|
||||
// TODO(zeph): this will limit speed moving away by distance
|
||||
// to target if we're close
|
||||
to = transform.position - 2 * (transform.position - to);
|
||||
}
|
||||
transform.position = to;
|
||||
}
|
||||
|
||||
private void FixedUpdate()
|
||||
{
|
||||
// We do behavior changes in the fixed update so that enemy actions
|
||||
// do not depend on the framerate. Since movement happens in
|
||||
// Update, the overall effect as framerate varies is that enemies
|
||||
// might change their behaviors at slightly different distances,
|
||||
// but they'll always be in a given state for the same duration.
|
||||
// TODO(zeph): we could probably do this with coroutines instead,
|
||||
// also i still haven't read anything about the actual fsm system
|
||||
_behaviorTime += Time.fixedDeltaTime;
|
||||
float dd;
|
||||
switch (_behavior)
|
||||
{
|
||||
case BehaviorState.Create:
|
||||
// TODO(zeph): calculate starting hp from CON attribute
|
||||
_hp = 1;
|
||||
SetBehavior(BehaviorState.Spawning);
|
||||
break;
|
||||
case BehaviorState.Spawning:
|
||||
// TODO(zeph): move spawn time to an asset
|
||||
if (_behaviorTime >= 0.5)
|
||||
{
|
||||
Retarget();
|
||||
SetBehavior(BehaviorState.Chasing);
|
||||
}
|
||||
break;
|
||||
case BehaviorState.Chasing:
|
||||
dd = SquareDistanceToTarget();
|
||||
// TODO(zeph): RNG attribute
|
||||
if (dd <= SpaceRange(20000))
|
||||
{
|
||||
SetBehavior(BehaviorState.Attacking);
|
||||
}
|
||||
break;
|
||||
case BehaviorState.Attacking:
|
||||
dd = SquareDistanceToTarget();
|
||||
if (dd > SpaceRange(20000))
|
||||
{
|
||||
SetBehavior(BehaviorState.Chasing);
|
||||
}
|
||||
break;
|
||||
case BehaviorState.Feared:
|
||||
if (_behaviorTime > _fear)
|
||||
{
|
||||
SetBehavior(BehaviorState.Chasing);
|
||||
}
|
||||
break;
|
||||
case BehaviorState.Dead:
|
||||
// do nothing
|
||||
break;
|
||||
default:
|
||||
// TODO(zeph): unreachable
|
||||
break;
|
||||
}
|
||||
if (_behavior != BehaviorState.Dead && _hp == 0)
|
||||
{
|
||||
SetBehavior(BehaviorState.Dead);
|
||||
// TODO(zeph): put a dead time constant somewhere
|
||||
Destroy(gameObject, 10f);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
Assert.IsNotNull(enemySet);
|
||||
enemySet.Add(this);
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
enemySet.Remove(this);
|
||||
}
|
||||
|
||||
private void Retarget()
|
||||
{
|
||||
if (heroSet == null || heroSet.Count == 0)
|
||||
{
|
||||
// No heroes to target.
|
||||
// TODO(zeph): switch to a special behavior?
|
||||
target = null;
|
||||
return;
|
||||
}
|
||||
// TODO(zeph): target based on threat, once threat exists
|
||||
var k = Random.Range(0, heroSet.Count);
|
||||
var it = heroSet.GetEnumerator();
|
||||
// This seems to be the best way to do this...?
|
||||
for (var i = 0; i < k; i++)
|
||||
{
|
||||
it.MoveNext();
|
||||
}
|
||||
target = it.Current.gameObject;
|
||||
}
|
||||
|
||||
private void SetBehavior(BehaviorState behavior)
|
||||
{
|
||||
_behavior = behavior;
|
||||
_behaviorTime = 0;
|
||||
}
|
||||
|
||||
private float SquareDistanceToTarget()
|
||||
{
|
||||
return Vector3.Magnitude(transform.position - target.transform.position);
|
||||
}
|
||||
|
||||
public void Fear(float dur)
|
||||
{
|
||||
// If the enemy is already feared for a longer duration, don't
|
||||
// shorten it.
|
||||
if (_behavior == BehaviorState.Feared && _fear - _behaviorTime >= dur)
|
||||
{
|
||||
return;
|
||||
}
|
||||
_fear = dur;
|
||||
SetBehavior(BehaviorState.Feared);
|
||||
}
|
||||
|
||||
private static float SpaceSpeed(long spd, float dt)
|
||||
{
|
||||
// For now, treat SPD as units of ten thousandths of a unit per second.
|
||||
return (spd / 10000) * dt;
|
||||
}
|
||||
|
||||
private static float SpaceRange(long rng)
|
||||
{
|
||||
// Treat RNG as units of ten thousandths of a sqrt-meter.
|
||||
return rng / 10000;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user