Project description

A game made under 72 hours for the game jam Ludum Dare 49. Recoilman is about a man that needs to move around using the recoil of different guns.
Do you have what it takes to complete the mission?

Do you have the courage to face your fears?
Do you have the skill to overcome your limitations?
Are you… the recoil man?

I determined to make everything as modular as possible for this project, and I know that 72 hours to make super modular systems will be challenging, but I did it anyway.

Before I started to program any mechanics or anything, I thought out what implementations were needed to create the game. I determined a Statemachine would fit great for Enemies and maybe the weapons.

using CoreSystem.IdentitySystem;
using UnityEngine;

namespace CoreSystem.StateMachine 
{
    public class SMController : MonoBehaviour, IProperty
    {
        [SerializeField]
        private BaseState startBaseState;
        
        private BaseState _currentBaseState;
        private Owner _owner;
        private void Start()
        {
            if (startBaseState)
                ChangeState(startBaseState);
            else
                throw new MissingReferenceException("No startingState assigned!");
        }

        private void Update() => _currentBaseState.UpdateState();

        public void ChangeState(BaseState newBaseState)
        {
            if (_currentBaseState) _currentBaseState.ExitState();
            _currentBaseState = Instantiate(newBaseState);
            _currentBaseState.EnterState(this);
        }

        private void OnTriggerEnter2D(Collider2D other)
        {
            _currentBaseState.OnTriggerEnter2D(other);
        }

        public void SetOwner(Owner owner) => _owner = owner;
        public Owner GetOwner() => _owner;
    }
}

                    

This made me think about a more modular Statemachine and how to make it even more modular with behaviors that are a small set of systems called by each state.

For example, if I want to make an enemy move, just move forward. I can add the behavior MoveTowardsPlayerBehavior to the state of the enemy, and it will be called. I can also add the same behavior for the projectiles but create another instance of the ScriptableObject to change some options like speed.

using UnityEngine;

namespace CoreSystem.StateMachine
{
    [CreateAssetMenu(menuName = "Create State", fileName = "State", order = 0)]
    public class BaseState : ScriptableObject
    {
        [SerializeField] private BaseBehavior[] behaviors;

        private void CreateCopyOfBehaviors()
        {
            for (var i = 0; i < behaviors.Length; i++) behaviors[i] = Instantiate(behaviors[i]);
        }
        
        public virtual void EnterState(SMController controller)
        {
            CreateCopyOfBehaviors();
            foreach (var behavior in behaviors) behavior.EnterBehavior(controller);
        }

        public virtual void UpdateState()
        {
            foreach (var behavior in behaviors) behavior.UpdateBehavior();
        }

        public virtual void ExitState()
        {
            foreach (var behavior in behaviors) behavior.ExitBehavior();
        }

        public void OnTriggerEnter2D(Collider2D other)
        {
            foreach (var behavior in behaviors) behavior.OnTriggerEnter2D(other);
        }
    }
}    

                    

Here is the base for a behavior.

using UnityEngine;

namespace CoreSystem.StateMachine
{
    public abstract class BaseBehavior : ScriptableObject
    {
        protected SMController Controller;
        
        public virtual void EnterBehavior(SMController controller) => Controller = controller;
        
        public virtual void UpdateBehavior() {}
        
        public virtual void ExitBehavior() {}

        public virtual void OnTriggerEnter2D(Collider2D other) {}
    }
}

                    

Here is an example of behavior for a state. It does only one thing, and it's to move forward. This can then be combined with other behaviors that make up a state.

using CoreSystem.DataSystem;
using CoreSystem.StateMachine;
using UnityEngine;

namespace Behavior
{

    [CreateAssetMenu(menuName = "Create Behavior/MoveForwardsBehavior", fileName = "MoveForwardsBehavior", order = 0)]
    public class MoveForwardsBehavior : BaseBehavior
    { 
        [SerializeField] private float movementSpeed;

        public override void UpdateBehavior()
        {
            base.UpdateBehavior();
            Move();
        }

        private void Move() => Controller.transform.position += Controller.transform.up * (movementSpeed * Time.deltaTime);
    }
}