Initial Commit
This commit is contained in:
+10
@@ -0,0 +1,10 @@
|
||||
using System;
|
||||
|
||||
namespace Zenject.SpaceFighter
|
||||
{
|
||||
[Serializable]
|
||||
public class EnemyCommonSettings
|
||||
{
|
||||
public float AttackDistance = 15.0f;
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 28bfa2b5275427041ae8782a2fa41625
|
||||
timeCreated: 1484712200
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Zenject.SpaceFighter
|
||||
{
|
||||
public class EnemyDeathHandler
|
||||
{
|
||||
readonly EnemyFacade _facade;
|
||||
readonly SignalBus _signalBus;
|
||||
readonly Settings _settings;
|
||||
readonly Explosion.Factory _explosionFactory;
|
||||
readonly AudioPlayer _audioPlayer;
|
||||
readonly EnemyView _view;
|
||||
|
||||
public EnemyDeathHandler(
|
||||
EnemyView view,
|
||||
AudioPlayer audioPlayer,
|
||||
Explosion.Factory explosionFactory,
|
||||
Settings settings,
|
||||
SignalBus signalBus,
|
||||
EnemyFacade facade)
|
||||
{
|
||||
_facade = facade;
|
||||
_signalBus = signalBus;
|
||||
_settings = settings;
|
||||
_explosionFactory = explosionFactory;
|
||||
_audioPlayer = audioPlayer;
|
||||
_view = view;
|
||||
}
|
||||
|
||||
public void Die()
|
||||
{
|
||||
var explosion = _explosionFactory.Create();
|
||||
explosion.transform.position = _view.Position;
|
||||
|
||||
_audioPlayer.Play(_settings.DeathSound, _settings.DeathSoundVolume);
|
||||
|
||||
_signalBus.Fire<EnemyKilledSignal>();
|
||||
|
||||
_facade.Dispose();
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class Settings
|
||||
{
|
||||
public AudioClip DeathSound;
|
||||
public float DeathSoundVolume = 1.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 52b884b41e3642c489f58f461470f589
|
||||
timeCreated: 1505656096
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+82
@@ -0,0 +1,82 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Zenject.SpaceFighter
|
||||
{
|
||||
// Here we can add some high-level methods to give some info to other
|
||||
// parts of the codebase outside of our enemy facade
|
||||
public class EnemyFacade : MonoBehaviour, IPoolable<float, float, IMemoryPool>, IDisposable
|
||||
{
|
||||
EnemyView _view;
|
||||
EnemyTunables _tunables;
|
||||
EnemyDeathHandler _deathHandler;
|
||||
EnemyStateManager _stateManager;
|
||||
EnemyRegistry _registry;
|
||||
IMemoryPool _pool;
|
||||
|
||||
[Inject]
|
||||
public void Construct(
|
||||
EnemyView view,
|
||||
EnemyTunables tunables,
|
||||
EnemyDeathHandler deathHandler,
|
||||
EnemyStateManager stateManager,
|
||||
EnemyRegistry registry)
|
||||
{
|
||||
_view = view;
|
||||
_tunables = tunables;
|
||||
_deathHandler = deathHandler;
|
||||
_stateManager = stateManager;
|
||||
_registry = registry;
|
||||
}
|
||||
|
||||
public EnemyStates State
|
||||
{
|
||||
get { return _stateManager.CurrentState; }
|
||||
}
|
||||
|
||||
public float Accuracy
|
||||
{
|
||||
get { return _tunables.Accuracy; }
|
||||
}
|
||||
|
||||
public float Speed
|
||||
{
|
||||
get { return _tunables.Speed; }
|
||||
}
|
||||
|
||||
public Vector3 Position
|
||||
{
|
||||
get { return _view.Position; }
|
||||
set { _view.Position = value; }
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_pool.Despawn(this);
|
||||
}
|
||||
|
||||
public void Die()
|
||||
{
|
||||
_deathHandler.Die();
|
||||
}
|
||||
|
||||
public void OnDespawned()
|
||||
{
|
||||
_registry.RemoveEnemy(this);
|
||||
_pool = null;
|
||||
}
|
||||
|
||||
public void OnSpawned(float accuracy, float speed, IMemoryPool pool)
|
||||
{
|
||||
_pool = pool;
|
||||
_tunables.Accuracy = accuracy;
|
||||
_tunables.Speed = speed;
|
||||
|
||||
_registry.AddEnemy(this);
|
||||
}
|
||||
|
||||
public class Factory : PlaceholderFactory<float, float, EnemyFacade>
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fac47e4da43b2ca4d955370e0dc6990e
|
||||
timeCreated: 1456415139
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Zenject.SpaceFighter
|
||||
{
|
||||
public class EnemyRegistry
|
||||
{
|
||||
readonly List<EnemyFacade> _enemies = new List<EnemyFacade>();
|
||||
|
||||
public IEnumerable<EnemyFacade> Enemies
|
||||
{
|
||||
get { return _enemies; }
|
||||
}
|
||||
|
||||
public void AddEnemy(EnemyFacade enemy)
|
||||
{
|
||||
_enemies.Add(enemy);
|
||||
}
|
||||
|
||||
public void RemoveEnemy(EnemyFacade enemy)
|
||||
{
|
||||
_enemies.Remove(enemy);
|
||||
}
|
||||
}
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 401e2ff37163f8f428397277aba9b3a0
|
||||
timeCreated: 1528644922
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Zenject.SpaceFighter
|
||||
{
|
||||
// Responsibilities:
|
||||
// - Interpolate rotation of enemy towards the current desired
|
||||
// direction
|
||||
public class EnemyRotationHandler : IFixedTickable
|
||||
{
|
||||
readonly Settings _settings;
|
||||
readonly EnemyView _view;
|
||||
|
||||
public EnemyRotationHandler(
|
||||
EnemyView view,
|
||||
Settings settings)
|
||||
{
|
||||
_settings = settings;
|
||||
_view = view;
|
||||
}
|
||||
|
||||
public Vector2 DesiredLookDir
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
public void FixedTick()
|
||||
{
|
||||
var lookDir = _view.LookDir;
|
||||
|
||||
var error = Vector3.Angle(lookDir, DesiredLookDir);
|
||||
|
||||
if (Vector3.Cross(lookDir, DesiredLookDir).z < 0)
|
||||
{
|
||||
error *= -1;
|
||||
}
|
||||
|
||||
_view.AddTorque(error * _settings.TurnSpeed);
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class Settings
|
||||
{
|
||||
public float TurnSpeed;
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 55eb8b68455f215489114a3a9a6f976d
|
||||
timeCreated: 1456672456
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+98
@@ -0,0 +1,98 @@
|
||||
using System.Collections.Generic;
|
||||
using ModestTree;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Zenject.SpaceFighter
|
||||
{
|
||||
public interface IEnemyState
|
||||
{
|
||||
void EnterState();
|
||||
void ExitState();
|
||||
void Update();
|
||||
void FixedUpdate();
|
||||
}
|
||||
|
||||
public enum EnemyStates
|
||||
{
|
||||
Idle,
|
||||
Attack,
|
||||
Follow,
|
||||
None
|
||||
}
|
||||
|
||||
// This class controls the basic "AI" of our enemy
|
||||
// Which works as a finite state machine, between three states:
|
||||
// - Attack
|
||||
// - Follow/Chase
|
||||
// - Idle
|
||||
public class EnemyStateManager : ITickable, IFixedTickable, IInitializable
|
||||
{
|
||||
IEnemyState _currentStateHandler;
|
||||
EnemyStates _currentState = EnemyStates.None;
|
||||
EnemyView _view;
|
||||
|
||||
List<IEnemyState> _states;
|
||||
|
||||
// We can't use a constructor due to a circular dependency issue
|
||||
[Inject]
|
||||
public void Construct(
|
||||
EnemyView view,
|
||||
EnemyStateIdle idle, EnemyStateAttack attack, EnemyStateFollow follow)
|
||||
{
|
||||
_view = view;
|
||||
_states = new List<IEnemyState>
|
||||
{
|
||||
// This needs to follow the enum order
|
||||
idle, attack, follow
|
||||
};
|
||||
}
|
||||
|
||||
public EnemyStates CurrentState
|
||||
{
|
||||
get { return _currentState; }
|
||||
}
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
Assert.IsEqual(_currentState, EnemyStates.None);
|
||||
Assert.IsNull(_currentStateHandler);
|
||||
|
||||
ChangeState(EnemyStates.Follow);
|
||||
}
|
||||
|
||||
public void ChangeState(EnemyStates state)
|
||||
{
|
||||
if (_currentState == state)
|
||||
{
|
||||
// Already in state
|
||||
return;
|
||||
}
|
||||
|
||||
//Log.Trace("View Changing state from {0} to {1}", _currentState, state);
|
||||
|
||||
_currentState = state;
|
||||
|
||||
if (_currentStateHandler != null)
|
||||
{
|
||||
_currentStateHandler.ExitState();
|
||||
_currentStateHandler = null;
|
||||
}
|
||||
|
||||
_currentStateHandler = _states[(int)state];
|
||||
_currentStateHandler.EnterState();
|
||||
}
|
||||
|
||||
public void Tick()
|
||||
{
|
||||
// Always ensure we are on the main plane
|
||||
_view.Position = new Vector3(_view.Position.x, _view.Position.y, 0);
|
||||
|
||||
_currentStateHandler.Update();
|
||||
}
|
||||
|
||||
public void FixedTick()
|
||||
{
|
||||
_currentStateHandler.FixedUpdate();
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4087d354966adf44fa24ee0fa597f6da
|
||||
timeCreated: 1456617036
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
using System;
|
||||
|
||||
namespace Zenject.SpaceFighter
|
||||
{
|
||||
// These values are given as parameters into dynamically created
|
||||
// EnemyFacade instances
|
||||
[Serializable]
|
||||
public class EnemyTunables
|
||||
{
|
||||
public float Accuracy;
|
||||
public float Speed;
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ff5c7e92da51264499aca355ed207cbd
|
||||
timeCreated: 1456626929
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace Zenject.SpaceFighter
|
||||
{
|
||||
public class EnemyView : MonoBehaviour
|
||||
{
|
||||
[SerializeField]
|
||||
MeshRenderer _renderer = null;
|
||||
|
||||
[SerializeField]
|
||||
Collider _collider = null;
|
||||
|
||||
[SerializeField]
|
||||
Rigidbody _rigidBody = null;
|
||||
|
||||
[Inject]
|
||||
public EnemyFacade Facade
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
public MeshRenderer Renderer
|
||||
{
|
||||
get { return _renderer; }
|
||||
}
|
||||
|
||||
public Collider Collider
|
||||
{
|
||||
get { return _collider; }
|
||||
}
|
||||
|
||||
public Rigidbody Rigidbody
|
||||
{
|
||||
get { return _rigidBody; }
|
||||
}
|
||||
|
||||
public Vector3 LookDir
|
||||
{
|
||||
get { return -_rigidBody.transform.right; }
|
||||
}
|
||||
|
||||
public Vector3 RightDir
|
||||
{
|
||||
get { return _rigidBody.transform.up; }
|
||||
}
|
||||
|
||||
public Vector3 ForwardDir
|
||||
{
|
||||
get { return _rigidBody.transform.right; }
|
||||
}
|
||||
|
||||
public Vector3 Position
|
||||
{
|
||||
get { return _rigidBody.transform.position; }
|
||||
set { _rigidBody.transform.position = value; }
|
||||
}
|
||||
|
||||
public Quaternion Rotation
|
||||
{
|
||||
get { return _rigidBody.rotation; }
|
||||
set { _rigidBody.rotation = value; }
|
||||
}
|
||||
|
||||
public Vector3 Velocity
|
||||
{
|
||||
get { return _rigidBody.linearVelocity; }
|
||||
}
|
||||
|
||||
public Vector3 AngularVelocity
|
||||
{
|
||||
get { return _rigidBody.angularVelocity; }
|
||||
set { _rigidBody.angularVelocity = value; }
|
||||
}
|
||||
|
||||
public void AddForce(Vector3 force)
|
||||
{
|
||||
_rigidBody.AddForce(force);
|
||||
}
|
||||
|
||||
public void AddTorque(float value)
|
||||
{
|
||||
_rigidBody.AddTorque(Vector3.forward * value);
|
||||
}
|
||||
}
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ae49206d36b59354b98f680504160bc4
|
||||
timeCreated: 1528640620
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
namespace Zenject.SpaceFighter
|
||||
{
|
||||
public struct PlayerDiedSignal
|
||||
{
|
||||
}
|
||||
|
||||
public struct EnemyKilledSignal
|
||||
{
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 432f74bf3abdd7e4facd4bba2a8fe60b
|
||||
timeCreated: 1483820546
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b1c94638b93aa2646a2643e39a156dd9
|
||||
folderAsset: yes
|
||||
timeCreated: 1456618194
|
||||
licenseType: Free
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+137
@@ -0,0 +1,137 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using Random = UnityEngine.Random;
|
||||
|
||||
namespace Zenject.SpaceFighter
|
||||
{
|
||||
public class EnemyStateAttack : IEnemyState
|
||||
{
|
||||
readonly EnemyRotationHandler _rotationHandler;
|
||||
readonly EnemyCommonSettings _commonSettings;
|
||||
readonly AudioPlayer _audioPlayer;
|
||||
readonly EnemyTunables _tunables;
|
||||
readonly EnemyStateManager _stateManager;
|
||||
readonly PlayerFacade _player;
|
||||
readonly Settings _settings;
|
||||
readonly EnemyView _view;
|
||||
readonly Bullet.Factory _bulletFactory;
|
||||
|
||||
float _lastShootTime;
|
||||
bool _strafeRight;
|
||||
float _lastStrafeChangeTime;
|
||||
|
||||
public EnemyStateAttack(
|
||||
Bullet.Factory bulletFactory,
|
||||
EnemyView view,
|
||||
Settings settings,
|
||||
PlayerFacade player,
|
||||
EnemyStateManager stateManager,
|
||||
EnemyTunables tunables,
|
||||
AudioPlayer audioPlayer,
|
||||
EnemyCommonSettings commonSettings,
|
||||
EnemyRotationHandler rotationHandler)
|
||||
{
|
||||
_rotationHandler = rotationHandler;
|
||||
_commonSettings = commonSettings;
|
||||
_audioPlayer = audioPlayer;
|
||||
_tunables = tunables;
|
||||
_stateManager = stateManager;
|
||||
_player = player;
|
||||
_settings = settings;
|
||||
_view = view;
|
||||
_bulletFactory = bulletFactory;
|
||||
_strafeRight = Random.Range(0.0f, 1.0f) < 0.5f;
|
||||
}
|
||||
|
||||
public void EnterState()
|
||||
{
|
||||
}
|
||||
|
||||
public void ExitState()
|
||||
{
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
if (_player.IsDead)
|
||||
{
|
||||
_stateManager.ChangeState(EnemyStates.Idle);
|
||||
return;
|
||||
}
|
||||
|
||||
_rotationHandler.DesiredLookDir = (_player.Position - _view.Position).normalized;
|
||||
|
||||
// Strafe back and forth over the given interval
|
||||
if (Time.realtimeSinceStartup - _lastStrafeChangeTime > _settings.StrafeChangeInterval)
|
||||
{
|
||||
_lastStrafeChangeTime = Time.realtimeSinceStartup;
|
||||
_strafeRight = !_strafeRight;
|
||||
}
|
||||
|
||||
// Shoot every X seconds
|
||||
if (Time.realtimeSinceStartup - _lastShootTime > _settings.ShootInterval)
|
||||
{
|
||||
_lastShootTime = Time.realtimeSinceStartup;
|
||||
Fire();
|
||||
}
|
||||
|
||||
// If the player runs away then chase them
|
||||
if ((_player.Position - _view.Position).magnitude > _commonSettings.AttackDistance + _settings.AttackRangeBuffer)
|
||||
{
|
||||
_stateManager.ChangeState(EnemyStates.Follow);
|
||||
}
|
||||
}
|
||||
|
||||
public void FixedUpdate()
|
||||
{
|
||||
// Strafe to avoid getting hit too easily
|
||||
if (_strafeRight)
|
||||
{
|
||||
_view.AddForce(_view.RightDir * _settings.StrafeMultiplier * _tunables.Speed);
|
||||
}
|
||||
else
|
||||
{
|
||||
_view.AddForce(-_view.RightDir * _settings.StrafeMultiplier * _tunables.Speed);
|
||||
}
|
||||
}
|
||||
|
||||
void Fire()
|
||||
{
|
||||
var bullet = _bulletFactory.Create(
|
||||
_settings.BulletSpeed, _settings.BulletLifetime, BulletTypes.FromEnemy);
|
||||
|
||||
// Randomize our aim a bit
|
||||
var accuracy = Mathf.Clamp(_tunables.Accuracy, 0, 1);
|
||||
var maxError = 1.0f - accuracy;
|
||||
var error = Random.Range(0, maxError);
|
||||
|
||||
if (Random.Range(0.0f, 1.0f) < 0.5f)
|
||||
{
|
||||
error *= -1;
|
||||
}
|
||||
|
||||
var thetaError = error * _settings.ErrorRangeTheta;
|
||||
|
||||
bullet.transform.position = _view.Position + _view.LookDir * _settings.BulletOffsetDistance;
|
||||
bullet.transform.rotation = Quaternion.AngleAxis(thetaError, Vector3.forward) * _view.Rotation;
|
||||
|
||||
_audioPlayer.Play(_settings.ShootSound, _settings.ShootSoundVolume);
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class Settings
|
||||
{
|
||||
public AudioClip ShootSound;
|
||||
public float ShootSoundVolume = 1.0f;
|
||||
|
||||
public float BulletLifetime;
|
||||
public float BulletSpeed;
|
||||
public float BulletOffsetDistance;
|
||||
public float ShootInterval;
|
||||
public float ErrorRangeTheta;
|
||||
public float AttackRangeBuffer;
|
||||
public float StrafeMultiplier;
|
||||
public float StrafeChangeInterval;
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3d2bd141d86c3fb40a0a616c19e6de0c
|
||||
timeCreated: 1456620041
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+110
@@ -0,0 +1,110 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using Random = UnityEngine.Random;
|
||||
|
||||
namespace Zenject.SpaceFighter
|
||||
{
|
||||
public class EnemyStateFollow : IEnemyState
|
||||
{
|
||||
readonly EnemyRotationHandler _rotationHandler;
|
||||
readonly EnemyCommonSettings _commonSettings;
|
||||
readonly Settings _settings;
|
||||
readonly EnemyTunables _tunables;
|
||||
readonly EnemyStateManager _stateManager;
|
||||
readonly EnemyView _view;
|
||||
readonly PlayerFacade _player;
|
||||
|
||||
bool _strafeRight;
|
||||
float _lastStrafeChangeTime;
|
||||
|
||||
public EnemyStateFollow(
|
||||
PlayerFacade player,
|
||||
EnemyView view,
|
||||
EnemyStateManager stateManager,
|
||||
EnemyTunables tunables,
|
||||
Settings settings,
|
||||
EnemyCommonSettings commonSettings,
|
||||
EnemyRotationHandler rotationHandler)
|
||||
{
|
||||
_rotationHandler = rotationHandler;
|
||||
_commonSettings = commonSettings;
|
||||
_settings = settings;
|
||||
_tunables = tunables;
|
||||
_stateManager = stateManager;
|
||||
_view = view;
|
||||
_player = player;
|
||||
}
|
||||
|
||||
public void EnterState()
|
||||
{
|
||||
_strafeRight = Random.Range(0, 1) == 0;
|
||||
_lastStrafeChangeTime = Time.realtimeSinceStartup;
|
||||
}
|
||||
|
||||
public void ExitState()
|
||||
{
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
if (_player.IsDead)
|
||||
{
|
||||
_stateManager.ChangeState(EnemyStates.Idle);
|
||||
return;
|
||||
}
|
||||
|
||||
var distanceToPlayer = (_player.Position - _view.Position).magnitude;
|
||||
|
||||
// Always look towards the player
|
||||
_rotationHandler.DesiredLookDir = (_player.Position - _view.Position).normalized;
|
||||
|
||||
// Strafe back and forth over the given interval
|
||||
// This helps avoiding being too easy a target
|
||||
if (Time.realtimeSinceStartup - _lastStrafeChangeTime > _settings.StrafeChangeInterval)
|
||||
{
|
||||
_lastStrafeChangeTime = Time.realtimeSinceStartup;
|
||||
_strafeRight = !_strafeRight;
|
||||
}
|
||||
|
||||
if (distanceToPlayer < _commonSettings.AttackDistance)
|
||||
{
|
||||
_stateManager.ChangeState(EnemyStates.Attack);
|
||||
}
|
||||
}
|
||||
|
||||
public void FixedUpdate()
|
||||
{
|
||||
MoveTowardsPlayer();
|
||||
Strafe();
|
||||
}
|
||||
|
||||
void Strafe()
|
||||
{
|
||||
// Strafe to avoid getting hit too easily
|
||||
if (_strafeRight)
|
||||
{
|
||||
_view.AddForce(_view.RightDir * _settings.StrafeMultiplier * _tunables.Speed);
|
||||
}
|
||||
else
|
||||
{
|
||||
_view.AddForce(-_view.RightDir * _settings.StrafeMultiplier * _tunables.Speed);
|
||||
}
|
||||
}
|
||||
|
||||
void MoveTowardsPlayer()
|
||||
{
|
||||
var playerDir = (_player.Position - _view.Position).normalized;
|
||||
|
||||
_view.AddForce(playerDir * _tunables.Speed);
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class Settings
|
||||
{
|
||||
public float StrafeMultiplier;
|
||||
public float StrafeChangeInterval;
|
||||
public float TeleportNewDistance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f6b8435c02df92747b53470f32656b93
|
||||
timeCreated: 1456620041
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using Random = UnityEngine.Random;
|
||||
|
||||
namespace Zenject.SpaceFighter
|
||||
{
|
||||
public class EnemyStateIdle : IEnemyState
|
||||
{
|
||||
readonly EnemyRotationHandler _rotationHandler;
|
||||
readonly Settings _settings;
|
||||
readonly EnemyView _view;
|
||||
|
||||
Vector3 _startPos;
|
||||
float _theta;
|
||||
Vector3 _startLookDir;
|
||||
|
||||
public EnemyStateIdle(
|
||||
EnemyView view, Settings settings,
|
||||
EnemyRotationHandler rotationHandler)
|
||||
{
|
||||
_rotationHandler = rotationHandler;
|
||||
_settings = settings;
|
||||
_view = view;
|
||||
}
|
||||
|
||||
public void EnterState()
|
||||
{
|
||||
_startPos = _view.Position;
|
||||
_theta = Random.Range(0, 2.0f * Mathf.PI);
|
||||
_startLookDir = _view.LookDir;
|
||||
}
|
||||
|
||||
public void ExitState()
|
||||
{
|
||||
}
|
||||
|
||||
// Just add a bit of subtle movement
|
||||
public void Update()
|
||||
{
|
||||
_theta += Time.deltaTime * _settings.Frequency;
|
||||
|
||||
_view.Position = _startPos + _view.RightDir * _settings.Amplitude * Mathf.Sin(_theta);
|
||||
|
||||
_rotationHandler.DesiredLookDir = _startLookDir;
|
||||
}
|
||||
|
||||
public void FixedUpdate()
|
||||
{
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class Settings
|
||||
{
|
||||
public float Amplitude;
|
||||
public float Frequency;
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7a99ffa8c241c8a4e97392c7903712f0
|
||||
timeCreated: 1456618194
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user