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:
		| @@ -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; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user