Jump to content

reepblue

Developers
  • Posts

    2,480
  • Joined

  • Last visited

Everything posted by reepblue

  1. I'm getting a Seek error with my game system, but this code should reproduce it with the map file below. #include "UltraEngine.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); //Load scene auto scene = LoadMap(world, "Maps/saveloadparent.ultra"); //Main loop while (window->Closed() == false and window->KeyDown(KEY_ESCAPE) == false) { if (window->KeyHit(KEY_F5)) { //Save the starting scene to a file scene->Save("game.sav"); } //Reload the starting scene when space key is pressed if (window->KeyHit(KEY_F6)) { scene->Reload("game.sav"); } world->Update(); world->Render(framebuffer); } return 0; } saveloadparent.zip
  2. I've been watching a few tutorials from other engine and the trend is that people like to visually construct things. Right now, to make a first-person player one would. Create a Pivot in the scene. Attach the FPSController component. Now, this is all well and good, but things can be complicated for the non-programmer type. I'm experimenting with an approach that the user "builds" their player out of multiple components. Place a Pivot or Model for the player. Create a camera and parent it to the player entity having the end user set the height of the camera in the editor. Attach a FPSController component that ONLY handles controller movement and gets the camera reference via GetChild(). End user can attach multiple components to the camera such as custom look controls, HUD, etc. User would ideally save this as a prefab, but this isn't in the current build of the editor yet. I've already created two components with this approach, and I can already tell you it's so much easier to segregate functionality.
  3. Might as well get this started now although it's not going to be useful for a while. Github I mostly copied and pasted code from Cyclone. All it supports right now. is the overlay, stats and achievements. I used my own class with achievements for Cyclone because the one in the Leadwerks API held up the achievement notification until the application was closed. To be honest, the overlay and achievements is what most people want anyway. The Steamworks API can be a chore to work with. I hope to streamline this in the future.
  4. Pushed out an update. Settings.bat has $PROJECTNAME token so the template system will swap it with the name of the project (untested) Both Debug and Release builds are Windowed applications to have a cleaner appearance launching from the editor. Replaced CallCMDWindow() function with what @klepto2 posted above. (Launch the app with -cmd to make the console window.) Prevented empty strings being sent through the console event. Terminate() now asks if you really mean to force close the application. Sorta annoying but it feels much cleaner. Now you know if your game closes without asking, it's decently a crash. (To close the game without a confirmation, just emit EVENT_QUIT. Terminate is only used to get you out of a sticky situation.) Added a note about UAC and the Component Preprocessor. This is kind of tricky to do since we need to store the previous pause state and I'm not sure where to store it. The softlock is due to the stock Components not being setup to unpause the game. Must be the component/engine. The system just calls World::Save()/World::Load() functions. In due time. This went from being something you dropped in your existing project to a full template. I decided to go this route because adding all the files was tedious, and it helps prevent any conflicts Josh may want to push out. Just set up a UAK project. I had to change a few things so I'm gonna battle test it before releasing it. But yes, went from 20MB to 744KB!
  5. Ok, makes sense. Sometimes the most convenient setups aren't the most secure.
  6. My guess is that Windows is blocking the direct execution because it's a foreign app. Manually run the application tell UAC to trust the app. Or build try building the Preprocessor from the GitHub and use your version. Ether way, I didn't factor that into account. 🫢
  7. It's now easier to get started! The system is installable as a project template and component registration is much more streamlined.
  8. A few changes: This is now installable as a Template. Copy the "C++ Game" into the engine's Template folder and create a new project off of that. I didn't redistribute any SDK files so right now the start.ultra map will not work unless you were to copy over the stock components yourself. Removed start.ultra from the config's start map to prevent said issues. Visual Studio project is now included with all files added. My Component Preprocesor is now being used to make the component registration much more seamless. The app icon is now orange. Fixed any issues causing build errors. Some things I'm considering: Create a new start.ultra map to override the stock one. Ship my own components making use of the rest of the system. I've got more developer textures. I was going to ship it in its own package. I'm not sure including it would more help or bother people. Editing the template solution is really annoying. I might revive my premake setup. This will really help when Ultra Engine goes multiplatform.
  9. If you were to have an entity with a component attached to it and in the flowgraph, deleting the component from the entity doesn't reflect on the flowgraph. I know this is a heavy WIP, but thought I should bring it up.
  10. This simple application will scan your Components directory and create a function that'll register all your components in the engine. Use this in your Pre-Build event and then call the RegisterComponents() on top of your main entry. I didn't battle test this yet, but it seems to work. You are free to fork it, improve it, etc via GitHub. You can download the exe here.
  11. One of the things I don't like is that you need to cut the full line of text by spaces every time. The system I had for Cyclone only had the arguments available in a vector. This is a limitation of the Event System, but having it event based makes commands less static and it's way easier to do component based commands. One thing I still don't like thats in both systems is you need to do size checks on the argument vector before using it to prevent crashing. Other than that, it's a canvas for the programmer to do whatever they need done.
  12. Although cxxopts looks interesting, I wanted to stay exclusive to the engine's API for this. I didn't want to do anything that could/would change the workflow of how things are normally done with the engine. I might consider separating the logs by type though, I already have it sorted by colors at this point.
  13. Back in July, I set out to create a base for creating a version of Cyclone with Ultra Engine. I've gotten a lot of feedback from the release and started to conceptualize ideas on how to prevent or limit on making the same mistakes. One major goal I had was to compartmentalize the system. This should just exist on it's own and game code should just lay on top of this. This was possible thanks to the event system which cuts down on the number of pointers that need to be passed. Components can just listen to events from the program and act accordingly. Lastly, I've made the decision to open source this on GitHub as this is the kind of thing that users think about when it's too late. People just want to work on their cool games and not worry about the window or if a setting will be applied correctly. So here are 6 reasons/features of the Ultra Game System! 1. Window Management One of the top complaints I got with Cyclone is that users could not resize the window in-game. This was an engine limitation of Leadwerks This is now possible in Ultra Engine but it needs some elbow grease to make it work. The Game System does this for you. All you need to do is call GetProgram()->ResizeApp() and the graphics window class will recreate the window and framebuffer for you. By default, pressing F11 will swap between windowed mode and full screen. 2. Out-Of-Game Settings The Game System has an isolated window application for changing settings. The idea behind this is so there's a way to modify any window or graphics setting if an in-game one is unavailable. You probably shouldn't ship with just this solution as many people prefer to edit any setting within the game itself. This is accessible by using the -settings flag with the program. 3. Action Based Input System Years of research into this paid off when the Input Update for Cyclone released. The knowledge was carried over for the Game System. Only this time it's more dynamic! Simply set the controls in your main.cpp file: // 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("Camera", AXIS_MOUSE, "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("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); } Then deriving your components off of the GameObject class, you can use GetInput() for input functionality. virtual void UpdateInput() { // Movement if (allowmovement) { float speed = movespeed / 60.0f; if (GetInput()->Down("Sprint")) { speed *= 10.0f; } else if (GetInput()->Down("Crouch")) { speed *= 0.25f; } if (GetInput()->Down("Climb")) GetEntity()->Translate(0, speed, 0); if (GetInput()->Down("Desent")) GetEntity()->Translate(0, -speed, 0); auto axis = GetInput()->Axis("Movement"); GetEntity()->Move(axis.x * speed, 0, axis.y * speed); } } Best part is the "Controls" tab will reflect whatever you have defined! 4. User Input via Console Having a developer console is essential for developing any game! The Game System has a very simple but flexible console that doesn't need any commands registered beforehand. To define a new command, just poll the EVENT_CONSOLEEXECUTE id in your component's ProcessEvent function. virtual void Start() { Listen(EVENT_CONSOLEEXECUTE, GetProgram()); } virtual bool ProcessEvent(const Event& e) { if (e.id == EVENT_CONSOLEEXECUTE) { auto line = e.text.Split(" "); auto cmd = line[0].ToString(); if (line.size() > 1 && !line[1].empty()) { if (cmd == "crosshair") { bool hide = (bool)line[1].ToInt(); hudcamera->SetHidden(!hide); Print(QuoteString(cmd) + " has been set to: " + line[1]); } } } } 5. Sound Managment A layer of functionality in the Game System allows for cleaner sound origination and playback. You can store sound variables (files, volume, pitch, range, etc) within a JSON script. { "audioProfile": { "file": "Sound/radio_dreamlandloop_mono.wav", "volume": 0.5, "range": 35.0, "loop": true } } Then load the file with the GameSpeaker class. auto file = "Sound/Profiles/Radio.json"; shared_ptr<GameSpeaker> speaker = CreateGameSpeaker(file, GetEntity()->GetPosition()); The GameSpeaker has Save/Load functions so the speaker's time and state can be restored. Here's an example of creating and restoring a GameSpeaker. virtual bool Load(table& properties, shared_ptr<Stream> binstream, shared_ptr<Map> scene, const LoadFlags flags) { Print("Loading component " + QuoteWString(name)); if (speaker) { if (!properties["componentspeaker"].is_null()) speaker->Load(properties["componentspeaker"], binstream, scene, flags); } else { auto file = "Sound/Profiles/Radio.json"; speaker = CreateGameSpeaker(file, GetEntity()->GetPosition()); if (!properties["componentspeaker"].is_null()) speaker->Load(properties["componentspeaker"], binstream, scene, flags); } return true; } virtual bool Save(table& properties, shared_ptr<Stream> binstream, shared_ptr<Map> scene, const SaveFlags flags) { properties["componentspeaker"] = {}; if (speaker) speaker->Save(properties["componentspeaker"], binstream, scene, flags); return true; } As of right now, the sound system doesn't support audio filters as I feel that the filter should be applied within a volume (or to the listener) and not the sound/speaker itself. I'm still thinking about how that should work. 6. Better Render Layer Managment Intergrated today, The Canvas class is a great way to ensure that there is one camera per render layer. I ran into an issue where a component would draw 2D graphics to the framebuffer, but when there were multiple instances of them, multiple cameras were being made and drawing over each other. Using GetCanvas() can prevent this from happening. auto canvas = GetCanvas(world, RENDERLAYER_HUD); auto sprite = CreateSprite(world, 1.0f, 1.0f); sprite->SetPosition((float)sz.x / 2, (float)sz.y / 2); RenderToCanvas(sprite, canvas); The Game System will be my main focus until the Ultra Engine version of Cyclone starts development which will use this as a foundation. I'll also be working on other small things to share so be on the lookout for those!
  14. Move the camera to the bottom edge of the framebuffer. 0,0 should be the top left of the screen. Then you can use the size of the framebuffer to position the sprite wherever you want like if you were using the Interface class. local size = framebuffer:GetSize() cam2:SetPosition(size.x * 0.5, size.y * 0.5f)
  15. You're not setting your cam2 or your sprite to a new render layer. Not much of a Lua user, but try this. --Get the displays local displays = GetDisplays() --Create window local window = CreateWindow("Ultra Engine", 0, 0, 1280, 720, displays[1]) --Create framebuffer local framebuffer = CreateFramebuffer(window) --Create world local world = CreateWorld() --env map local specmap = LoadTexture("./Materials/Environment/Default/specular.dds") local diffmap = LoadTexture("./Materials/Environment/Default/diffuse.dds") world:SetEnvironmentMap(specmap, ENVIRONMENTMAP_BACKGROUND) world:SetEnvironmentMap(specmap, ENVIRONMENTMAP_SPECULAR) world:SetEnvironmentMap(diffmap, ENVIRONMENTMAP_DIFFUSE) --[[load map local mapname = "Maps/scene1.ultra" local cl = CommandLine() if type(cl["map"]) == "string" then mapname = cl["map"] end local scene = LoadMap(world, mapname) --]] --Create light local light = CreateBoxLight(world) light:SetRange(-10, 10) light:SetArea(15, 15) light:SetRotation(45, 35, 0) light:SetColor(2) --Load a font local font = LoadFont("Fonts/arial.ttf") local fontsize = 36 -- Create a camera local camera = CreateCamera(world,PROJECTION_PERSPECTIVE) camera:SetClearColor(0.125) camera:SetPosition(0, 5, 0) --Create second camera local cam2 = CreateCamera(world,PROJECTION_ORTHOGRAPHIC) cam2:SetPosition(0, 0, 0) cam2:SetClearMode(CLEAR_DEPTH) cam2:SetRenderLayers(1) -- Create sprite local sprite = CreateSprite(world, font,"HEY YOU !",fontsize) sprite:SetColor(1, 1, 1, 1) sprite:SetPosition(0, 0, 0) sprite:SetRenderLayers(1) --Camera controls to look around require 'Components/Player/CameraControls' camera:AddComponent(CameraControls) --main loop while not window:KeyDown(KEY_ESCAPE) do world:Update() world:Render(framebuffer) end This should spawn the sprite in the center of the screen.
  16. Was playing with this. This truly makes it super easy to get skyboxes into Ultra Engine! If you place the extension within the Engine's Tool Directory, you can use this script to launch the application right from the editor which is much more convenient. --[[ This extention is intended to be used with Klepto2's PBR Texture Generator https://www.ultraengine.com/community/topic/62344-pbr-texture-generator/ ]]-- local extension = {} extension.toolpath = "/Tools/PBRTextureGen/UltraPBRTextureGen.exe" function extension.hook(event, extension) if event.id == EVENT_WIDGETACTION then RunFile(AppDir()..extension.toolpath) end end -------------------------------------------------------------------- -- Add menu item -------------------------------------------------------------------- local menu = program.menu:FindChild("Scripting", false) if menu == nil then Print("Error: Could not find \"Scripting\" menu.") return end if menu ~= nil then local submenu = menu:FindChild("Generate", false) if submenu == nil then submenu = CreateMenu("Generate", menu) end extension.menuitem = CreateMenu("PBR Texture Generator", submenu) end ListenEvent(EVENT_WIDGETACTION, extension.menuitem, extension.hook, extension)
  17. It looks like the selected object loses focus causing the panel to hide.
  18. One thing I noticed when making an extension to the editor is that Windows gets really annoying when saving anything manually to the Program Files folder. Another thing is that you may want certain extensions for specific projects. Maybe move "global" extensions to the user Documents folder, and then have the editor also read extensions within the project folder if it doesn't already. On that note, I also think the blacklist filter should also be extended to be project based with an entry defined within the Ultra.json file. Maybe I don't want to see .dds files in a project that I want to use .basis files, etc.
  19. It's now live! Blog post will follow soon!
  20. I didn't want to make a new topic for this but when are the brush measurement information coming back while the brush is being created? I forgot to put that on my list in the opening post. I assumed it was on the back burner because you wanted to think about how to render the text in 3D.
  21. reepblue

    Finishing Touches

    What a wild six years this has been. I remember the first build being a. bunch of boxes in a blue void and look where it is now. Congrats on the release and it should only get better from here. Thank you for having me as part of the process through out the years! Now I'm gonna harass tell all my friends about how they should be using this instead of whatever they are currently using.
  22. Revisited my extension to generate basic materials from images and textures. This is good if you want non-game image formats part of your file blacklist. It was very helpful with the conversion script above to convert ten or so basic textures. local extension = {} function extension:CreateBlankMaterial() local defaultpath = CurrentDir().."/Materials/" local file = RequestFile("Select Material Location", defaultpath, "Ultra Engine Material File (*.mat):mat", 0, true) if file ~= nil then local mat = CreateMaterial() if mat ~= nil then mat:SetColor(1,1,1,1) local shaderfamily = LoadShaderFamily("Shaders/PBR.fam") if shaderfamily then mat:SetShaderFamily(shaderfamily) shaderfamily = nil end mat:Save(file); end end end function extension:Image2DDS(path) --Load image local pixmap = LoadPixmap(path) if pixmap==nil then Print("Error: Failed to generate texture \"" .. FixPath(output) .. "\"") return false end local mipchain = {} table.insert(mipchain,pixmap) --Generate mipmaps local w = pixmap.size.x local h = pixmap.size.y local mipmap = pixmap while (w > 4 and h > 4) do w = math.max(4, w / 2) h = math.max(4, h / 2) mipmap = mipmap:Resize(w,h) table.insert(mipchain,mipmap) end --Convert each image to BC1 (DXT1) compression for n=1, #mipchain do mipchain[n] = mipchain[n]:Convert(TEXTURE_BC1) end --Save mipchain to texture file local output = StripExt(path)..".dds" SaveTexture(output, TEXTURE_2D, mipchain, 1, SAVE_DEFAULT) return output end function extension:GenerateMaterialFromTexture() local defaultpath = CurrentDir().."/Materials/" local file = RequestFile("Select Texture", defaultpath, "Supported Files:dds,basis,png,jpg,jpeg,tga,bmp", 0) -- If we're not a texture, convert it if (ExtractExt(file) == "png" or ExtractExt(file) == "jpg" or ExtractExt(file) == "jpeg" or ExtractExt(file) == "tga" or ExtractExt(file) == "bmp") then local image = file file = extension:Image2DDS(image) end local output = StripExt(file)..".mat" if file ~= nil then local mat = CreateMaterial() if mat ~= nil then local tex = LoadTexture(file) if tex~=nil then mat:SetTexture(tex) mat:SetColor(1,1,1,1) local shaderfamily = LoadShaderFamily("Shaders/PBR.fam") if shaderfamily then mat:SetShaderFamily(shaderfamily) shaderfamily = nil end if mat:Save(output) then Print("Successfully saved \"" .. output .. "\"") end else Print("Failed to save \"" .. output .. "\" as texture is invaild!") end end mat = nil else Print("Failed to save \"" .. output .. "\" as file is invaild!") end end function extension.hook(event, extension) if event.id == EVENT_WIDGETACTION then if event.source == extension.menuitem_blankmaterial then extension:CreateBlankMaterial() elseif event.source == extension.menuitem_matfromtex then extension:GenerateMaterialFromTexture() end end end -------------------------------------------------------------------- -- Add menu item -------------------------------------------------------------------- local menu = program.menu:FindChild("Scripting", false) if menu == nil then Print("Error: Could not find \"Scripting\" menu.") return end local submenu = menu:FindChild("Generate Material", false) if submenu == nil then submenu = CreateMenu("Generate Material", menu) end extension.menuitem_blankmaterial = CreateMenu("New Blank Material", submenu) extension.menuitem_matfromtex = CreateMenu("New Material From Texture/Image", submenu) ListenEvent(EVENT_WIDGETACTION, extension.menuitem_blankmaterial, extension.hook, extension) ListenEvent(EVENT_WIDGETACTION, extension.menuitem_matfromtex, extension.hook, extension)
  23. It works better as a converter. Works exactly how Leadwerks behaves. I understand the reason for texture creation for being manual since DDS doesn't cover all needs. local converter = {} converter.informat = "png" converter.outformat = "dds" function converter:Convert(path, output) --Load image local pixmap = LoadPixmap(path) if pixmap==nil then Print("Error: Failed to generate texture " ..path) return false end local mipchain = {} table.insert(mipchain,pixmap) --Generate mipmaps local w = pixmap.size.x local h = pixmap.size.y local mipmap = pixmap while (w > 4 and h > 4) do w = math.max(4, w / 2) h = math.max(4, h / 2) mipmap = mipmap:Resize(w,h) table.insert(mipchain,mipmap) end --Convert each image to BC1 (DXT1) compression for n=1, #mipchain do mipchain[n] = mipchain[n]:Convert(TEXTURE_BC1) end --Save mipchain to texture file SaveTexture(output, TEXTURE_2D, mipchain, 1, SAVE_DEFAULT) return true end program:AddConverter(converter)
  24. I guess I'm just so used making textures in Leadwerks. That makes my life easier! Code is still useful regardless.
×
×
  • Create New...