This is a brief techical description of my current engine I'm making at home.
Parts of it is also used for teaching. Some elements were written to show techniques or implementations.
It can use different renderers (two currently). One is for 2d and the other for 3d and therefore they don't support the same features.
For this to work, both renderers has the same basic interface used by the engine (Update, Late update, Update camera).
Each renderer also has it's own set of RenderCommand classes.
Both uses the same Resource system though. The resource manager has a basic set of functions for loading different kind of resources (sound, textures, fonts, models). All basic types of resources has their own base class with the most basic members (width, height, size, etc).
Each system using resources inherits from a ResLoader baseclass and tells Resource Manager to use that class to load a specific resource.
A thread pool is used to run sub-systems threaded.
All updates are made per object. Every GameObject gets an Opaque dictionary, in which you can register data of any type. Game objects instanced variables are in cache during it's update.
It solves the problem how different components should communicate with each other. Now they do it through the game object and never needs to know of each other.
In case of messaging, components can broadcast a message through it's game object to all components (and child objects).
Game object has a pointer to a blue print which defines the game object and which components to use.
Game objects and blue prints are created in whose memory pool.
All blueprints are defined in XML.
Each module/game can register their own components via an variadic macro:
CreateComponentFactoryMethod("aicomponent", Shooter3d::AIComponent, std::ref(aGameObjectManager));
The macro creates a lambda, using a global memory pool for creating the component, and sends the lambda to a Component factory for registration.
This keeps component ownership close to their module.
TODO: All game objects allocated at same memory size. Some game objects has a lot of unused data. Some others create data on the heap since the preallocated memory doesn't suffice.
Each blue print needs to calculate how much space it needs (instance data), and a more difficult memory allocater would be needed to allow objects of different space.
Verdicts: Update per component instead of per game object would probably produced fewer cache misses and allowed for easier threading.
If used with a script language the implementation is more useful. All variables are already “exposed”, and adding new ones from scripting already has an interface.
The engine has the state manager. The definition of “quit” is when there is no states.
The states are put on a stack and can be layered, for example if you add gameState->options and chooce quit, both last states can be layered and popped together. A state can add or pop states through a proxy object to stop states from updating or destroying other states.
GUI / Widgets
GUI system works with both DirectX and HGE and has no connection to any module except the common utilities and resource system.
GUI is loaded from XML and a GUI hierarchy can be loaded from another XML file:
This increases the readability (which is currently needed since it lacks GUI editor) and allows for less text and reuse of widgets.
Widgets inherit parents visibility, position and enabled:ness.
The system has support for decorators which can be “hanged” onto widgets.
The decorator replaces the current widget in the hierarchy and forward all calls to them. The only decorator used at the moment is tooltip, but technicly it could be used for dropshadow, frames/hi-lights and other effects.
Non-atomic widgets uses other widgets to function: i.e. a VerticalScrollWidget is a WidgetContainer with three ButtonWidgets (up, down, current position).
Renderer - using directX 11
Shader materials are defined in XML: https://www.dropbox.com/s/xe5bwhp3e5cqshv/DeferredInstanced.xml?dl=0
A PhysicWrapper keeps physX separated from the engine.
Object colliding is given an collision tag. Other systems can send a message with a collision function that runs if object with defined tags collided.
Has CharacterControllerComponent for controlling actors and components for static and dynamic objects.
Music and sound is played from Wwise. I was really impressed by the spatial effect in some VR-games so I had to try the OculusSpatializer.
I have basic knowledge of Wwise. I use it to play sound events with random pitch and random sound effects.
I can control RTPC values (exposed values from Wwise) from the engine.
Wwise is wrapped behind a SoundLayer class which the engine uses.
All input (except for VR) goes through the input mapper.
Games can map an input to an event (eInputAction::eLeftMouseDown) to an action (fire). The action-event will be sent to all registered classes listening to action-events.
Blueprints can aquire a VRControllerComponent to be moved by the helmet.
Blueprints can aquire a OculusTouchComponent, and depending on with controller ID is assigned, the gameobject will follow an occulus controller.
Rendering is affected as little as possible when VR support is activated. The render pipeline is run two times, but with a different camera each time and a different swap chain as targets.
There's two games using the engine. A FPS and 2d dungeon crawler with procedural generated levels.
The shooter has basic features working and implemented and is work in progress:
- Shooting enemies
- Enemies following waypoint made in editor. Changing behavior if player is near or heard.
- Using teleporters
- Missions - when all enemies died, lights are turned off and end of level lamp turned on.
- Changing levels
- Dying / reseting level
The dungeon crawler was made at first for demonstration in class. Features:
- Fully using the custom GUI
- Procedural generated levels, algorithm for creating levels is exchangable
* One room-creating algorithm which connect rooms, create doors, level objects, enemies, calculate if level fills requirements.
* Cellular automata used for creating cave-levels
* One algorithm made for making it easy to test different parts of algorithms
- Threaded A* for enemies