reepblue Posted September 16 Share Posted September 16 Back in July, I talked about the idea of building a compartmentalized base application for Ultra Engine. I now want to share what I've been working on and my future plans going forward. This foundation in which I'm dubbing "The Ultra Game Template" is base code and assets to help developers start their next project right. The goal was to have a base layer that did all the "not so fun" but critical elements when building a game such as scene management, input handling, window resizing, etc. The scene/map space is left alone and attaching components works the same if you started your project from scratch and a lot of the back-end stuff can be customized via the config.json file. Here's a list of some of the features. Splash Screen A splash screen is sometimes required for displaying legal notices and is useful for showing the end user that the application is loading in the background. The splash screen can be customized via the config file or disabled entirely. It should also look very familiar. :^) Built-In Settings All engine settings are exposed file the built-in settings window. You no longer need to tell testers to edit any config files ever again! They can now freely change their target display, resolution, graphic settings, and key bindings! Dynamic, Configurable and Easy-To-Use Input System. Coming from the input system from Cyclone, the template has support for key bindings. This is modeled after Steam Input where everything is defined as an action and can be contained in action sets. You can bind the same key to different actions in separated sets. Defining a base scheme is simple as making a function in the main.cpp file and passing the game controller pointer through it. // Define default controls. static void InstallControls(shared_ptr<GameController> controller) { if (controller == NULL) return; // Program actions controller->SetAction("Pause", BUTTON_KEY_ESCAPE); controller->SetAction("Terminate", BUTTON_KEY_END); controller->SetAction("ConsoleApp", BUTTON_KEY_F1); controller->SetAction("Fullscreen", BUTTON_KEY_F11); controller->SetAction("Screenshot", BUTTON_KEY_F2); controller->SetAction("Quick Save", BUTTON_KEY_F5); controller->SetAction("Quick Load", BUTTON_KEY_F6); // Camera ButtonAxis moveaxis = { BUTTON_KEY_W, BUTTON_KEY_S, BUTTON_KEY_A, BUTTON_KEY_D }; controller->SetAction("Movement", moveaxis, "InGameControls"); controller->SetAction("Sprint", BUTTON_KEY_SHIFT, "InGameControls"); controller->SetAction("Crouch", BUTTON_KEY_CONTROL, "InGameControls"); controller->SetAction("Climb", BUTTON_KEY_Q, "InGameControls"); controller->SetAction("Desent", BUTTON_KEY_E, "InGameControls"); controller->SetAction("Camera", AXIS_MOUSE, "InGameControls"); // Settings controller->SetSetting("Raw Mouse", false); controller->SetSetting("Inverse Mouse", false); controller->SetSetting("Mouse Smoothing", 0.0f); controller->SetSetting("Mouse Look Speed", 1.0f); } Then in the settings application will build the binding list for you separating the sets and settings into their own tabs. All of this is built using the engine's API so it's cross platform compatible! Event Based Console A developer console is something you can't live without when developing a game. When called (Default F1), a console window will pop up and will await your input. The commands are event based and there's no registering of commands required. Just put something like this in your component's ProcessEvent function. if (e.id == EVENT_CONSOLEEXECUTE) { auto line = e.text.Split(" "); auto cmd = line[0].ToString(); if (line.size() > 1 && !line[1].empty()) { if (cmd == "mycommand") { Print("Applied command " + QuoteString(cmd) + " to: " + line[1]); } } } Existing commands are: map - loading maps (Example: map box) restart - Restart the existing map. clear - Clears the current map. quit - Terminates the application. Future Plans! Only the base systems have been implemented, but there is still so much more to add. These things include a GameSpeaker class that'll read soundscripts for the correct volume, pitch, and range as well as supporting close captioning. I'd also would like to add in my Map Collection system like the one shipped in Cyclone. This is only a preview build. There's a lot of bugs that are both my fault and possible engine bugs I got to research and report. My goal is to release the template with the source code on GitHub close if not the day of the engine's release. Anyone will be free to use and modify it for their own purposes. I may charge for compiled binaries as it's possible to build your game using Lua on top of this and I would be compiling this on every platform. Play with it, suggest things, and let me know if this is something that could be useful. This probably should have been a blog post but I'm looking for feedback and conversations regarding this. UGTEarlyPreview.zip 5 Quote I made this with Leadwerks Game Engine! Cyclone - Vectronic - Darkness Awaits Template - Rolly: The Rollable Ball That Rolls Too Much If you like my work, consider supporting me on Patreon! Link to comment Share on other sites More sharing options...
reepblue Posted Saturday at 11:32 PM Author Share Posted Saturday at 11:32 PM I re-arranged the source code so it's isolated from everything else in your project. It's less of a template and more of a system you drop in and call from your main.cpp file. I think I'll be shipping just this folder and the config.json file. As for the main.cpp, I think I'm just going to post an example of one in the readme. But it's not that complicated. The idea is you register your components and input controls here. You can also install a custom loading screen if you don't like the default one. #include "UltraEngine.h" #include "Game/Game.h" using namespace UltraEngine; using namespace UltraEngine::Game; // Components #include "Components/Motion/Mover.hpp" #include "Components/Player/CameraControls.hpp" static void RegisterComponents() { RegisterComponent<Mover>(); RegisterComponent<CameraControls>(); } // Define default controls. static void InstallControls(shared_ptr<GameController> controller) { if (controller == NULL) return; // Program actions controller->SetAction("Pause", BUTTON_KEY_ESCAPE); controller->SetAction("Terminate", BUTTON_KEY_END); controller->SetAction("ConsoleApp", BUTTON_KEY_F1); controller->SetAction("Fullscreen", BUTTON_KEY_F11); controller->SetAction("Screenshot", BUTTON_KEY_F2); // Camera ButtonAxis moveaxis = { BUTTON_KEY_W, BUTTON_KEY_S, BUTTON_KEY_A, BUTTON_KEY_D }; controller->SetAction("Movement", moveaxis, "InGameControls"); controller->SetAction("Sprint", BUTTON_KEY_SHIFT, "InGameControls"); controller->SetAction("Crouch", BUTTON_KEY_CONTROL, "InGameControls"); controller->SetAction("Climb", BUTTON_KEY_Q, "InGameControls"); controller->SetAction("Desent", BUTTON_KEY_E, "InGameControls"); controller->SetAction("Camera", AXIS_MOUSE, "InGameControls"); controller->SetAction("Jump", BUTTON_KEY_SPACE, "InGameControls"); // Settings controller->SetSetting("Raw Mouse", false); controller->SetSetting("Inverse Mouse", false); controller->SetSetting("Mouse Smoothing", 0.0f); controller->SetSetting("Mouse Look Speed", 1.0f); } int main(int argc, const char* argv[]) { // Register Components. RegisterComponents(); // Init the program! auto app = GetProgram(); app->commandline = ParseCommandLine(argc, argv); if (!app->Initialize()) return 1; // Load all plugins. if (!app->LoadPlugins()) { Print("Warning: Failed to successfully load all plugins!"); } // Load packages if (!app->LoadPackages()) { Print("Warning: Failed to load one or more packages."); } // Load Settings if (!app->LoadSettings()) { Print("Warning: Failed to load program settings."); } // Set the correct program app type based on the commandline. ProgramApp programtype = PROGRAMAPP_GAME; if (app->commandline[CFLAG_SETTINGSAPP] == true) { programtype = PROGRAMAPP_SETTINGS; } else if (app->commandline[CFLAG_LUASCRIPT] == true) { programtype = PROGRAMAPP_LUASCRIPT; } // Init the game controller. app->gamecontroller = CreateGameController(); InstallControls(app->gamecontroller); if (!app->LoadControllerBindings()) { Print("Warning: Failed to load controller bindings."); } // Launch the app. if (app->LaunchApp(programtype) == false) return 1; // Install the loading screen. app->stage->SetLoadingScreen<LoadingScreen>(); // Main Loop bool running = true; while (running) { while (PeekEvent()) { const auto e = WaitEvent(); if (!app->ProcessEvent(e)) { EmitEvent(EVENT_QUIT); } if (e.id == EVENT_QUIT) { if (app->Shutdown()) { if (!app->SaveSettings()) { Print("Error: Failed to save program settings."); } running = false; break; } } } app->Update(); } // Write this line before we terminate! Print("Application terminated successfully!"); return 0; } 3 Quote I made this with Leadwerks Game Engine! Cyclone - Vectronic - Darkness Awaits Template - Rolly: The Rollable Ball That Rolls Too Much If you like my work, consider supporting me on Patreon! Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.