InputMaster Primer

From Unify Community Wiki
(Difference between revisions)
Jump to: navigation, search
(OneWayControl)
(AxisControl)
Line 218: Line 218:
 
[[File:BSGTools_InputMaster_ScaleUpTest.gif‎]]
 
[[File:BSGTools_InputMaster_ScaleUpTest.gif‎]]
  
== AxisControl ==
+
== Moving The Player ==
  
Add two new AxisControls to PlayerController and add them as parameters for CreateMaster().
+
Add two new KeyControls to InputManager.
  
 
You can configure the Sensitivity, Gravity, Dead and Snap parameters. These have the same effect here as they do in Unity's Input system.
 
You can configure the Sensitivity, Gravity, Dead and Snap parameters. These have the same effect here as they do in Unity's Input system.
Note that while it isn't configured here, these settings can also be configured for a OneWayControl. DigitalControl does not use these settings.
 
  
 
<syntaxhighlight lang="csharp">
 
<syntaxhighlight lang="csharp">
 
using UnityEngine;
 
using UnityEngine;
using System.Collections;
 
 
using BSGTools.IO;
 
using BSGTools.IO;
 +
using BSGTools.Structure;
  
public class InputManager : MonoBehaviour {
+
public class InputManager : Singleton<InputManager> {
public readonly DigitalControl changeColor = new DigitalControl(KeyCode.Space, "Change Color");
+
public readonly KeyControl changeColor = new KeyControl(KeyCode.Space){
 +
Name = "Change Color"
 +
};
  
public readonly OneWayControl scaleUp = new OneWayControl(KeyCode.R, "Scale Up");
+
public readonly KeyControl scaleUp = new KeyControl(KeyCode.R){
 +
Name = "Scale Up"
 +
};
  
//Sensitivity = 2f, Gravity = 1.5f
+
public readonly KeyControl horizontal = new KeyControl(KeyCode.D, KeyCode.A){
public readonly AxisControl horizontal = new AxisControl(KeyCode.D, KeyCode.A, "Horizontal Movement", 3f, 1.5f);
+
Name = "Horizontal Movement",
 +
Sensitivity = 2f,
 +
Name = 1.5f
 +
};
  
//Depending on personal taste, this may look cleaner.
+
public readonly AxisControl vertical = new AxisControl(KeyCode.W, KeyCode.S) {
public readonly AxisControl vertical = new AxisControl(KeyCode.W, KeyCode.S, "Vertical Movement") {
+
Name = "Vertical Movement",
 
Sensitivity = 3f,
 
Sensitivity = 3f,
 
Gravity = 1.5f,
 
Gravity = 1.5f,
Line 249: Line 255:
  
 
void Start() {
 
void Start() {
Master = InputMaster.CreateMaster(changeColor, scaleUp, horizontal, vertical);
+
Master = InputMaster.CreateMaster(this);
 
DontDestroyOnLoad(this);
 
DontDestroyOnLoad(this);
 
}
 
}
Line 261: Line 267:
 
<syntaxhighlight lang="csharp">
 
<syntaxhighlight lang="csharp">
 
using UnityEngine;
 
using UnityEngine;
using System.Collections;
+
using BSGTools.IO;
  
 
public class PlayerController : MonoBehaviour {
 
public class PlayerController : MonoBehaviour {
Line 280: Line 286:
  
 
void Update() {
 
void Update() {
InputManager io = ManagerAccessor.InputManager;
+
var io = InputManager.Instance;
  
if(io.changeColor.Down)
+
if(io.changeColor.Down == ControlState.Positive)
 
renderer.material.color = new Color(Random.value, Random.value, Random.value);
 
renderer.material.color = new Color(Random.value, Random.value, Random.value);
  

Revision as of 21:42, 5 November 2014

Contents

InputMaster Primer

Relevant for version v7.1.1b

Initial Notes

  • This tutorial is NOT a Unity tutorial nor a primer to C# or programming in general. This tutorial assumes that you already have a good grasp of how Unity works and that you have intimate knowledge of programming concepts and the C# language.
  • BSGTools is C# ONLY. Unityscript and Boo are NOT supported.
  • InputMaster was designed for programmers, by a programmer. It has no configurable options in the Inspector, and should not be manually added to a GameObject for any reason. All configuration is done in code. However, if you'd like to make some variables editable in the Inspector, you can expose them through your InputManager.
  • It is worth mentioning that this tutorial uses a Singleton pattern to provide access to the InputManager script we will be creating. This pattern is easy and fast to implement, which is why I'm using it here. Some people argue that the Singleton pattern is bad design. Regardless, this is a beginner's tutorial, and I'll be treating it as such.

Setting Up The Scene

Our basic scene will be comprised of a few very basic objects:

  1. An orthographic camera
  2. A quad primitive (our basic "player")
  3. A directional light (to make things actually visible)

Creating The InputManager

Once the basic scene is set up, create a new C# script, name it InputManager. This InputManager script should inherit from the Singleton<T> class provided with InputMaster. Your script should look like this:

using UnityEngine;
using BSGTools.IO;
using BSGTools.Structure;
 
public class InputManager : Singleton<InputManager> {
 
}

The initial goal is to create a single control and an InputMaster object. Creating the Master object sets several things in motion:

  • It tells the system to create a new instance of InputMaster
  • To call DontDestroyOnLoad on InputMaster.
  • To provide the new master instance with the default controls for the game.


Note that you will never actually see your instance of InputMaster. It will be attached to a hidden GameObject.

First things first, creating a Master object. Creating a new master is as easy as calling CreateMaster:

public static InputMaster CreateMaster(object controlClass)

We pass in this as a parameter to CreateMaster(object) so that it can use some awesome reflection magic to get all of the Controls automatically from your InputManager instance. Note that this will cause a spike in performance if you have a lot of controls in your InputManager class and InputMaster gets created during gameplay. If this is the case, I recommend either explicitly adding an instance of your InputManager to a GameObject in a bootloader scene or to use this alternative method:

public static InputMaster CreateMaster(params Control[] controls)

Using this will require editing if you add additional controls later.

We want to make sure that this object is also not destroyed on a new scene load, so add DontDestroyOnLoad(this) to your Start method.

All in all, your starting point should look like this:

using UnityEngine;
using BSGTools.IO;
using BSGTools.Structure;
 
public class InputManager : Singleton<InputManager> {
	public InputMaster Master { get; private set; }
 
	void Start () {
		Master = InputMaster.CreateMaster(this);
		DontDestroyOnLoad(this);
	}
}

Creating The Controls

InputMaster has been tirelessly reworked to be as simple yet as powerful as possible. One major step of this change was to reduce the 3 original control types (DigitalControl, AxisControl, OneWayControl) to a single type, KeyControl. Controls for Xbox 360 controllers have their own types. This monolith control type:

  • Has two bindings.
    • Positive - REQUIRED
    • Negative - Optional
  • Maintains three states:
    • Down - Was the control pressed this frame?
    • Up - Was the control released this frame?
    • Held - True as long as the control is pressed.
  • Tracks two values:
    • Value - The smoothed value between -1...0...1
      • Will only ever be negative if you have a Negative binding for the control.
    • FixedValue - An integer value (actually a sbyte) version of Value. Will only ever be -1, 0 or 1

We're going to have our player (read: quad) change color every time its pressed. There is only one required parameter, the KeyCode binding. However, if you'd like to give it a friendly name you can as well. This is a good idea, as doing a ToString() on a Control object returns this name. If the name is not defined, a name of this pattern is created for you: UNNAMED_[Random 12 Char String]. Defining this name now and not later makes debugging MUCH easier, as this name is used in exceptions and stack traces.

Your InputManager should look something like this at this point:

using UnityEngine;
using BSGTools.IO;
using BSGTools.Structure;
 
public class InputManager : Singleton<T> {
	public readonly KeyControl changeColor = new KeyControl(KeyCode.Space){
		Name = "Change Color"
	};
 
	public InputMaster Master { get; private set; }
 
	void Start() {
		Master = InputMaster.CreateMaster(this);
		DontDestroyOnLoad(this);
	}
}


This doesn't add functionality to the control yet, but when the game is run the system will be updating the control's state.

Control Functionality, or, The PlayerController

The InputManager is configured with a instance of InputMaster and the initial control is configured.

The last major step is to set up the PlayerController to turn the control's state into actual functionality.

Create a new C# script, name it PlayerController, and add it to the Quad primitive.

We need to check if the KeyControl's Down property has a specific flag. In previous versions of InputMaster, the Down/Up/Held properties were bools.

These states are no longer of type bool. They are now of type ControlState, which is an enumeration with the following flags:

  • ControlState.Positive - Does the positive binding have this state?
  • ControlState.Negative - Does the negative binding have this state?
  • ControlState.Neither - Do neither the positive or the negative binding have this state?
  • ControlState.Either - Do either of the positive or negative bindings have this state?
  • ControlState.Both - Do both of the positive and negative bindings have this state?

For example, to check if a control's positive binding is in the Down state, you can check by comparing the Down property of the control to ControlState.Positive. The trade off from previous versions of InputMaster is verbosity. However, you gain intimate knowledge of exactly what state your controls are in.

This script is really simple (for now). In our Update method we're going to get an instance of the InputManager via the Singleton<T>'s Instance property and poll the state of the changeColor KeyControl. If the Down state has the Positive flag, we assign a random color to the renderer's material.

using UnityEngine;
using System.Collections;
using BSGTools.IO;
 
public class PlayerController : MonoBehaviour {
	void Update() {
		var io = InputManager.Instance;
 
		if(io.changeColor.Down == ControlState.Positive)
			renderer.material.color = new Color(Random.value, Random.value, Random.value);
	}
}


Go to the editor, press play, and test the control:

BSGTools InputMaster ColorChangeTest.gif

Using Values

Go back to the InputManager script and add a new KeyControl:

using UnityEngine;
using BSGTools.IO;
using BSGTools.Structure;
 
public class InputManager : Singleton<InputManager> {
	public readonly KeyControl changeColor = new KeyControl(KeyCode.Space){
		Name = "Change Color"
	};
 
	public readonly KeyControl scaleUp = new KeyControl(KeyCode.R){
		Name = "Scale Up"
	};
 
	public InputMaster Master { get; private set; }
 
	void Start() {
		Master = InputMaster.CreateMaster(this);
		DontDestroyOnLoad(this);
	}
}


Returning to the PlayerController, we need to add several items:

  1. A Vector3 that will define the addition we will make to the original scale of the transform.
  2. A Vector3 that will store the original scale.
  3. The functionality that will take the Value of the scaleUp Control and apply it so that it scales up the quad.


using UnityEngine;
using BSGTools.IO;
 
public class PlayerController : MonoBehaviour {
	[SerializeField] //serialized so that it is editable in the Inspector
	private Vector3 scaleAdditive = new Vector3(5f, 5f, 0f);
 
	Vector3 originalScale;
 
	void Start() {
		originalScale = transform.localScale;
	}
 
	void Update() {
		var io = InputManager.Instance;
 
		if(io.changeColor.Down == ControlState.Positive)
			renderer.material.color = new Color(Random.value, Random.value, Random.value);
 
		//When scaleUp.Value == 0, we're adding nothing to the original scale.
		//When scaleUp.Value == 1, we're adding the full value of scaleAdditive to the original scale.
		transform.localScale = originalScale + (scaleAdditive * io.scaleUp.Value);
	}
}


Go to the editor, press play, and test the control:

BSGTools InputMaster ScaleUpTest.gif

Moving The Player

Add two new KeyControls to InputManager.

You can configure the Sensitivity, Gravity, Dead and Snap parameters. These have the same effect here as they do in Unity's Input system.

using UnityEngine;
using BSGTools.IO;
using BSGTools.Structure;
 
public class InputManager : Singleton<InputManager> {
	public readonly KeyControl changeColor = new KeyControl(KeyCode.Space){
		Name = "Change Color"
	};
 
	public readonly KeyControl scaleUp = new KeyControl(KeyCode.R){
		Name = "Scale Up"
	};
 
	public readonly KeyControl horizontal = new KeyControl(KeyCode.D, KeyCode.A){
		Name = "Horizontal Movement",
		Sensitivity = 2f,
		Name = 1.5f
	};
 
	public readonly AxisControl vertical = new AxisControl(KeyCode.W, KeyCode.S) {
		Name = "Vertical Movement",
		Sensitivity = 3f,
		Gravity = 1.5f,
		Dead = 0.1f,
		Snap = true
	};
 
	public InputMaster Master { get; private set; }
 
	void Start() {
		Master = InputMaster.CreateMaster(this);
		DontDestroyOnLoad(this);
	}
}


Finally, configure the PlayerController to use the values of these new inputs to adjust the transform position:


using UnityEngine;
using BSGTools.IO;
 
public class PlayerController : MonoBehaviour {
	[SerializeField] //serialized so that it is editable in the Inspector
	private Vector3 scaleAdditive = new Vector3(5f, 5f, 0f);
	Vector3 originalScale;
 
	[SerializeField]
	private float moveSpeed = 3f;
	[SerializeField]
	private Vector3 maxPosition = new Vector3(5f, 5f, 0f);
	Vector3 originalPosition;
 
	void Start() {
		originalScale = transform.localScale;
		originalPosition = transform.position;
	}
 
	void Update() {
		var io = InputManager.Instance;
 
		if(io.changeColor.Down == ControlState.Positive)
			renderer.material.color = new Color(Random.value, Random.value, Random.value);
 
		//When scaleUp.Value == 0, we're adding nothing to the original scale.
		//When scaleUp.Value == 1, we're adding the full value of scaleAdditive to the original scale.
		transform.localScale = originalScale + (scaleAdditive * io.scaleUp.Value);
 
		Vector3 position = transform.position;
 
		//While InputMaster uses Time.deltaTime to smooth values, not including it here
		//makes controlling values like this overly sensitive.
		position.x += io.horizontal.Value * Time.deltaTime * moveSpeed;
		position.y += io.vertical.Value * Time.deltaTime * moveSpeed;
 
		position.x = Mathf.Clamp(position.x, -maxPosition.x, maxPosition.x);
		position.y = Mathf.Clamp(position.y, -maxPosition.y, maxPosition.y);
 
		transform.position = position;
	}
}


Go to the editor, press play, and test the controls:

BSGTools InputMaster MovementTest.gif

Personal tools
Namespaces

Variants
Actions
Navigation
Extras
Toolbox