Wanted to get some thoughts on how I’ve tackled this problem.
I have a spaceship with multiple modules, each module has a function and is activated by pressing a particular key.
I want AI to also be able to fly the ship, but I don’t want to hook directly into the modules, I want the AI to “Press Keys” which in turn activate the modules.
What I’ve done is instead of using “Input.GetKeyDown” directly, my Module class has a “IUnityService” property which is an interface with “GetKeyDown”, “GetKeyUp”, “GetKey” etc.
The standard implementation (for the user) just acts like a wrapper around the Unity “Input” class:
public class UnityService : IUnityService
{
public bool GetKeyDown(KeyCode keyCode)
{
return Input.GetKeyDown(keyCode);
}
public bool GetKeyUp(KeyCode keyCode)
{
return Input.GetKeyUp(keyCode);
}
public bool GetKey(KeyCode keyCode)
{
return Input.GetKey(keyCode);
}
}
The AI Implementation also has “SetKeyDown”, “SetKey”, “SetKeyUp” etc. Each of these functions have an associated hashtable with the key as the KeyCode and a boolean for the value.
public class AIInputService : IUnityService
{
Hashtable _keysHeld = new Hashtable();
Hashtable _keysPressed = new Hashtable();
Hashtable _keysReleased = new Hashtable();
public void SetKey(KeyCode keyCode, bool isHeld)
{
if (_keysHeld.ContainsKey(keyCode))
{
_keysHeld[keyCode] = isHeld;
}
else
{
_keysHeld.Add(keyCode, isHeld);
}
}
public void SetKeyDown(KeyCode keyCode, bool isPressed)
{
if (_keysPressed.ContainsKey(keyCode))
{
_keysPressed[keyCode] = isPressed;
}
else
{
_keysPressed.Add(keyCode, isPressed);
}
}
public void SetKeyUp(KeyCode keyCode, bool isReleased)
{
if (_keysReleased.ContainsKey(keyCode))
{
_keysReleased[keyCode] = isReleased;
}
else
{
_keysReleased.Add(keyCode, isReleased);
}
}
public bool GetKey(KeyCode keyCode)
{
if (_keysHeld.ContainsKey(keyCode))
{
return (bool)_keysHeld[keyCode];
}
return false;
}
public bool GetKeyDown(KeyCode keyCode)
{
if (_keysPressed.ContainsKey(keyCode))
{
var value = (bool)_keysPressed[keyCode];
return value;
}
return false;
}
public bool GetKeyUp(KeyCode keyCode)
{
if (_keysReleased.ContainsKey(keyCode))
{
var value = (bool)_keysReleased[keyCode];
return value;
}
return false;
}
}
The AI will override the UnityService property on the modules with the AI Implementation, and then it can use the “Set” functions to change keys to be pressed and not pressed etc.
This is all great because I don’t even have to worry that AI will be able to do things that the player is not able to do, and because it’s all running the same code, there’s no repeated code and I don’t need to expose a bunch of functions on my modules.
I learned the “UnityService” injection for testing purposes, but it turns out it has another great use case!
I also realized this could be (situationally) used for multiplayer, if you wanted to pass the inputs over the network rather than just syncing the transforms.
Hopefully that helps anyone trying to keep their player and AI controllers more in sync!