From 4fd9a924a0b692bce74d95bef55d11991c3a8d3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20L=C3=BCbe=C3=9F?= Date: Wed, 29 Mar 2023 16:32:46 +0200 Subject: [PATCH] Added basic component access in Scripts --- GlitchyEngine/src/Scripting/ScriptEngine.bf | 2 + GlitchyEngine/src/Scripting/ScriptGlue.bf | 118 +++++++++++++++++++- GlitchyEngineHelper/src/Mono/Mono.bf | 10 ++ ScriptCore/Components/Component.cs | 4 +- ScriptCore/Components/RigidBody2D.cs | 30 +++++ ScriptCore/Components/Transform.cs | 16 ++- ScriptCore/Entity.cs | 110 ++++++++++++++++-- ScriptCore/Math/Vector3.cs | 13 ++- ScriptCore/MyTestEntity.cs | 27 +++-- ScriptCore/ScriptCore.csproj | 1 + ScriptCore/ScriptGlue.cs | 34 +++++- 11 files changed, 332 insertions(+), 33 deletions(-) create mode 100644 ScriptCore/Components/RigidBody2D.cs diff --git a/GlitchyEngine/src/Scripting/ScriptEngine.bf b/GlitchyEngine/src/Scripting/ScriptEngine.bf index fd6f10b..f9306d1 100644 --- a/GlitchyEngine/src/Scripting/ScriptEngine.bf +++ b/GlitchyEngine/src/Scripting/ScriptEngine.bf @@ -52,6 +52,8 @@ static class ScriptEngine LoadAssembly("resources/scripts/ScriptCore.dll"); + ScriptGlue.RegisterManagedComponents(); + //Samples(); } diff --git a/GlitchyEngine/src/Scripting/ScriptGlue.bf b/GlitchyEngine/src/Scripting/ScriptGlue.bf index 712c0b3..c49fbaa 100644 --- a/GlitchyEngine/src/Scripting/ScriptGlue.bf +++ b/GlitchyEngine/src/Scripting/ScriptGlue.bf @@ -6,11 +6,16 @@ using GlitchyEngine.Events; using GlitchyEngine.Math; using GlitchyEngine.World; using GlitchyEngine.Core; +using System.Collections; namespace GlitchyEngine.Scripting; static class ScriptGlue { + private static Dictionary s_AddComponentMethods = new .() ~ delete _; + private static Dictionary s_HasComponentMethods = new .() ~ delete _; + private static Dictionary s_RemoveComponentMethods = new .() ~ delete _; + struct RegisterCallAttribute : Attribute { public String MethodName; @@ -47,12 +52,48 @@ static class ScriptGlue RegisterCalls(); } + public static void RegisterManagedComponents() + { + RegisterComponent("GlitchyEngine.Transform"); + RegisterComponent("GlitchyEngine.RigidBody2D"); + } + [RegisterMethod] private static void RegisterCalls() { // Generated at CompTime } + private static void RegisterComponent(StringView cSharpClassName = "") where T : struct, new + { + String className; + + if (cSharpClassName.IsWhiteSpace) + { + className = scope:: $"GlitchyEngine."; + typeof(T).GetName(className); + } + else + { + className = scope:: String(cSharpClassName); + } + + className.EnsureNullTerminator(); + + MonoType* managedType = Mono.mono_reflection_type_from_name(className.CStr(), ScriptEngine.[Friend]s_CoreAssemblyImage); + + if (managedType != null) + { + s_AddComponentMethods[managedType] = (entity) => entity.AddComponent(); + s_HasComponentMethods[managedType] = (entity) => entity.HasComponent(); + s_RemoveComponentMethods[managedType] = (entity) => entity.RemoveComponent(); + } + else + { + Log.EngineLogger.AssertDebug(managedType != null, scope $"No C# component with name \"{{className}}\" found for Beef type \"{typeof(T)}\""); + } + } + [RegisterCall("Log::LogMessage_Impl")] static void Log(int32 logLevel, MonoString* message) @@ -98,23 +139,64 @@ static class ScriptGlue #region Scene/Entity stuff + [RegisterCall("ScriptGlue::Entity_AddComponent")] + static void Entity_AddComponent(UUID entityId, MonoReflectionType* componentType) + { + MonoType* type = Mono.mono_reflection_type_get_type(componentType); + + Entity entity = ScriptEngine.Context.GetEntityByID(entityId); + if (s_AddComponentMethods.TryGetValue(type, let addMethod)) + addMethod(entity); + + Log.EngineLogger.AssertDebug(false, "No managed component with the given type registered."); + } + + [RegisterCall("ScriptGlue::Entity_HasComponent")] + static bool Entity_HasComponent(UUID entityId, MonoReflectionType* componentType) + { + MonoType* type = Mono.mono_reflection_type_get_type(componentType); + + Entity entity = ScriptEngine.Context.GetEntityByID(entityId); + if (s_HasComponentMethods.TryGetValue(type, let hasMethod)) + return hasMethod(entity); + + Log.EngineLogger.AssertDebug(false, "No managed component with the given type registered."); + + return false; + } + + [RegisterCall("ScriptGlue::Entity_RemoveComponent")] + static void Entity_RemoveComponent(UUID entityId, MonoReflectionType* componentType) + { + MonoType* type = Mono.mono_reflection_type_get_type(componentType); + + Entity entity = ScriptEngine.Context.GetEntityByID(entityId); + if (s_RemoveComponentMethods.TryGetValue(type, let removeMethod)) + removeMethod(entity); + + Log.EngineLogger.AssertDebug(false, "No managed component with the given type registered."); + } /*[RegisterCall("Input::IsMouseButtonReleasing")] static void Destroy() { // TODO: }*/ - [RegisterCall("ScriptGlue::Entity_GetTranslation")] - static void Entity_GetTranslation(UUID entityId, ref Vector3 translation) +#endregion + +#region TransformComponent + + [RegisterCall("ScriptGlue::Transform_GetTranslation")] + static void Transform_GetTranslation(UUID entityId, ref Vector3 translation) { Scene scene = ScriptEngine.Context; Entity entity = scene.GetEntityByID(entityId); translation = entity.Transform.Position; } - - [RegisterCall("ScriptGlue::Entity_SetTranslation")] - static void Entity_SetTranslation(UUID entityId, ref Vector3 translation) + + [RegisterCall("ScriptGlue::Transform_SetTranslation")] + static void Transform_SetTranslation(UUID entityId, ref Vector3 translation) { Scene scene = ScriptEngine.Context; Entity entity = scene.GetEntityByID(entityId); @@ -122,7 +204,31 @@ static class ScriptGlue entity.Transform.Position = translation; } -#endregion +#endregion TransformComponent + +#region RigidBody2D + + [RegisterCall("ScriptGlue::RigidBody2D_ApplyForce")] + static void RigidBody2D_ApplyForce(UUID entityId, ref Vector2 force, ref Vector2 point, bool wakeUp) + { + Scene scene = ScriptEngine.Context; + Entity entity = scene.GetEntityByID(entityId); + + var rigidBody = entity.GetComponent(); + Box2D.Body.ApplyForce(rigidBody.[Friend]RuntimeBody, ref *(Box2D.b2Vec2*)&force, ref *(Box2D.b2Vec2*)&point, wakeUp); + } + + [RegisterCall("ScriptGlue::RigidBody2D_ApplyForceToCenter")] + static void RigidBody2D_ApplyForceToCenter(UUID entityId, ref Vector2 force, bool wakeUp) + { + Scene scene = ScriptEngine.Context; + Entity entity = scene.GetEntityByID(entityId); + + var rigidBody = entity.GetComponent(); + Box2D.Body.ApplyForceToCenter(rigidBody.[Friend]RuntimeBody, ref *(Box2D.b2Vec2*)&force, wakeUp); + } + +#endregion RigidBody2D private static void RegisterCall(String name, T method) where T : var { diff --git a/GlitchyEngineHelper/src/Mono/Mono.bf b/GlitchyEngineHelper/src/Mono/Mono.bf index ff38319..2c92518 100644 --- a/GlitchyEngineHelper/src/Mono/Mono.bf +++ b/GlitchyEngineHelper/src/Mono/Mono.bf @@ -123,6 +123,12 @@ static class Mono [LinkName(.C)] public static extern void mono_field_set_value(MonoObject* obj, MonoClassField* field, void* value); + + [LinkName(.C)] + public static extern MonoType* mono_reflection_type_from_name(char8* name, MonoImage* image); + + [LinkName(.C)] + public static extern MonoType* mono_reflection_type_get_type(MonoReflectionType* reflectionType); } struct MonoDomain; @@ -145,6 +151,10 @@ struct MonoException; struct MonoClassField; +struct MonoType; + +struct MonoReflectionType; + enum MonoImageOpenStatus { Ok, diff --git a/ScriptCore/Components/Component.cs b/ScriptCore/Components/Component.cs index 62da665..e108021 100644 --- a/ScriptCore/Components/Component.cs +++ b/ScriptCore/Components/Component.cs @@ -2,7 +2,7 @@ using GlitchyEngine.Core; namespace GlitchyEngine; -public class Component : EngineObject +public abstract class Component { - + public Entity Entity { get; internal set; } } \ No newline at end of file diff --git a/ScriptCore/Components/RigidBody2D.cs b/ScriptCore/Components/RigidBody2D.cs new file mode 100644 index 0000000..36ebe05 --- /dev/null +++ b/ScriptCore/Components/RigidBody2D.cs @@ -0,0 +1,30 @@ +using GlitchyEngine.Math; + +namespace GlitchyEngine; + +public class RigidBody2D : Component +{ + /// + /// Apply a force at a world point. + /// If the force is not applied at the center of mass, it will generate a torque and affect the angular velocity. + /// This wakes up the body. + /// + /// The world force vector, usually in Newtons (N). + /// The world position of the point of application. + /// Wake up the body + public void ApplyForce(Vector2 force, Vector2 point, bool wakeUp = true) + { + ScriptGlue.RigidBody2D_ApplyForce(Entity._uuid, force, point, wakeUp); + } + + /// + /// Apply a force to the center of mass. + /// This wakes up the body. + /// + /// The world force vector, usually in Newtons (N). + /// Wake up the body + public void ApplyForceToCenter(Vector2 force, bool wakeUp = true) + { + ScriptGlue.RigidBody2D_ApplyForceToCenter(Entity._uuid, force, wakeUp); + } +} diff --git a/ScriptCore/Components/Transform.cs b/ScriptCore/Components/Transform.cs index bd891bd..939f3bc 100644 --- a/ScriptCore/Components/Transform.cs +++ b/ScriptCore/Components/Transform.cs @@ -1,8 +1,20 @@ using GlitchyEngine.Core; +using GlitchyEngine.Math; namespace GlitchyEngine; -public class Transform : EngineObject +public class Transform : Component { - + public Vector3 Translation + { + get + { + ScriptGlue.Transform_GetTranslation(Entity.UUID, out Vector3 translation); + return translation; + } + set + { + ScriptGlue.Transform_SetTranslation(Entity.UUID, in value); + } + } } diff --git a/ScriptCore/Entity.cs b/ScriptCore/Entity.cs index ac46fc3..559077d 100644 --- a/ScriptCore/Entity.cs +++ b/ScriptCore/Entity.cs @@ -1,4 +1,5 @@ using System; +using System.Runtime.CompilerServices; using GlitchyEngine.Core; using GlitchyEngine.Math; @@ -27,24 +28,113 @@ public abstract class Entity : EngineObject // _uuid = uuid; //} - public T GetComponent() where T : Component + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool HasComponent() => HasComponent(typeof(T)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool HasComponent(Type type) => ScriptGlue.Entity_HasComponent(_uuid, type); + + /// + /// Gets the component with the specified type. + /// + /// The type of the component to get. + /// The component or null, if the entity doesn't have a component of the specified type. + public T GetComponent() where T : Component, new() { + Log.Info("Trying to get component"); + + if (HasComponent()) + { + return new T + { + Entity = this + }; + } + return null; } + + /// + /// Gets the component with the given type. + /// + /// The type of the component to get. + /// The component or null, if the entity doesn't have a component of the given type. + /// For performance reasons it is recommended to use if possible. + public Component GetComponent(Type componentType) + { + // TODO: probably throw! + if (!componentType.IsSubclassOf(typeof(Component))) return null; + + if (HasComponent(componentType)) + { + return Activator.CreateInstance(componentType) as Component; + } - //public Transform Transform => new (){_uuid = _uuid}; + return null; + } + + /// + /// Gets the component with the specified component type. + /// + /// The type of the component to add. + /// The component. + public T AddComponent() where T : Component, new() + { + ScriptGlue.Entity_AddComponent(_uuid, typeof(T)); + + return new T + { + Entity = this + }; + } + + /// + /// Gets the component with the given component type. + /// + /// The type of the component to add. + /// The component or null, if the given type is not a valid component type. + /// For performance reasons it is recommended to use if possible. + public Component AddComponent(Type componentType) + { + // TODO: probably throw! + if (!componentType.IsSubclassOf(typeof(Component))) return null; + + ScriptGlue.Entity_AddComponent(_uuid, componentType); + + Component component = Activator.CreateInstance(componentType) as Component; + return component; + } + + /// + /// Removes the component with the specified component type. + /// + /// The type of the component to remove. + public void RemoveComponent() where T : Component, new() + { + ScriptGlue.Entity_RemoveComponent(_uuid, typeof(T)); + } + + /// + /// Removes the component with the given component type. + /// + /// The type of the component to remove. + /// For performance reasons it is recommended to use if possible. + public void RemoveComponent(Type componentType) + { + // TODO: probably throw! + if (!componentType.IsSubclassOf(typeof(Component))) return; + + ScriptGlue.Entity_RemoveComponent(_uuid, componentType); + } + + public Transform Transform => GetComponent(); public Vector3 Translation { - get - { - ScriptGlue.Entity_GetTranslation(_uuid, out Vector3 translation); - return translation; - } - - set => ScriptGlue.Entity_SetTranslation(_uuid, value); + get => Transform.Translation; + set => Transform.Translation = value; } - + // Will be executed once after the entity as be created. // void OnCreate(); diff --git a/ScriptCore/Math/Vector3.cs b/ScriptCore/Math/Vector3.cs index 299f08f..ab4b524 100644 --- a/ScriptCore/Math/Vector3.cs +++ b/ScriptCore/Math/Vector3.cs @@ -120,7 +120,18 @@ public struct Vector3 { return !(a == b); } - + + public override int GetHashCode() + { + unchecked + { + var hashCode = X.GetHashCode(); + hashCode = (hashCode * 397) ^ Y.GetHashCode(); + hashCode = (hashCode * 397) ^ Z.GetHashCode(); + return hashCode; + } + } + public override string ToString() { return $"X:{X}, Y:{Y}, Z:{Z}"; diff --git a/ScriptCore/MyTestEntity.cs b/ScriptCore/MyTestEntity.cs index f8e81af..e657db2 100644 --- a/ScriptCore/MyTestEntity.cs +++ b/ScriptCore/MyTestEntity.cs @@ -5,7 +5,7 @@ namespace GlitchyEngine; class MyTestEntity : Entity { - //Rigidbody2D _rigidBody; + RigidBody2D _rigidBody; /// /// Called after the script component was created. (The entity might not be fully created yet) @@ -14,7 +14,7 @@ class MyTestEntity : Entity { Log.Info($"Create! {UUID}"); - //_rigidBody = GetComponent(); + _rigidBody = GetComponent(); } ///// @@ -31,18 +31,25 @@ class MyTestEntity : Entity /// void OnUpdate(float deltaTime) { + Vector2 force = Vector2.Zero; + if (Input.IsKeyPressed(Key.A)) { - Log.Info($"HALLO!"); - - Vector3 translation = Translation; - translation.Y += 1.0f * deltaTime; - - Translation = translation; - - //_rigidBody.ApplyImpulse(new Vector2(0, 10)); + force.X -= 1000 * deltaTime; } + if (Input.IsKeyPressed(Key.D)) + { + force.X += 1000 * deltaTime; + } + + if (Input.IsKeyPressing(Key.Space)) + { + force.Y += 2000; + } + + _rigidBody.ApplyForceToCenter(force); + if (Input.IsMouseButtonReleasing(MouseButton.LeftButton)) { Log.Info("Ouha!"); diff --git a/ScriptCore/ScriptCore.csproj b/ScriptCore/ScriptCore.csproj index b558724..5fd9386 100644 --- a/ScriptCore/ScriptCore.csproj +++ b/ScriptCore/ScriptCore.csproj @@ -44,6 +44,7 @@ + diff --git a/ScriptCore/ScriptGlue.cs b/ScriptCore/ScriptGlue.cs index b809d7d..f84310f 100644 --- a/ScriptCore/ScriptGlue.cs +++ b/ScriptCore/ScriptGlue.cs @@ -1,3 +1,4 @@ +using System; using System.Runtime.CompilerServices; using GlitchyEngine.Core; using GlitchyEngine.Math; @@ -9,8 +10,37 @@ namespace GlitchyEngine; /// internal static class ScriptGlue { +#region Entity + [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void Entity_GetTranslation(UUID entityId, out Vector3 translation); + internal static extern void Entity_AddComponent(UUID entityId, Type componentType); + [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void Entity_SetTranslation(UUID entityId, in Vector3 translation); + internal static extern bool Entity_HasComponent(UUID entityId, Type componentType); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern void Entity_RemoveComponent(UUID entityId, Type componentType); + +#endregion Entity + +#region TransformComponent + + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern void Transform_GetTranslation(UUID entityId, out Vector3 translation); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern void Transform_SetTranslation(UUID entityId, in Vector3 translation); + +#endregion TransformComponent + +#region RigidBody2D + + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern void RigidBody2D_ApplyForce(UUID entityId, in Vector2 force, Vector2 point, bool wakeUp); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern void RigidBody2D_ApplyForceToCenter(UUID entityId, in Vector2 force, bool wakeUp); + +#endregion RigidBody2D + }