Skip to content

Instantly share code, notes, and snippets.

@disconnect3d
Last active September 12, 2017 11:24
Show Gist options
  • Save disconnect3d/1aa65e9613736e9a7ec310f3d5ab7e34 to your computer and use it in GitHub Desktop.
Save disconnect3d/1aa65e9613736e9a7ec310f3d5ab7e34 to your computer and use it in GitHub Desktop.
//BallController.cs
//Created by: <Name Surname>
using UnityEngine;
using System;
using UnityEngine.Assertions;
using Arkanoid.Utils;
namespace Arkanoid.Game
{
/// <summary>
/// BallController class is responsible for definig ball behaviour, changing state of ball (and thus current behaviour) and sending data to save to SaveGameController on proper event.
/// BallStateGame changes when ball is fired (in GameStateReadyToFireBehaviour()) or when one of events is recived: LOSE_ROUND, WIN_ROUND, RESET_TO_NEW_ROUND, PREPARE_NEW_GAME, PREPARE_LOADED_GAME.
/// In class there is an Action "_gameStateBehaviour" which is called in Update(). Depending on state it has proper method assigned (assigning is in BallGameState State accessor).
/// </summary>
[RequireComponent(typeof(Rigidbody2D))]
public class BallController : MonoBehaviour, IResetable, IDataToSave
{
public enum BallGameState
{
Reset,
Running,
ReadyToFire
}
//#region PRIVATE_FIELDS ----------------------------------------------------------------------------------------
private GameCore _gameCore = null;
private float _ballMovementSpeed = 3f;
private float _ballSpeedMovementAccelerationPerLevel = 1f;
private Action _gameStateBehaviour = null;
private bool _wasShootedAfterReset = false;
private BallGameState _state;
[SerializeField]
private BallColliderController _ballColliderController = null;
[SerializeField]
private Rigidbody2D _rigidbody = null;
[SerializeField]
private Transform _resetPosition = null;
//#endregion ----------------------------------------------------------------------------------------------------
//#region ACCESSORS ---------------------------------------------------------------------------------------------
private BallGameState State
{
set
{
if (value == BallGameState.Reset)
{
_gameStateBehaviour = GameStateResetBehaviour;
}
else if (value == BallGameState.Running)
{
_gameStateBehaviour = GameStateRunningBehaviour;
}
else if (value == BallGameState.ReadyToFire)
{
_gameStateBehaviour = GameStateReadyToFireBehaviour;
}
_state = value;
}
get
{
return _state;
}
}
//#endregion ----------------------------------------------------------------------------------------------------
//#region VALIDATION --------------------------------------------------------------------------------------------
protected void OnValidate()
{
Assert.IsNotNull<BallColliderController>(_ballColliderController, ErrorMessage.NoComponentAttached<BallColliderController>(typeof(BallController).Name));
Assert.IsNotNull<Rigidbody2D>(_rigidbody, ErrorMessage.NoComponentAttached<Rigidbody2D>(typeof(BallController).Name));
Assert.IsNotNull<Transform>(_resetPosition, ErrorMessage.NoComponentAttached<Transform>(typeof(BallController).Name));
}
//#endregion ----------------------------------------------------------------------------------------------------
//#region INIT_AND_EXIT -----------------------------------------------------------------------------------------
public void Init(GameCore gameCore)
{
_gameCore = gameCore;
_gameCore.Dispatcher.AddHandler(EventNames.LOSE_ROUND, OnLoseRound);
_gameCore.Dispatcher.AddHandler(EventNames.WIN_ROUND, OnWinRound);
_gameCore.Dispatcher.AddHandler(EventNames.RESET_TO_NEW_ROUND, OnResetToNewRound);
_gameCore.Dispatcher.AddHandler(EventNames.SAVE_DATA, OnSaveData);
_gameCore.Dispatcher.AddHandler(EventNames.PREPARE_NEW_GAME, OnPrepareNewGame);
_gameCore.Dispatcher.AddHandler(EventNames.PREPARE_LOADED_GAME, OnPrepareLoadedGame);
_ballColliderController.Init(this);
}
public void Exit()
{
_gameCore.Dispatcher.RemoveHandler(EventNames.LOSE_ROUND, OnLoseRound);
_gameCore.Dispatcher.RemoveHandler(EventNames.WIN_ROUND, OnWinRound);
_gameCore.Dispatcher.RemoveHandler(EventNames.RESET_TO_NEW_ROUND, OnResetToNewRound);
_gameCore.Dispatcher.RemoveHandler(EventNames.SAVE_DATA, OnSaveData);
_gameCore.Dispatcher.RemoveHandler(EventNames.PREPARE_NEW_GAME, OnPrepareNewGame);
_gameCore.Dispatcher.RemoveHandler(EventNames.PREPARE_LOADED_GAME, OnPrepareLoadedGame);
}
//#endregion ----------------------------------------------------------------------------------------------------
//#region BALL_BEHAVIOUR ----------------------------------------------------------------------------------------
protected void Update()
{
if (!_gameCore.TimeController.IsPaused)
{
_gameStateBehaviour();
}
}
private void GameStateResetBehaviour()
{
_rigidbody.velocity = ConstantValues.VECTOR2_ZERO;
_wasShootedAfterReset = false;
}
private void GameStateRunningBehaviour()
{
//do nothing in this state
}
private void GameStateReadyToFireBehaviour()
{
if (!_wasShootedAfterReset && _gameCore.Input.Fire.State == GameInputButton.ButtonState.JustPressed)
{
_wasShootedAfterReset = true;
FireBallOnStartRound();
State = BallGameState.Running;
}
}
private void FireBallOnStartRound()
{
Vector3 playerPosition = _gameCore.Player.transform.position;
Vector3 ballMovementDirection = (playerPosition - this.transform.position).normalized;
_rigidbody.velocity = -ballMovementDirection * _ballMovementSpeed;
}
public void BounceFromTheRocket()
{
Vector3 playerPosition = _gameCore.Player.transform.position;
Vector3 ballMovementDirection = (playerPosition - this.transform.position).normalized;
_rigidbody.velocity = -ballMovementDirection * _ballMovementSpeed;
}
//#endregion ----------------------------------------------------------------------------------------------------
//#region PUBLIC_METHODS (EVENTS) -------------------------------------------------------------------------------
public void OnWinRound(object obj)
{
State = BallGameState.Reset;
_ballMovementSpeed += _ballSpeedMovementAccelerationPerLevel;
}
public void OnLoseRound(object obj)
{
State = BallGameState.Reset;
}
public void OnResetToNewRound(object obj)
{
this.transform.position = _resetPosition.position;
State = BallGameState.ReadyToFire;
}
public void OnPrepareNewGame(object obj)
{
this.transform.position = _resetPosition.position;
State = BallGameState.ReadyToFire;
_ballMovementSpeed = _gameCore.GameDataManager.DesignData.BasicBallSpeedMovement;
_ballSpeedMovementAccelerationPerLevel = _gameCore.GameDataManager.DesignData.BallSpeedMovementAccelerationPerLevel;
}
public void OnPrepareLoadedGame(object obj)
{
SavedGameData data = _gameCore.SaveGameController.SavedGameData;
State = data.Ball.State;
_ballMovementSpeed = data.Ball.Speed;
_ballSpeedMovementAccelerationPerLevel = _gameCore.GameDataManager.DesignData.BallSpeedMovementAccelerationPerLevel;
if (State == BallGameState.Reset)
{
this.transform.position = _resetPosition.position;
_rigidbody.velocity = ConstantValues.VECTOR2_ZERO;
State = BallGameState.ReadyToFire;
}
else if (State == BallGameState.Running)
{
this.transform.position = new Vector3(
data.Ball.PositionX,
data.Ball.PositionY,
0f);
_rigidbody.velocity = new Vector2(
data.Ball.MovementDirectonX,
data.Ball.MovementDirectonY);
}
else if (State == BallGameState.ReadyToFire)
{
this.transform.position = _resetPosition.position;
_rigidbody.velocity = ConstantValues.VECTOR2_ZERO;
}
}
public void OnSaveData(object saveGameController)
{
SaveGameController controller = (SaveGameController)saveGameController;
controller.SaveBallDataCallback(
State,
_ballMovementSpeed,
this.transform.position,
_rigidbody.velocity);
}
//#endregion ----------------------------------------------------------------------------------------------------
}
}
//BlocksManager.cs
//Created by: <Name Surname>
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Assertions;
using Arkanoid.Utils;
namespace Arkanoid.Game
{
/// <summary>
/// BlockManager is responsible for reciving events from dispatcher and reacting for them (sending request to BlocksGenerator to create new level or load saved level),
/// counting blocks on map and dispatching event WIN_ROUND when last block on map is beated by ball.
/// It has accessor for BlocksPoolController.
/// </summary>
[RequireComponent(typeof(BlocksPoolController))]
[RequireComponent(typeof(BlocksGenerator))]
public class BlocksManager : MonoBehaviour, IResetable, IDataToSave
{
//#region PRIVATE_FIELDS ----------------------------------------------------------------------------------------
private GameCore _gameCore = null;
private BlocksGenerator _blocksGenerator = null;
private BlocksPoolController _blocksPool = null;
private List<BlockController> _blocksOnMapList = null;
private int _blocksOnMap = 0;
private int _numberOfRows = 2;
private int _numberOfColumns = 18;
[SerializeField]
private BlockController _blockPrefab = null;
//#endregion ----------------------------------------------------------------------------------------------------
public BlocksPoolController Pool { get { return _blocksPool; } }
//#region INIT_AND_EXIT -----------------------------------------------------------------------------------------
public void Init(GameCore gameCore)
{
_gameCore = gameCore;
_gameCore.Dispatcher.AddHandler(EventNames.RESET_TO_NEW_ROUND, OnResetToNewRound);
_gameCore.Dispatcher.AddHandler(EventNames.ADD_BLOCK_TO_MAP, OnAddBlockToMap);
_gameCore.Dispatcher.AddHandler(EventNames.REMOVE_BLOCK_FROM_MAP, OnRemoveBlockFromMap);
_gameCore.Dispatcher.AddHandler(EventNames.SAVE_DATA, OnSaveData);
_gameCore.Dispatcher.AddHandler(EventNames.PREPARE_NEW_GAME, OnPrepareNewGame);
_gameCore.Dispatcher.AddHandler(EventNames.PREPARE_LOADED_GAME, OnPrepareLoadedGame);
LoadDesignData(gameCore.GameDataManager.DesignData);
_blocksOnMapList = new List<BlockController>();
_blocksPool = this.GetComponent<BlocksPoolController>();
_blocksPool.Init(_blockPrefab, _numberOfRows * _numberOfColumns);
_blocksGenerator = this.GetComponent<BlocksGenerator>();
_blocksGenerator.Init();
}
public void Exit()
{
_gameCore.Dispatcher.RemoveHandler(EventNames.RESET_TO_NEW_ROUND, OnResetToNewRound);
_gameCore.Dispatcher.RemoveHandler(EventNames.ADD_BLOCK_TO_MAP, OnAddBlockToMap);
_gameCore.Dispatcher.RemoveHandler(EventNames.REMOVE_BLOCK_FROM_MAP, OnRemoveBlockFromMap);
_gameCore.Dispatcher.RemoveHandler(EventNames.SAVE_DATA, OnSaveData);
_gameCore.Dispatcher.RemoveHandler(EventNames.PREPARE_NEW_GAME, OnPrepareNewGame);
_gameCore.Dispatcher.RemoveHandler(EventNames.PREPARE_LOADED_GAME, OnPrepareLoadedGame);
}
private void LoadDesignData(DesignDataSettings data)
{
_numberOfRows = data.NumberOfRows;
_numberOfColumns = data.NumberOfColumns;
}
//#endregion ----------------------------------------------------------------------------------------------------
//#region VALIDATION --------------------------------------------------------------------------------------------
protected void OnValidate()
{
Assert.IsNotNull<BlockController>(_blockPrefab, ErrorMessage.NoComponentAttached<BlockController>(typeof(BlocksManager).Name));
}
//#endregion ----------------------------------------------------------------------------------------------------
//#region PUBLIC_METHODS (EVENTS) -------------------------------------------------------------------------------
public void OnResetToNewRound(object obj)
{
_blocksOnMap = 0;
_gameCore.Dispatcher.DispatchEvent(EventNames.CLEAR_BLOCKS);
_blocksGenerator.GenerateNewLevel(_numberOfColumns, _numberOfRows, Pool);
}
public void OnAddBlockToMap(object blockController)
{
_blocksOnMapList.Add((BlockController)blockController);
_blocksOnMap += 1;
}
public void OnRemoveBlockFromMap(object blockController)
{
_blocksOnMapList.Remove((BlockController)blockController);
_blocksOnMap -= 1;
if (_blocksOnMap == 0)
{
_gameCore.Dispatcher.DispatchEvent(EventNames.WIN_ROUND);
}
else if (_blocksOnMap < 0)
{
Debug.LogError(ErrorMessage.BlocksLessThanZero);
}
}
public void OnSaveData(object saveGameController)
{
SaveGameController controller = (SaveGameController)saveGameController;
controller.SaveBlocksDataCallback(_blocksOnMapList);
}
public void OnPrepareNewGame(object obj)
{
_blocksGenerator.GenerateNewLevel(_numberOfColumns, _numberOfRows, Pool);
}
public void OnPrepareLoadedGame(object obj)
{
_blocksGenerator.GenerateLevelFromLoadedData(
_gameCore.SaveGameController.SavedGameData.Blocks,
Pool);
}
//#endregion ----------------------------------------------------------------------------------------------------
}
}
//BlocksPoolController.cs
//Created by: <Name Surname>
using UnityEngine;
using System.Collections.Generic;
namespace Arkanoid.Game
{
/// <summary>
/// BlocksPoolController provides object pooling for NormalBlock prefabs which has BlockController.
/// </summary>
public class BlocksPoolController : MonoBehaviour
{
private Stack<BlockController> _poolStack = null;
private BlockController _blockPrefab = null;
public void Init(BlockController blockPrefab, int blocksCount)
{
_poolStack = new Stack<BlockController>();
_blockPrefab = blockPrefab;
PrepareBlocksPool(blocksCount);
}
private void PrepareBlocksPool(int blocksCount)
{
for (int i = 0; i < blocksCount; ++i)
{
CreateBlockOnStack(_blockPrefab);
}
}
private void CreateBlockOnStack(BlockController blockPrefab)
{
BlockController block = Instantiate<BlockController>(blockPrefab);
block.Init();
block.transform.SetParent(this.transform);
block.gameObject.SetActive(false);
_poolStack.Push(block);
}
/// <summary>
/// Returns BlockController in disabled GameObject.
/// </summary>
public BlockController GetBlockFromPool()
{
BlockController blockToReturn = _poolStack.Pop();
if (blockToReturn == null)
{
CreateBlockOnStack(_blockPrefab);
}
blockToReturn.IsPooled = true;
return blockToReturn;
}
/// <summary>
/// Gives back to pool BlockController passed in argument and disabling it gameobject.
/// </summary>
public void ReturnBlockToPool(BlockController block)
{
block.IsPooled = false;
block.gameObject.SetActive(false);
_poolStack.Push(block);
}
}
}

