Archived, I’ve grown past this blog post, but it remains for historical reasons
In this post, we’ll address the development of the input system for Butter. Throughout projects I’ve developed in Unity, I’ve found Input to be a crucial but mostly under-developed aspect of a game’s ecosystem. In Butter, I aim to develop a system that is flexible, simple and effective.
Typically, in designing a system, I believe it is important to first outline the requirements that the system will have to fulfill. These requirements are in no way set in stone and may change as design decisions are made.
- Butter will have a simple combo system revolving around multiple buttons pressed in succession, thus input will require a way to be queued up and evaluated as a group. An effective data structure would be a modified circular buffer that would erase older elements as new ones arrive.
- An input device will require the ability to be mapped to any fighter.
- Input needs to support keyboard or controller input, and the combat inputs must be map-able to various keys.
- The system should support playing with multiple types of devices at the same time.
- An input device should always be able to be queried and if ever no device is present, the system should handle it gracefully. A Null object might be a good fit for this.
- It should be simple to replace the input device currently mapped to a fighter.
- The user of the device should be notified if ever the device becomes disconnected so that they can take appropriate action.
In games I’ve previously developed, I would have the character register to events that he cared about. This approach worked decently; however changing the controller that was driving the character required too many steps for my liking. A minimal amount of steps to change the driving controller would be preferred. Thus, I will instead have the controller drive events to the character without requiring the character to register to the events explicitly. The character will instead react accordingly to the events sent to him. If the character doesn’t care about a specific event, he will simply ignore it.
The design will be as follows:
Input devices will produce raw input events that an intermediary converter will massage into combat commands and then sent to the linked character. The linking of objects will happen in the character select and at the start of combat. During character select, a listener will listen to all active input devices for a trigger input event. Once this event is triggered for a device, a converter will be instantiated and placed into the correct character slot. When combat begins, an event will be fired indicating the start of the battle. The converter will retrieve the character associated to his slot and map raw inputs to the character’s commands using a configurable key map. This part addresses points 2, 6 and the last part of 3.
In order to always have a query-able input device, there would be a global input device which can be queried at any point in order to handle menu traversals and other single player type areas. In order to support multiple types of inputs, input devices will be retrieved from a homogeneous container of IInputDevices that will be populated by an input system. The input system and input devices will send off an event whenever a device gets disconnected in order to allow systems to react accordingly.
The only point left is the first, this point will be handled by the combat controller, the combat controller will maintain a circular buffer of the combat commands he’s received and he will process them using a priority queue. The more complex combos will be tested first down to the simple combat commands in order to support invoking combos before invoking simple attacks. Any commands that are not to be tested for combos will be filtered out before being placed into the circular buffer.
That’s everything for the design of the system, the implementation will be available on the repository. If you have any questions or thoughts, comment or send me an email.
Keep on keeping on!