The forbidden place

This is my first solo/indie game. 'The Forbidden Place' is a rogue-like game created in the Unity engine. This is also a project to level up my game development skills

Project details:

  • code language : C#

  • Software : Unity engine and GitHub

  • Team size : Solo

  • Date : Still in progress


My Contribution

Since this is my first solo/indie game, I'm essentially doing everything myself. Currently, my primary focus is on establishing the fundamental gameplay and systems required to create a game loop.

NOTE : This project is still in progress, so things may change

State machine & states

State machine

When I first started implementing the player's functionality, I realized that the player would need to perform various actions, and managing them all together could lead to conflicts. Therefore, I decided to use a state machine to keep the logic for each functionality separate.

Each state will add itself to the state machine through the addPlayerState function. The states are stored in a dictionary. This way, I have easy access to every state of the player.

Switchen player state function

                    
public void SwitchState(playerState newState)
{
    _currentState?.Exit();
    _currentState = _playerStates[newState];
    _currentState.Enter(this);
}
                                  
                

Adds the player state that is given to the playerStates dictionary

                    
public void AddPlayerState(playerState playerState, State state)
{
    if (_playerStates.ContainsKey(playerState)) return;

    _playerStates.Add(playerState, state);
}
                                  
                

States

To easily create new states, I've implemented a State interface. This interface includes four functions: Enter(), Tick(), FixedTick(), and Exit(). These functions are called within the state machine.

When creating a new state, you must have it inherit from the State interface. It's mandatory to implement the Enter() function in the new state. However, the other functions are optional, as not every state may require them.

State interface

                    
public interface State
{
    public void Enter();
    public void Tick(){}
    public void FixedTick(){}
    public void Exit(){}
}
                                  
                

EXAMPLE : DodgeState

                
public class DodgeState : MonoBehaviour, State
{
    [SerializeField] private float dodgeForce;
    private StateMachine _stateMachine;
    private Rigidbody _rigidbody;
    private Animator _animator;

    private Vector2 _movementDirection;
    private float timer;

    private void Awake()
    {
        _stateMachine = GetComponent();
        _rigidbody = GetComponent();
        _animator = GetComponentInChildren();
        _stateMachine.AddPlayerState(playerState.Dodge, this);
    }

    public void Enter()
    {
        timer = 0;
        _movementDirection = InputHandler.Instance.GetMovementDirectionValue();
        _animator.SetTrigger("Dodge");
        RotatePlayer();
        AddForce();
    }

    public void Exit()
    {
        _rigidbody.velocity = Vector3.zero;
    }

    public void DodgeEnded()
    {
        _stateMachine.SwitchState(playerState.Idle);
    }

    private void RotatePlayer()
    {
        if (_movementDirection == Vector2.zero) return;
        var targetRotation = Quaternion.LookRotation(new Vector3(_movementDirection.x, 0, _movementDirection.y));
        transform.rotation = targetRotation;
    }

    private void AddForce()
    {
        _rigidbody.velocity += transform.forward * dodgeForce;
    }
}
                              
            

Combat

Attack & combo

I have created a combo system where you can freely choose which attacks are included in a combo and in which order they will be executed. When creating a new attack, you can select an animation, which will be played when the attack is executed. Additionally, you can specify the damage of the attack and whether there should be a little force applied when executing it. The 'combo exit time' is the time before the combo is ended after the specific attack.

Attack State

If you attack the system will look for the animator override controller in the current attack. It will swap the attack animation and set the damage for the weapon. When the attack is done the system will invoke the EndCombo() method. The method will be invoked within a delay so the player has time to activate the next attack in the combo.

Attacking method

                    
private void Attack()
{
    if (_comboCounter > combo.Count || !(_lastAttackTime >= secondsBetweenAttacks)) return;
    CancelInvoke(nameof(EndCombo));
    if (_comboCounter >= combo.Count)
    {
        _comboCounter = 0;
    }
    _rigidbody.velocity = transform.forward * attackMoveForce;
    _animator.runtimeAnimatorController = combo[_comboCounter].AnimatorOverrideController;
    _damageSource.damage = combo[_comboCounter].Damage;
    _animator.Play("Attack",0,0);
    _comboCounter++;
    _lastAttackTime = 0;
}
                                  
                

Check if the player stopped attacking

                    
private void ExitAttack()
{
    if (_animator.GetCurrentAnimatorStateInfo(0).normalizedTime > 0.9 && _animator.GetCurrentAnimatorStateInfo(0).IsTag("Attack"))
    {
        Invoke(nameof(EndCombo), secondsBetweenAttacks + .2f);
    }
}