Update Project
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f4da5393b1187f247a13ce38e3062f7e
|
||||
folderAsset: yes
|
||||
timeCreated: 1456415461
|
||||
licenseType: Free
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+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:
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 578ceac11e9188244b4d0db534cb351a
|
||||
folderAsset: yes
|
||||
timeCreated: 1456414609
|
||||
licenseType: Free
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
namespace Zenject.SpaceFighter
|
||||
{
|
||||
public class EnemyInstaller : Installer<EnemyInstaller>
|
||||
{
|
||||
public override void InstallBindings()
|
||||
{
|
||||
Container.Bind<EnemyTunables>().AsSingle();
|
||||
|
||||
Container.BindInterfacesAndSelfTo<EnemyStateManager>().AsSingle();
|
||||
|
||||
Container.Bind<EnemyStateIdle>().AsSingle();
|
||||
Container.Bind<EnemyStateAttack>().AsSingle();
|
||||
Container.Bind<EnemyStateFollow>().AsSingle();
|
||||
|
||||
Container.BindInterfacesAndSelfTo<EnemyDeathHandler>().AsSingle();
|
||||
Container.BindInterfacesAndSelfTo<EnemyRotationHandler>().AsSingle();
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ba7d60db0911cee42bd0cba6d4ed9b85
|
||||
timeCreated: 1456415201
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+80
@@ -0,0 +1,80 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Zenject.SpaceFighter
|
||||
{
|
||||
// Main installer for our game
|
||||
public class GameInstaller : MonoInstaller
|
||||
{
|
||||
[Inject]
|
||||
Settings _settings = null;
|
||||
|
||||
public override void InstallBindings()
|
||||
{
|
||||
Container.BindInterfacesAndSelfTo<EnemySpawner>().AsSingle();
|
||||
|
||||
Container.BindFactory<float, float, EnemyFacade, EnemyFacade.Factory>()
|
||||
// We could just use FromMonoPoolableMemoryPool here instead, but
|
||||
// for IL2CPP to work we need our pool class to be used explicitly here
|
||||
.FromPoolableMemoryPool<float, float, EnemyFacade, EnemyFacadePool>(poolBinder => poolBinder
|
||||
// Spawn 5 enemies right off the bat so that we don't incur spikes at runtime
|
||||
.WithInitialSize(5)
|
||||
.FromSubContainerResolve()
|
||||
.ByNewPrefabInstaller<EnemyInstaller>(_settings.EnemyFacadePrefab)
|
||||
// Place each enemy under an Enemies game object at the root of scene hierarchy
|
||||
.UnderTransformGroup("Enemies"));
|
||||
|
||||
Container.BindFactory<float, float, BulletTypes, Bullet, Bullet.Factory>()
|
||||
// We could just use FromMonoPoolableMemoryPool here instead, but
|
||||
// for IL2CPP to work we need our pool class to be used explicitly here
|
||||
.FromPoolableMemoryPool<float, float, BulletTypes, Bullet, BulletPool>(poolBinder => poolBinder
|
||||
// Spawn 20 right off the bat so that we don't incur spikes at runtime
|
||||
.WithInitialSize(20)
|
||||
// Bullets are simple enough that we don't need to make a subcontainer for them
|
||||
// The logic can all just be in one class
|
||||
.FromComponentInNewPrefab(_settings.BulletPrefab)
|
||||
.UnderTransformGroup("Bullets"));
|
||||
|
||||
Container.Bind<LevelBoundary>().AsSingle();
|
||||
|
||||
Container.BindFactory<Explosion, Explosion.Factory>()
|
||||
// We could just use FromMonoPoolableMemoryPool here instead, but
|
||||
// for IL2CPP to work we need our pool class to be used explicitly here
|
||||
.FromPoolableMemoryPool<Explosion, ExplosionPool>(poolBinder => poolBinder
|
||||
// Spawn 4 right off the bat so that we don't incur spikes at runtime
|
||||
.WithInitialSize(4)
|
||||
.FromComponentInNewPrefab(_settings.ExplosionPrefab)
|
||||
.UnderTransformGroup("Explosions"));
|
||||
|
||||
Container.Bind<AudioPlayer>().AsSingle();
|
||||
|
||||
Container.BindInterfacesTo<GameRestartHandler>().AsSingle();
|
||||
|
||||
Container.Bind<EnemyRegistry>().AsSingle();
|
||||
|
||||
GameSignalsInstaller.Install(Container);
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class Settings
|
||||
{
|
||||
public GameObject EnemyFacadePrefab;
|
||||
public GameObject BulletPrefab;
|
||||
public GameObject ExplosionPrefab;
|
||||
}
|
||||
|
||||
// We could just use FromMonoPoolableMemoryPool above, but we have to use these instead
|
||||
// for IL2CPP to work
|
||||
class EnemyFacadePool : MonoPoolableMemoryPool<float, float, IMemoryPool, EnemyFacade>
|
||||
{
|
||||
}
|
||||
|
||||
class BulletPool : MonoPoolableMemoryPool<float, float, BulletTypes, IMemoryPool, Bullet>
|
||||
{
|
||||
}
|
||||
|
||||
class ExplosionPool : MonoPoolableMemoryPool<IMemoryPool, Explosion>
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e432172e250876f4ab278b0d24d58673
|
||||
timeCreated: 1456414615
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
using System;
|
||||
|
||||
namespace Zenject.SpaceFighter
|
||||
{
|
||||
// We prefer to use ScriptableObjectInstaller for installers that contain game settings
|
||||
// There's no reason why you couldn't use a MonoInstaller here instead, however
|
||||
// using ScriptableObjectInstaller has advantages here that make it nice for settings:
|
||||
//
|
||||
// 1) You can change these values at runtime and have those changes persist across play
|
||||
// sessions. If it was a MonoInstaller then any changes would be lost when you hit stop
|
||||
// 2) You can easily create multiple ScriptableObject instances of this installer to test
|
||||
// different customizations to settings. For example, you might have different instances
|
||||
// for each difficulty mode of your game, such as "Easy", "Hard", etc.
|
||||
// 3) If your settings are associated with a game object composition root, then using
|
||||
// ScriptableObjectInstaller can be easier since there will only ever be one definitive
|
||||
// instance for each setting. Otherwise, you'd have to change the settings for each game
|
||||
// object composition root separately at runtime
|
||||
//
|
||||
// Uncomment if you want to add alternative game settings
|
||||
//[CreateAssetMenu(menuName = "Space Fighter/Game Settings")]
|
||||
public class GameSettingsInstaller : ScriptableObjectInstaller<GameSettingsInstaller>
|
||||
{
|
||||
public EnemySpawner.Settings EnemySpawner;
|
||||
public GameRestartHandler.Settings GameRestartHandler;
|
||||
public GameInstaller.Settings GameInstaller;
|
||||
public PlayerSettings Player;
|
||||
public EnemySettings Enemy;
|
||||
|
||||
[Serializable]
|
||||
public class PlayerSettings
|
||||
{
|
||||
public PlayerMoveHandler.Settings PlayerMoveHandler;
|
||||
public PlayerShootHandler.Settings PlayerShootHandler;
|
||||
public PlayerDamageHandler.Settings PlayerCollisionHandler;
|
||||
public PlayerHealthWatcher.Settings PlayerHealthWatcher;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class EnemySettings
|
||||
{
|
||||
public EnemyTunables DefaultSettings;
|
||||
public EnemyStateIdle.Settings EnemyStateIdle;
|
||||
public EnemyRotationHandler.Settings EnemyRotationHandler;
|
||||
public EnemyStateFollow.Settings EnemyStateFollow;
|
||||
public EnemyStateAttack.Settings EnemyStateAttack;
|
||||
public EnemyDeathHandler.Settings EnemyHealthWatcher;
|
||||
public EnemyCommonSettings EnemyCommonSettings;
|
||||
}
|
||||
|
||||
public override void InstallBindings()
|
||||
{
|
||||
// Use IfNotBound to allow overriding for eg. from play mode tests
|
||||
Container.BindInstance(EnemySpawner).IfNotBound();
|
||||
Container.BindInstance(GameRestartHandler).IfNotBound();
|
||||
Container.BindInstance(GameInstaller).IfNotBound();
|
||||
|
||||
Container.BindInstance(Player.PlayerMoveHandler).IfNotBound();
|
||||
Container.BindInstance(Player.PlayerShootHandler).IfNotBound();
|
||||
Container.BindInstance(Player.PlayerCollisionHandler).IfNotBound();
|
||||
Container.BindInstance(Player.PlayerHealthWatcher).IfNotBound();
|
||||
|
||||
Container.BindInstance(Enemy.EnemyStateIdle).IfNotBound();
|
||||
Container.BindInstance(Enemy.EnemyRotationHandler).IfNotBound();
|
||||
Container.BindInstance(Enemy.EnemyStateFollow).IfNotBound();
|
||||
Container.BindInstance(Enemy.EnemyStateAttack).IfNotBound();
|
||||
Container.BindInstance(Enemy.EnemyHealthWatcher).IfNotBound();
|
||||
Container.BindInstance(Enemy.EnemyCommonSettings).IfNotBound();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1db4d8ab51ca4ad48844c113bfbd80fa
|
||||
timeCreated: 1461784513
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace Zenject.SpaceFighter
|
||||
{
|
||||
// Include this just to ensure BindSignal with an object mapping works
|
||||
public class PlayerDiedSignalObserver
|
||||
{
|
||||
public void OnPlayerDied()
|
||||
{
|
||||
Debug.Log("Fired PlayerDiedSignal");
|
||||
}
|
||||
}
|
||||
|
||||
public class GameSignalsInstaller : Installer<GameSignalsInstaller>
|
||||
{
|
||||
public override void InstallBindings()
|
||||
{
|
||||
SignalBusInstaller.Install(Container);
|
||||
|
||||
Container.DeclareSignal<EnemyKilledSignal>();
|
||||
Container.DeclareSignal<PlayerDiedSignal>();
|
||||
|
||||
// Include these just to ensure BindSignal works
|
||||
Container.BindSignal<PlayerDiedSignal>().ToMethod<PlayerDiedSignalObserver>(x => x.OnPlayerDied).FromNew();
|
||||
Container.BindSignal<EnemyKilledSignal>().ToMethod(() => Debug.Log("Fired EnemyKilledSignal"));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 23aa26bc747410a40ac441fedb64f05d
|
||||
timeCreated: 1506513911
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Zenject.SpaceFighter
|
||||
{
|
||||
public class PlayerInstaller : MonoInstaller
|
||||
{
|
||||
[SerializeField]
|
||||
Settings _settings = null;
|
||||
|
||||
public override void InstallBindings()
|
||||
{
|
||||
Container.Bind<Player>().AsSingle()
|
||||
.WithArguments(_settings.Rigidbody, _settings.MeshRenderer);
|
||||
|
||||
Container.BindInterfacesTo<PlayerInputHandler>().AsSingle();
|
||||
Container.BindInterfacesTo<PlayerMoveHandler>().AsSingle();
|
||||
Container.BindInterfacesAndSelfTo<PlayerDamageHandler>().AsSingle();
|
||||
Container.BindInterfacesTo<PlayerDirectionHandler>().AsSingle();
|
||||
Container.BindInterfacesTo<PlayerShootHandler>().AsSingle();
|
||||
|
||||
Container.Bind<PlayerInputState>().AsSingle();
|
||||
|
||||
Container.BindInterfacesTo<PlayerHealthWatcher>().AsSingle();
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class Settings
|
||||
{
|
||||
public Rigidbody Rigidbody;
|
||||
public MeshRenderer MeshRenderer;
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ff1b44a433bb75344a686526fba7452b
|
||||
timeCreated: 1456415489
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8b24cebc08e04c147950145f9a70049b
|
||||
folderAsset: yes
|
||||
timeCreated: 1456416096
|
||||
licenseType: Free
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace Zenject.SpaceFighter
|
||||
{
|
||||
public class AudioPlayer
|
||||
{
|
||||
readonly Camera _camera;
|
||||
|
||||
public AudioPlayer(Camera camera)
|
||||
{
|
||||
_camera = camera;
|
||||
}
|
||||
|
||||
public void Play(AudioClip clip)
|
||||
{
|
||||
Play(clip, 1);
|
||||
}
|
||||
|
||||
public void Play(AudioClip clip, float volume)
|
||||
{
|
||||
_camera.GetComponent<AudioSource>().PlayOneShot(clip, volume);
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a57395b833349ce43a64f59377d0e398
|
||||
timeCreated: 1456615647
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,91 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace Zenject.SpaceFighter
|
||||
{
|
||||
public enum BulletTypes
|
||||
{
|
||||
FromEnemy,
|
||||
FromPlayer
|
||||
}
|
||||
|
||||
public class Bullet : MonoBehaviour, IPoolable<float, float, BulletTypes, IMemoryPool>
|
||||
{
|
||||
float _startTime;
|
||||
BulletTypes _type;
|
||||
float _speed;
|
||||
float _lifeTime;
|
||||
|
||||
[SerializeField]
|
||||
MeshRenderer _renderer = null;
|
||||
|
||||
[SerializeField]
|
||||
Material _playerMaterial = null;
|
||||
|
||||
[SerializeField]
|
||||
Material _enemyMaterial = null;
|
||||
|
||||
IMemoryPool _pool;
|
||||
|
||||
public BulletTypes Type
|
||||
{
|
||||
get { return _type; }
|
||||
}
|
||||
|
||||
public Vector3 MoveDirection
|
||||
{
|
||||
get { return transform.right; }
|
||||
}
|
||||
|
||||
public void OnTriggerEnter(Collider other)
|
||||
{
|
||||
var enemyView = other.GetComponent<EnemyView>();
|
||||
|
||||
if (enemyView != null && _type == BulletTypes.FromPlayer)
|
||||
{
|
||||
enemyView.Facade.Die();
|
||||
_pool.Despawn(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
var player = other.GetComponent<PlayerFacade>();
|
||||
|
||||
if (player != null && _type == BulletTypes.FromEnemy)
|
||||
{
|
||||
player.TakeDamage(MoveDirection);
|
||||
_pool.Despawn(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
transform.position -= transform.right * _speed * Time.deltaTime;
|
||||
|
||||
if (Time.realtimeSinceStartup - _startTime > _lifeTime)
|
||||
{
|
||||
_pool.Despawn(this);
|
||||
}
|
||||
}
|
||||
|
||||
public void OnSpawned(float speed, float lifeTime, BulletTypes type, IMemoryPool pool)
|
||||
{
|
||||
_pool = pool;
|
||||
_type = type;
|
||||
_speed = speed;
|
||||
_lifeTime = lifeTime;
|
||||
|
||||
_renderer.material = type == BulletTypes.FromEnemy ? _enemyMaterial : _playerMaterial;
|
||||
|
||||
_startTime = Time.realtimeSinceStartup;
|
||||
}
|
||||
|
||||
public void OnDespawned()
|
||||
{
|
||||
_pool = null;
|
||||
}
|
||||
|
||||
public class Factory : PlaceholderFactory<float, float, BulletTypes, Bullet>
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4bcfdc0e00dba5645b88ed3608c61161
|
||||
timeCreated: 1456594035
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
using UnityEngine;
|
||||
|
||||
#pragma warning disable 649
|
||||
|
||||
namespace Zenject.SpaceFighter
|
||||
{
|
||||
public class ControlsDisplay : MonoBehaviour
|
||||
{
|
||||
[SerializeField]
|
||||
float _leftPadding;
|
||||
|
||||
[SerializeField]
|
||||
float _topPadding;
|
||||
|
||||
[SerializeField]
|
||||
float _width;
|
||||
|
||||
[SerializeField]
|
||||
float _height;
|
||||
|
||||
public void OnGUI()
|
||||
{
|
||||
var bounds = new Rect(_leftPadding, _topPadding, _width, _height);
|
||||
GUI.Label(bounds, "CONTROLS: WASD to move, Mouse to aim, Left Mouse to fire");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3bc2ba00bfd0f6044825d1848b10a7e0
|
||||
timeCreated: 1456683520
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+123
@@ -0,0 +1,123 @@
|
||||
using System;
|
||||
using ModestTree;
|
||||
using UnityEngine;
|
||||
using Random = UnityEngine.Random;
|
||||
|
||||
namespace Zenject.SpaceFighter
|
||||
{
|
||||
public class EnemySpawner : ITickable, IInitializable
|
||||
{
|
||||
readonly EnemyFacade.Factory _enemyFactory;
|
||||
readonly SignalBus _signalBus;
|
||||
readonly LevelBoundary _levelBoundary;
|
||||
readonly Settings _settings;
|
||||
|
||||
float _desiredNumEnemies;
|
||||
int _enemyCount;
|
||||
float _lastSpawnTime;
|
||||
|
||||
public EnemySpawner(
|
||||
Settings settings,
|
||||
LevelBoundary levelBoundary,
|
||||
SignalBus signalBus,
|
||||
EnemyFacade.Factory enemyFactory)
|
||||
{
|
||||
_enemyFactory = enemyFactory;
|
||||
_signalBus = signalBus;
|
||||
_levelBoundary = levelBoundary;
|
||||
_settings = settings;
|
||||
|
||||
_desiredNumEnemies = settings.NumEnemiesStartAmount;
|
||||
}
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
_signalBus.Subscribe<EnemyKilledSignal>(OnEnemyKilled);
|
||||
}
|
||||
|
||||
void OnEnemyKilled()
|
||||
{
|
||||
_enemyCount--;
|
||||
}
|
||||
|
||||
public void Tick()
|
||||
{
|
||||
_desiredNumEnemies += _settings.NumEnemiesIncreaseRate * Time.deltaTime;
|
||||
|
||||
if (_enemyCount < (int)_desiredNumEnemies
|
||||
&& Time.realtimeSinceStartup - _lastSpawnTime > _settings.MinDelayBetweenSpawns)
|
||||
{
|
||||
SpawnEnemy();
|
||||
_enemyCount++;
|
||||
}
|
||||
}
|
||||
|
||||
void SpawnEnemy()
|
||||
{
|
||||
float speed = Random.Range(_settings.SpeedMin, _settings.SpeedMax);
|
||||
float accuracy = Random.Range(_settings.AccuracyMin, _settings.AccuracyMax);
|
||||
|
||||
var enemyFacade = _enemyFactory.Create(accuracy, speed);
|
||||
enemyFacade.Position = ChooseRandomStartPosition();
|
||||
|
||||
_lastSpawnTime = Time.realtimeSinceStartup;
|
||||
}
|
||||
|
||||
Vector3 ChooseRandomStartPosition()
|
||||
{
|
||||
var side = Random.Range(0, 3);
|
||||
var posOnSide = Random.Range(0, 1.0f);
|
||||
|
||||
float buffer = 2.0f;
|
||||
|
||||
switch (side)
|
||||
{
|
||||
case 0:
|
||||
// top
|
||||
{
|
||||
return new Vector3(
|
||||
_levelBoundary.Left + posOnSide * _levelBoundary.Width,
|
||||
_levelBoundary.Top + buffer, 0);
|
||||
}
|
||||
case 1:
|
||||
// right
|
||||
{
|
||||
return new Vector3(
|
||||
_levelBoundary.Right + buffer,
|
||||
_levelBoundary.Top - posOnSide * _levelBoundary.Height, 0);
|
||||
}
|
||||
case 2:
|
||||
// bottom
|
||||
{
|
||||
return new Vector3(
|
||||
_levelBoundary.Left + posOnSide * _levelBoundary.Width,
|
||||
_levelBoundary.Bottom - buffer, 0);
|
||||
}
|
||||
case 3:
|
||||
// left
|
||||
{
|
||||
return new Vector3(
|
||||
_levelBoundary.Left - buffer,
|
||||
_levelBoundary.Top - posOnSide * _levelBoundary.Height, 0);
|
||||
}
|
||||
}
|
||||
|
||||
throw Assert.CreateException();
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class Settings
|
||||
{
|
||||
public float SpeedMin;
|
||||
public float SpeedMax;
|
||||
|
||||
public float AccuracyMin;
|
||||
public float AccuracyMax;
|
||||
|
||||
public float NumEnemiesIncreaseRate;
|
||||
public float NumEnemiesStartAmount;
|
||||
|
||||
public float MinDelayBetweenSpawns = 0.5f;
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1daabbbef5fff9a4b9a2c94659ecdf57
|
||||
timeCreated: 1456420312
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
using UnityEngine;
|
||||
|
||||
#pragma warning disable 649
|
||||
|
||||
namespace Zenject.SpaceFighter
|
||||
{
|
||||
public class Explosion : MonoBehaviour, IPoolable<IMemoryPool>
|
||||
{
|
||||
[SerializeField]
|
||||
float _lifeTime;
|
||||
|
||||
[SerializeField]
|
||||
ParticleSystem _particleSystem;
|
||||
|
||||
float _startTime;
|
||||
|
||||
IMemoryPool _pool;
|
||||
|
||||
public void Update()
|
||||
{
|
||||
if (Time.realtimeSinceStartup - _startTime > _lifeTime)
|
||||
{
|
||||
_pool.Despawn(this);
|
||||
}
|
||||
}
|
||||
|
||||
public void OnDespawned()
|
||||
{
|
||||
}
|
||||
|
||||
public void OnSpawned(IMemoryPool pool)
|
||||
{
|
||||
_particleSystem.Clear();
|
||||
_particleSystem.Play();
|
||||
|
||||
_startTime = Time.realtimeSinceStartup;
|
||||
_pool = pool;
|
||||
}
|
||||
|
||||
public class Factory : PlaceholderFactory<Explosion>
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 32bea9a1bf6287e4e936f2de85375172
|
||||
timeCreated: 1456680988
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEngine.SceneManagement;
|
||||
|
||||
namespace Zenject.SpaceFighter
|
||||
{
|
||||
public class GameRestartHandler : IInitializable, IDisposable, ITickable
|
||||
{
|
||||
readonly SignalBus _signalBus;
|
||||
readonly Settings _settings;
|
||||
|
||||
bool _isDelaying;
|
||||
float _delayStartTime;
|
||||
|
||||
public GameRestartHandler(
|
||||
Settings settings,
|
||||
SignalBus signalBus)
|
||||
{
|
||||
_signalBus = signalBus;
|
||||
_settings = settings;
|
||||
}
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
_signalBus.Subscribe<PlayerDiedSignal>(OnPlayerDied);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_signalBus.Unsubscribe<PlayerDiedSignal>(OnPlayerDied);
|
||||
}
|
||||
|
||||
public void Tick()
|
||||
{
|
||||
if (_isDelaying)
|
||||
{
|
||||
if (Time.realtimeSinceStartup - _delayStartTime > _settings.RestartDelay)
|
||||
{
|
||||
SceneManager.LoadScene(SceneManager.GetActiveScene().name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OnPlayerDied()
|
||||
{
|
||||
// Wait a bit before restarting the scene
|
||||
_delayStartTime = Time.realtimeSinceStartup;
|
||||
_isDelaying = true;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class Settings
|
||||
{
|
||||
public float RestartDelay;
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2244a42ec7f6e62498da08eaf3d40e0d
|
||||
timeCreated: 1456683096
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace Zenject.SpaceFighter
|
||||
{
|
||||
public class LevelBoundary
|
||||
{
|
||||
readonly Camera _camera;
|
||||
|
||||
public LevelBoundary(Camera camera)
|
||||
{
|
||||
_camera = camera;
|
||||
}
|
||||
|
||||
public float Bottom
|
||||
{
|
||||
get { return -ExtentHeight; }
|
||||
}
|
||||
|
||||
public float Top
|
||||
{
|
||||
get { return ExtentHeight; }
|
||||
}
|
||||
|
||||
public float Left
|
||||
{
|
||||
get { return -ExtentWidth; }
|
||||
}
|
||||
|
||||
public float Right
|
||||
{
|
||||
get { return ExtentWidth; }
|
||||
}
|
||||
|
||||
public float ExtentHeight
|
||||
{
|
||||
get { return _camera.orthographicSize; }
|
||||
}
|
||||
|
||||
public float Height
|
||||
{
|
||||
get { return ExtentHeight * 2.0f; }
|
||||
}
|
||||
|
||||
public float ExtentWidth
|
||||
{
|
||||
get { return _camera.aspect * _camera.orthographicSize; }
|
||||
}
|
||||
|
||||
public float Width
|
||||
{
|
||||
get { return ExtentWidth * 2.0f; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6307c1ccd85f4f7478da6f3ec5a63996
|
||||
timeCreated: 1484693361
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7781c3596a2d16a4988f90c8511c1cef
|
||||
folderAsset: yes
|
||||
timeCreated: 1456415464
|
||||
licenseType: Free
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Zenject.SpaceFighter
|
||||
{
|
||||
public class PlayerDamageHandler
|
||||
{
|
||||
readonly AudioPlayer _audioPlayer;
|
||||
readonly Settings _settings;
|
||||
readonly Player _player;
|
||||
|
||||
public PlayerDamageHandler(
|
||||
Player player,
|
||||
Settings settings,
|
||||
AudioPlayer audioPlayer)
|
||||
{
|
||||
_audioPlayer = audioPlayer;
|
||||
_settings = settings;
|
||||
_player = player;
|
||||
}
|
||||
|
||||
public void TakeDamage(Vector3 moveDirection)
|
||||
{
|
||||
_audioPlayer.Play(_settings.HitSound, _settings.HitSoundVolume);
|
||||
|
||||
_player.AddForce(-moveDirection * _settings.HitForce);
|
||||
|
||||
_player.TakeDamage(_settings.HealthLoss);
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class Settings
|
||||
{
|
||||
public float HealthLoss;
|
||||
public float HitForce;
|
||||
|
||||
public AudioClip HitSound;
|
||||
public float HitSoundVolume = 1.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b6cd15ebe0f3c3243af6b6a44d7f2ded
|
||||
timeCreated: 1484757106
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace Zenject.SpaceFighter
|
||||
{
|
||||
public class PlayerDirectionHandler : ITickable
|
||||
{
|
||||
readonly Player _player;
|
||||
readonly Camera _mainCamera;
|
||||
|
||||
public PlayerDirectionHandler(
|
||||
Camera mainCamera,
|
||||
Player player)
|
||||
{
|
||||
_player = player;
|
||||
_mainCamera = mainCamera;
|
||||
}
|
||||
|
||||
public void Tick()
|
||||
{
|
||||
var mouseRay = _mainCamera.ScreenPointToRay(Input.mousePosition);
|
||||
|
||||
var mousePos = mouseRay.origin;
|
||||
mousePos.z = 0;
|
||||
|
||||
var goalDir = mousePos - _player.Position;
|
||||
goalDir.z = 0;
|
||||
goalDir.Normalize();
|
||||
|
||||
_player.Rotation = Quaternion.LookRotation(goalDir) * Quaternion.AngleAxis(90, Vector3.up);
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a99d48a043510d549a4f7078a8653233
|
||||
timeCreated: 1456592909
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace Zenject.SpaceFighter
|
||||
{
|
||||
public class PlayerFacade : MonoBehaviour
|
||||
{
|
||||
Player _model;
|
||||
PlayerDamageHandler _hitHandler;
|
||||
|
||||
[Inject]
|
||||
public void Construct(Player player, PlayerDamageHandler hitHandler)
|
||||
{
|
||||
_model = player;
|
||||
_hitHandler = hitHandler;
|
||||
}
|
||||
|
||||
public bool IsDead
|
||||
{
|
||||
get { return _model.IsDead; }
|
||||
}
|
||||
|
||||
public Vector3 Position
|
||||
{
|
||||
get { return _model.Position; }
|
||||
}
|
||||
|
||||
public Quaternion Rotation
|
||||
{
|
||||
get { return _model.Rotation; }
|
||||
}
|
||||
|
||||
public void TakeDamage(Vector3 moveDirection)
|
||||
{
|
||||
_hitHandler.TakeDamage(moveDirection);
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2db4310dd7706af419ae62fb99fac156
|
||||
timeCreated: 1456415473
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+81
@@ -0,0 +1,81 @@
|
||||
using ModestTree;
|
||||
using UnityEngine;
|
||||
|
||||
#pragma warning disable 649
|
||||
|
||||
namespace Zenject.SpaceFighter
|
||||
{
|
||||
public class PlayerGui : MonoBehaviour
|
||||
{
|
||||
[SerializeField]
|
||||
float _leftPadding;
|
||||
|
||||
[SerializeField]
|
||||
float _bottomPadding;
|
||||
|
||||
[SerializeField]
|
||||
float _labelWidth;
|
||||
|
||||
[SerializeField]
|
||||
float _labelHeight;
|
||||
|
||||
[SerializeField]
|
||||
float _textureWidth;
|
||||
|
||||
[SerializeField]
|
||||
float _textureHeight;
|
||||
|
||||
[SerializeField]
|
||||
float _killCountOffset;
|
||||
|
||||
[SerializeField]
|
||||
Color _foregroundColor;
|
||||
|
||||
[SerializeField]
|
||||
Color _backgroundColor;
|
||||
|
||||
Player _player;
|
||||
Texture2D _textureForeground;
|
||||
Texture2D _textureBackground;
|
||||
int _killCount;
|
||||
|
||||
[Inject]
|
||||
public void Construct(Player player, SignalBus signalBus)
|
||||
{
|
||||
_player = player;
|
||||
|
||||
_textureForeground = CreateColorTexture(_foregroundColor);
|
||||
_textureBackground = CreateColorTexture(_backgroundColor);
|
||||
|
||||
signalBus.Subscribe<EnemyKilledSignal>(OnEnemyKilled);
|
||||
}
|
||||
|
||||
void OnEnemyKilled()
|
||||
{
|
||||
_killCount++;
|
||||
}
|
||||
|
||||
Texture2D CreateColorTexture(Color color)
|
||||
{
|
||||
var texture = new Texture2D(1, 1);
|
||||
texture.SetPixel(1, 1, color);
|
||||
texture.Apply();
|
||||
return texture;
|
||||
}
|
||||
|
||||
public void OnGUI()
|
||||
{
|
||||
var healthLabelBounds = new Rect(_leftPadding, Screen.height - _bottomPadding, _labelWidth, _labelHeight);
|
||||
GUI.Label(healthLabelBounds, "Health: {0:0}".Fmt(_player.Health));
|
||||
|
||||
var killLabelBounds = new Rect(healthLabelBounds.xMin, healthLabelBounds.yMin - _killCountOffset, _labelWidth, _labelHeight);
|
||||
GUI.Label(killLabelBounds, "Kill Count: {0}".Fmt(_killCount));
|
||||
|
||||
var boundsBackground = new Rect(healthLabelBounds.xMax, healthLabelBounds.yMin, _textureWidth, _textureHeight);
|
||||
GUI.DrawTexture(boundsBackground, _textureBackground);
|
||||
|
||||
var boundsForeground = new Rect(boundsBackground.xMin, boundsBackground.yMin, (_player.Health / 100.0f) * _textureWidth, _textureHeight);
|
||||
GUI.DrawTexture(boundsForeground, _textureForeground);
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 301188284719932429bd73b2047b328d
|
||||
timeCreated: 1456681654
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Zenject.SpaceFighter
|
||||
{
|
||||
public class PlayerHealthWatcher : ITickable
|
||||
{
|
||||
readonly SignalBus _signalBus;
|
||||
readonly AudioPlayer _audioPlayer;
|
||||
readonly Settings _settings;
|
||||
readonly Explosion.Factory _explosionFactory;
|
||||
readonly Player _player;
|
||||
|
||||
public PlayerHealthWatcher(
|
||||
Player player,
|
||||
Explosion.Factory explosionFactory,
|
||||
Settings settings,
|
||||
AudioPlayer audioPlayer,
|
||||
SignalBus signalBus)
|
||||
{
|
||||
_signalBus = signalBus;
|
||||
_audioPlayer = audioPlayer;
|
||||
_settings = settings;
|
||||
_explosionFactory = explosionFactory;
|
||||
_player = player;
|
||||
}
|
||||
|
||||
public void Tick()
|
||||
{
|
||||
if (_player.Health <= 0 && !_player.IsDead)
|
||||
{
|
||||
Die();
|
||||
}
|
||||
}
|
||||
|
||||
void Die()
|
||||
{
|
||||
_player.IsDead = true;
|
||||
|
||||
var explosion = _explosionFactory.Create();
|
||||
explosion.transform.position = _player.Position;
|
||||
|
||||
_player.Renderer.enabled = false;
|
||||
|
||||
_signalBus.Fire<PlayerDiedSignal>();
|
||||
|
||||
_audioPlayer.Play(_settings.DeathSound, _settings.DeathSoundVolume);
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class Settings
|
||||
{
|
||||
public AudioClip DeathSound;
|
||||
public float DeathSoundVolume = 1.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ff82b5e08504b89408eddc95e9dad280
|
||||
timeCreated: 1456681376
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace Zenject.SpaceFighter
|
||||
{
|
||||
public class PlayerInputHandler : ITickable
|
||||
{
|
||||
readonly PlayerInputState _inputState;
|
||||
|
||||
public PlayerInputHandler(PlayerInputState inputState)
|
||||
{
|
||||
_inputState = inputState;
|
||||
}
|
||||
|
||||
public void Tick()
|
||||
{
|
||||
_inputState.IsMovingLeft = Input.GetKey(KeyCode.LeftArrow) || Input.GetKey(KeyCode.A);
|
||||
_inputState.IsMovingRight = Input.GetKey(KeyCode.RightArrow) || Input.GetKey(KeyCode.D);
|
||||
_inputState.IsMovingUp = Input.GetKey(KeyCode.UpArrow) || Input.GetKey(KeyCode.W);
|
||||
_inputState.IsMovingDown = Input.GetKey(KeyCode.DownArrow) || Input.GetKey(KeyCode.S);
|
||||
|
||||
_inputState.IsFiring = Input.GetKey(KeyCode.Space) || Input.GetMouseButton(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5b062bb00fc459540964842bdf1068a9
|
||||
timeCreated: 1456417258
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
namespace Zenject.SpaceFighter
|
||||
{
|
||||
public class PlayerInputState
|
||||
{
|
||||
public bool IsMovingLeft
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public bool IsMovingRight
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public bool IsMovingUp
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public bool IsMovingDown
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public bool IsFiring
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9f9f53db91ff64e40af5b30b70927e13
|
||||
timeCreated: 1484693045
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace Zenject.SpaceFighter
|
||||
{
|
||||
public class Player
|
||||
{
|
||||
readonly Rigidbody _rigidBody;
|
||||
readonly MeshRenderer _renderer;
|
||||
|
||||
float _health = 100.0f;
|
||||
|
||||
public Player(
|
||||
Rigidbody rigidBody,
|
||||
MeshRenderer renderer)
|
||||
{
|
||||
_rigidBody = rigidBody;
|
||||
_renderer = renderer;
|
||||
}
|
||||
|
||||
public MeshRenderer Renderer
|
||||
{
|
||||
get { return _renderer; }
|
||||
}
|
||||
|
||||
public bool IsDead
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
public float Health
|
||||
{
|
||||
get { return _health; }
|
||||
}
|
||||
|
||||
public Vector3 LookDir
|
||||
{
|
||||
get { return -_rigidBody.transform.right; }
|
||||
}
|
||||
|
||||
public Quaternion Rotation
|
||||
{
|
||||
get { return _rigidBody.rotation; }
|
||||
set { _rigidBody.rotation = value; }
|
||||
}
|
||||
|
||||
public Vector3 Position
|
||||
{
|
||||
get { return _rigidBody.position; }
|
||||
set { _rigidBody.position = value; }
|
||||
}
|
||||
|
||||
public Vector3 Velocity
|
||||
{
|
||||
get { return _rigidBody.linearVelocity; }
|
||||
}
|
||||
|
||||
public void TakeDamage(float healthLoss)
|
||||
{
|
||||
_health = Mathf.Max(0.0f, _health - healthLoss);
|
||||
}
|
||||
|
||||
public void AddForce(Vector3 force)
|
||||
{
|
||||
_rigidBody.AddForce(force);
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 08563d6b38c31254692837726a00f677
|
||||
timeCreated: 1456623157
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+102
@@ -0,0 +1,102 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Zenject.SpaceFighter
|
||||
{
|
||||
public class PlayerMoveHandler : IFixedTickable
|
||||
{
|
||||
readonly LevelBoundary _levelBoundary;
|
||||
readonly Settings _settings;
|
||||
readonly Player _player;
|
||||
readonly PlayerInputState _inputState;
|
||||
|
||||
public PlayerMoveHandler(
|
||||
PlayerInputState inputState,
|
||||
Player player,
|
||||
Settings settings,
|
||||
LevelBoundary levelBoundary)
|
||||
{
|
||||
_levelBoundary = levelBoundary;
|
||||
_settings = settings;
|
||||
_player = player;
|
||||
_inputState = inputState;
|
||||
}
|
||||
|
||||
public void FixedTick()
|
||||
{
|
||||
if (_player.IsDead)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_inputState.IsMovingLeft)
|
||||
{
|
||||
_player.AddForce(
|
||||
Vector3.left * _settings.MoveSpeed);
|
||||
}
|
||||
|
||||
if (_inputState.IsMovingRight)
|
||||
{
|
||||
_player.AddForce(
|
||||
Vector3.right * _settings.MoveSpeed);
|
||||
}
|
||||
|
||||
if (_inputState.IsMovingUp)
|
||||
{
|
||||
_player.AddForce(
|
||||
Vector3.up * _settings.MoveSpeed);
|
||||
}
|
||||
|
||||
if (_inputState.IsMovingDown)
|
||||
{
|
||||
_player.AddForce(
|
||||
Vector3.down * _settings.MoveSpeed);
|
||||
}
|
||||
|
||||
// Always ensure we are on the main plane
|
||||
_player.Position = new Vector3(_player.Position.x, _player.Position.y, 0);
|
||||
|
||||
KeepPlayerOnScreen();
|
||||
}
|
||||
|
||||
void KeepPlayerOnScreen()
|
||||
{
|
||||
var extentLeft = (_levelBoundary.Left + _settings.BoundaryBuffer) - _player.Position.x;
|
||||
var extentRight = _player.Position.x - (_levelBoundary.Right - _settings.BoundaryBuffer);
|
||||
|
||||
if (extentLeft > 0)
|
||||
{
|
||||
_player.AddForce(
|
||||
Vector3.right * _settings.BoundaryAdjustForce * extentLeft);
|
||||
}
|
||||
else if (extentRight > 0)
|
||||
{
|
||||
_player.AddForce(
|
||||
Vector3.left * _settings.BoundaryAdjustForce * extentRight);
|
||||
}
|
||||
|
||||
var extentTop = _player.Position.y - (_levelBoundary.Top - _settings.BoundaryBuffer);
|
||||
var extentBottom = (_levelBoundary.Bottom + _settings.BoundaryBuffer) - _player.Position.y;
|
||||
|
||||
if (extentTop > 0)
|
||||
{
|
||||
_player.AddForce(
|
||||
Vector3.down * _settings.BoundaryAdjustForce * extentTop);
|
||||
}
|
||||
else if (extentBottom > 0)
|
||||
{
|
||||
_player.AddForce(
|
||||
Vector3.up * _settings.BoundaryAdjustForce * extentBottom);
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class Settings
|
||||
{
|
||||
public float BoundaryBuffer;
|
||||
public float BoundaryAdjustForce;
|
||||
public float MoveSpeed;
|
||||
public float SlowDownSpeed;
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d1291ab8856aa9e44a39831fce2c2f0c
|
||||
timeCreated: 1456593836
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Zenject.SpaceFighter
|
||||
{
|
||||
public class PlayerShootHandler : ITickable
|
||||
{
|
||||
readonly AudioPlayer _audioPlayer;
|
||||
readonly Player _player;
|
||||
readonly Settings _settings;
|
||||
readonly Bullet.Factory _bulletFactory;
|
||||
readonly PlayerInputState _inputState;
|
||||
|
||||
float _lastFireTime;
|
||||
|
||||
public PlayerShootHandler(
|
||||
PlayerInputState inputState,
|
||||
Bullet.Factory bulletFactory,
|
||||
Settings settings,
|
||||
Player player,
|
||||
AudioPlayer audioPlayer)
|
||||
{
|
||||
_audioPlayer = audioPlayer;
|
||||
_player = player;
|
||||
_settings = settings;
|
||||
_bulletFactory = bulletFactory;
|
||||
_inputState = inputState;
|
||||
}
|
||||
|
||||
public void Tick()
|
||||
{
|
||||
if (_player.IsDead)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_inputState.IsFiring && Time.realtimeSinceStartup - _lastFireTime > _settings.MaxShootInterval)
|
||||
{
|
||||
_lastFireTime = Time.realtimeSinceStartup;
|
||||
Fire();
|
||||
}
|
||||
}
|
||||
|
||||
void Fire()
|
||||
{
|
||||
_audioPlayer.Play(_settings.Laser, _settings.LaserVolume);
|
||||
|
||||
var bullet = _bulletFactory.Create(
|
||||
_settings.BulletSpeed, _settings.BulletLifetime, BulletTypes.FromPlayer);
|
||||
|
||||
bullet.transform.position = _player.Position + _player.LookDir * _settings.BulletOffsetDistance;
|
||||
bullet.transform.rotation = _player.Rotation;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class Settings
|
||||
{
|
||||
public AudioClip Laser;
|
||||
public float LaserVolume = 1.0f;
|
||||
|
||||
public float BulletLifetime;
|
||||
public float BulletSpeed;
|
||||
public float MaxShootInterval;
|
||||
public float BulletOffsetDistance;
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5a7938ea9f043d24fa235c0e0d2c0f39
|
||||
timeCreated: 1456593836
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user