-
Posts
2,600 -
Joined
-
Last visited
Content Type
Blogs
Forums
Store
Gallery
Videos
Downloads
Everything posted by reepblue
-
Also, speaking of layers, how would one blend 2 perspective cameras in one image. An example would be drawing a first-person view model on Layer 1 so it can have its own FOV settings and not be obstructed by anything. Here's what I tried. I expected the blue spin box to have a red cube being rendered on top of it, but I only see the red cube. #include "UltraEngine.h" #include "ComponentSystem.h" using namespace UltraEngine; int main(int argc, const char* argv[]) { //Get the displays auto displays = GetDisplays(); //Create a window auto window = CreateWindow("Ultra Engine", 0, 0, 1280, 720, displays[0], WINDOW_CENTER | WINDOW_TITLEBAR); //Create framebuffer auto framebuffer = CreateFramebuffer(window); //Create world auto world = CreateWorld(); //Create camera auto camera = CreateCamera(world, PROJECTION_ORTHOGRAPHIC); camera->SetClearColor(0.125); camera->SetFov(70); camera->SetPosition(0, 0, -3); //Create a light auto light = CreateBoxLight(world); light->SetRotation(35, 45, 0); light->SetRange(-10, 10); //Create a box auto box = CreateBox(world); box->SetColor(0, 0, 1); //Entity component system auto actor = CreateActor(box); auto component = actor->AddComponent<Mover>(); component->rotation.y = 45; //Create a view model camera auto cameraviewmodel = CreateCamera(world); cameraviewmodel->SetFov(70); cameraviewmodel->SetMatrix(camera->GetMatrix()); cameraviewmodel->SetClearMode(UltraEngine::CLEAR_DEPTH); cameraviewmodel->SetRenderLayers(UltraEngine::RENDERLAYER_1); auto viewmodel = CreateBox(world, 0.5f); viewmodel->SetRenderLayers(UltraEngine::RENDERLAYER_1); viewmodel->SetColor(1, 0, 0); viewmodel->Turn(45, 45, 0); // Although I'm further away, I should render on top! viewmodel->SetPosition(0, 0, 1); //Main loop while (window->Closed() == false and window->KeyHit(KEY_ESCAPE) == false) { world->Update(); world->Render(framebuffer); } return 0; }
-
Just found out my shadow text example breaks if SetDepthPrepass isn't set to false. The render layer I'm using is only meant for text so it's not a huge deal but something to note.
-
This example shows UI not updating properly or I'm doing it wrong. Either way, the list box stops working properly. Also, just want to bring up that the UI is still drawing ugly in 3D. This means I have to wait to work on game UI until this is fixed. #include "UltraEngine.h" #include "ComponentSystem.h" using namespace UltraEngine; int main(int argc, const char* argv[]) { //Get the displays auto displays = GetDisplays(); //Create a window auto window = CreateWindow("Ultra Engine", 0, 0, 1280, 720, displays[0], WINDOW_CENTER | WINDOW_TITLEBAR); //Create a world auto world = CreateWorld(); //Create a framebuffer auto framebuffer = CreateFramebuffer(window); //Create a camera auto camera = CreateCamera(world); camera->SetClearColor(0.125); camera->SetFov(70); camera->SetPosition(0, 0, -3); camera->SetViewport(200, 0, framebuffer->size.x - 200, framebuffer->size.y); auto uiCamera = CreateCamera(world, PROJECTION_ORTHOGRAPHIC); uiCamera->SetRenderLayers(RENDERLAYER_1); uiCamera->SetClearMode(CLEAR_DEPTH); uiCamera->SetPosition(framebuffer->size.x / 2, framebuffer->size.y / 2); auto ui = CreateInterface(world, LoadFont("Fonts/arial.ttf"), iVec2(200, framebuffer->size.y)); ui->SetRenderLayers(RENDERLAYER_1); auto sz = ui->root->ClientSize(); auto listbox = CreateListBox(5, 5, sz.x - 10, 200, ui->root, LISTBOX_DEFAULT)->As<ListBox>(); auto tabber = CreateTabber(5, 205, sz.x - 10, sz.y - 205, ui->root)->As<Tabber>(); tabber->AddItem("Settings", true); tabber->AddItem("Output"); for (int i = 0; i < 100; i++) { listbox->AddItem("Item " + String(i)); } //Create a light auto light = CreateBoxLight(world); light->SetRotation(35, 45, 0); light->SetRange(-10, 10); //Create a box auto box = CreateBox(world); box->SetColor(0, 0, 1); //Entity component system auto actor = CreateActor(box); auto component = actor->AddComponent<Mover>(); component->rotation.y = 45; //Main loop while (window->Closed() == false and window->KeyDown(KEY_ESCAPE) == false) { if (PeekEvent()) { auto ev = WaitEvent(); ui->ProcessEvent(ev); switch (ev.id) { case UltraEngine::EVENT_WINDOWCLOSE: if (ev.source == window) exit(0); break; default: break; } } // Rebuild the window. if (window->KeyDown(KEY_SPACE)) { static bool bSwapped = false; framebuffer = NULL; window = NULL; if (!bSwapped) { // Make it a tad bigger window = CreateWindow("Ultra Engine", 0, 0, 1400, 800, displays[0], WINDOW_CENTER | WINDOW_TITLEBAR); framebuffer = CreateFramebuffer(window); } else { window = CreateWindow("Ultra Engine", 0, 0, 1280, 720, displays[0], WINDOW_CENTER | WINDOW_TITLEBAR); framebuffer = CreateFramebuffer(window); } // Reposition the camera. uiCamera->SetPosition(framebuffer->size.x / 2, framebuffer->size.y / 2); // Resize the ui (Am I doing this right??) ui->Redraw(0, 0, framebuffer->size.x, framebuffer->size.y); ui->UpdateLayout(); bSwapped = !bSwapped; } world->Update(); if (framebuffer) world->Render(framebuffer); } return 0; }
-
I couldn't get a sprite with a material to show at all on a different layer. CreateSprite() works fine, but using LoadSprite or changing it's material to anything causes it to go invisible. Example: #include "UltraEngine.h" #include "ComponentSystem.h" using namespace UltraEngine; int main(int argc, const char* argv[]) { //Get the displays auto displays = GetDisplays(); //Create a window auto window = CreateWindow("Ultra Engine", 0, 0, 1280, 720, displays[0], WINDOW_CENTER | WINDOW_TITLEBAR); //Create framebuffer auto framebuffer = CreateFramebuffer(window); //Create world auto world = CreateWorld(); //Create camera auto camera = CreateCamera(world, PROJECTION_ORTHOGRAPHIC); camera->SetClearColor(0.125); //Create a ortho camera auto camera2D = CreateCamera(world, UltraEngine::PROJECTION_ORTHOGRAPHIC); camera2D->SetDepthPrepass(false); camera2D->SetClearMode(UltraEngine::CLEAR_DEPTH); camera2D->SetRenderLayers(UltraEngine::RENDERLAYER_7); camera2D->SetPosition((float)framebuffer->size.x * 0.5f, (float)framebuffer->size.y * 0.5f, 0); //Create sprite auto sprite = LoadSprite(world, "https://raw.githubusercontent.com/UltraEngine/Documentation/master/Assets/Materials/Sprites/nightraider.dds"); sprite->SetPosition(0, 0); sprite->mesh->material->SetAlphaMask(true); // I should appear in the bottom left corner but I don't. sprite->SetRenderLayers(UltraEngine::RENDERLAYER_7); //Main loop while (window->Closed() == false and window->KeyHit(KEY_ESCAPE) == false) { world->Update(); world->Render(framebuffer); } return 0; } I created a new project and I had to manually do this myself. Please update. The good news is that this works as expected now. #include "UltraEngine.h" #include "ComponentSystem.h" using namespace UltraEngine; int main(int argc, const char* argv[]) { //Get the displays auto displays = GetDisplays(); //Create a window auto window = CreateWindow("Ultra Engine", 0, 0, 1280, 720, displays[0], WINDOW_CENTER | WINDOW_TITLEBAR); //Create framebuffer auto framebuffer = CreateFramebuffer(window); //Create world auto world = CreateWorld(); //Create camera auto camera = CreateCamera(world, PROJECTION_ORTHOGRAPHIC); camera->SetClearColor(0.5); //Create a ortho camera auto camera2D = CreateCamera(world, UltraEngine::PROJECTION_ORTHOGRAPHIC); camera2D->SetDepthPrepass(false); camera2D->SetClearMode(UltraEngine::CLEAR_DEPTH); camera2D->SetRenderLayers(UltraEngine::RENDERLAYER_7); camera2D->SetPosition((float)framebuffer->size.x * 0.5f, (float)framebuffer->size.y * 0.5f, 0); // Create Text DropShadow; auto font = LoadFont("Fonts/arial.ttf"); auto text_dropshadow = CreateSprite(world, font, "Hello World!", 128); text_dropshadow->SetRenderLayers(UltraEngine::RENDERLAYER_7); // The Z position should push this back. text_dropshadow->SetPosition(4 + 2, 4 - 2, 1.0f); // Color me black text_dropshadow->SetColor(0.0f, 0.0f, 0.0f, 1.0f); // Create Text auto text = text_dropshadow->Instantiate(world)->As<UltraEngine::Sprite>(); text->SetPosition(4, 4, 0.0f); text->SetColor(1.0f, 1.0f, 1.0f, 1.0f); //Main loop while (window->Closed() == false and window->KeyHit(KEY_ESCAPE) == false) { static bool bSwap = false; if (window->KeyHit(KEY_SPACE)) { if (bSwap) { Print("Push drop shadow back!"); text_dropshadow->SetPosition(4 + 2, 4 - 2, 1.0f); text->SetPosition(4, 4, 0.0f); } else { Print("Bring drop shadow forward!"); text_dropshadow->SetPosition(4 + 2, 4 - 2, -1.0f); text->SetPosition(4, 4, 0.0f); } bSwap = !bSwap; } world->Update(); world->Render(framebuffer); } return 0; }
-
I was under the impression that an entity can be set to multiple layers. If the information is false, then yeah it doesn't make sense.
-
Cool, I'll be sure to try these again when I can get back to my project. Spent Christmas writing (hopefully) production code and I gotta say minus a few bumps along the way, it's been fantastic. I wrote everything I want all my games to do (Load Splash, Create Window, etc) within one class. Cameras not being tied to the world is such a game changer. I create all my cameras at start and point to it if I need it. In Cyclone, I had to consistently recreate a camera every time the world cleared. Only thing still not working for me is the interface drawn in 3D. Last time I checked, it wasn't responsive and after I've I rebuilt the window with repositioning the 2D camera, it's position and size was all messed up and it stopped drawing the children widgets. I'll be sure to isolate the problem asap, but maybe we should retry the examples that are on this thread if we can find them. Finally, something I've noticed is that you can't link static libraries. I ended up having to do some hackory of reconfiguring my premake script to include the files from my core static library and have my PCH file be a public header titled "pch.h". My PCH will only include the engine header if the preprocessor definition _ULTRA_ENGINE is set. Nothing you can do about it as I think the current library loading setting is probably essential to how the engine works. Just thought you'd like to know. I think a nice thing to have is a collection of examples showing how to do certain things. These would be something above a level above raw API examples and would help people to see how they would do X. This should have everything in one cpp file like the API examples. Some ideas include: How to safely destroy and rebuild the window to change displays and modes. (I've written a class that does this already.) Drawing text with a drop shadow (Like I've written above. It's pretty simple, but it should be on the books.) Using ParseCommandLine for loading and saving settings. It can go into how to apply those settings to the world and camera to make it more than an API example. Using a thread to load a scene while displaying a loading screen / loading bar. (I want to know how to do this please! :P) Simple networking. (Thinking of an example where a client and server is created. Then a map is loaded to the scene is loaded with the players being represented by floating boxes which are actually just a camera with the CameraControls Component attached. This can ether with the included enet, Steamworks or both) So far so good. We'll eventually get all these Early Access bumps flattened out.
-
-
Gave the package loader a go. I zipped the shaders folder so I can paste in the shader files from Leadwerks to make a hybrid project for testing. Notice this causing errors loading some things. Skipping Plugin Loading... Loading font "Fonts/arial.ttf" Loading package "UltraShaders.zip" Creating World... Loading shader family "Shaders/PBR.json" Loading shader family "Shaders/unlit.json" Loading shader family "/PBR.json" Error: Failed to read file "D:/UltraGame/game/sampleapp//PBR.json" Deleting shader family "/PBR.json" Deleting shader family "Shaders/unlit.json" Loading shader family "Shaders/unlit.json" Loading shader family "/PBR.json" Error: Failed to read file "D:/UltraGame/game/sampleapp//PBR.json" Deleting shader family "/PBR.json" Deleting shader family "Shaders/unlit.json" Loading shader family "Shaders/Sky.json" Loading posteffect "Shaders/PostEffects/Refraction.json" Edit: Doesn't seem to work if you do this. Right now, I have shaders from both engines joined in one folder.
-
I was going to write you an example demonstrating the pure agony I've had making text with a drop shadowed but I can't nail it what's causing it. It'll work fine until I change something that has nothing to do with positioning and my shadow text will draw on top the colored text. This is one example of it. With this, it works file if the text is not absolutely white, but if you subtract 0.1 from it, it works as expected. #include "UltraEngine.h" #include "ComponentSystem.h" using namespace UltraEngine; int main(int argc, const char* argv[]) { //Get the displays auto displays = GetDisplays(); //Create a window auto window = CreateWindow("Ultra Engine", 0, 0, 1280, 720, displays[0], WINDOW_CENTER | WINDOW_TITLEBAR); //Create a world auto world = CreateWorld(); //Create a framebuffer auto framebuffer = CreateFramebuffer(window); //Create a camera auto camera = CreateCamera(world); camera->SetClearColor(0.125); camera->SetFov(70); camera->SetPosition(0, 0, -3); // 2D auto m_spContextCamera = CreateCamera(world, UltraEngine::PROJECTION_ORTHOGRAPHIC); m_spContextCamera->SetDepthPrepass(false); m_spContextCamera->SetClearMode(UltraEngine::CLEAR_DEPTH); m_spContextCamera->SetRenderLayers(UltraEngine::RENDERLAYER_7); m_spContextCamera->SetPosition((float)framebuffer->size.x * 0.5f, (float)framebuffer->size.y * 0.5f, 0); // Create Text DropShadow; auto font = LoadFont("Fonts/arial.ttf"); auto text_dropshadow = CreateSprite(world, font, "Hello World!", 128); text_dropshadow->SetRenderLayers(UltraEngine::RENDERLAYER_7); // The Z position should push this back. text_dropshadow->SetPosition(4 + 2, 4 - 2, 1.0f); // Color me black text_dropshadow->SetColor(0.0f, 0.0f, 0.0f, 1.0f); // Create Text auto text = text_dropshadow->Instantiate(world)->As<UltraEngine::Sprite>(); text->SetPosition(4, 4, 0.0f); text->SetColor(1.0f, 0.0f, 0.0f, 1.0f); // If the text is ABSOULTE white, the drop shadow renders above this. text->SetColor(1.0f, 1.0f, 1.0f, 1.0f); // Nock it down by 0.1, and it's fixed. //text->SetColor(0.9f, 0.9f, 0.9f, 1.0f); //Create a light auto light = CreateBoxLight(world); light->SetRotation(35, 45, 0); light->SetRange(-10, 10); //Create a box auto box = CreateBox(world); box->SetColor(0, 0, 1); //Entity component system auto actor = CreateActor(box); auto component = actor->AddComponent<Mover>(); component->rotation.y = 45; //Main loop while (window->Closed() == false and window->KeyDown(KEY_ESCAPE) == false) { world->Update(); world->Render(framebuffer); } return 0; } Also noticed text sprites don't render correctly when drawn in 3D and I was having issues getting 3D interface to respond.
-
-
Yep, that's on my things to do. I'm going to change the path into a macro and replace all hard paths with that.
-
I expect the editor to load all the component .hpp files under "Source/Components". If so, would there be an option to have it look for the component header files elsewhere? It would be disappointing if these things were hardcoded. Sorry for me wanting to break the mold and be a Ultra power user!
-
Before I build my source tree to be like how I have everything set up in Cyclone, will the editor allow dynamic search paths for the components?
-
I rewrote my premake script and it all works. This adds flexibility to on how to configure more complex projects. Only thing is that you'll have to build your own preprocessor to acuminate any changes as it works based on the location on the project, not the soultion. This example will generate a project and put the executable within the "Game" folder. Run ./premake5.exe vs2022 in PowerShell within the directory.UltraPremakeTest.zip ------------------------------------------------------------------------- -- Example Premake Script for Ultra Engine 1.0 (Early Access) ------------------------------------------------------------------------- UltraEnginePath = "D:/Ultra Engine" ToolsPath = UltraEnginePath.."/Tools" GameDir = "Game" workspace "UltraPremake" location "./" startproject "UltraPremake" configurations { "Debug", "Release"} filter {"system:windows", "configurations:*"} architecture "x86_64" project "UltraPremake" location "./" language "C++" cppdialect "C++17" staticruntime "off" editandcontinue "on" flags { "MultiProcessorCompile" } conformancemode (false) -- Game Location targetdir("%{GameDir}") debugdir ("%{GameDir}") -- OBJ Output Location objdir "%{prj.location}/%{cfg.buildcfg}_%{cfg.architecture}/%{cfg.system}" -- Include Directories includedirs { -- Engine "%{UltraEnginePath}/Include", "%{UltraEnginePath}/Include/Libraries/Box2D", "$(UniversalCRT_LibraryPath)", "%{UltraEnginePath}/Include/Libraries/freetype/include", "%{UltraEnginePath}/Include/Libraries/OpenAL/include", "%{UltraEnginePath}/Include/Libraries/RecastNavigation/RecastDemo/Include", "%{UltraEnginePath}/Include/Libraries/RecastNavigation/DetourCrowd/Include", "%{UltraEnginePath}/Include/Libraries/RecastNavigation/DetourTileCache/Include", "%{UltraEnginePath}/Include/Libraries/RecastNavigation/DebugUtils/Include", "%{UltraEnginePath}/Include/Libraries/RecastNavigation/Recast/Include", "%{UltraEnginePath}/Include/Libraries/RecastNavigation/Detour/Include", "%{UltraEnginePath}/Include/Libraries/sol3/include", "%{UltraEnginePath}/Include/Libraries/Lua/src", "%{UltraEnginePath}/Include/Libraries/enet/include", "%{UltraEnginePath}/Include/Libraries/newton/sdk/dTinyxml", "%{UltraEnginePath}/Include/Libraries/newton/sdk/dExtensions", "%{UltraEnginePath}/Include/Libraries/newton/sdk/dlkSolver", "%{UltraEnginePath}/Include/Libraries/newton/sdk/dJoints", "%{UltraEnginePath}/Include/Libraries/newton/sdk/dModels/dVehicle", "%{UltraEnginePath}/Include/Libraries/newton/sdk/dModels/dCharacter", "%{UltraEnginePath}/Include/Libraries/newton/sdk/dModels", "%{UltraEnginePath}/Include/Libraries/newton/sdk/dParticles", "%{UltraEnginePath}/Include/Libraries/newton/sdk/dNewton", "%{UltraEnginePath}/Include/Libraries/newton/sdk/dCore", "%{UltraEnginePath}/Include/Libraries/newton/sdk/dCollision", "%{UltraEnginePath}/Include/Libraries/NewtonDynamics/sdk/dVehicle/dMultiBodyVehicle", "%{UltraEnginePath}/Include/Libraries/NewtonDynamics/sdk/dVehicle", "%{UltraEnginePath}/Include/Libraries/NewtonDynamics/sdk/dMath", "%{UltraEnginePath}/Include/Libraries/NewtonDynamics/sdk/dgCore", "%{UltraEnginePath}/Include/Libraries/NewtonDynamics/sdk/dgNewton", "%{UltraEnginePath}/Include/Libraries/NewtonDynamics/sdk/dAnimation", "%{UltraEnginePath}/Include/Libraries/NewtonDynamics/sdk/dgTimeTracker", "%{UltraEnginePath}/Include/Libraries/NewtonDynamics/sdk/dContainers", "%{UltraEnginePath}/Include/Libraries/NewtonDynamics/sdk/dCustomJoints", -- Game "%{prj.location}/Source" } files { "%{prj.location}/Source/**.h", "%{prj.location}/Source/**.hpp", "%{prj.location}/Source/**.cpp", "%{prj.location}/Source/**.ico", "%{prj.location}/Source/**.rc", } -- Global Defines: defines { "_NEWTON_STATIC_LIB", "_CUSTOM_JOINTS_STATIC_LIB", } -- Shared PCH pchheader "UltraEngine.h" --------------------------------------------------------- -- Windows Exclusive --------------------------------------------------------- filter {"system:windows", "configurations:*"} systemversion "latest" entrypoint "mainCRTStartup" pchsource "%{prj.location}/Source/UltraEngine.cpp" libdirs { "%{UltraEnginePath}/Library" } defines { "NOMINMAX", "_HAS_STD_BYTE=0", } -- Preprocessor prebuildcommands { "\"%{ToolsPath}/preprocessor.exe\"" } filter {"system:windows", "configurations:Debug"} links { "UltraEngine_d.lib" } filter {"system:windows", "configurations:Release"} links { "UltraEngine.lib" } filter "configurations:Debug" defines { "DEBUG", "_DEBUG" } kind "ConsoleApp" targetsuffix "_d" runtime "Debug" symbols "on" filter "configurations:Release" defines { "NDEBUG" } -- UPX compression postbuildcommands { "\"%{ToolsPath}/upx.exe\" \"%{GameDir}/%{prj.name}.exe\"" } kind "WindowedApp" runtime "Release" symbols "off" optimize "on" --------------------------------------------------------- --------------------------------------------------------- UltraPremakeTest.zip
-
Preprocessor needs to be updated. Preprocessor/main.cpp at main · UltraEngine/Preprocessor (github.com) Change: stream->WriteLine(" if (c->" + m.name + " != NULL && c->" + m.name + "->As<Entity>()) j3[\"" + m.name + "\"] = c->" + m.name + "->As<Entity>()->GetGUID();"); To (I think): stream->WriteLine(" if (c->" + m.name + " != NULL && c->" + m.name + "->As<Entity>()) j3[\"" + m.name + "\"] = c->" + m.name + "->As<Entity>()->GetUuid();"); I would do a pull request but I'm going to bed now. The change will make this actor work. #pragma once #include "UltraEngine.h" #include "../ComponentSystem.h" using namespace UltraEngine; class FPSPlayer : public Component { UltraEngine::Vec3 camrotation; UltraEngine::Vec2 mouseaxis; public: std::shared_ptr<UltraEngine::Camera> camera; float lookspeed = 200; float movespeed = 3.5; float maxaccel = 40; float maxdecel = 15; float mousesmoothing = 3; float runspeed = 2; float jumpstrength = 8; float lunge = 1.5; float eyeheight = 1.7f; FPSPlayer::FPSPlayer() { mouseaxis = UltraEngine::Vec2(0); } virtual void Start() { UltraEngine::Assert(camera); camera->SetRotation(UltraEngine::Vec3(0, entity->rotation.y,0)); camera->SetPosition(entity->position.x, entity->position.y + eyeheight, entity->position.z); camrotation = camera->GetRotation(); entity->SetPhysicsMode(PHYSICS_PLAYER); entity->SetMass(10); entity->SetCollisionType(COLLISION_PLAYER); } virtual void Update() { if (ActiveWindow() != NULL) { if (mouseaxis.x == 0 && mouseaxis.y == 0) { ActiveWindow()->GetMouseAxis(); } //Camera look Vec2 newaxis = ActiveWindow()->GetMouseAxis(); Vec2 mousedelta = newaxis - mouseaxis; mouseaxis = newaxis; camrotation.x = Mix(camrotation.x + mousedelta.y * lookspeed, camrotation.x, 1.0f / mousesmoothing); camrotation.x = Clamp(camrotation.x, -90.0f, 90.0f); camrotation.y = Mix(camrotation.y + mousedelta.x * lookspeed, camrotation.y, 1.0f / mousesmoothing); camera->SetRotation(camrotation, true); //Movement float accel = maxaccel; Vec2 movement; movement.y = (ActiveWindow()->KeyDown(KEY_W) - ActiveWindow()->KeyDown(KEY_S)); movement.x = (ActiveWindow()->KeyDown(KEY_D) - ActiveWindow()->KeyDown(KEY_A)); if (movement.x != 0.0f and movement.y != 0.0f) { //Adjust speed on each axis if both are in use movement *= 0.7071f; } movement *= movespeed; float jump = ActiveWindow()->KeyHit(KEY_SPACE) * jumpstrength; bool crouch = ActiveWindow()->KeyDown(KEY_C); if (entity->GetAirborne()) jump = 0; if (crouch == false and ActiveWindow()->KeyDown(KEY_SHIFT) and !entity->GetAirborne()) { movement *= runspeed; } if (jump > 0 and crouch == false) { movement *= lunge; accel *= 100; } //Set input entity->SetInput(camrotation.y, movement.y, movement.x, jump, crouch, accel, maxdecel); } //Adjust camera position static float current_eyehight = eyeheight; if (entity->GetCrouched()) { current_eyehight = 1.8f * 0.5f - 0.1f; } else { current_eyehight = eyeheight; } camera->SetPosition(Mix(camera->position.x, entity->position.x, 0.5f), MoveTowards(camera->position.y, entity->position.y + current_eyehight, 0.1f), Mix(camera->position.z, entity->position.z, 0.5f)); camera->SetPosition(entity->position.x, MoveTowards(camera->position.y, entity->position.y + current_eyehight, 0.1f), camera->position.z); } }; //Create Player Actor auto actor = CreateActor(player); actor->AddComponent<FPSPlayer>(); actor->GetComponent<FPSPlayer>()->camera = camera; // Assign camera actor... Would be nice if this got cleaned up before the big release also... 1>D:\Ultra Engine\Include\Classes\Scripting\DynamicValue.h(114,43): warning C4244: 'return': conversion from 'const double' to 'float', possible loss of data 1>D:\Ultra Engine\Include\Classes\Scripting\DynamicValue.h(115,74): warning C4244: 'return': conversion from 'T' to 'float', possible loss of data 1> with 1> [ 1> T=Tu 1> ] 1>D:\Ultra Engine\Include\Classes\Scripting\DynamicValue.h(120,48): warning C4244: 'return': conversion from 'int64_t' to 'int', possible loss of data 1>D:\Ultra Engine\Include\Classes\Scripting\DynamicValue.h(121,74): warning C4244: 'return': conversion from 'T' to 'int', possible loss of data 1> with 1> [ 1> T=Tu 1> ] 1>D:\Ultra Engine\Include\Classes\Scripting\DynamicValue.h(122,11): warning C4244: 'return': conversion from 'float' to 'int', possible loss of data 1>D:\Ultra Engine\Include\Hub\RegKeys.h(2,10): warning C4067: unexpected tokens following preprocessor directive - expected a newline 1>D:\Ultra Engine\Include\Classes\Loaders\GLTFModelLoader.h(19,2): warning C4099: 'UltraCore::GLTFImage': type name first seen using 'class' now seen using 'struct' 1>D:\Ultra Engine\Include\Classes\Loaders\GLTFModelLoader.h(18): message : see declaration of 'UltraCore::GLTFImage' 1>D:\Ultra Engine\Include\Classes\Loaders\GLTFModelLoader.h(222,25): warning C4099: 'UltraCore::GLTFModelLoader': type name first seen using 'class' now seen using 'struct' 1>D:\Ultra Engine\Include\Classes\Loaders\GLTFModelLoader.h(222): message : see declaration of 'UltraCore::GLTFModelLoader' 1>D:\Ultra Engine\Include\Classes\Pathfinding\NavTile.h(8,24): warning C4099: 'UltraNavigate::NavMeshDebugger': type name first seen using 'class' now seen using 'struct' 1>D:\Ultra Engine\Include\Classes\Graphics\Mesh.h(6): message : see declaration of 'UltraNavigate::NavMeshDebugger' 1>D:\Ultra Engine\Include\Classes\Pathfinding\NavMeshDebugger.h(6,25): warning C4099: 'UltraNavigate::NavMeshDebugger': type name first seen using 'class' now seen using 'struct' 1>D:\Ultra Engine\Include\Classes\Pathfinding\NavMeshDebugger.h(6): message : see declaration of 'UltraNavigate::NavMeshDebugger' 1>D:\Ultra Engine\Include\Classes\Physics\PhysicsWorld.h(78,10): warning C4244: 'initializing': conversion from 'uint64_t' to 'int', possible loss of data
-
I'll check out this RC build tonight. I think it's important to double check Leadwerks compatibility is perfect.
-
Do people who already have access need to take action at this time or is this an option for other members?
-
Hopefully the transition will go smoothly. I would like to put money down for the year with my early adoption discount. 🙂 Under the weather right now so hopefully I'll feel better by then.
-
Did a quick sound check after helping Josh and it seems like there's no "3D" to this sound. I think the listener isn't being updated as the audio sounds the same no matter how far way you are from it. I've also found out that the radio loops from Cyclone also don't seem to load and I've already sent an example to him. #include "UltraEngine.h" #include "ComponentSystem.h" using namespace UltraEngine; // Use any .wav file here. #define SOUNDSAMPLE_PATH "Sound/error.wav" #define SPEAKER_RANGE 1.0f int main(int argc, const char* argv[]) { //Get the displays auto displays = GetDisplays(); //Create a window auto window = UltraEngine::CreateWindow("Ultra Engine", 0, 0, 1280, 720, displays[0], WINDOW_CENTER | WINDOW_TITLEBAR); //Create a world auto world = CreateWorld(); //Create a framebuffer auto framebuffer = CreateFramebuffer(window); //Create a camera auto camera = CreateCamera(world); camera->SetClearColor(0.125); camera->SetFov(70); camera->SetPosition(0, 0, -3); //Entity component system auto camera_actor = CreateActor(camera); auto camera_component = camera_actor->AddComponent<CameraControls>(); camera->Listen(); //Create a light auto light = CreateBoxLight(world); light->SetRotation(35, 45, 0); light->SetRange(-10, 10); //Create a box auto box = CreateBox(world); box->SetColor(0, 0, 1); //Entity component system auto actor = CreateActor(box); auto component = actor->AddComponent<Mover>(); component->rotation.y = 45; // Sound auto sound = LoadSound(SOUNDSAMPLE_PATH); auto speaker = CreateSpeaker(sound); speaker->SetLooping(true); speaker->SetPosition(box->GetPosition(true)); speaker->SetRange(SPEAKER_RANGE); speaker->Play(); //Main loop while (window->Closed() == false and window->KeyDown(KEY_ESCAPE) == false) { if (window->KeyDown(KEY_P)) { if (speaker->GetState() == SPEAKER_PLAYING) speaker->Pause(); else speaker->Resume(); } world->Update(); world->Render(framebuffer); } return 0; } I also get this junk upon closing. [ALSOFT] (WW) Error generated on context 0000021704B51B00, code 0xa001, "Invalid source ID 0" Deleting sound "Sound/error.wav" Deleting shader family "Shaders/Sky.json" [ALSOFT] (WW) Releasing orphaned context 0000021704B51B00
-
Hopefully you'll make a press kit with the logos and such.
-
Check your DM. 🙂
-
How did you retrieve the framerate in your demos? I've made a simple application to load Cyclone maps and world->renderstats.framerate returns 0. Somethings I've encountered. Coming from Leadwerks, I was using ui->GetBase() instead of ui->root. DebugLog() does not compile under release for some reason. I would just #ifdef out the printing within the function. Having an issue resizing an interface in 3D when the window goes to fullscreen. Here's my code. It has a lot of custom functions but it gives you an idea of what I'm trying to do. #include "UltraEngine.h" #include "ComponentSystem.h" #include "Core/command.h" #include "GameWindow.h" using namespace UltraEngine; #define DEFAULT_MAP "Maps/lvl7.map" int main(int argc, const char* argv[]) { Core::ParseCommandLine(argc, argv); //auto plugin = LoadPlugin("Plugins/LELegacy.dll"); //Create a world auto world = CreateWorld(); //Get the displays auto displays = GetDisplays(); auto settings = GameWindowSettings { 0, 0, 0, 1280, 720, WINDOW_CENTER | WINDOW_TITLEBAR }; // Create the window. // This will also create a framebuffer. auto window = CreateGameWindow("Ultra Preview", settings); //Create a camera auto camera = CreateCamera(world); camera->SetClearColor(0); camera->SetFov(70); camera->SetPosition(0, 0.32, 0); auto actor = CreateActor(camera); actor->AddComponent<CameraControls>(); // HUD auto viewport_size = window->GetFramebuffer()->size; auto hudcamera = CreateCamera(world, PROJECTION_ORTHOGRAPHIC); hudcamera->SetClearMode(CLEAR_DEPTH); hudcamera->SetPosition(viewport_size.x / 2, viewport_size.y / 2); hudcamera->SetRenderLayers(RENDERLAYER_1); //Create user interface auto ui = CreateInterface(world, LoadFont("Fonts/arial.ttf"), viewport_size); auto background = ui->background; background->SetColor(Vec4(0.0f, 0.0f, 0.0f, 0.0f)); //background->SetLayout(1, 0, 1, 1); ui->SetRenderLayers(RENDERLAYER_1); //Create widget iVec2 sz = ui->root->ClientSize(); auto fpscounter = CreateLabel("FPS: ", 2, 2, 100, 12, ui->root); WString map = Core::GetArgument("map"); Print(map); if (map.empty()) map = DEFAULT_MAP; auto scene = LoadScene(world, map); if (scene == NULL) { Print("Failed to load scene!"); camera->SetClearColor(0.125); //Create a light auto light = CreateBoxLight(world); light->SetRotation(35, 45, 0); light->SetRange(-10, 10); auto box = CreateBox(world); box->SetColor(0,0,1); } window->Activate(); //Main loop while (window->Closed() == false and window->KeyDown(KEY_END) == false) { // Toggle between modes. if (window->KeyHit(KEY_SPACE)) { static bool fullscreen = false; fullscreen = !fullscreen; window->SetNativeFullscreen(fullscreen); // This is broke, IDK. viewport_size = window->GetFramebuffer()->size; hudcamera->SetClearMode(CLEAR_DEPTH); hudcamera->SetPosition(viewport_size.x / 2, viewport_size.y / 2); hudcamera->SetRenderLayers(RENDERLAYER_1); ui->Redraw(0, 0, window->GetFramebuffer()->size.x, window->GetFramebuffer()->size.y); ui->UpdateLayout(); ui->root->Redraw(); } world->Update(); //const int fps = world->renderstats.framerate; //if (fpscounter) fpscounter->SetText("FPS: " + String(fps)); // Since the framebuffer is destoryed and rebuilt between changing modes, // We need to tell the window to manage our world's updating window->Render(world); } return 0; }
-
The update for Cyclone is now live including this.
-
I've spent the last few months pressing buttons, clicking joysticks and shaking my computer mouse to solve the solution input for Cyclone. Back when it shipped in June, I've created a system that allowed users to assign keys to actions, in which the game would detect as input. My player code never knew what button was pressed; it just knew what action was caused. This is very similar to how Steam Input works. There were a few flaws with my original system. Some of which didn't surface until I shipped. I stored the Keycode as int32 values. Unless someone had a chart, they couldn't easily bind their keys. Things got really confusing when it came to international keyboards. Not all keycodes are universal. My system only supported buttons. Actions for axis controls were not a thing and were hard coded. The system relied on the Leadwerks API which would cause making a utility app kind of tricky. Mouse aim wasn't generally liked. I wanted a library that did nothing but input. But I got dead libraries or libraries that needed dependencies to work. I wanted Steam Input, but for the Keyboard and Mouse. I knew I couldn't let this sit based on the amount of feedback I was getting because from this. Since nobody else thought it was necessary to make one, it looked like I had to make an input system in-which input has a name. Before we get into input, I first had to re-arrange how my repo file structure was set up. I had Cyclone set up as a generic Leadwerks project, but this wasn't going to work if I wanted to create shared libraries and utilities. Before I did anything stupid with Cyclone, I made a new repo to figure out how everything should be laid out. I decided to follow a file structure much like Valve has their Source engine and use premake to generate the files. Shell scripts are used to generate projects via WSL. This allowed me to compile for Windows AND Linux on the same machine and I did casual build tests on macOS. I never want to write an input system ever again. Before I could do any form of action detection, I needed to create a "Driver" sort of speak and have the operating system pump events into it. This interface class allows classes to be derived from it and work no matter what driver is created. class INPUTSYTEM_API IInputDriver { public: IInputDriver() {}; virtual ~IInputDriver() {}; virtual void EnablePumpEvents(const bool bState) = 0; virtual void PumpButtonDown(const button_t btn, const int controller = 0) = 0; virtual void PumpButtonUp(const button_t btn, const int controller = 0) = 0; virtual void PumpButtonCodeDown(const ButtonCode& btncode, const int controller = 0) = 0; virtual void PumpButtonCodeUp(const ButtonCode& btncode, const int controller = 0) = 0; virtual void PumpMouseWheelDir(const int dir) = 0; virtual void PumpAxis(const AxisCode& axiscode, const float x, const float y, const int controller = 0) = 0; virtual void PumpPointer(const PointerCode& pointer, const int x, const int y, const int controller = 0) = 0; virtual void PumpRumble(const float left, const float right, uint64_t duration_ms, const int controller = 0) = 0; virtual const bool ButtonDown(const ButtonCode& btncode, const int controller = 0) = 0; virtual const bool ButtonHit(const ButtonCode& btncode, const int controller = 0) = 0; virtual const bool ButtonReleased(const ButtonCode& btncode, const int controller = 0) = 0; virtual const bool ButtonAnyDown() = 0; virtual const bool ButtonAnyHit() = 0; virtual AxisVector GetAxis(const AxisCode& axiscode, const int controller = 0) = 0; virtual AxisVector GetButtonAxis(const ButtonCode& up, const ButtonCode& down, const ButtonCode& left, const ButtonCode& right) = 0; virtual void UpdateController(const int controller) = 0; virtual void SuspendControllerInput(const bool bState) = 0; virtual void Flush() = 0; virtual void FlushButtons() = 0; virtual void FlushAxis() = 0; virtual ButtonCode GetLastButtonPressed() = 0; virtual ButtonCode StringToButtonCode(const std::string& btnstring) = 0; virtual const char* ButtonCodeToString(const ButtonCode& btncode) = 0; virtual void SetCursorPosition(const int x, const int y) = 0; virtual void CenterCursorPosition() = 0; virtual ScreenPosition GetCursorPosition() = 0; virtual void MouseVisibility(const bool bState, const bool bRecenter) = 0; virtual void SetPointerPosition(const PointerCode& pointer, const int x, const int y) = 0; virtual ScreenPosition GetPointerPosition(const PointerCode& pointer) = 0; virtual void CenterPointerPosition(const PointerCode& pointer) = 0; virtual void SetActiveDevice(InputDevice device) = 0; virtual InputDevice GetActiveDevice() = 0; virtual bool DeviceChanged() = 0; virtual void Cleanup() = 0; }; You may notice that there is button_t and ButtonCode. The type: button_t is the unit32_t type from the OS and the ButtonCode is button_t reassigned to my own enum structure. This way, my end values remain consistent between OS and drivers. For example, WinAPI pumps Virtual Keyboard flag values while Coco/X11 will pump its own flag definitions. If you wanted to use SDL, all you need to do is make an SDL driver for it and the code on top of it will not care. Also note that there are no separate functions to detect button and key presses. There are only Buttons, Axis, and Pointer values when it comes to my input library. I was also going to omit the cursor commands, but it's part of the desktop environment and really can't be ignored. The Set/GetCursorPosition is there for redundancy really. The pump functions are made public to allow external pumping of events. For Windows, you can "hijack" the Window polling with a custom function. Here's an example of how I did it in Leadwerks and the same thing can be done in Ultra Engine for Windows exclusive games. LRESULT CALLBACK MyWndProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) { InputSystem::ProcInputWin32(hwnd, message, wparam, lparam); return Leadwerks::WndProc(hwnd, message, wparam, lparam); } int main(int argc, const char* argv[]) { auto window = Leadwerks::Window::Create("Game", 0, 0, 1280, 720, Leadwerks::Window::Titlebar | Leadwerks::Window::Center); if (window == NULL) return 1; // Init the input system. InputSystem::Init(window->hwnd); #ifdef _WIN32 // Swap the callback with ours WNDPROC OldProc = reinterpret_cast<WNDPROC>(SetWindowLongPtr( hwnd, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(MyWndProc))); #endif .... That's all it takes to start my input system. Just pass the window handle to the Init function and the library will load the rest. I only have a WinAPI driver right now, but the idea is that of you were building on Linux or macOS, the driver will be created. I still need to work out how polling and key events are processed on those platforms. We can get a button press as easily as this: auto b = InputSystem::GetDriver()->ButtonHit(InputSystem::BUTTON_KEY_ESCAPE); Josh provided me with insight on using the raw mouse value to return a Vector valve so you can easily obtain it by: auto a = InputSystem::GetDriver()->GetAxis(InputSystem::AXIS_MOUSE); You can even do neat things like this: if (InputSystem::GetDriver()->ButtonAnyDown()) { auto btn = InputSystem::GetDriver()->GetLastButtonPressed(); std::cout << InputSystem::GetDriver()->ButtonCodeToString(btn) << " is held down!" << std::endl; } Great, we now have an input API like everyone else. Although it's richer and more flexible than what Leadwerks provides, we're not done. We need another layer on top of the driver in which our game will actually use. Meet the Action Controller! class INPUTSYTEM_API IActionController { public: int32_t controller_port = 0; IActionController() {}; virtual ~IActionController() {}; virtual const int32_t GetControllerID() = 0; virtual void SetActionSet(const char* actionsetid) = 0; virtual const char* GetActionSet() = 0; virtual bool Down(const char* actionid, const char* actionsetid = "") = 0; virtual bool Hit(const char* actionid, const char* actionsetid = "") = 0; virtual bool Released(const char* actionid, const char* actionsetid = "") = 0; virtual AxisVector Axis(const char* actionid, const char* actionsetid = "") = 0; virtual void Rumble(const float leftmotor, const float rightmotor, uint64_t duration_ms = 10) = 0; virtual void FlushAllInput(const bool bCenterPointDevice = false) = 0; virtual const bool NoActionSets() = 0; virtual const Action GetAction(const char* actionid, const char* actionsetid = "") = 0; virtual const int ButtonCount(const char* actionid, const char* actionsetid = "") = 0; virtual const int AxisCount(const char* actionid, const char* actionsetid = "") = 0; virtual const ButtonCode GetButton(const char* actionid, const int index = 0, const char* actionsetid = "") = 0; virtual const AxisCode GetAxis(const char* actionid, const int index = 0, const char* actionsetid = "") = 0; virtual const ButtonAxis GetButtonAxis(const char* actionid, const char* actionsetid = "") = 0; virtual const PointerCode GetPointDevice() = 0; virtual const bool IsKBM() = 0; virtual const float GetSetting(const char* setting, const float defaultsetting = 0) = 0; virtual void SetPointDevice(const PointerCode& pointdevice) = 0; virtual void SetPointDevicePosition(const int x, const int y) = 0; virtual ScreenPosition GetPointDevicePosition() = 0; virtual void CenterPointDevice() = 0; virtual void TogglePointerVisibility(const bool bShow) = 0; virtual const bool GetPointerVisibility() = 0; // Writting of data virtual void SetSetting(const char* setting, const float fValue) = 0; virtual void RegisterActionSet(const char* actionsetid) = 0; virtual void BindAction(const char* actionsetid, const char* actionid, const ButtonCode& buttoncode) = 0; virtual void BindAction(const char* actionsetid, const char* actionid, const AxisCode& axiscode) = 0; virtual void BindAction(const char* actionsetid, const char* actionid, const ButtonAxis& buttonaxis) = 0; virtual void ClearAction(const char* actionsetid, const char* actionid) = 0; virtual IInputDriver* GetDriver() = 0; friend class IInputDriver; }; Did notice that we pass strings instead of button codes? So instead of checking if the Space bar is pressed, we check if the Space action is pressed. // Bad, don't do this. const bool b = InputSystem::GetDriver()->ButtonHit(InputSystem::BUTTON_KEY_SPACE) if (b) pPlayer->Jump(); // Do this! auto controller = InputSystem::GetDriver()->GetController(); if (controller->Hit("Jump")) pPlayer->Jump(); The keys can be ether assigned in code or loaded form a JSON file. All actions are converted to lower case to prevent confusion between "Jump" and "jump". Actions can have multiple buttons binded to it which is good for also storing controller buttons. The controller also supports having 4 buttons act as an axis which should be used for movement. It's easy to create an action controller. auto action_controller = InputSystem::CreateActionController("actioncontroller.json"); Don't have a script yet? You can directly bind buttons and axis values after its creation and save the results to get started. auto action_controller = InputSystem::CreateActionController(""); const char* action_set = "TestActionSet"; action_controller->RegisterActionSet(action_set); action_controller->BindAction(action_set, "Camera", InputSystem::AXIS_MOUSE); action_controller->BindAction(action_set, "Camera", InputSystem::AXIS_GAMEPAD_RSTICK); action_controller->BindAction(action_set, "Jump", InputSystem::BUTTON_KEY_SPACE); action_controller->BindAction(action_set, "Jump", InputSystem::BUTTON_KEY_C); action_controller->BindAction(action_set, "Jump", InputSystem::BUTTON_GAMEPAD_A); InputSystem::ButtonAxis move_axis = { InputSystem::BUTTON_KEY_W, InputSystem::BUTTON_KEY_S, InputSystem::BUTTON_KEY_A, InputSystem::BUTTON_KEY_D }; action_controller->BindAction(action_set, "Move", move_axis); action_controller->BindAction(action_set, "Move", InputSystem::AXIS_GAMEPAD_LSTICK); InputSystem::SaveControllerProfile(action_controller, "inputtest.json"); This is all well and good, but the real payout will be to have an app that can easily bind raw input to actions. For this, I had to use Ultra App Kit as I needed something compatible with my Win32 libraries. Otherwise, I would have used the full engine. Like I said, it's pretty straight forward to hook this up with the Ultra API. #ifdef _WIN32 LRESULT CALLBACK MyWndProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) { InputSystem::ProcInputWin32(hwnd, message, wparam, lparam); return UltraEngine::Window::WndProc(hwnd, message, wparam, lparam); } #endif std::shared_ptr<UltraEngine::Window> BuildWindow(const int w, const int h) { //Get displays auto displays = GetDisplays(); //Create window auto mainwindow = CreateWindow("Action Mapper", 0, 0, w, h, displays[0], WINDOW_CENTER | WINDOW_TITLEBAR | WINDOW_HIDDEN); mainwindow->SetMinSize(w, h); #ifdef _WIN32 // Get device context HWND hwnd = mainwindow->GetHandle(); HDC hdc = GetDC(hwnd); // Load the icon for window titlebar and taskbar HICON icon = LoadIconA(GetModuleHandle(NULL), (LPCSTR)101); SendMessage(hwnd, WM_SETICON, ICON_SMALL, reinterpret_cast<LPARAM>(icon)); // Swap the callback with ours WNDPROC OldProc = reinterpret_cast<WNDPROC>(SetWindowLongPtr( hwnd, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(MyWndProc))); #endif InputSystem::Init(); InputSystem::GetDriver()->EnablePumpEvents(false); InputSystem::GetDriver()->SuspendControllerInput(true); return mainwindow; } I first initialize the driver, then tell it to not pump any OS events into it. This is because I wanted the events to pump to my driver only when requested by my bind button class. When that is pressed, the button gets disabled until InputSystem::GetDriver()->ButtonAnyHit() returns true and saves the last pressed key. I didn't need to pass the window handle as that's only needed to toggle the visibility of the cursor. This ended up working really well, and I'm happy how it came out. But you know what would be really customer friendly? What if this had a native Linux build? Ultra App Kit has Linux64 libraries, and my entire build environment uses premake with WSL so building one was no issue. The issue is that I didn't look into X11 and I don't wanna hold the next update much longer. Using my input library, I just pump the events from the Ultra API into my WinAPI driver and most keys worked. I had to fix Control, Alt, and Shift, but it was pretty easy. This only gets used with the Linux build. #ifdef __linux__ #define VK_LSHIFT 0xA0 #define VK_RSHIFT 0xA1 #define VK_LCONTROL 0xA2 #define VK_RCONTROL 0xA3 #define VK_LMENU 0xA4 #define VK_RMENU 0xA5 #endif namespace UltraInputWrapper { // We need to convert some keys to translate // engine values to Windows VK values. const int Convert(const int in) { int key = in; if (in == UltraEngine::KEY_CONTROL) return VK_LCONTROL; //if (in == UltraEngine::KEY_CONTROL) return VK_RCONTROL; if (in == UltraEngine::KEY_SHIFT) return VK_LSHIFT; //if (in == UltraEngine::KEY_SHIFT) return VK_RSHIFT; if (in == UltraEngine::KEY_ALT) return VK_LMENU; //if (in == UltraEngine::KEY_ALT) return VK_RMENU; return key; } // For non-Windows, push engine values. // Since UltraEngine uses the same values as WinAPI, it should be fine.. void PollIntoDriver(const Event& e) { switch (e.id) { case EVENT_KEYDOWN: InputSystem::GetDriver()->PumpButtonDown(Convert(e.data)); break; case EVENT_KEYUP: InputSystem::GetDriver()->PumpButtonUp(Convert(e.data)); break; case EVENT_MOUSEDOWN: InputSystem::GetDriver()->PumpButtonDown(Convert(e.data)); break; case EVENT_MOUSEUP: InputSystem::GetDriver()->PumpButtonUp(Convert(e.data)); break; default: break; } } } The final step was to gut the old input system out of Cyclone and replace it with my action controller. I also had to make it co-exist with Steam Input which I found out nulls out my XInput calls. I wrote a new singleton class that checks the state of both my action controller and Steam Input. Then it was making sure the right glyphs showed up per action. One last thing I want to share is a little bit of how my GameController class works. Since this follows the same ideology as Steam Input, it all works nicely. // Button Example const bool GameController::OnJump() { if (actioncontroller == NULL) return false; return actioncontroller->Hit("Jump") || SteamInputController::Hit(SteamInputController::eControllerDigitalAction_Jump); } // Axis Example Leadwerks::Vec2 GameController::GetMovementAxis() { Leadwerks::Vec2 ret = Leadwerks::Vec2(0); if (actioncontroller != NULL) { // Shared movement between KB and Controller. InputSystem::AxisVector moveAxis = actioncontroller->Axis("Move"); ret = Leadwerks::Vec2(moveAxis.x + SteamInputController::Axis(SteamInputController::eControllerAnalogAction_MoveControls).x, moveAxis.y + SteamInputController::Axis(SteamInputController::eControllerAnalogAction_MoveControls).y); } ret.x = Leadwerks::Math::Clamp(ret.x, -1.0f, 1.0f); ret.y = Leadwerks::Math::Clamp(ret.y, -1.0f, 1.0f); return ret; } This was an absolute time vampire, but I'm glad it's done minus the Coco/X11 drivers. Cyclone will be updated soon with this and hopefully this fixes international bindings. I couldn't find any information on the subject, and my virtual Turkish keyboard works properly. I'm just hoping the storing of the VK values is enough. I can easily build this for Windows, Mac and Linux to work with Leadwerks or the upcoming Ultra Engine. This is how we should be programming our input for our games. Stop using window->KeyHit(). I plan on releasing the binaries so everyone can have better input code.