Code review projektu gry

Ogólne uwagi

  1. Moim zdaniem tego typu komentarze na początku pliku są złą praktyka:
//GameInput.cs
//Created by: <Name Surname>

Czemu? Pierwsza linia totalnie nic nie wnosi. Druga linia, też bardzo tak sobie. Od sprawdzenia kto jest autorem kodu jest git blame (nazywane czasem annotate albo history w różnych okienkowych gitach). Czasem można się spotkać, że na początku pliku źródłowego umieszcza się notę licencyjną - i takie coś pewnie byłoby ok, ale np. Github sugeruje, żeby dorzucić plik z licencją do repozytorium.

  1. Pliki mają pomieszane wcięcia ze spacjami. Łatwo to zaobserwować edytując je w czymś co domyślnie zamienia tabulator na 8 spacji, wtedy pliki przykładowo wyglądają tak:
namespace Arkanoid.Game
{
    /// <summary>
    /// GameInput is responsible for defining and updating game input.
    /// </summary>
	public class GameInput : MonoBehaviour 
	{
        public GameInputButton Left = null;

Chyba całe summary powinno być na tym samym poziomie co public class .... Pewnie warto przelecieć wszystkie pliki jakąś kombinacją klawiszy w VS która takie rzeczy poprawi.

  1. public void Init() i protected void Update() - czy napewno potrzebujemy specyfikować klasyfikatory dostępu? Może bez nich kod dalej będzie ok, ze względu na dziedziczenie (bo np. Update jest dziedziczone z MonoBehaviour). Przykładowo w dokumentacji nie piszą dostępu: https://docs.unity3d.com/ScriptReference/MonoBehaviour.Update.html

To ofc bardzo dyskusyjna kwestia... pytanie czy jest sens pisać coś co i tak jest generowane. Bycie eksplicit zazwyczaj nie jest złe, ale może sprawić że kod będzie rozwlekły albo że będzie mniej czytelny.

Plik GameInput.cs

