Introducing Cryptographer!

Cryptographer is a challenging puzzle-platformer that rewards player skill. Make use of bombs, switches, gates, and cannons to clear a path to the escape portal. Made in the Godot engine. Available now for $7.99!

View on Steam View on Itch.io

tutorial, godot, c#,

Dependency Injection and Player Control

Firebelley Firebelley Jun 24, 2019 · 5 mins read
Dependency Injection and Player Control

Dependency injection is a useful design tool in the software engineering world. It sounds fancy and intimidating at first, but it’s a very straightforward yet powerful technique that I would encourage you to consider in your game. This tutorial will show you how you can use dependency injection to great effect for player movement. This tutorial’s code is available in my tutorials repository here.

What is Dependency Injection?

Wikipedia has a fine definition for it:

…dependency injection is a technique whereby one object supplies the dependencies of another object…

It’s useful to clearly understand the idea of a dependency. A dependency is what it sounds like; a service, object, etc that is required by another. An object is dependent on its dependencies. A given piece of code cannot function well or at all without its dependencies.

So, to continue on with semantics, what is injection? Simply put, it is the placing of one object into another.

Defining Our Dependency

In my newest game, Cryptographer, I wanted to separate out the input-detecting code from the code that handles how the player moves. Let’s suppose for this tutorial’s purpose that we have a game where a player can move. Let’s also say that we have a concern about the tight coupling between the input-detecting code and the code that controls the player movement.

To decouple the code, we’ll put the input-detecting code in another object and reference that object to move the player. It will look something like this:

using Godot;

public class Player : KinematicBody2D
{
    private const float SPEED = 100f;

    private IPlayerController _controller;

    public override void _Ready()
    {
        _controller = new PlayerMovementController();
    }

    public override void _Process(float delta)
    {
        var velocity = _controller.GetMovementVector();
        MoveAndSlide(velocity * SPEED);

        if (_controller.IsJumpJustPressed())
        {
            GlobalPosition += Vector2.Up * 25f;
        }
    }

    public void SetController(IPlayerController controller)
    {
        _controller = controller;
    }
}

And our input-detecting code:

using Godot;

public class PlayerMovementController : IPlayerController
{
    private const string INPUT_MOVE_LEFT = "move_left";
    private const string INPUT_MOVE_RIGHT = "move_right";
    private const string INPUT_MOVE_UP = "move_up";
    private const string INPUT_MOVE_DOWN = "move_down";
    private const string INPUT_JUMP = "jump";

    public Vector2 GetMovementVector()
    {
        var movementVector = new Vector2();
        movementVector.x = Input.GetActionStrength(INPUT_MOVE_RIGHT) - Input.GetActionStrength(INPUT_MOVE_LEFT);
        movementVector.y = Input.GetActionStrength(INPUT_MOVE_DOWN) - Input.GetActionStrength(INPUT_MOVE_UP);
        movementVector = movementVector.Normalized();
        return movementVector;
    }

    public bool IsJumpJustPressed()
    {
        return Input.IsActionJustPressed(INPUT_JUMP);
    }
}

Do you spot the dependency? Our dependency is _controller which has an interface type of IPlayerController. The interface is defined as follows:

using Godot;

public interface IPlayerController
{
    Vector2 GetMovementVector();
    bool IsJumpJustPressed();
}

Using the Dependency

The reason we put our concrete code behind an interface IPlayerController is because now we can create other concrete code that implements the methods defined in the interface. We can define concrete code that returns whatever numbers we want out of IPlayerController.GetMovementVector, for example, that is still compatible with our Player.

This is where the injection portion of dependency injection comes into play. Notice that we have a SetController method on our Player class, which takes a single argument of type IPlayerController. Here is that method:

public void SetController(IPlayerController controller)
{
    _controller = controller;
}

Whenever we call this method, we are injecting the controller dependency into the Player. The newly assigned _controller is referenced on frame update (_Process) to move the player.

Consider the following class that implements IPlayerController:

using Godot;

public class PlayerPlaceholderController : IPlayerController
{
    public Vector2 GetMovementVector()
    {
        return Vector2.Zero;
    }

    public bool IsJumpJustPressed()
    {
        return false;
    }
}

In here, default values are returned. What happens if we inject this version of our dependency into the player class? The player stops moving! Notice how we can achieve that behavior without any code changes to the Player class. We can simply swap out (or inject) this dependency as needed to affect how the player moves. Here is a gif to demonstrate this and other behaviors more clearly:

1

By clicking the buttons, I am injecting different player controller dependencies to alter the movement. I am holding down A on the keyboard while clicking the UI buttons to inject different controller dependencies. I highly recommend checking out the tutorial project to understand fully what’s being demonstrated here.

Conclusion

By defining our dependencies using an interface, we can swap out concrete definitions on the fly without altering the code that makes up our dependent Player class. So with all that said, I would implore you to consider how this technique might be useful in your projects. Hopefully that helps. If you have any questions please don’t hesitate to contact me.

Firebelley
Written by Firebelley
I am a hobbyist game developer and professional software engineer. I stream game development on Twitch.