Signal Parameters

Introduction

This tutorial teaches how to add parameters to a signal to configure a commands execution.

In this tutorial we continue with the result of the previous tutorial. If this is the first time using IoC+, we suggest to read the introduction first.

The Move Signal

Now that our player view can jump and fall down, let’s also add the ability to move horizontally both left and right. Instead of making two signals and two commands to accomplish this we’re going to make one single move signal with a parameter to define its direction.

Create a new script file called “MoveInputSignal.cs” and add the following code.

using IoCPlus;
using UnityEngine;

public class MoveInputSignal : Signal<Vector3> { }

We use the generic Signal<T> class to define our direction parameter. IoC+ signals can go up to three parameters. To store more data in the parameters, simply make a class or struct and use that as a parameter.

Binding signals with parameters works exactly the same as with signals without parameters. Open up the GameContext and update it with the following code.

using IoCPlus;

public class GameContext : Context {

    protected override void SetBindings() {
        base.SetBindings();

        Bind<string>("Welcome! Look at me, I'm injecting a value!");
        Bind<JumpInputSignal>();
        Bind<MoveInputSignal>();
        Bind<JumpSignal>();

        BindMediator<PlayerInputMediator, PlayerInputView>();
        BindMediator<PlayerMediator, PlayerView>();

        On<EnterContextSignal>().Do<WelcomeCommand>()
                                .Do<InstantiatePlayerInputCommand>()
                                .Do<InstantiatePlayerCommand>();

        On<JumpInputSignal>().Do<JumpCommand>();
    }

}

Now that the signal is bound we can inject it into commands and mediators.

Dispatching the input Signal

Let’s open up our PlayerView script to handle the player’s input to let the mediator know when to dispatch the signal. Add the following code.

using IoCPlus;
using UnityEngine;

public class PlayerInputView : View {

    public readonly Signal JumpInputSignal = new Signal();
    public readonly Signal<Vector3> MoveInputSignal = new Signal<Vector3>();

    private void Awake() {
        Debug.Log("PlayerInputView is instantiated!");
    }

    private void Update() {
        if (Input.GetKeyDown(KeyCode.UpArrow)) {
            JumpInputSignal.Dispatch();
        }
        if (Input.GetKeyDown(KeyCode.LeftArrow)) {
            MoveInputSignal.Dispatch(Vector3.left);
        }
        if (Input.GetKeyDown(KeyCode.RightArrow)) {
            MoveInputSignal.Dispatch(Vector3.right);
        }
    }

}

Signals used in views can also contain parameters by using the same generic class. In the Update() method we check if the left or right arrow is pressed. If so, we dispatch the MoveInputSignal. Because the MoveInputSignal is a Signal<Vector3> we can only dispatch it with a Vector3 parameter, so we include the Vector3.left or Vector3.right accordingly.

Let’s open up the PlayerInputMediator and add the following code.

using IoCPlus;
using UnityEngine;

public class PlayerInputMediator : Mediator<PlayerInputView> {

    [Inject] JumpInputSignal jumpInputSignal;
    [Inject] MoveInputSignal moveInputSignal;

    public override void Initialize() {
        view.JumpInputSignal.AddListener(OnViewJumpInputSignal);
        view.MoveInputSignal.AddListener(OnViewMoveInputSignal);
    }

    public override void Dispose() {
        view.JumpInputSignal.RemoveListener(OnViewJumpInputSignal);
        view.MoveInputSignal.RemoveListener(OnViewMoveInputSignal);
    }

    private void OnViewJumpInputSignal() {
        jumpInputSignal.Dispatch();
    }

    private void OnViewMoveInputSignal(Vector3 direction) {
        moveInputSignal.Dispatch(direction);
    }

}

We repeat the same process: inject the MoveInputSignal, listen to our view, and dispatch the signal accordingly. Because the view’s MoveInputSignal has a parameter, we use a method with a Vector3 parameter as a listener. There, we dispatch the moveInputSignal that will be injected by the GameContext so we can add a command as a response.

Injecting Signal Parameters

We’ll need a command to respond to the input signal. Create a new script file called “MoveCommand.cs” and add the following code.

using IoCPlus;
using UnityEngine;

public class MoveCommand : Command {

    [InjectParameter] Vector3 inputDirection;

    protected override void Execute() {
        Debug.Log("Move command in direction: " + inputDirection);
    }

}

Woah, that’s a new one. We use the [InjectParameter] to inject a parameter of the signal that will trigger this command. In this case, the inputDirection will be set to the Vector3 parameter of the MoveInputSignal. We can then use this value in our command.

Let’s bind the MoveCommand as a response to the InputMoveSignal in the context. Open up the GameContext and add the following code.

using IoCPlus;

public class GameContext : Context {