  1. Nie jestem pewien czy:
/// <summary>
/// GameInput is responsible for defining and updating game input.
/// </summary>

to najlepszy możliwy opis. Warto zobaczyć jakie są konwencje pisania takich rzeczy w C# lub w projektach Unity. Być może czasem nie warto robić stringa dokumentacyjnego (bardzo dyskusyjna kwestia).

Może: /// Defines and updates game inputs. - ale tak jak mówię, nie znam się na konwencjach Unity/C#.

  1. W GameInput.Update jest podwójne Fire.UpdateState() i brakuje Enter.UpdateState() (oj, testy by to wykryły :P).

Plik BlocksManager.cs

  1. Opis:
    /// <summary>
    /// BlockManager is responsible for reciving events from dispatcher and reacting for them (sending request to BlocksGenerator to create new level or load saved level), 
    /// counting blocks on map and dispatching event WIN_ROUND when last block on map is beated by ball.
    /// It has accessor for BlocksPoolController.
    /// </summary>

Uwagi:

  • Typo reciving -> receiving
  • react for them - raczej - react to them, ale tu nie jestem pewien.
  • WIN_ROUND event zamiast event WIN_ROUND
  • Gdy powtarzamy on map to powinno raczej być on the map.
  • Wydaje mi się, że powinno być is hit by the ball zamiast is beated by the ball. Samo beated jest złą odmianą. Kolejne odmiany słowa beat to beat beat beaten.
  • It has an accessor zamiast It has accessor

Być może lepiej to napisać w takiej formie:

Receives events from dispatcher and reacts to them by:
- sending request to BlocksGenerator to create new or load saved level
- counting bloks on map and dispatching WIN_ROUND event when last block on the map is hit by the ball

Nie jestem przekonany co do pisania o It has an accessor for BlocksPoolController. - użytkownik - tu, inny programista - może znaleźć to dość szybko przeglądająć listę pól/metod/akcesorów danego obiektu w IDE.

