mirror of
https://github.com/aharabada/glitchy-engine-beef.git
synced 2026-09-05 13:01:52 +00:00
Scripting: Basic copying of entities during runtime + Reference Components using UUID
This commit is contained in:
@@ -299,20 +299,26 @@ static class ScriptEngine
|
||||
}
|
||||
|
||||
/// Creates an instance of the given component class referencing the specified entity instance.
|
||||
private static MonoObject* CreateComponentReferenceInstance(ScriptClass componentClass, MonoObject* entityReferenceInstance)
|
||||
private static MonoObject* CreateComponentReferenceInstance(ScriptClass componentClass, UUID id)//MonoObject* entityReferenceInstance)
|
||||
{
|
||||
MonoObject* componentInstance = componentClass.CreateInstance();
|
||||
|
||||
// TODO: We could cache the property, but this might be fine
|
||||
MonoClassField* idField = Mono.mono_class_get_field_from_name(componentClass.[Friend]_monoClass, "_uuid");
|
||||
|
||||
s_ComponentRoot.SetFieldValue<UUID>(componentInstance, idField, id);
|
||||
|
||||
/*// TODO: We could cache the property, but this might be fine
|
||||
MonoProperty* entityProperty = Mono.mono_class_get_property_from_name(componentClass.[Friend]_monoClass, "Entity");
|
||||
|
||||
MonoObject* exception = null;
|
||||
|
||||
|
||||
//#unwarn
|
||||
//Mono.mono_property_set_value(entityProperty, componentInstance, (void**)&entityReferenceInstance, &exception);
|
||||
#unwarn
|
||||
Mono.mono_property_set_value(entityProperty, componentInstance, (void**)&entityReferenceInstance, &exception);
|
||||
Mono.mono_property_set_value(entityProperty, componentInstance, (void**)&id, &exception);
|
||||
|
||||
if (exception != null)
|
||||
ScriptEngine.HandleMonoException((MonoException*)exception, null);
|
||||
ScriptEngine.HandleMonoException((MonoException*)exception, null);*/
|
||||
|
||||
return componentInstance;
|
||||
}
|
||||
@@ -345,7 +351,7 @@ static class ScriptEngine
|
||||
case .Component:
|
||||
// Get or create entity reference
|
||||
UUID referencedId = field.GetData<UUID>();
|
||||
MonoObject* referencedEntity = GetOrCreateScriptReferenceInstance(referencedId);
|
||||
//MonoObject* referencedEntity = GetOrCreateScriptReferenceInstance(referencedId);
|
||||
|
||||
MonoType* fieldMonoType = scriptField.GetMonoType();
|
||||
|
||||
@@ -353,7 +359,8 @@ static class ScriptEngine
|
||||
|
||||
var componentClass = ComponentClasses[componentType.FullName];
|
||||
|
||||
MonoObject* componentInstance = CreateComponentReferenceInstance(componentClass, referencedEntity);
|
||||
//MonoObject* componentInstance = CreateComponentReferenceInstance(componentClass, referencedEntity);
|
||||
MonoObject* componentInstance = CreateComponentReferenceInstance(componentClass, referencedId);
|
||||
|
||||
script.Instance.SetFieldValue(scriptField, componentInstance);
|
||||
|
||||
@@ -363,26 +370,81 @@ static class ScriptEngine
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void CopyFieldsToInstance(ScriptComponent* targetScript, ScriptComponent* sourceScript, Dictionary<UUID, UUID> sourceIdToTargetId)
|
||||
{
|
||||
Debug.Profiler.ProfileFunction!();
|
||||
|
||||
Log.EngineLogger.AssertDebug(targetScript.Instance.ScriptClass == sourceScript.Instance.ScriptClass);
|
||||
|
||||
for (let (name, scriptField) in sourceScript.Instance.ScriptClass.Fields)
|
||||
{
|
||||
Debug.Profiler.ProfileScope!("Copy Field");
|
||||
|
||||
targetScript.Instance.CopyFieldValue(scriptField, sourceScript.Instance);
|
||||
switch (scriptField.FieldType)
|
||||
{
|
||||
case .Entity:
|
||||
let sourceEntityReference = sourceScript.Instance.GetFieldValue<MonoObject*>(scriptField);
|
||||
|
||||
MonoObject* referencedEntity = sourceEntityReference;
|
||||
|
||||
if (sourceEntityReference != null)
|
||||
{
|
||||
let idField = Mono.mono_class_get_field_from_name(s_EntityRoot._monoClass, "_uuid");
|
||||
UUID sourceId = s_EntityRoot.GetFieldValue<UUID>(sourceEntityReference, idField);
|
||||
|
||||
// Check if we need to translate, copy otherwise
|
||||
if (sourceIdToTargetId.TryGetValue(sourceId, let referencedId))
|
||||
{
|
||||
// On the C# side we actually differentiate between an Entity and the Script
|
||||
// in the sense that getting an entity and a script yields two different results (one creates a new Entity-Class instance, the other returns the actual instance).
|
||||
// But here its just easier to always use the script instance.
|
||||
// Obviously breaks once we support multiple scripts per entity.
|
||||
referencedEntity = GetOrCreateScriptReferenceInstance(referencedId);
|
||||
}
|
||||
}
|
||||
|
||||
targetScript.Instance.SetFieldValue(scriptField, referencedEntity);
|
||||
case .Component:
|
||||
// We create a new instance of a component class
|
||||
MonoType* type = scriptField.GetMonoType();
|
||||
|
||||
SharpType sharpType = ScriptEngine.GetSharpType(type);
|
||||
let sourceComponentReference = sourceScript.Instance.GetFieldValue<MonoObject*>(scriptField);
|
||||
|
||||
MonoObject* componentInstance = sourceComponentReference;
|
||||
|
||||
// Get or create entity reference
|
||||
if (sourceComponentReference != null)
|
||||
{
|
||||
let idField = Mono.mono_class_get_field_from_name(s_EngineObject._monoClass, "_uuid");
|
||||
UUID sourceId = s_EntityRoot.GetFieldValue<UUID>(sourceComponentReference, idField);
|
||||
|
||||
MonoType* fieldMonoType = scriptField.GetMonoType();
|
||||
|
||||
var componentClass = ComponentClasses[sharpType.FullName];
|
||||
SharpType componentType = ScriptEngine.GetSharpType(fieldMonoType);
|
||||
|
||||
MonoObject* componentInstance = script.Instance.CreateComponentInstance(componentClass);
|
||||
script.Instance.SetFieldValue(scriptField, componentInstance);
|
||||
var componentClass = ComponentClasses[componentType.FullName];
|
||||
|
||||
if (sourceIdToTargetId.TryGetValue(sourceId, let targetId))
|
||||
{
|
||||
// Create reference for translated id
|
||||
componentInstance = CreateComponentReferenceInstance(componentClass, targetId);
|
||||
}
|
||||
|
||||
componentType.ReleaseRef();
|
||||
}
|
||||
|
||||
targetScript.Instance.SetFieldValue(scriptField, componentInstance);
|
||||
|
||||
sharpType.ReleaseRef();
|
||||
default:
|
||||
script.Instance.SetFieldValue(scriptField, field._data);
|
||||
targetScript.Instance.CopyFieldValue(scriptField, sourceScript.Instance);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static MonoAssembly* LoadCSharpAssembly(StringView assemblyPath, bool loadPDB = false)
|
||||
{
|
||||
Debug.Profiler.ProfileFunction!();
|
||||
|
||||
List<uint8> data = new List<uint8>(1024);
|
||||
|
||||
File.ReadAll(assemblyPath, data);
|
||||
|
||||
@@ -114,6 +114,13 @@ class ScriptInstance : RefCounter
|
||||
_scriptClass.SetFieldValue<T>(_instance, field.[Friend]_monoField, value);
|
||||
}
|
||||
|
||||
public void CopyFieldValue(ScriptField field, ScriptInstance sourceInstance)
|
||||
{
|
||||
// TODO: I hate this!
|
||||
var data = sourceInstance.GetFieldValue<uint8[sizeof(GlitchyEngine.Math.Matrix)]>(field);
|
||||
SetFieldValue(field, data);
|
||||
}
|
||||
|
||||
/// Creates a new instance of the given component class and initializes it for the current entity.
|
||||
public MonoObject* CreateComponentInstance(ScriptClass componentClassType)
|
||||
{
|
||||
|
||||
@@ -29,6 +29,8 @@ namespace GlitchyEngine.World
|
||||
// Maps ids to the entities they represent.
|
||||
private Dictionary<UUID, EcsEntity> _idToEntity = new .() ~ delete _;
|
||||
|
||||
private HashSet<EcsEntity> _updateBlockList = new .() ~ delete _;
|
||||
|
||||
public Entity ActiveCamera => {
|
||||
Entity cameraEntity = .();
|
||||
|
||||
@@ -99,7 +101,7 @@ namespace GlitchyEngine.World
|
||||
|
||||
if (initializeScripts)
|
||||
{
|
||||
CopyComponents<NativeScriptComponent>(this, target);
|
||||
//CopyComponents<NativeScriptComponent>(this, target);
|
||||
|
||||
// Copy ScriptComponents... needs extra handling for the script instances
|
||||
for (let (sourceHandle, sourceComponent) in _ecsWorld.Enumerate<ScriptComponent>())
|
||||
@@ -583,6 +585,8 @@ namespace GlitchyEngine.World
|
||||
|
||||
if (mode.HasFlag(.Scripts))
|
||||
{
|
||||
Debug.Profiler.ProfileScope!("Update scripts");
|
||||
|
||||
// Run scripts
|
||||
for (var (entity, script) in _ecsWorld.Enumerate<NativeScriptComponent>())
|
||||
{
|
||||
@@ -595,12 +599,9 @@ namespace GlitchyEngine.World
|
||||
|
||||
script.Instance.[Friend]OnUpdate(gameTime);
|
||||
}
|
||||
/*}
|
||||
|
||||
if (mode.HasFlag(.Runtime) || mode.HasFlag(.Editor))
|
||||
{*/
|
||||
|
||||
// Run scripts
|
||||
for (var (entity, script) in _ecsWorld.Enumerate<ScriptComponent>())
|
||||
for (let (entity, script) in _ecsWorld.Enumerate<ScriptComponent>())
|
||||
{
|
||||
if (!script.IsCreated)
|
||||
{
|
||||
@@ -615,7 +616,12 @@ namespace GlitchyEngine.World
|
||||
}
|
||||
|
||||
if (mode.HasFlag(.Runtime))
|
||||
script.Instance.InvokeOnUpdate(gameTime.DeltaTime);
|
||||
{
|
||||
if (_updateBlockList.Contains(entity))
|
||||
_updateBlockList.Remove(entity);
|
||||
else
|
||||
script.Instance.InvokeOnUpdate(gameTime.DeltaTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -761,11 +767,50 @@ namespace GlitchyEngine.World
|
||||
*/
|
||||
public Entity CreateInstance(Entity entity)
|
||||
{
|
||||
Log.EngineLogger.Info("Ja moin");
|
||||
|
||||
List<Entity> newEntities = scope .();
|
||||
Dictionary<EcsEntity, EcsEntity> sourceToTargetEntity = scope .();
|
||||
Dictionary<UUID, UUID> sourceIdToTargetId = scope .();
|
||||
Dictionary<UUID, Entity> targetIdToSourceEntity = scope .();
|
||||
|
||||
Entity CopyEntityAndChildren(Entity original)
|
||||
{
|
||||
Entity copy = CreateEntity(original.Name);
|
||||
|
||||
// TODO: Copy components
|
||||
newEntities.Add(copy);
|
||||
sourceToTargetEntity.Add(original.Handle, copy.Handle);
|
||||
sourceIdToTargetId.Add(original.UUID, copy.UUID);
|
||||
targetIdToSourceEntity.Add(copy.UUID, original);
|
||||
_updateBlockList.Add(copy.Handle);
|
||||
|
||||
CopyComponent<MeshRendererComponent>(original, copy);
|
||||
CopyComponent<MeshComponent>(original, copy);
|
||||
CopyComponent<EditorComponent>(original, copy);
|
||||
CopyComponent<SpriteRendererComponent>(original, copy);
|
||||
CopyComponent<CircleRendererComponent>(original, copy);
|
||||
CopyComponent<CameraComponent>(original, copy);
|
||||
CopyComponent<LightComponent>(original, copy);
|
||||
|
||||
CopyComponent<Rigidbody2DComponent>(original, copy);
|
||||
CopyComponent<BoxCollider2DComponent>(original, copy);
|
||||
CopyComponent<CircleCollider2DComponent>(original, copy);
|
||||
CopyComponent<PolygonCollider2DComponent>(original, copy);
|
||||
|
||||
// Copy ScriptComponent... needs extra handling for the script instances
|
||||
if (original.TryGetComponent<ScriptComponent>(let sourceScript))
|
||||
{
|
||||
ScriptComponent* targetScript = copy.AddComponent<ScriptComponent>();
|
||||
|
||||
targetScript.ScriptClassName = sourceScript.ScriptClassName;
|
||||
|
||||
// Initializes the created instance
|
||||
// TODO: this returns false, if no script with ScriptClassName exists, we have to handle this case correctly I think.
|
||||
ScriptEngine.InitializeInstance(copy, targetScript);
|
||||
|
||||
// TODO: Copy Data from one instance to another
|
||||
//ScriptEngine.CopyFieldsToInstance(targetScript, sourceScript);
|
||||
}
|
||||
|
||||
// This is kinda slow because it's in O(n*m) where n is the tree depth and m is the total number of entities in the scene...
|
||||
for (let child in original.EnumerateChildren)
|
||||
@@ -778,6 +823,22 @@ namespace GlitchyEngine.World
|
||||
}
|
||||
|
||||
Entity newEntity = CopyEntityAndChildren(entity);
|
||||
|
||||
for (let copy in newEntities)
|
||||
{
|
||||
if (copy.TryGetComponent<ScriptComponent>(let targetScript))
|
||||
{
|
||||
Entity originalEntity = targetIdToSourceEntity[copy.UUID];
|
||||
|
||||
if (!originalEntity.TryGetComponent<ScriptComponent>(let sourceScript))
|
||||
continue;
|
||||
|
||||
ScriptEngine.CopyFieldsToInstance(targetScript, sourceScript, sourceIdToTargetId);
|
||||
|
||||
//targetScript.Instance.InvokeOnCreate();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return newEntity;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ using GlitchyEngine.Core;
|
||||
|
||||
namespace GlitchyEngine;
|
||||
|
||||
public abstract class Component
|
||||
public abstract class Component : EngineObject
|
||||
{
|
||||
public Entity Entity { get; internal set; }
|
||||
public Entity Entity => new(_uuid);
|
||||
}
|
||||
@@ -14,7 +14,7 @@ public class Rigidbody2D : Component
|
||||
/// <param name="wakeUp">Wake up the body</param>
|
||||
public void ApplyForce(float2 force, float2 point, bool wakeUp = true)
|
||||
{
|
||||
ScriptGlue.Rigidbody2D_ApplyForce(Entity._uuid, force, point, wakeUp);
|
||||
ScriptGlue.Rigidbody2D_ApplyForce(_uuid, force, point, wakeUp);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -25,7 +25,7 @@ public class Rigidbody2D : Component
|
||||
/// <param name="wakeUp">Wake up the body</param>
|
||||
public void ApplyForceToCenter(float2 force, bool wakeUp = true)
|
||||
{
|
||||
ScriptGlue.Rigidbody2D_ApplyForceToCenter(Entity._uuid, force, wakeUp);
|
||||
ScriptGlue.Rigidbody2D_ApplyForceToCenter(_uuid, force, wakeUp);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -35,11 +35,11 @@ public class Rigidbody2D : Component
|
||||
{
|
||||
get
|
||||
{
|
||||
ScriptGlue.Rigidbody2D_GetPosition(Entity._uuid, out float2 position);
|
||||
ScriptGlue.Rigidbody2D_GetPosition(_uuid, out float2 position);
|
||||
|
||||
return position;
|
||||
}
|
||||
set => ScriptGlue.Rigidbody2D_SetPosition(Entity._uuid, value);
|
||||
set => ScriptGlue.Rigidbody2D_SetPosition(_uuid, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -49,11 +49,11 @@ public class Rigidbody2D : Component
|
||||
{
|
||||
get
|
||||
{
|
||||
ScriptGlue.Rigidbody2D_GetRotation(Entity._uuid, out float rotation);
|
||||
ScriptGlue.Rigidbody2D_GetRotation(_uuid, out float rotation);
|
||||
|
||||
return rotation;
|
||||
}
|
||||
set => ScriptGlue.Rigidbody2D_SetRotation(Entity._uuid, value);
|
||||
set => ScriptGlue.Rigidbody2D_SetRotation(_uuid, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -63,11 +63,11 @@ public class Rigidbody2D : Component
|
||||
{
|
||||
get
|
||||
{
|
||||
ScriptGlue.Rigidbody2D_GetLinearVelocity(Entity._uuid, out float2 velocity);
|
||||
ScriptGlue.Rigidbody2D_GetLinearVelocity(_uuid, out float2 velocity);
|
||||
|
||||
return velocity;
|
||||
}
|
||||
set => ScriptGlue.Rigidbody2D_SetLinearVelocity(Entity._uuid, value);
|
||||
set => ScriptGlue.Rigidbody2D_SetLinearVelocity(_uuid, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -77,10 +77,10 @@ public class Rigidbody2D : Component
|
||||
{
|
||||
get
|
||||
{
|
||||
ScriptGlue.Rigidbody2D_GetAngularVelocity(Entity._uuid, out float velocity);
|
||||
ScriptGlue.Rigidbody2D_GetAngularVelocity(_uuid, out float velocity);
|
||||
|
||||
return velocity;
|
||||
}
|
||||
set => ScriptGlue.Rigidbody2D_SetAngularVelocity(Entity._uuid, value);
|
||||
set => ScriptGlue.Rigidbody2D_SetAngularVelocity(_uuid, value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ public class Entity : EngineObject
|
||||
{
|
||||
return new T
|
||||
{
|
||||
Entity = this
|
||||
_uuid = _uuid
|
||||
};
|
||||
}
|
||||
|
||||
@@ -111,7 +111,7 @@ public class Entity : EngineObject
|
||||
|
||||
return new T
|
||||
{
|
||||
Entity = this
|
||||
_uuid = _uuid
|
||||
};
|
||||
}
|
||||
|
||||
@@ -132,7 +132,9 @@ public class Entity : EngineObject
|
||||
|
||||
Component component = Activator.CreateInstance(componentType) as Component;
|
||||
if (component != null)
|
||||
component.Entity = this;
|
||||
{
|
||||
component._uuid = _uuid;
|
||||
}
|
||||
|
||||
return component;
|
||||
}
|
||||
@@ -160,7 +162,7 @@ public class Entity : EngineObject
|
||||
foreach ((Type componentType, int index) in componentTypes.WithIndex())
|
||||
{
|
||||
components[index] = Activator.CreateInstance(componentType) as Component;
|
||||
components[index].Entity = this;
|
||||
components[index]._uuid = _uuid;
|
||||
}
|
||||
|
||||
return components;
|
||||
@@ -176,7 +178,7 @@ public class Entity : EngineObject
|
||||
{
|
||||
ScriptGlue.Entity_AddComponents(_uuid, new []{typeof(T1), typeof(T2)});
|
||||
|
||||
return (new T1 { Entity = this }, new T2 { Entity = this });
|
||||
return (new T1 { _uuid = _uuid }, new T2 { _uuid = _uuid });
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -190,7 +192,7 @@ public class Entity : EngineObject
|
||||
{
|
||||
ScriptGlue.Entity_AddComponents(_uuid, new []{typeof(T1), typeof(T2), typeof(T3)});
|
||||
|
||||
return (new T1 { Entity = this }, new T2 { Entity = this }, new T3 { Entity = this });
|
||||
return (new T1 { _uuid = _uuid }, new T2 { _uuid = _uuid }, new T3 { _uuid = _uuid });
|
||||
}
|
||||
|
||||
#endregion Add Components
|
||||
|
||||
@@ -26,10 +26,10 @@ public struct Collision2D
|
||||
/// Gets the rigidbody whose Collider takes part in the collision.
|
||||
/// This Rigidbody is either a component of the entity whose script instance received the event or a parent of it.
|
||||
/// </summary>
|
||||
public Rigidbody2D Rigidbody => new() { Entity = Entity };
|
||||
public Rigidbody2D Rigidbody => new() { _uuid = _entity };
|
||||
|
||||
/// <summary>
|
||||
/// Gets the other rigidbody whose Collider takes part in the collision.
|
||||
/// </summary>
|
||||
public Rigidbody2D OtherRigidbody => new() { Entity = OtherEntity };
|
||||
public Rigidbody2D OtherRigidbody => new() { _uuid = _otherEntity };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user