    protected override void SetBindings() {
        base.SetBindings();

        Bind<string>("Welcome! Look at me, I'm injecting a value!");
        Bind<JumpInputSignal>();
        Bind<MoveInputSignal>();
        Bind<JumpSignal>();

        BindMediator<PlayerInputMediator, PlayerInputView>();
        BindMediator<PlayerMediator, PlayerView>();

        On<EnterContextSignal>().Do<WelcomeCommand>()
                                .Do<InstantiatePlayerInputCommand>()
                                .Do<InstantiatePlayerCommand>();

        On<JumpInputSignal>().Do<JumpCommand>();

        On<MoveInputSignal>().Do<MoveCommand>();
    }

}

By binding the MoveCommand as a response to the MoveInputSignal the Vector3 parameter of the signal will automatically be injected in the command.

Go ahead and hit play. The debugger should now log the correct message when pressing down the left or right mouse button.

Signal Parameter from Command to Mediator

Now that the parameter is successfully injected into our MoveCommand, we can dispatch a MoveSignal to actually move the player. Create a new script file called “MoveSignal.cs” and add the following code.

using IoCPlus;
using UnityEngine;

public class MoveSignal : Signal<Vector3> { }

Let’s bind this MoveSignal injection in our context. Open up the GameContext and add the following code.

using IoCPlus;

public class GameContext : Context {

    protected override void SetBindings() {
        base.SetBindings();

        Bind<string>("Welcome! Look at me, I'm injecting a value!");
        Bind<JumpInputSignal>();
        Bind<MoveInputSignal>();
        Bind<JumpSignal>();
        Bind<MoveSignal>();

        BindMediator<PlayerInputMediator, PlayerInputView>();
        BindMediator<PlayerMediator, PlayerView>();

        On<EnterContextSignal>().Do<WelcomeCommand>()
                                .Do<InstantiatePlayerInputCommand>()
                                .Do<InstantiatePlayerCommand>();

        On<JumpInputSignal>().Do<JumpCommand>();

        On<MoveInputSignal>().Do<MoveCommand>();
    }

}

Let’s dispatch the signal in the MoveCommand. Open up the MoveCommand and add the following code.

using IoCPlus;
using UnityEngine;

public class MoveCommand : Command {

    [InjectParameter] Vector3 inputDirection;

    [Inject] MoveSignal moveSignal;

    protected override void Execute() {
        moveSignal.Dispatch(inputDirection);
    }

}

The MoveCommand uses the same technique to include the signal parameter as the views and mediators do; by adding it as a parameter in the signal’s Dispatch() method.

Before listening to the MoveSignal in the PlayerMediator, let’s first add the functionality to move to the PlayerView. Open up the PlayerView and add the following code.

using IoCPlus;
using UnityEngine;

public class PlayerView : View {

    Rigidbody myRigidbody;

    public void Jump() {
        myRigidbody.AddForce(Vector3.up * 300.0f);
    }

    public void Move(Vector3 direction) {
        myRigidbody.AddForce(direction * 100.0f);
    }

    private void Awake() {
        myRigidbody = GetComponent<Rigidbody>();
    }

}

We’ve added the Move(Vector3) method to add force to the player’s rigidbody in the given direction. Now, open up the PlayerMediator and add the following code.

using IoCPlus;
using UnityEngine;

public class PlayerMediator : Mediator<PlayerView> {

    [Inject] JumpSignal jumpSignal;
    [Inject] MoveSignal moveSignal;

    public override void Initialize() {
        jumpSignal.AddListener(OnJumpSignal);
        moveSignal.AddListener(OnMoveSignal);
    }

    public override void Dispose() {
        jumpSignal.RemoveListener(OnJumpSignal);
        moveSignal.RemoveListener(OnMoveSignal);
    }

    private void OnJumpSignal() {
        view.Jump();
    }

    private void OnMoveSignal(Vector3 direction) {
        view.Move(direction);
    }

}

There! The PlayerMediator now injects the MoveSignal, listens to it and moves its view based on the given direction! All dots are now connected. Go ahead and hit play! Once we hit the left or right arrow key the player will start to move in that direction.

The IoC+ Monitor

Go ahead and open up the IoC+ Monitor, you’ll see the MoveInputSignal, MoveSignal and MoveCommand light up while it is happening in game.

Note that we can’t dispatch signals with parameters from the IoC+ Monitor.

Conclusion

In this tutorial we’ve seen how to create a signal with parameters. We’ve seen how to dispatch it from a view and listen to it from the view’s mediator. Commands use the [InjectParameter] to inject a signal parameter and can use the value in its execution. We’ve also seen how to dispatch a signal with a parameter from a command and listen to that signal in a mediator to make its view act accordingly.