  1. Tak patrząc koncepcyjnie, to jest sens trzymać stan gracza z wyłącznie dwoma stanami Reset i Running? Nie patrzyłem na resztę kodu jeszcze, ale wydaje się to zbędne, jakby można było to zastąpić przez bool _gameRunning = false; czy coś podobnego.

  2. Regiony w VS nie używało się w ten sposób: #region nazwa? Tbh nie podoba mi się to zakomentowanie i te myślniki/kreski.

  3. Heh, [SerializeField] jak widzę w Unity dalej jest dziwnie określane. Via dokumentacja:

You will almost never need this.

A w tutorialach do Unity i wszelkich forach używają tego do pokazywania zmiennych w inspektorze np. https://unity3d.com/learn/tutorials/topics/tips/show-private-variables-inspector.

Anyway, w takim razie być może warto więcej zmiennych tym uwzględnić, jak na przykład:

private int _blocksOnMap = 0;
private int _numberOfRows = 2;
private int _numberOfColumns = 18;

a przynajmniej trochę wydaje się, że może się to przydać jakiemuś programiście/designerowi/testerowi gry.

Powyższe skreśliłem, bo widzę, że dalej wykorzystywane jest DesignDataSettings i tam są te opcje. Domyślam się, że ich edycja jest przyjemna. Wydaje się to rozsądnym rozwiązaniem :).

