diff --git a/docs/tasks/core.todo b/docs/tasks/core.todo index e5eabba..19331e1 100644 --- a/docs/tasks/core.todo +++ b/docs/tasks/core.todo @@ -34,6 +34,61 @@ Core: ✔ Add run mode interface class @done (9/15/2021, 8:22:35 PM) ✔ Read the window size and position on shutdown and write these to the state file @done (2/8/2022, 4:39:37 PM) +Physics: + ✔ Research Box2D as a possible physics library @done (10/27/2021, 7:40:44 PM) + ✔ Add Box2D to the project as an external library @done (10/27/2021, 7:40:46 PM) + ✔ Add a scene to the tester to test Box2D usage @done (10/28/2021, 2:42:45 PM) + ☐ Come up with a way to wrap Box2D into an API + ☐ Joint Component to allow parent/child entities to both use the physics system + +Scripting: + ☐ Allow scripts to interact (calling methods in other scripts) + - In order for a script to be registered with the World API it MUST + - contain a class that inherits from EntityBehavior and that class MUST + - have the same name as the script (case sensitive). + - Only methods in that class will be available to other registered scripts! + + ✔ Switch to Wren instead of LUA (https://github.com/wren-lang/wren) @high @done(22-11-02 18:56) + ✔ Remove SOL @done(22-11-02 18:56) + + ✔ Script Asset @done(22-11-14 18:19) + + Script Managment class: + ✘ Manage LUA states @cancelled(22-05-13 17:31) + ☐ Initialize new scripts + ☐ Run given script with given state + ☐ Add any generated errors to the Script object + + Interface Class (Core API): + ☐ Provide Methods that give access to the C++ code + +Audio: + ☐ Research the usage of OpenAL + ☐ Add OpenAL to the project + ☐ Design Audio API + + Assets: + ✔ Internal Asset Manager @high @done (1/25/2022, 3:58:20 PM) + ☐ Document Index.Dat and the AssetIndex class + ✔ Move the GenerateFont method from internal_font.h into data_manager.h @done(22-06-29 17:42) + + Types: + - Classes that represent each resource Types + ✔ Image class @done (9/16/2021, 2:46:34 PM) + ✔ Decouple Image class from OGLRenderer @high @done (10/27/2021, 7:41:50 PM) + ☐ Font class + ☐ Sound class + ☐ Animation + ✔ Script class @done (1/25/2022, 3:58:28 PM) + + Loaders: + - Need class (or classes?) to load resources from the packed format that the pipeline generates + Come up with binary file formats for each type: + ☐ .json (This will probably be multiple different formats depending on what the .json file is describing) + ☐ Image + ☐ Script + ☐ Audio + Graphics: ☐ Implement parent transform for all draw methods ☐ Ellipse @@ -85,20 +140,7 @@ GUI: ✔ Add a "New Directory" button @done (11/8/2021, 7:15:51 PM) ✔ Selected files should show up in the text box @done(22-04-18 13:33) -Scripting: - ✔ Switch to Wren instead of LUA (https://github.com/wren-lang/wren) @high @done(22-11-02 18:56) - ✔ Remove SOL @done(22-11-02 18:56) - ✔ Script Asset @done(22-11-14 18:19) - - Script Managment class: - ✘ Manage LUA states @cancelled(22-05-13 17:31) - ☐ Initialize new scripts - ☐ Run given script with given state - ☐ Add any generated errors to the Script object - - Interface Class (Core API): - ☐ Provide Methods that give access to the C++ code ECS: ✔ Figure out how to serialize Entities @done(22-06-28 14:16) @@ -153,16 +195,7 @@ Input: ✔ Port over the Element2D input system and adjust it to use glfw @done (9/8/2021, 8:20:07 PM) ✔ Add the InputManager to the core @done (9/9/2021, 2:57:06 PM) -Physics: - ✔ Research Box2D as a possible physics library @done (10/27/2021, 7:40:44 PM) - ✔ Add Box2D to the project as an external library @done (10/27/2021, 7:40:46 PM) - ✔ Add a scene to the tester to test Box2D usage @done (10/28/2021, 2:42:45 PM) - ☐ Come up with a way to wrap Box2D into an API -Audio: - ☐ Research the usage of OpenAL - ☐ Add OpenAL to the project - ☐ Design Audio API Utils: @@ -171,30 +204,7 @@ Utils: ☐ Add a templated return value to the OK variant of OpRes @low -Assets: - ✔ Internal Asset Manager @high @done (1/25/2022, 3:58:20 PM) - ☐ Document Index.Dat and the AssetIndex class - ✔ Move the GenerateFont method from internal_font.h into data_manager.h @done(22-06-29 17:42) - - Types: - - Classes that represent each resource Types - ✔ Image class @done (9/16/2021, 2:46:34 PM) - ✔ Decouple Image class from OGLRenderer @high @done (10/27/2021, 7:41:50 PM) - ☐ Font class - ☐ Sound class - ☐ Animation - ✔ Script class @done (1/25/2022, 3:58:28 PM) - - Loaders: - - Need class (or classes?) to load resources from the packed format that the pipeline generates - Come up with binary file formats for each type: - ☐ .json (This will probably be multiple different formats depending on what the .json file is describing) - ☐ Image - ☐ Script - ☐ Audio - - Asset Pipeline: - ☐ Read through the contents folder and generate asset files in a custom format (useable by the engine) + Testbed: - A special class that is used to unit-test features of the engine diff --git a/docs/tasks/editor.todo b/docs/tasks/editor.todo index 76887d4..0c1c9a3 100644 --- a/docs/tasks/editor.todo +++ b/docs/tasks/editor.todo @@ -30,6 +30,9 @@ Editor: ✔ Figure out how to make game asset types integrate with editor asset types @critical @done(22-06-27 13:32) - Probably just wrap the game object in an EditorAsset object if needed + Asset Pipeline: + ☐ Read through the contents folder and generate asset files in a custom format (useable by the engine) + Panel System: ☐ Allow for saving custom panel layouts @low @@ -53,6 +56,7 @@ Editor: ✔ Generate new content file @done (2/24/2022, 3:16:00 PM) ✔ Load existing contents @done (3/3/2022, 3:16:21 PM) ✔ Save/Update contents file @done (3/3/2022, 3:16:23 PM) + GUI Panels: World View: diff --git a/src/scripting/coreAPI.cpp b/src/scripting/coreAPI.cpp index 4cf168e..3f06763 100644 --- a/src/scripting/coreAPI.cpp +++ b/src/scripting/coreAPI.cpp @@ -44,7 +44,7 @@ namespace lunarium // Generate Code std::string keys_script = GenerateWrenKeyCodes(); - sman.RunSnippet("KeyCodes", keys_script); + sman.RunSnippet("KeyCodes", {}, keys_script); // DEBUG - SAVE GENERATED SCRIPT TO FILE FOR REVIEW File::WriteTextFile("key_codes.wren", keys_script); diff --git a/src/scripting/internal_scripts/components.wren b/src/scripting/internal_scripts/components.wren index bc1ff9a..6e5f488 100644 --- a/src/scripting/internal_scripts/components.wren +++ b/src/scripting/internal_scripts/components.wren @@ -24,8 +24,11 @@ foreign class VelocityComponent { foreign class BlockOutComponent { construct new(entity_id) {} + // Returns the color as a list: [r, g, b, a] foreign GetColor() foreign SetColor(r, g, b, a) + + // Returns the size as a list: [width, height] foreign GetSize() foreign SetSize(w, h) } \ No newline at end of file diff --git a/src/scripting/internal_scripts/world_interface.wren b/src/scripting/internal_scripts/world_interface.wren index d9b55aa..c5d9d0d 100644 --- a/src/scripting/internal_scripts/world_interface.wren +++ b/src/scripting/internal_scripts/world_interface.wren @@ -12,36 +12,55 @@ import "Components" for VelocityComponent, BlockOutComponent class WorldInterface { static Init() { - __Behaviors = [] + __ScriptObjects = {} System.print("WorldInterface initialized") } + static RegisterScript(entity_id, script_name, script_class) { + + System.print(entity_id.type) + System.print(script_name.type) + System.print(script_class.type) + + if (!__ScriptObjects.containsKey(entity_id)){ + __ScriptObjects[entity_id] = {} + } + + __ScriptObjects[entity_id][script_name] = script_class.new(entity_id) + } + ///////////////////////////////////////////////////////////////////// // EVENTS ///////////////////////////////////////////////////////////////////// - static RegisterBehavior(behavior) { - __Behaviors.add(behavior) - // behavior.OnLoad() - } + // static RegisterBehavior(behavior) { + // __ScriptObjects.add(behavior) + // // behavior.OnLoad() + // } static DoOnLoad() { - for (behavior in __Behaviors) { - behavior.OnLoad() + for (entity in __ScriptObjects) { + for (script in entity.value) { + script.value.OnLoad() + } } } static DoOnUnload() { - for (behavior in __Behaviors) { - behavior.OnUnload() + for (entity in __ScriptObjects) { + for (script in entity.value) { + script.value.OnUnload() + } } } static Update(dt) { - //System.print("Updating %(__Behaviors.count) behaviors...") - for (behavior in __Behaviors) { - behavior.Update(dt) + for (entity in __ScriptObjects) { + for (script in entity.value) { + script.value.Update(dt) + } } + } ///////////////////////////////////////////////////////////////////// @@ -55,6 +74,14 @@ class WorldInterface { static GetBlockOutComponent(entity_id) { return BlockOutComponent.new(entity_id) } + + static GetScriptObject(entity_id, script_name) { + if (__ScriptObjects.containsKey(entity_id) && __ScriptObjects[entity_id].containsKey(script_name)) { + return __ScriptObjects[entity_id][script_name] + } + + return null + } } diff --git a/src/scripting/world_api.cpp b/src/scripting/world_api.cpp index f5abd27..7e81c81 100644 --- a/src/scripting/world_api.cpp +++ b/src/scripting/world_api.cpp @@ -13,6 +13,7 @@ #include #include +#include #include namespace lunarium @@ -48,10 +49,10 @@ namespace lunarium mScriptState.RunScript(&world_interface); - // Get class and method handles mEventHandles.mWIHandle = mScriptState.GetWrenClassHandle("WorldInterface", "WorldInterface"); mEventHandles.mWIInitMethod = mScriptState.GetWrenMethodHandle("Init()"); + mEventHandles.mWIRegisterScriptMethod = mScriptState.GetWrenMethodHandle("RegisterScript(_,_,_)"); mEventHandles.mWIDoOnLoadMethod = mScriptState.GetWrenMethodHandle("DoOnLoad()"); mEventHandles.mWIDoOnUnloadMethod = mScriptState.GetWrenMethodHandle("DoOnUnload()"); mEventHandles.mWIUpdateMethod = mScriptState.GetWrenMethodHandle("Update(_)"); @@ -73,6 +74,29 @@ namespace lunarium mScriptState.RunScript(&script); } + void WorldAPI::RegisterScript(LUUID entity, WrenScript& script) + { + mScriptState.RunScript(&script); + // // Get Handle to script class type to use as second paraneter to RegisterScript + // WrenHandle* pScriptClass = mScriptState.GetWrenClassHandle("WorldInterface", script.GetScriptName()); + // std::vector params; + + // WrenParameter p; + // p.Type = WrenParamType::WPT_STR; + // p.As.String = std::to_string(entity).c_str(); + // params.push_back(p); + // p.As.String = script.GetScriptName().c_str(); + // params.push_back(p); + // p.Type = WrenParamType::WPT_HANDLE; + // p.As.Handle = pScriptClass; + // params.push_back(p); + // mScriptState.CallWrenMethod(mEventHandles.mWIRegisterScriptMethod, mEventHandles.mWIHandle, params, "WorldInterface.entity_id, script_name, script_class"); + + std::string script_class = script.GetScriptName(); + std::string code = "WorldInterface.RegisterScript(\"" + std::to_string(entity) + "\", \"" + script_class + "\", " + script_class + ")"; + mScriptState.RunSnippet("RegisterScript", {{"WorldInterface", "WorldInterface"}, { script_class, script_class }}, code); + } + void WorldAPI::InvokeEvent(Event e, ...) { switch (e) diff --git a/src/scripting/world_api.h b/src/scripting/world_api.h index 87799b9..e7f1b64 100644 --- a/src/scripting/world_api.h +++ b/src/scripting/world_api.h @@ -25,7 +25,9 @@ namespace lunarium { WrenHandle* mWIHandle; + WrenHandle* mWIInitMethod; + WrenHandle* mWIRegisterScriptMethod; WrenHandle* mWIDoOnLoadMethod; WrenHandle* mWIDoOnUnloadMethod; WrenHandle* mWIUpdateMethod; @@ -42,6 +44,7 @@ namespace lunarium static OpRes Initialize(World* pWorld); static void Shutdown(); + static void RegisterScript(LUUID entity, WrenScript& script); static void RunScript(WrenScript& script); static void InvokeEvent(Event e, ...); diff --git a/src/scripting/wren_script.cpp b/src/scripting/wren_script.cpp index 03e9b6a..5dc2df7 100644 --- a/src/scripting/wren_script.cpp +++ b/src/scripting/wren_script.cpp @@ -10,11 +10,15 @@ namespace lunarium { - WrenScript::WrenScript(std::string module_name, std::string code) - : mModuleName(module_name), mCode(code) + WrenScript::WrenScript(std::string script_name,std::string module_name, std::string code) + : mScriptName(script_name), mModuleName(module_name), mCode(code) { } + void WrenScript::SetScriptName(std::string name) + { + mScriptName = name; + } void WrenScript::SetModuleName(std::string name) { @@ -36,6 +40,11 @@ namespace lunarium mCode = ""; } + std::string WrenScript::GetScriptName() const + { + return mScriptName; + } + std::string WrenScript::GetModuleName() const { return mModuleName; diff --git a/src/scripting/wren_script.h b/src/scripting/wren_script.h index efbe648..2dc1d73 100644 --- a/src/scripting/wren_script.h +++ b/src/scripting/wren_script.h @@ -15,17 +15,24 @@ namespace lunarium class WrenScript { public: - WrenScript(std::string module_name = "", std::string code = ""); + WrenScript(std::string script_name = "", std::string module_name = "", std::string code = ""); + void SetScriptName(std::string name); void SetModuleName(std::string name); void SetScriptCode(std::string code); void AppendScriptCode(std::string code); void ClearScript(); + /// In order for a script to be registered with the World API it MUST + /// contain a class that inherits from EntityBehavior and that class MUST + /// have the same name as the script (case sensitive). + /// Only methods in that class will be available to other registered scripts! + std::string GetScriptName() const; std::string GetModuleName() const; std::string GetScriptCode() const; private: + std::string mScriptName; std::string mModuleName; std::string mCode; }; diff --git a/src/scripting/wren_state.cpp b/src/scripting/wren_state.cpp index 9b76ebe..a2f1cae 100644 --- a/src/scripting/wren_state.cpp +++ b/src/scripting/wren_state.cpp @@ -92,6 +92,7 @@ namespace lunarium case WrenParamType::WPT_DOUBLE: wrenSetSlotDouble(mpVM, i + 1, params[i].As.Double); break; case WrenParamType::WPT_BOOL: wrenSetSlotBool(mpVM, i + 1, params[i].As.Bool); break; case WrenParamType::WPT_STR: wrenSetSlotString(mpVM, i + 1, params[i].As.String); break; + case WrenParamType::WPT_HANDLE: wrenSetSlotHandle(mpVM, i + 1, params[i].As.Handle); break; default: Logger::Warn(mLogCat, "Unknown wren parameter type: %d", (int)params[i].Type); } } @@ -135,7 +136,7 @@ namespace lunarium } - void WrenState::RunSnippet(std::string name, std::string code) + void WrenState::RunSnippet(std::string name, std::vector imports, std::string code) { if (!mpVM) { @@ -143,7 +144,15 @@ namespace lunarium return; } - WrenInterpretResult result = wrenInterpret(mpVM, name.c_str(), code.c_str()); + std::string import_code = ""; + for (Import import : imports) + { + import_code += "import \"" + import.Module + "\" for " + import.Classes + "\n"; + } + import_code += "\n"; + std::string final_code = import_code + code; + + WrenInterpretResult result = wrenInterpret(mpVM, name.c_str(), final_code.c_str()); switch (result) { case WREN_RESULT_COMPILE_ERROR: Logger::Trace(mLogCat, "Snippet compile error in module: %s", name.c_str()); break; diff --git a/src/scripting/wren_state.h b/src/scripting/wren_state.h index fedc9e9..0c59196 100644 --- a/src/scripting/wren_state.h +++ b/src/scripting/wren_state.h @@ -24,6 +24,7 @@ namespace lunarium WPT_DOUBLE, WPT_BOOL, WPT_STR, + WPT_HANDLE, }; struct WrenParameter @@ -36,7 +37,8 @@ namespace lunarium int Int; char Char; bool Bool; - char* String; + const char* String; + WrenHandle* Handle; } As; }; @@ -76,6 +78,12 @@ namespace lunarium } }; + struct Import + { + std::string Module; + std::string Classes; + }; + public: ~WrenState(); @@ -87,7 +95,7 @@ namespace lunarium void RegisterForeignClass(ForeignClassDesc class_desc); void RunScript(WrenScript* script); - void RunSnippet(std::string name, std::string code); + void RunSnippet(std::string name, std::vector imports, std::string code); WrenHandle* GetWrenClassHandle(std::string from_module, std::string class_name); WrenHandle* GetWrenMethodHandle(std::string method_signature); // signature example (mehtod takes 2 parameters): method_name(_,_) diff --git a/src/world/world.cpp b/src/world/world.cpp index e8d9912..a3e1197 100644 --- a/src/world/world.cpp +++ b/src/world/world.cpp @@ -103,43 +103,35 @@ namespace lunarium } rigid_body.pBody = mpPhysicsWorld->CreateBody(&rigid_body.Defintion); + // Handle collision components if (mECSRegistry.all_of(entity)) { auto& comp = mECSRegistry.get(entity); + b2FixtureDef fixtureDef; + fixtureDef.isSensor = comp.IsSensor; + fixtureDef.density = comp.Density; + fixtureDef.friction = comp.Friction; + fixtureDef.restitution = comp.Restitution; + fixtureDef.filter.groupIndex = comp.CollisionGroup; + + b2PolygonShape Shape; switch (comp.Type) { case CollisionComponent::ShapeType::Box: - { - b2PolygonShape Shape; - Shape.SetAsBox(comp.Width / scale_factor, comp.Height / scale_factor); - b2FixtureDef fixtureDef; - fixtureDef.shape = &Shape; - fixtureDef.isSensor = comp.IsSensor; - fixtureDef.density = comp.Density; - fixtureDef.friction = comp.Friction; - fixtureDef.restitution = comp.Restitution; - - rigid_body.pBody->CreateFixture(&fixtureDef); - } break; - - case CollisionComponent::ShapeType::Circle: - { - b2CircleShape Shape; - Shape.m_radius = comp.Radius / scale_factor; - - b2FixtureDef fixtureDef; - fixtureDef.shape = &Shape; - fixtureDef.isSensor = comp.IsSensor; - fixtureDef.density = comp.Density; - fixtureDef.friction = comp.Friction; - fixtureDef.restitution = comp.Restitution; - - rigid_body.pBody->CreateFixture(&fixtureDef); - } break; - + Shape.SetAsBox(comp.Width / scale_factor, comp.Height / scale_factor); break; + + case CollisionComponent::ShapeType::Circle: + Shape.m_radius = comp.Radius / scale_factor; break; } + + fixtureDef.shape = &Shape; + + b2Fixture* fixture = rigid_body.pBody->CreateFixture(&fixtureDef); + + // Associate the fixture to it's entity so that collision callbacks can work + mEntitiesByColliders[fixture] = GetEntity(entity); } } @@ -153,10 +145,11 @@ namespace lunarium // If were attached to the editor we need to get the script from the editor's content manager #if !BUILD_NO_EDITOR + // TODO: Modify to add all scripts contained in the ScriptComponent (eventually they will have lists of scripts instead of just one) editor::Script* pScript = (editor::Script*) editor::ContentManager::GetInstance().GetAsset(script_comp.ScriptID); - WrenScript script(pScript->GetScriptFile().filename().string().c_str(), pScript->GetScript()); - // mScriptState.RunScript(&script); - WorldAPI::RunScript(script); + std::string name = pScript->GetScriptFile().stem().string(); + WrenScript script(name.c_str(), name.c_str(), pScript->GetScript()); + WorldAPI::RegisterScript(GetEntity(entity)->GetUUID(), script); #else // Get Binary assets #endif @@ -174,6 +167,8 @@ namespace lunarium delete mpPhysicsWorld; mpPhysicsWorld = nullptr; + + mEntitiesByColliders.clear(); } void World::SetRunMode(RunMode mode) @@ -224,7 +219,7 @@ namespace lunarium float scale_factor = WorldSettings.Physics.PhysicsScaleFactor; transform.Position = glm::vec3(rigid_body.pBody->GetPosition().x * scale_factor, rigid_body.pBody->GetPosition().y * scale_factor, 0.0f); - transform.Rotation.z = rigid_body.pBody->GetAngle(); + transform.Rotation.z = rigid_body.pBody->GetAngle() * -1.0f; } } @@ -297,6 +292,12 @@ namespace lunarium return mEntitiesByUUID[id]; } + + Entity* World::GetEntity(entt::entity entt_id) + { + return GetEntity(mECSRegistry.get(entt_id).UUID); + } + unsigned int World::GetNumEntities() const { return mEntities.size(); diff --git a/src/world/world.h b/src/world/world.h index 693440c..7e3336b 100644 --- a/src/world/world.h +++ b/src/world/world.h @@ -91,6 +91,7 @@ namespace lunarium LUUID CreateEntity(); void RemoveEntity(LUUID id); Entity* GetEntity(LUUID id); + Entity* GetEntity(entt::entity entt_id); unsigned int GetNumEntities() const; std::vector::iterator EntitiesBegin(); bool EntitiesIsEnd(std::vector::iterator& iter); @@ -122,6 +123,7 @@ namespace lunarium entt::registry mECSRegistry; std::vector mEntities; std::map mEntitiesByUUID; + std::map mEntitiesByColliders; // Needed for collision detection callbacks FrameBuffer* mFrameBuffer; OrthographicCamera* mpActiveCamera; b2World* mpPhysicsWorld;