From 04e4f63fd786011cf3c42cbf4f03ff7a89a16ba3 Mon Sep 17 00:00:00 2001 From: Timerix Date: Fri, 18 Apr 2025 18:24:02 +0500 Subject: [PATCH] GameObject and Component --- src-csharp/Component.cs | 16 +++++++ src-csharp/ExampleComponent.cs | 15 +++++++ src-csharp/GameObject.cs | 78 +++++++++++++++++++++++++++++----- src-csharp/NativeMethods.cs | 13 ++++++ src-csharp/Script.cs | 15 ------- src/Mono/Mono.hpp | 7 ++- src/main.cpp | 33 +++++++------- 7 files changed, 136 insertions(+), 41 deletions(-) create mode 100644 src-csharp/Component.cs create mode 100644 src-csharp/ExampleComponent.cs create mode 100644 src-csharp/NativeMethods.cs delete mode 100644 src-csharp/Script.cs diff --git a/src-csharp/Component.cs b/src-csharp/Component.cs new file mode 100644 index 0000000..4122be6 --- /dev/null +++ b/src-csharp/Component.cs @@ -0,0 +1,16 @@ +using System; + +namespace Ougge; + +public abstract class Component { + private GameObject _gameObject; + + internal Component(GameObject gameObject) + { + _gameObject = gameObject; + } + + public GameObject Owner { get => _gameObject.IsDestroyed ? throw new Exception("GameObject") : _gameObject; } + + public virtual void Update(double deltaTime) {} +} diff --git a/src-csharp/ExampleComponent.cs b/src-csharp/ExampleComponent.cs new file mode 100644 index 0000000..77c5458 --- /dev/null +++ b/src-csharp/ExampleComponent.cs @@ -0,0 +1,15 @@ +using System; + +namespace Ougge; + +public class ExampleComponent : Component +{ + public ExampleComponent(GameObject gameObject) : base(gameObject) + { + } + + public override void Update(double deltaTime) + { + Console.WriteLine($"C# deltaTime {deltaTime} object id {Owner.Id}"); + } +} \ No newline at end of file diff --git a/src-csharp/GameObject.cs b/src-csharp/GameObject.cs index 235cb4b..676273b 100644 --- a/src-csharp/GameObject.cs +++ b/src-csharp/GameObject.cs @@ -1,4 +1,5 @@ -using System.Management.Instrumentation; +using System; +using System.Collections.Generic; using System.Numerics; using System.Runtime.InteropServices; @@ -7,18 +8,75 @@ namespace Ougge; [StructLayout(LayoutKind.Sequential)] public struct Transform { - Vector2 scale; + Vector2 scale; Vector2 position; float rotation; } -public class Component -{ - GameObject owner; -} - public class GameObject { - Transform transform { get; } - GameObject? parent; -} \ No newline at end of file + // index in GameObjectPool + private ulong _id; + private uint _index; + private bool _isDestroyed; + + public ulong Id => _id; + public bool IsDestroyed => _isDestroyed; + + public Transform Transform { get; } + public GameObject? Parent; + + public readonly Dictionary Components = new(); + + private GameObject() + { + } + + private void Init(ulong id, uint nativePoolIndex) + { + _id = id; + _index = nativePoolIndex; + } + + static public GameObject Create() + { + NativeMethods.createGameObject(out ulong id, out uint index); + var o = new GameObject(); + o.Init(id, index); + return o; + } + + public void Destroy() + { + if(_isDestroyed) + return; + + _isDestroyed = NativeMethods.destroyGameObject(_index); + if(!_isDestroyed) + throw new Exception($"Can't destroy GameObject({_id})"); + } + + public bool TryCreateComponent(Type t) + { + if(!t.IsSubclassOf(typeof(Component))) + throw new Exception($"type {t.FullName} is not a derived class of {typeof(Component).FullName}"); + + if(Components.ContainsKey(t)) + return false; + + Components.Add(t, (Component)Activator.CreateInstance(t, this)); + return true; + } + private bool TryCreateComponent(string fullName) + { + return TryCreateComponent(Type.GetType(fullName)); + } + + private void UpdateComponents(double deltaTime) + { + foreach(var p in Components) + { + p.Value.Update(deltaTime); + } + } +} diff --git a/src-csharp/NativeMethods.cs b/src-csharp/NativeMethods.cs new file mode 100644 index 0000000..7ec413f --- /dev/null +++ b/src-csharp/NativeMethods.cs @@ -0,0 +1,13 @@ +using System; +using System.Runtime.InteropServices; + +namespace Ougge; + +internal static class NativeMethods +{ + [DllImport("__Internal")] + internal extern static bool destroyGameObject(uint index); + + [DllImport("__Internal")] + internal extern static void createGameObject(out ulong id, out uint index); +} \ No newline at end of file diff --git a/src-csharp/Script.cs b/src-csharp/Script.cs deleted file mode 100644 index 0713b16..0000000 --- a/src-csharp/Script.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; - -namespace Ougge; - -public abstract class ScriptBase { - public virtual void Update(double deltaTime) {} -} - -public class ExampleScript : ScriptBase -{ - public override void Update(double deltaTime) - { - Console.WriteLine($"C# deltaTime {deltaTime}"); - } -} \ No newline at end of file diff --git a/src/Mono/Mono.hpp b/src/Mono/Mono.hpp index 67de32b..d77f7ff 100644 --- a/src/Mono/Mono.hpp +++ b/src/Mono/Mono.hpp @@ -73,8 +73,11 @@ public: void* arg_array[] = { valueToVoidPtr(args)..., nullptr }; MonoObject* ex = nullptr; MonoObject* result = mono_runtime_invoke(method_ptr, class_instance, arg_array, &ex); - if(ex) //TODO: call mono_trace_set_printerr_handler from mono/mono/utils/mono-logger.h + if(ex){ + //TODO: call mono_trace_set_printerr_handler from mono/mono/utils/mono-logger.h mono_print_unhandled_exception(ex); + throw UsefulException("Some C# exception occured"); + } return valueFromMonoObject(result); }; template @@ -107,6 +110,8 @@ public: Assembly(const Assembly&) = delete; MonoClass* getClass(const std::string& name_space, const std::string& name); + MonoAssembly* getMonoAssembly() const { return ptr; } + MonoImage* getMonoImage() const { return image; } }; diff --git a/src/main.cpp b/src/main.cpp index 9d4f5a3..a0874fc 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -42,22 +42,25 @@ int main(int argc, const char** argv){ GameObjectPool p(GAMEOBJECTPOOL_SIZE); auto a = mono.loadAssembly("Ougge.dll"); - auto gameObjectClass = a->getClass("Ougge", "GameObject"); - for(int i = 0; i < GAMEOBJECTPOOL_SIZE; i++){ - Mono::Object gameObjectManaged = mono_object_new(mono.getDomain(), gameObjectClass); - p.emplace(GameObject(gameObjectManaged, nullptr)); - GameObject& o = p.get(0); - if(i+1 % 8192 == 0) - std::cout<<'['<getClass("Ougge", "GameObject"); + MonoObject* exampleObjectManaged = mono_object_new(mono.getDomain(), gameObjectClass); + u64 obj_id = 0; + auto pair = p.emplace(GameObject(exampleObjectManaged, nullptr)); + mono_runtime_object_init(exampleObjectManaged); + auto exampleObjectInit = Mono::Method(gameObjectClass, "Init"); + exampleObjectInit(exampleObjectManaged, obj_id++, pair.first); + auto exampleObjectUpdate = Mono::Method(gameObjectClass, "UpdateComponents"); - // mono_runtime_object_init(scriptInstance); - // auto scriptUpdate = Mono::Method(c, "Update"); - // updateCallbacks.push_back([scriptInstance, scriptUpdate](f64 deltaTime) -> void { - // scriptUpdate(scriptInstance, deltaTime); - // }); + updateCallbacks.push_back([exampleObjectManaged, exampleObjectUpdate](f64 deltaTime) -> void { + exampleObjectUpdate(exampleObjectManaged, deltaTime); + }); + + auto tryCreateComponent = Mono::Method(gameObjectClass, "TryCreateComponent"); + Mono::String componentNameManaged = mono_string_new(mono.getDomain(), "ExampleComponent"); + Mono::ObjectHandle componentNameObjectHandle((MonoObject*)(void*)componentNameManaged); + Mono::Bool created = tryCreateComponent(exampleObjectManaged, componentNameManaged); + if(!created.wide_bool) + throw UsefulException("couldn't create ExampleComponent"); GUI::MainWindow w; w.open("ougge", update);