  1. Podoba mi się to całe OnValidate, chociaż dziwne że wymaga to aż tyle kodu.

Plik BallController.cs

  1. FireBallOnStartRound oraz BounceFromTheRocket współdzielą tą samą logikę - może metoda pomocnicza, która zostanie zawołana?

  2. Dwie pierwsze linie OnResetToNewRound i OnPrepareNewGame również; ale nie wiem czy bym to wyciągał do osobnej metody; chyyba tak.

  3. Gdzieś się też przewija taki kod, który być może warto wrzucić do jakiegoś private void _resetPosition()

                this.transform.position = _resetPosition.position;
                _rigidbody.velocity = ConstantValues.VECTOR2_ZERO;

Plik BlocksPollController.cs

  1. english:
/// BlocksPoolController provides object pooling for NormalBlock prefabs which has BlockController.
  • has -> have a
Returns BlockController in disabled GameObject.
  • in disabled -> in a disabled albo może from a disabled

Swoją drogą nei weim czy nie zamieniłbym nazw metod GetBlockFromPool i ReturnBlockToPool na jakieś Push~ i Pop~.

Gives back to pool BlockController passed in argument and disabling it gameobject.
  • może Pushes given BlockController back to pool and so disables its gameobject
//DesignDataSettings.cs
//Created by: <Name Surname>
using UnityEngine;
namespace Arkanoid.Game
{
/// <summary>
/// DesignDataSettings is responsible for creating scriptlable object with design data.
/// Thanks to this solution design data are stored in one place and it is more comfortable to manipulate them.
/// </summary>
[CreateAssetMenu(fileName = "DesignDataSettings", menuName = "Arkanoid.Game/DesignDataSettings")]
public class DesignDataSettings : ScriptableObject
{
[Header("Player settings")]
public int Lifes = 3;
public float PlayerSpeedMovement = 8f;
[Header("Ball settings")]
public float BasicBallSpeedMovement = 5f;
public float BallSpeedMovementAccelerationPerLevel = 1f;
[Header("Block settings")]
public int NumberOfRows = 2;
public int NumberOfColumns = 18;
[Range(0, 100)]
public float ChanceForDoubleScoreBlock = 20f;
[Range(0, 100)]
public float ChanceForBulletTimeBlock = 5f;
[Range(0, 100)]
public float ChanceForAdditionalLifeBlock = 5f;
[Range(0, 100)]
public float ChanceForNormalBlock = 100f;
public int ScoreForNormalBlock = 100;
public int ScoreForDoubleScoreBlock = 200;
public int AddLifesCount = 1;
public float BulletTimeScale = 0.5f;
public float BulletTimeDuration = 3f;
}
}
//GameInput.cs
//Created by: <Name Surname>
using UnityEngine;
using Arkanoid.Utils;
namespace Arkanoid.Game
{
/// <summary>
/// GameInput is responsible for defining and updating game input.
/// </summary>
public class GameInput : MonoBehaviour
{
public GameInputButton Left = null;
public GameInputButton Right = null;
public GameInputButton Fire = null;
public GameInputButton Enter = null;
public GameInputButton Esc = null;
public void Init()
{
Left = new GameInputButton(InputNames.LEFT);
Right = new GameInputButton(InputNames.RIGHT);
Fire = new GameInputButton(InputNames.FIRE);
Enter = new GameInputButton(InputNames.ENTER);
Esc = new GameInputButton(InputNames.ESC);
}
protected void Update()
{
Left.UpdateState();
Right.UpdateState();
Fire.UpdateState();
Fire.UpdateState();
Esc.UpdateState();
}
}
}
//PlayerController.cs
//Created by: <Name Surname>
using UnityEngine;
using UnityEngine.Assertions;
using System;
using Arkanoid.Utils;
namespace Arkanoid.Game
{
/// <summary>
/// PlayerController class is responsible for definig player behaviour, changing state of player (and thus current behaviour) and sending data to save to SaveGameController on proper event.
/// PlayerGameState changes when one of events is recived: LOSE_ROUND, WIN_ROUND, RESET_TO_NEW_ROUND, PREPARE_NEW_GAME, PREPARE_LOADED_GAME.
/// In class there is an Action "_gameStateBehaviour" which is called in FixedUpdate(). Depending on state it has proper method assigned (assigning is in PlayerGameState State accessor).
/// In GameStateRunningBehaviour() it checks proper input to decide what to do.
/// </summary>
[RequireComponent(typeof(Rigidbody2D))]
public class PlayerController : MonoBehaviour, IResetable, IDataToSave
{
public enum PlayerGameState
{
Reset,
Running
}
//region PRIVATE_FIELDS -----------------------------------------------------------------------------------------
private GameCore _gameCore = null;
private Rigidbody2D _rigidbody = null;
private Vector2 _leftMovementSpeedVector;
private Vector2 _rightMovementSpeedVector;
private float _movementSpeed = 3f;
private Action _gameStateBehaviour = null;
[SerializeField]
private Transform _resetPosition = null;
//#endregion ----------------------------------------------------------------------------------------------------
//#region ACCESSORS ---------------------------------------------------------------------------------------------
private PlayerGameState State
{
set
{
if (value == PlayerGameState.Reset)
{
_gameStateBehaviour = GameStateResetBehaviour;
}
else if (value == PlayerGameState.Running)
{
_gameStateBehaviour = GameStateRunningBehaviour;
}
}
}
//#endregion ----------------------------------------------------------------------------------------------------
//#region VALIDAION ---------------------------------------------------------------------------------------------
protected void OnValidate()
{
Assert.IsNotNull<Transform>(_resetPosition, ErrorMessage.NoComponentAttached<Transform>(typeof(PlayerController).Name));
}
//#endregion ----------------------------------------------------------------------------------------------------
//#region INIT_AND_EXIT -----------------------------------------------------------------------------------------
public void Init(GameCore gameCore)
{
_gameCore = gameCore;
_gameCore.Dispatcher.AddHandler(EventNames.WIN_ROUND, OnEndRound);
_gameCore.Dispatcher.AddHandler(EventNames.LOSE_ROUND, OnEndRound);
_gameCore.Dispatcher.AddHandler(EventNames.RESET_TO_NEW_ROUND, OnResetToNewRound);
_gameCore.Dispatcher.AddHandler(EventNames.SAVE_DATA, OnSaveData);
_gameCore.Dispatcher.AddHandler(EventNames.PREPARE_NEW_GAME, OnPrepareNewGame);
_gameCore.Dispatcher.AddHandler(EventNames.PREPARE_LOADED_GAME, OnPrepareLoadedGame);
_rigidbody = GetComponent<Rigidbody2D>();
_movementSpeed = _gameCore.GameDataManager.DesignData.PlayerSpeedMovement;
_leftMovementSpeedVector = new Vector2(-_movementSpeed, 0f);
_rightMovementSpeedVector = new Vector2(_movementSpeed, 0f);
}
public void Exit()
{
_gameCore.Dispatcher.RemoveHandler(EventNames.WIN_ROUND, OnEndRound);
_gameCore.Dispatcher.RemoveHandler(EventNames.LOSE_ROUND, OnEndRound);
_gameCore.Dispatcher.RemoveHandler(EventNames.RESET_TO_NEW_ROUND, OnResetToNewRound);
_gameCore.Dispatcher.RemoveHandler(EventNames.SAVE_DATA, OnSaveData);
_gameCore.Dispatcher.RemoveHandler(EventNames.PREPARE_NEW_GAME, OnPrepareNewGame);
_gameCore.Dispatcher.RemoveHandler(EventNames.PREPARE_LOADED_GAME, OnPrepareLoadedGame);
}
//#endregion ----------------------------------------------------------------------------------------------------
//#region PLAYER_BEHAVIOUR --------------------------------------------------------------------------------------
protected void FixedUpdate()
{
_gameStateBehaviour();
}
private void GameStateResetBehaviour()
{
DontMove();
}
private void GameStateRunningBehaviour()
{
if (_gameCore.Input.Left.State == GameInputButton.ButtonState.Pressed
&& _gameCore.Input.Right.State == GameInputButton.ButtonState.Pressed)
{
DontMove();
}
else if (_gameCore.Input.Left.State == GameInputButton.ButtonState.Pressed)
{
MoveLeft();
}
else if (_gameCore.Input.Right.State == GameInputButton.ButtonState.Pressed)
{
MoveRight();
}
else
{
DontMove();
}
}
private void DontMove()
{
_rigidbody.velocity = ConstantValues.VECTOR2_ZERO;
}
private void MoveLeft()
{
_rigidbody.velocity = _leftMovementSpeedVector;
}
private void MoveRight()
{
_rigidbody.velocity = _rightMovementSpeedVector;
}
//#endregion ---------------------------------------------------------------------------------------------------
//#region PUBLIC_METHODS (EVENTS) ------------------------------------------------------------------------------
public void OnEndRound(object obj)
{
State = PlayerGameState.Reset;
}
public void OnResetToNewRound(object obj)
{
this.transform.position = _resetPosition.position;
State = PlayerGameState.Running;
}
public void OnPrepareNewGame(object obj)
{
this.transform.position = _resetPosition.position;
State = PlayerGameState.Running;
}
public void OnPrepareLoadedGame(object obj)
{
SavedGameData data = _gameCore.SaveGameController.SavedGameData;
this.transform.position = new Vector3(
data.Player.PositionX,
data.Player.PositionY,
0f);
State = PlayerGameState.Running;
}
public void OnSaveData(object saveGameController)
{
SaveGameController controller = (SaveGameController)saveGameController;
controller.SavePlayerDataCallback(this.transform.position);
}
//#endregion ---------------------------------------------------------------------------------------------------
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment