reepblue Posted September 17, 2023 Share Posted September 17, 2023 Note: Scene::Save() works fine but doesn't copy over component data. The current map doesn't load any components in this test, but it should be worth testing. #include "UltraEngine.h" using namespace UltraEngine; void main(const char* args, const int argc) { //Get the displays auto displays = GetDisplays(); //Create a window auto window = CreateWindow("Ultra Engine", 0, 0, 1280, 720, displays[0], WINDOW_TITLEBAR | WINDOW_CENTER); //Create a framebuffer auto framebuffer = CreateFramebuffer(window); //Create a world auto world = CreateWorld(); //Load a scene auto scene = LoadMap(world, "Maps/savetest.ultra"); //Main loop while (!window->Closed() and !window->KeyHit(KEY_ESCAPE)) { // Save if (window->KeyHit(KEY_F5)) { world->Save("save.ultra", SAVE_DEFAULT); } //Load if (window->KeyHit(KEY_F6)) { scene = NULL; scene = LoadMap(world, "save.ultra"); } //Update world world->Update(); //Render world world->Render(framebuffer, true); } } savetest.zip Cyclone - Ultra Game System - Component Preprocessor - Tex2TGA - Darkness Awaits Template (Leadwerks) If you like my work, consider supporting me on Patreon! Link to comment Share on other sites More sharing options...
Josh Posted September 18, 2023 Share Posted September 18, 2023 This method just creates a scene object and adds the world entities to it. However, the functionality to add all entities into a list of weak pointers was never added! The fastest fix is for me to just remove this method and the user can create a scene and add entities themselves. My job is to make tools you love, with the features you want, and performance you can't live without. Link to comment Share on other sites More sharing options...
reepblue Posted September 18, 2023 Author Share Posted September 18, 2023 I hope this will be demonstrated in the documentation. I was under the impression we would have "snapshot" saves by default with the world/scene::Save() function. It probably only worked with Lua components, but I'm looking forward to an example. Cyclone - Ultra Game System - Component Preprocessor - Tex2TGA - Darkness Awaits Template (Leadwerks) If you like my work, consider supporting me on Patreon! Link to comment Share on other sites More sharing options...
Josh Posted September 19, 2023 Share Posted September 19, 2023 Yeah, that's the idea. This appears to be working: auto p = CreatePivot(NULL); p->AddComponent<Mover>(); auto map = std::make_shared<Scene>(); map->AddEntity(p); map->Save("Maps/out.ultra"); Here is the output: { "scene": { "base": [], "entities": [ { "castShadows": false, "collisionType": 0, "extras": { "components": { "Mover": { "globalcoords": false, "movementspeed": [ 0, 0, 0 ], "rotationspeed": [ 0, 0, 0 ] } } }, "pickMode": 0, "reflection": true, "uuid": "ffe028a9-4abe-49de-ba7b-30c2373cf196" } ] } } 1 My job is to make tools you love, with the features you want, and performance you can't live without. Link to comment Share on other sites More sharing options...
reepblue Posted September 19, 2023 Author Share Posted September 19, 2023 Ok, so can just for loop all the entities within the world, and then add it to the scene? I'll give it a try and play around with it. My goal is to have Half Life 2 like save/load states almost transparent to the end user. Might want to do this for every save call since things like bullets or instanced objects can spawn at any time. Cyclone - Ultra Game System - Component Preprocessor - Tex2TGA - Darkness Awaits Template (Leadwerks) If you like my work, consider supporting me on Patreon! Link to comment Share on other sites More sharing options...
reepblue Posted September 20, 2023 Author Share Posted September 20, 2023 Regarding components. they don't seem to get updated upon a reload and I have no idea why. This save file has the Mover and a CameraControls component defined in the map file but upon a reload, it's like the components aren't actually being attached for some reason. Does the editor do something different? quicksave.zip Cyclone - Ultra Game System - Component Preprocessor - Tex2TGA - Darkness Awaits Template (Leadwerks) If you like my work, consider supporting me on Patreon! Link to comment Share on other sites More sharing options...
Josh Posted September 21, 2023 Share Posted September 21, 2023 Working on docs: https://www.ultraengine.com/learn/Map_Reload?lang=cpp 2 My job is to make tools you love, with the features you want, and performance you can't live without. Link to comment Share on other sites More sharing options...
reepblue Posted September 23, 2023 Author Share Posted September 23, 2023 On 9/21/2023 at 5:25 PM, Josh said: Working on docs: https://www.ultraengine.com/learn/Map_Reload?lang=cpp While this example works, it's not working in the way needed for game saves. The reload function throws an exception when trying to read a file and you get a bufferstream overflow with the example below. Also, since Reload doesn't create anything, would I need to load the map file before the save file? If so, what's the best way to know way to know what save file goes to which map? #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); //Create a scene auto scene = LoadMap(world, "Maps/savetest.ultra"); //Save the starting scene to memory shared_ptr<BufferStream> stream = NULL; shared_ptr<BufferStream> binstream = NULL; //Main loop while (window->Closed() == false and window->KeyDown(KEY_ESCAPE) == false) { if (window->KeyHit(KEY_F5)) { Print("SAVE"); if (stream) stream = NULL; if (binstream) binstream = NULL; stream = CreateBufferStream(); binstream = CreateBufferStream(); scene->Save(stream, binstream); } //Reload the starting scene when space key is pressed if (window->KeyHit(KEY_F6)) { if (stream and binstream) { stream->Seek(0); binstream->Seek(0); scene->Reload(stream, binstream); } } world->Update(); world->Render(framebuffer); } return 0; } Cyclone - Ultra Game System - Component Preprocessor - Tex2TGA - Darkness Awaits Template (Leadwerks) If you like my work, consider supporting me on Patreon! Link to comment Share on other sites More sharing options...
Josh Posted September 29, 2023 Share Posted September 29, 2023 I can confirm the way components are being saved in the built-in save routine was incorrect. They are not supposed to be stored in the "extras" field... My job is to make tools you love, with the features you want, and performance you can't live without. Link to comment Share on other sites More sharing options...
Josh Posted September 29, 2023 Share Posted September 29, 2023 As for the save-to-file issue, I need to decide what I am trying to do here. My first implementation of the Ultra map format used an accompanying .bin file like glTF does, but after seeing how often people send glTF files and forget the .bin file I decided that was a bad idea. My job is to make tools you love, with the features you want, and performance you can't live without. Link to comment Share on other sites More sharing options...
reepblue Posted September 29, 2023 Author Share Posted September 29, 2023 Why don't you take a copy out of Valve's book and just make a map a package (zip) file. It might sound like the stupidest idea ever but hear me out: You can keep your bin/json setup. You can store baked cubemap files in there. People can put in custom assets exclusive for that map. I love the fact that map files are raw text. It makes it 100% easier to debug and find issues but we need binary and information available too. 1 Cyclone - Ultra Game System - Component Preprocessor - Tex2TGA - Darkness Awaits Template (Leadwerks) If you like my work, consider supporting me on Patreon! Link to comment Share on other sites More sharing options...
Josh Posted September 29, 2023 Share Posted September 29, 2023 The design I've been using for both maps and colliders is a JSON string, and then if there is any binary data, this is followed by a null character, and then the bin data. If there is no binary data then the file is just a normal JSON file. This is very simple, and I figure some tool or plugin will arise that allows editing of the JSON text info without destroying the binary data. If the size of the JSON string changes, that's okay because all the offsets that point to the binary data are counted from the start of the data. The Map:Reload() method is changed to only use one stream, and the example below is now using a file: https://www.ultraengine.com/learn/Map_Reload?lang=cpp The update is available now and I have tested it. 1 My job is to make tools you love, with the features you want, and performance you can't live without. Link to comment Share on other sites More sharing options...
reepblue Posted September 30, 2023 Author Share Posted September 30, 2023 Looks good. I'll reimplement my quick save/load feature soon and I'll try this with components. Cyclone - Ultra Game System - Component Preprocessor - Tex2TGA - Darkness Awaits Template (Leadwerks) If you like my work, consider supporting me on Patreon! Link to comment Share on other sites More sharing options...
reepblue Posted September 30, 2023 Author Share Posted September 30, 2023 I got this error when using Map::Reload(). My map has a component for the player. This map has two components in it. When using Map::LoadMap on the sav file, it works fine minus me forgetting to save a few values. It looks like I'll need to do work on my end. I'm not 100% sure what Reload would do if the map is completely different. It seems safer to load the save file as a new map and trick the rest of my system to think you're playing the actual map file. Cyclone - Ultra Game System - Component Preprocessor - Tex2TGA - Darkness Awaits Template (Leadwerks) If you like my work, consider supporting me on Patreon! Link to comment Share on other sites More sharing options...
Josh Posted September 30, 2023 Share Posted September 30, 2023 4 minutes ago, reepblue said: I got this error when using Map::Reload(). My map has a component for the player. This map has two components in it. When using Map::LoadMap on the sav file, it works fine minus me forgetting to save a few values. It looks like I'll need to do work on my end. I'm not 100% sure what Reload would do if the map is completely different. It seems safer to load the save file as a new map and trick the rest of my system to think you're playing the actual map file. It searches for each entity by UUID and reloads its state. It's okay for there to be different entities or entities missing. My job is to make tools you love, with the features you want, and performance you can't live without. Link to comment Share on other sites More sharing options...
reepblue Posted September 30, 2023 Author Share Posted September 30, 2023 41 minutes ago, Josh said: It searches for each entity by UUID and reloads its state. It's okay for there to be different entities or entities missing. Oh, so it's just a more efficient LoadMap so I can use this for any save files for any map at any time? You can get my seek error with this code and the map below. It has something to do with loading editor made maps. 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); auto scene = LoadMap(world, "Maps/savetest.ultra"); ////Create a camera //auto camera = CreateCamera(world); //camera->SetClearColor(0.125); //camera->SetPosition(0, 1, -4); //camera->AddComponent<Player>(); ////Create light //auto light = CreateBoxLight(world); //light->SetRange(-10, 10); //light->SetArea(15, 15); //light->SetRotation(45, 35, 0); //light->SetColor(2); ////Create the ground //auto ground = CreateBox(world, 10, 1, 10); //ground->SetPosition(0, -0.5, 0); //ground->SetColor(0, 1, 0); ////Create a scene //auto scene = CreateMap(); //scene->entities.push_back(ground); //scene->entities.push_back(light); //ground = NULL; //light = NULL; ////Add some boxes //for (int n = 0; n < 10; ++n) //{ // auto box = CreateBox(world); // box->SetColor(0, 0, 1); // box->SetPosition(Random(-5, 5), Random(5, 10), Random(-5, 5)); // box->SetMass(1); // scene->entities.push_back(box); //} //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; } savetest.zip Cyclone - Ultra Game System - Component Preprocessor - Tex2TGA - Darkness Awaits Template (Leadwerks) If you like my work, consider supporting me on Patreon! Link to comment Share on other sites More sharing options...
Josh Posted September 30, 2023 Share Posted September 30, 2023 34 minutes ago, reepblue said: Oh, so it's just a more efficient LoadMap so I can use this for any save files for any map at any time? Yes. The idea is to load the entity states instead of having to re-create the entire scene. This can cause some restrictions on your code. Entities should not be created in code unless it is a result of some component code, and that component is responsible for saving and reloading any auxillary entity data. But if you follow the rules you get automatic quick/load states. My job is to make tools you love, with the features you want, and performance you can't live without. Link to comment Share on other sites More sharing options...
reepblue Posted September 30, 2023 Author Share Posted September 30, 2023 15 minutes ago, Josh said: Yes. The idea is to load the entity states instead of having to re-create the entire scene. This can cause some restrictions on your code. Entities should not be created in code unless it is a result of some component code, and that component is responsible for saving and reloading any auxillary entity data. But if you follow the rules you get automatic quick/load states. Ok, thanks. I can't easily test it until this works with map files but I'm assuming you just use scene->AddEntity() or scene->entities.push_back() in the Save() function for any entitles needed by the component. Cyclone - Ultra Game System - Component Preprocessor - Tex2TGA - Darkness Awaits Template (Leadwerks) If you like my work, consider supporting me on Patreon! Link to comment Share on other sites More sharing options...
Josh Posted September 30, 2023 Share Posted September 30, 2023 4 minutes ago, reepblue said: Ok, thanks. I can't easily test it until this works with map files but I'm assuming you just use scene->AddEntity() or scene->entities.push_back() in the Save() function for any entitles needed by the component. If the entity creates a component, then it just needs to delete and re-create it in the Load method, or save the important properties into the saved JSON data. If a component references another entity that another component owns, it can call GetUuid() to retrieve that entity's ID and store it in the JSON data. My job is to make tools you love, with the features you want, and performance you can't live without. Link to comment Share on other sites More sharing options...
Josh Posted September 30, 2023 Share Posted September 30, 2023 I get the same error when I run your code. Testing now. The saved game does open in the editor, which is kind of cool 1 My job is to make tools you love, with the features you want, and performance you can't live without. Link to comment Share on other sites More sharing options...
Josh Posted September 30, 2023 Share Posted September 30, 2023 Your example above works now with the current engine build I just uploaded. 1 My job is to make tools you love, with the features you want, and performance you can't live without. Link to comment Share on other sites More sharing options...
reepblue Posted September 30, 2023 Author Share Posted September 30, 2023 3 hours ago, Josh said: If the entity creates a component, then it just needs to delete and re-create it in the Load method, or save the important properties into the saved JSON data. If a component references another entity that another component owns, it can call GetUuid() to retrieve that entity's ID and store it in the JSON data. I can confirm that it's working as intended. I think I need to rethink how to structure components as I'm having issues/conflicts. Cyclone - Ultra Game System - Component Preprocessor - Tex2TGA - Darkness Awaits Template (Leadwerks) If you like my work, consider supporting me on Patreon! Link to comment Share on other sites More sharing options...
reepblue Posted October 1, 2023 Author Share Posted October 1, 2023 I think my problem is that Save/Load doesn't get called when Map::Reload() is used. Load gets called from LoadScene, however. #include "UltraEngine.h" using namespace UltraEngine; class Player : public Component { public: virtual void Start() { Print("Start()"); } virtual bool Load(table& properties, shared_ptr<Stream> binstream, shared_ptr<Map> scene, const LoadFlags flags) { Print("Load()"); return true; } virtual bool Save(table& properties, shared_ptr<Stream> binstream, shared_ptr<Map> scene, const SaveFlags flags) { Print("Save()"); return true; } }; 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->SetPosition(0, 1, -4); camera->AddComponent<Player>(); //Create light auto light = CreateBoxLight(world); light->SetRange(-10, 10); light->SetArea(15, 15); light->SetRotation(45, 35, 0); light->SetColor(2); //Create the ground auto ground = CreateBox(world, 10, 1, 10); ground->SetPosition(0, -0.5, 0); ground->SetColor(0, 1, 0); //Create a scene auto scene = CreateMap(); scene->entities.push_back(ground); scene->entities.push_back(light); ground = NULL; light = NULL; //Add some boxes for (int n = 0; n < 10; ++n) { auto box = CreateBox(world); box->SetColor(0, 0, 1); box->SetPosition(Random(-5, 5), Random(5, 10), Random(-5, 5)); box->SetMass(1); scene->entities.push_back(box); } //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; } Cyclone - Ultra Game System - Component Preprocessor - Tex2TGA - Darkness Awaits Template (Leadwerks) If you like my work, consider supporting me on Patreon! Link to comment Share on other sites More sharing options...
Josh Posted October 1, 2023 Share Posted October 1, 2023 It should be called. I will check it out. 1 My job is to make tools you love, with the features you want, and performance you can't live without. Link to comment Share on other sites More sharing options...
reepblue Posted October 1, 2023 Author Share Posted October 1, 2023 Looks like we're crashing again right when it goes to load the map file again. Cyclone - Ultra Game System - Component Preprocessor - Tex2TGA - Darkness Awaits Template (Leadwerks) If you like my work, consider supporting me on Patreon! Link to comment Share on other sites More sharing options...
Recommended Posts