Initial Commit

This commit is contained in:
2026-03-16 14:38:46 +02:00
commit b8f7327a21
2327 changed files with 253610 additions and 0 deletions
@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: f4da5393b1187f247a13ce38e3062f7e
folderAsset: yes
timeCreated: 1456415461
licenseType: Free
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,10 @@
using System;
namespace Zenject.SpaceFighter
{
[Serializable]
public class EnemyCommonSettings
{
public float AttackDistance = 15.0f;
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 28bfa2b5275427041ae8782a2fa41625
timeCreated: 1484712200
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -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;
}
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 52b884b41e3642c489f58f461470f589
timeCreated: 1505656096
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -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>
{
}
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: fac47e4da43b2ca4d955370e0dc6990e
timeCreated: 1456415139
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -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);
}
}
}
@@ -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:
@@ -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;
}
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 55eb8b68455f215489114a3a9a6f976d
timeCreated: 1456672456
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -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();
}
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 4087d354966adf44fa24ee0fa597f6da
timeCreated: 1456617036
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -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;
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: ff5c7e92da51264499aca355ed207cbd
timeCreated: 1456626929
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -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);
}
}
}
@@ -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:
@@ -0,0 +1,10 @@
namespace Zenject.SpaceFighter
{
public struct PlayerDiedSignal
{
}
public struct EnemyKilledSignal
{
}
}
@@ -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:
@@ -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;
}
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 3d2bd141d86c3fb40a0a616c19e6de0c
timeCreated: 1456620041
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -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;
}
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: f6b8435c02df92747b53470f32656b93
timeCreated: 1456620041
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -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;
}
}
}
@@ -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:
@@ -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();
}
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: ba7d60db0911cee42bd0cba6d4ed9b85
timeCreated: 1456415201
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -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>
{
}
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: e432172e250876f4ab278b0d24d58673
timeCreated: 1456414615
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -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();
}
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 1db4d8ab51ca4ad48844c113bfbd80fa
timeCreated: 1461784513
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -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"));
}
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 23aa26bc747410a40ac441fedb64f05d
timeCreated: 1506513911
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -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;
}
}
}
@@ -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:
@@ -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);
}
}
}
@@ -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>
{
}
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 4bcfdc0e00dba5645b88ed3608c61161
timeCreated: 1456594035
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -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");
}
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 3bc2ba00bfd0f6044825d1848b10a7e0
timeCreated: 1456683520
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -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;
}
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 1daabbbef5fff9a4b9a2c94659ecdf57
timeCreated: 1456420312
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -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>
{
}
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 32bea9a1bf6287e4e936f2de85375172
timeCreated: 1456680988
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -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;
}
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 2244a42ec7f6e62498da08eaf3d40e0d
timeCreated: 1456683096
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -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; }
}
}
}
@@ -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:
@@ -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;
}
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: b6cd15ebe0f3c3243af6b6a44d7f2ded
timeCreated: 1484757106
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -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);
}
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: a99d48a043510d549a4f7078a8653233
timeCreated: 1456592909
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -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);
}
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 2db4310dd7706af419ae62fb99fac156
timeCreated: 1456415473
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -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);
}
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 301188284719932429bd73b2047b328d
timeCreated: 1456681654
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -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;
}
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: ff82b5e08504b89408eddc95e9dad280
timeCreated: 1456681376
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -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);
}
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 5b062bb00fc459540964842bdf1068a9
timeCreated: 1456417258
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -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;
}
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 9f9f53db91ff64e40af5b30b70927e13
timeCreated: 1484693045
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -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);
}
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 08563d6b38c31254692837726a00f677
timeCreated: 1456623157
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -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;
}
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: d1291ab8856aa9e44a39831fce2c2f0c
timeCreated: 1456593836
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -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;
}
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 5a7938ea9f043d24fa235c0e0d2c0f39
timeCreated: 1456593836
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: