mirror of
https://github.com/aharabada/glitchy-engine-beef.git
synced 2026-09-05 13:01:52 +00:00
Entity Creation and Destruction from scripts
- ScriptEngine: Allow destroying scripts during runtime
- Scripting:
- Create new entities using Constructors
- Added Destroy functions for Entities
- Added SetScript/RemoveScript functions for Entities
- Allow adding multiple components at once
- Added Is-Method
- Mono Wrapper: Added Array-Functions
- WorldEnumerator: Fixed crash due to dangling list pointer after entity creation
- Scene:
- Basic create Rigidbodies during runtime
- Start of entity copying (For runtime use)
- Added DestroyEntityDeferred
- SceneSerializer:
- Added option to assign new IDs to entities
This commit is contained in:
@@ -383,7 +383,7 @@ class ScriptClass : SharpClass
|
|||||||
{
|
{
|
||||||
MonoObject* instance = Mono.mono_object_new(ScriptEngine.[Friend]s_AppDomain, _monoClass);
|
MonoObject* instance = Mono.mono_object_new(ScriptEngine.[Friend]s_AppDomain, _monoClass);
|
||||||
|
|
||||||
// Invoke empty constructor to fill fields
|
// Invoke empty constructor to initialize fields with default values specified in the script itself
|
||||||
Mono.mono_runtime_object_init(instance);
|
Mono.mono_runtime_object_init(instance);
|
||||||
|
|
||||||
// Invoke constructor with UUID
|
// Invoke constructor with UUID
|
||||||
|
|||||||
@@ -238,7 +238,7 @@ static class ScriptEngine
|
|||||||
{
|
{
|
||||||
for (var entry in _entityScriptInstances)
|
for (var entry in _entityScriptInstances)
|
||||||
{
|
{
|
||||||
entry.value.ReleaseRef();
|
entry.value?.ReleaseRef();
|
||||||
}
|
}
|
||||||
_entityScriptInstances.Clear();
|
_entityScriptInstances.Clear();
|
||||||
|
|
||||||
@@ -257,6 +257,9 @@ static class ScriptEngine
|
|||||||
script.Instance = new ScriptInstance(entityId, scriptClass);
|
script.Instance = new ScriptInstance(entityId, scriptClass);
|
||||||
script.Instance..ReleaseRef();
|
script.Instance..ReleaseRef();
|
||||||
|
|
||||||
|
if (_entityScriptInstances.TryGetValue(entityId, let currentInstance))
|
||||||
|
currentInstance.ReleaseRef();
|
||||||
|
|
||||||
_entityScriptInstances[entityId] = script.Instance..AddRef();
|
_entityScriptInstances[entityId] = script.Instance..AddRef();
|
||||||
|
|
||||||
script.Instance.Instantiate(entityId);
|
script.Instance.Instantiate(entityId);
|
||||||
@@ -264,6 +267,21 @@ static class ScriptEngine
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void DestroyInstance(Entity entity, ScriptComponent* script)
|
||||||
|
{
|
||||||
|
UUID entityId = entity.UUID;
|
||||||
|
|
||||||
|
if (script.Instance != null)
|
||||||
|
script.Instance = null;
|
||||||
|
|
||||||
|
script.ScriptClassName = null;
|
||||||
|
|
||||||
|
if (_entityScriptInstances.TryGetValue(entityId, let currentInstance))
|
||||||
|
currentInstance.ReleaseRef();
|
||||||
|
|
||||||
|
_entityScriptInstances[entityId] = null;
|
||||||
|
}
|
||||||
|
|
||||||
public static void CopyEditorFieldsToInstance(Entity entity, ScriptComponent* script)
|
public static void CopyEditorFieldsToInstance(Entity entity, ScriptComponent* script)
|
||||||
{
|
{
|
||||||
Log.EngineLogger.AssertDebug(script.Instance != null);
|
Log.EngineLogger.AssertDebug(script.Instance != null);
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ using GlitchyEngine.World;
|
|||||||
using GlitchyEngine.Core;
|
using GlitchyEngine.Core;
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
using Box2D;
|
using Box2D;
|
||||||
|
using GlitchyEngine.Scripting;
|
||||||
|
|
||||||
namespace GlitchyEngine.Scripting;
|
namespace GlitchyEngine.Scripting;
|
||||||
|
|
||||||
@@ -146,7 +147,56 @@ static class ScriptGlue
|
|||||||
#endregion Input
|
#endregion Input
|
||||||
|
|
||||||
#region Scene/Entity stuff
|
#region Scene/Entity stuff
|
||||||
|
|
||||||
|
[RegisterCall("ScriptGlue::Entity_Create")]
|
||||||
|
static void Entity_Create(MonoObject* scriptInstance, MonoString* monoEntityName, MonoArray* componentTypes, out UUID entityId)
|
||||||
|
{
|
||||||
|
char8* entityName = Mono.mono_string_to_utf8(monoEntityName);
|
||||||
|
|
||||||
|
Entity entity = ScriptEngine.Context.CreateEntity(StringView(entityName));
|
||||||
|
|
||||||
|
Mono.mono_free(entityName);
|
||||||
|
|
||||||
|
entityId = entity.UUID;
|
||||||
|
|
||||||
|
// TODO: Script instance
|
||||||
|
|
||||||
|
if (componentTypes != null)
|
||||||
|
{
|
||||||
|
Entity_AddComponents(entityId, componentTypes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[RegisterCall("ScriptGlue::Entity_Destroy")]
|
||||||
|
static void Entity_Destroy(UUID entityId)
|
||||||
|
{
|
||||||
|
Entity entity = ScriptEngine.Context.GetEntityByID(entityId);
|
||||||
|
ScriptEngine.Context.DestroyEntityDeferred(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
[RegisterCall("ScriptGlue::Entity_CreateInstance")]
|
||||||
|
static void Entity_CreateInstance(UUID entityId, out UUID newEntityId)
|
||||||
|
{
|
||||||
|
Entity entity = ScriptEngine.Context.GetEntityByID(entityId);
|
||||||
|
Entity newEntity = ScriptEngine.Context.CreateInstance(entity);
|
||||||
|
|
||||||
|
newEntityId = newEntity.UUID;
|
||||||
|
}
|
||||||
|
|
||||||
|
[RegisterCall("ScriptGlue::Entity_AddComponents")]
|
||||||
|
static void Entity_AddComponents(UUID entityId, MonoArray* componentTypes)
|
||||||
|
{
|
||||||
|
if (componentTypes == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
uint length = Mono.mono_array_length(componentTypes);
|
||||||
|
for (uint i < length)
|
||||||
|
{
|
||||||
|
MonoReflectionType* reflectionType = Mono.mono_array_get<MonoReflectionType*>(componentTypes, i);
|
||||||
|
Entity_AddComponent(entityId, reflectionType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[RegisterCall("ScriptGlue::Entity_AddComponent")]
|
[RegisterCall("ScriptGlue::Entity_AddComponent")]
|
||||||
static void Entity_AddComponent(UUID entityId, MonoReflectionType* componentType)
|
static void Entity_AddComponent(UUID entityId, MonoReflectionType* componentType)
|
||||||
{
|
{
|
||||||
@@ -208,6 +258,47 @@ static class ScriptGlue
|
|||||||
return ScriptEngine.GetManagedInstance(entityId);
|
return ScriptEngine.GetManagedInstance(entityId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[RegisterCall("ScriptGlue::Entity_SetScript")]
|
||||||
|
static MonoObject* Entity_SetScript(UUID entityId, MonoReflectionType* scriptType)
|
||||||
|
{
|
||||||
|
Entity entity = ScriptEngine.Context.GetEntityByID(entityId);
|
||||||
|
|
||||||
|
if (!entity.HasComponent<ScriptComponent>())
|
||||||
|
{
|
||||||
|
entity.AddComponent<ScriptComponent>();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entity.TryGetComponent<ScriptComponent>(let scriptComponent))
|
||||||
|
{
|
||||||
|
scriptComponent.Instance = null;
|
||||||
|
|
||||||
|
MonoType* type = Mono.mono_reflection_type_get_type(scriptType);
|
||||||
|
|
||||||
|
scriptComponent.ScriptClassName = StringView(Mono.mono_type_full_name(type));
|
||||||
|
|
||||||
|
// 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(entity, scriptComponent);
|
||||||
|
|
||||||
|
return scriptComponent.Instance.MonoInstance;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
[RegisterCall("ScriptGlue::Entity_RemoveScript")]
|
||||||
|
static void Entity_RemoveScript(UUID entityId)
|
||||||
|
{
|
||||||
|
Entity entity = ScriptEngine.Context.GetEntityByID(entityId);
|
||||||
|
|
||||||
|
if (entity.TryGetComponent<ScriptComponent>(let scriptComponent))
|
||||||
|
{
|
||||||
|
ScriptEngine.DestroyInstance(entity, scriptComponent);
|
||||||
|
|
||||||
|
entity.RemoveComponent<ScriptComponent>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region TransformComponent
|
#region TransformComponent
|
||||||
|
|||||||
@@ -51,14 +51,17 @@ namespace GlitchyEngine.World
|
|||||||
cameraComponent.Camera.SetViewportSize(e.Scene._viewportWidth, e.Scene._viewportHeight);
|
cameraComponent.Camera.SetViewportSize(e.Scene._viewportWidth, e.Scene._viewportHeight);
|
||||||
});
|
});
|
||||||
|
|
||||||
/*_onComponentAddedHandlers.Add(typeof(Rigidbody2DComponent), (e, t, c) => {
|
_onComponentAddedHandlers.Add(typeof(Rigidbody2DComponent), (e, t, c) => {
|
||||||
if ()
|
Rigidbody2DComponent* rigidbodyComponent = (.)c;
|
||||||
|
|
||||||
|
Scene scene = e.Scene;
|
||||||
|
|
||||||
Rigidbody2DComponent* rigidBodyComponent = (.)c;
|
if (scene._physicsWorld2D != null)
|
||||||
|
{
|
||||||
//cameraComponent.Camera.SetViewportSize(e.Scene._viewportWidth, e.Scene._viewportHeight);
|
// TODO: Add rigidbody
|
||||||
});*/
|
Log.EngineLogger.Error("Should have added a rigidbody");
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public ~this()
|
public ~this()
|
||||||
@@ -154,6 +157,14 @@ namespace GlitchyEngine.World
|
|||||||
targetEntity.AddComponent<TComponent>(*sourceComponent);
|
targetEntity.AddComponent<TComponent>(*sourceComponent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void CopyComponent<TComponent>(Entity source, Entity target) where TComponent : struct, new
|
||||||
|
{
|
||||||
|
if (source.TryGetComponent<TComponent>(let component))
|
||||||
|
{
|
||||||
|
target.AddComponent<TComponent>(*component);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
b2Vec2 _gravity2D = .(0.0f, -9.8f);
|
b2Vec2 _gravity2D = .(0.0f, -9.8f);
|
||||||
|
|
||||||
@@ -562,7 +573,7 @@ namespace GlitchyEngine.World
|
|||||||
Runtime = Scripts | Physics,
|
Runtime = Scripts | Physics,
|
||||||
}
|
}
|
||||||
|
|
||||||
//private append List<UUID> _destroyQueue = .();
|
private append List<Entity> _destroyQueue = .();
|
||||||
|
|
||||||
public void Update(GameTime gameTime, UpdateMode mode)
|
public void Update(GameTime gameTime, UpdateMode mode)
|
||||||
{
|
{
|
||||||
@@ -680,6 +691,17 @@ namespace GlitchyEngine.World
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!_destroyQueue.IsEmpty)
|
||||||
|
{
|
||||||
|
for (let entity in _destroyQueue)
|
||||||
|
{
|
||||||
|
DestroyEntity(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
_destroyQueue.Clear();
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a new Entity with the given name.
|
/// Creates a new Entity with the given name.
|
||||||
@@ -719,6 +741,46 @@ namespace GlitchyEngine.World
|
|||||||
|
|
||||||
_ecsWorld.RemoveEntity(entity.Handle);
|
_ecsWorld.RemoveEntity(entity.Handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Marks the given entity and it's children, so that they will be destroyed at the end of the frame.
|
||||||
|
* @param entity The entity to delete.
|
||||||
|
*/
|
||||||
|
public void DestroyEntityDeferred(Entity entity)
|
||||||
|
{
|
||||||
|
_destroyQueue.Add(entity);
|
||||||
|
|
||||||
|
for (Entity child in entity.EnumerateChildren)
|
||||||
|
{
|
||||||
|
DestroyEntityDeferred(child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Creates a copy of the given entity, including all components and children.
|
||||||
|
* @param entity the entity to copy.
|
||||||
|
* @returns the newly create entity.
|
||||||
|
*/
|
||||||
|
public Entity CreateInstance(Entity entity)
|
||||||
|
{
|
||||||
|
Entity CopyEntityAndChildren(Entity original)
|
||||||
|
{
|
||||||
|
Entity copy = CreateEntity(original.Name);
|
||||||
|
|
||||||
|
// TODO: Copy components
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
{
|
||||||
|
Entity childCopy = CopyEntityAndChildren(child);
|
||||||
|
childCopy.Parent = copy;
|
||||||
|
}
|
||||||
|
|
||||||
|
return copy;
|
||||||
|
}
|
||||||
|
|
||||||
|
Entity newEntity = CopyEntityAndChildren(entity);
|
||||||
|
|
||||||
|
return newEntity;
|
||||||
|
}
|
||||||
|
|
||||||
public Result<Entity> GetEntityByID(UUID id)
|
public Result<Entity> GetEntityByID(UUID id)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -22,6 +22,9 @@ class SceneSerializer
|
|||||||
private Dictionary<UUID, Entity> _parentIdToChild;
|
private Dictionary<UUID, Entity> _parentIdToChild;
|
||||||
|
|
||||||
private List<(Entity Entity, UUID ParentId)> _entitiesMissingParent;
|
private List<(Entity Entity, UUID ParentId)> _entitiesMissingParent;
|
||||||
|
|
||||||
|
// Maps from ID in the prefab file to the actual ID in the scene.
|
||||||
|
private Dictionary<UUID, UUID> _fileToSceneId;
|
||||||
|
|
||||||
public this(Scene scene)
|
public this(Scene scene)
|
||||||
{
|
{
|
||||||
@@ -262,12 +265,13 @@ class SceneSerializer
|
|||||||
Runtime.NotImplemented();
|
Runtime.NotImplemented();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Result<void> Deserialize(StringView filePath)
|
public Result<void> Deserialize(StringView filePath, bool loadAsPrefab = false)
|
||||||
{
|
{
|
||||||
Debug.Profiler.ProfileResourceFunction!();
|
Debug.Profiler.ProfileResourceFunction!();
|
||||||
|
|
||||||
_parentIdToChild = scope Dictionary<UUID, Entity>();
|
_parentIdToChild = scope Dictionary<UUID, Entity>();
|
||||||
_entitiesMissingParent = scope List<(Entity Entity, UUID ParentId)>();
|
_entitiesMissingParent = scope List<(Entity Entity, UUID ParentId)>();
|
||||||
|
_fileToSceneId = scope Dictionary<UUID, UUID>();
|
||||||
|
|
||||||
String buffer = scope String();
|
String buffer = scope String();
|
||||||
File.ReadAllText(filePath, buffer);
|
File.ReadAllText(filePath, buffer);
|
||||||
@@ -298,7 +302,7 @@ class SceneSerializer
|
|||||||
Try!(reader.EntryEnd());
|
Try!(reader.EntryEnd());
|
||||||
}
|
}
|
||||||
|
|
||||||
Try!(DeserializeEntity(reader));
|
Try!(DeserializeEntity(reader, loadAsPrefab));
|
||||||
|
|
||||||
first = false;
|
first = false;
|
||||||
}
|
}
|
||||||
@@ -325,23 +329,12 @@ class SceneSerializer
|
|||||||
return .Ok;
|
return .Ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Result<void> DeserializeEntity(BonReader reader)
|
/// Deserializes the next entity in the file
|
||||||
|
/// @param reader the reader
|
||||||
|
/// @param replaceId If false, the ID that is stored in the file will be used as ID in the scene.
|
||||||
|
/// If true, the ID in the file will be replaced with a new id (e.g. for loading prefabs)
|
||||||
|
private Result<void> DeserializeEntity(BonReader reader, bool newId)
|
||||||
{
|
{
|
||||||
/*mixin DeserializeAsset<T>(StringView identifier) where T : Asset
|
|
||||||
{
|
|
||||||
Asset asset = null;
|
|
||||||
|
|
||||||
Try!(Deserialize.Value(reader, identifier, out asset));
|
|
||||||
|
|
||||||
if (asset != null && !(asset is T))
|
|
||||||
{
|
|
||||||
Log.EngineLogger.Error($"Asset {asset.Identifier} is not a {nameof(T)}.");
|
|
||||||
return .Err;
|
|
||||||
}
|
|
||||||
|
|
||||||
(T)asset
|
|
||||||
}*/
|
|
||||||
|
|
||||||
mixin DeserializeAssetHandle<T>(StringView identifier) where T : Asset
|
mixin DeserializeAssetHandle<T>(StringView identifier) where T : Asset
|
||||||
{
|
{
|
||||||
Asset asset = null;
|
Asset asset = null;
|
||||||
@@ -357,11 +350,29 @@ class SceneSerializer
|
|||||||
asset?.Handle ?? .Invalid
|
asset?.Handle ?? .Invalid
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UUID RemapId(UUID id)
|
||||||
|
{
|
||||||
|
if (!newId)
|
||||||
|
return id;
|
||||||
|
|
||||||
|
if (_fileToSceneId.TryGetValue(id, let sceneId))
|
||||||
|
return sceneId;
|
||||||
|
|
||||||
|
// If we remap IDs map the file Id to a random Id.
|
||||||
|
UUID newId = UUID.Create();
|
||||||
|
|
||||||
|
_fileToSceneId.Add(id, newId);
|
||||||
|
|
||||||
|
return newId;
|
||||||
|
}
|
||||||
|
|
||||||
Try!(reader.ObjectBlock());
|
Try!(reader.ObjectBlock());
|
||||||
|
|
||||||
Deserialize.Value<uint64>(reader, "Id", let uuid);
|
Deserialize.Value<uint64>(reader, "Id", let rawUuid);
|
||||||
|
|
||||||
Entity entity = _scene.CreateEntity("", UUID(uuid));
|
UUID uuid = RemapId(UUID(rawUuid));
|
||||||
|
|
||||||
|
Entity entity = _scene.CreateEntity("", uuid);
|
||||||
|
|
||||||
while(reader.ObjectHasMore())
|
while(reader.ObjectHasMore())
|
||||||
{
|
{
|
||||||
@@ -416,11 +427,13 @@ class SceneSerializer
|
|||||||
let nextId = Try!(reader.Identifier());
|
let nextId = Try!(reader.Identifier());
|
||||||
if (nextId == "ParentId")
|
if (nextId == "ParentId")
|
||||||
{
|
{
|
||||||
UUID pId;
|
UUID rawParentId;
|
||||||
Deserialize.Value(reader, out pId);
|
Deserialize.Value(reader, out rawParentId);
|
||||||
reader.EntryEnd();
|
reader.EntryEnd();
|
||||||
|
|
||||||
var parentEntity = _scene.GetEntityByID(pId);
|
UUID parentId = RemapId(rawParentId);
|
||||||
|
|
||||||
|
var parentEntity = _scene.GetEntityByID(parentId);
|
||||||
|
|
||||||
if (parentEntity case .Ok(let parent))
|
if (parentEntity case .Ok(let parent))
|
||||||
{
|
{
|
||||||
@@ -428,7 +441,7 @@ class SceneSerializer
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_entitiesMissingParent.Add((entity, pId));
|
_entitiesMissingParent.Add((entity, parentId));
|
||||||
}
|
}
|
||||||
|
|
||||||
Deserialize.Value(reader, "Position", out component.[Friend]_position);
|
Deserialize.Value(reader, "Position", out component.[Friend]_position);
|
||||||
|
|||||||
@@ -10,16 +10,14 @@ namespace GlitchyEngine.World
|
|||||||
{
|
{
|
||||||
internal EcsWorld _world;
|
internal EcsWorld _world;
|
||||||
internal BitArray _bitMask;
|
internal BitArray _bitMask;
|
||||||
internal EcsWorld.BitmaskEntry* _currentEntry;
|
internal List<EcsWorld.BitmaskEntry>.Enumerator _entitiesEnumerator;
|
||||||
internal EcsWorld.BitmaskEntry* _endEntry;
|
|
||||||
|
|
||||||
public bool IsEmpty => _bitMask == null;
|
public bool IsEmpty => _bitMask == null;
|
||||||
|
|
||||||
public this(EcsWorld world, Type[] componentTypes)
|
public this(EcsWorld world, Type[] componentTypes)
|
||||||
{
|
{
|
||||||
_world = world;
|
_world = world;
|
||||||
_currentEntry = _world._entities.Ptr;
|
_entitiesEnumerator = _world._entities.GetEnumerator();
|
||||||
_endEntry = _world._entities.Ptr + _world._entities.Count;
|
|
||||||
|
|
||||||
_bitMask = new BitArray(_world._componentPools.Count);
|
_bitMask = new BitArray(_world._componentPools.Count);
|
||||||
for(var type in componentTypes)
|
for(var type in componentTypes)
|
||||||
@@ -39,7 +37,6 @@ namespace GlitchyEngine.World
|
|||||||
Log.EngineLogger.Warning($"Queried component of type \"{type}\" is not registered for this world. The query will never return any results.");
|
Log.EngineLogger.Warning($"Queried component of type \"{type}\" is not registered for this world. The query will never return any results.");
|
||||||
#endif
|
#endif
|
||||||
DeleteAndNullify!(_bitMask);
|
DeleteAndNullify!(_bitMask);
|
||||||
_endEntry = _currentEntry;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -47,9 +44,12 @@ namespace GlitchyEngine.World
|
|||||||
|
|
||||||
public Result<EcsEntity> GetNext() mut
|
public Result<EcsEntity> GetNext() mut
|
||||||
{
|
{
|
||||||
while(_currentEntry < _endEntry)
|
if (IsEmpty)
|
||||||
|
return .Err;
|
||||||
|
|
||||||
|
while(_entitiesEnumerator.GetNext() case .Ok(let entry))
|
||||||
{
|
{
|
||||||
EcsWorld.BitmaskEntry* entry = _currentEntry++;
|
//EcsWorld.BitmaskEntry* entry = _currentEntry++;
|
||||||
|
|
||||||
// Skip deleted entities
|
// Skip deleted entities
|
||||||
if(entry.ID.Index == EcsEntity.InvalidEntity.Index)
|
if(entry.ID.Index == EcsEntity.InvalidEntity.Index)
|
||||||
|
|||||||
@@ -207,6 +207,9 @@ static class Mono
|
|||||||
[LinkName(.C)]
|
[LinkName(.C)]
|
||||||
public static extern char8* mono_type_get_name(MonoType* type);
|
public static extern char8* mono_type_get_name(MonoType* type);
|
||||||
|
|
||||||
|
[LinkName(.C)]
|
||||||
|
public static extern char8* mono_type_full_name(MonoType* type);
|
||||||
|
|
||||||
[LinkName(.C)]
|
[LinkName(.C)]
|
||||||
public static extern MonoClass* mono_type_get_class(MonoType* type);
|
public static extern MonoClass* mono_type_get_class(MonoType* type);
|
||||||
|
|
||||||
@@ -269,6 +272,59 @@ static class Mono
|
|||||||
|
|
||||||
[LinkName(.C)]
|
[LinkName(.C)]
|
||||||
public static extern MonoObject* mono_value_box(MonoDomain* domain, MonoClass* klass, gpointer value);
|
public static extern MonoObject* mono_value_box(MonoDomain* domain, MonoClass* klass, gpointer value);
|
||||||
|
|
||||||
|
#region MonoArray
|
||||||
|
|
||||||
|
typealias uintptr_t = uint;
|
||||||
|
typealias intptr_t = int;
|
||||||
|
|
||||||
|
[LinkName(.C)]
|
||||||
|
public static extern MonoArray* mono_array_new(MonoDomain *domain, MonoClass *eclass, uintptr_t length);
|
||||||
|
|
||||||
|
[LinkName(.C)]
|
||||||
|
public static extern MonoArray* mono_array_new_checked(MonoDomain *domain, MonoClass *eclass, uintptr_t length, MonoError *error);
|
||||||
|
|
||||||
|
[LinkName(.C)]
|
||||||
|
public static extern MonoArray* mono_array_new_full(MonoDomain *domain, MonoClass *array_class, uintptr_t* lengths, uintptr_t* lower_bounds);
|
||||||
|
|
||||||
|
[LinkName(.C)]
|
||||||
|
public static extern MonoArray* mono_array_new_full_checked(MonoDomain *domain, MonoClass *array_class, uintptr_t* lengths, uintptr_t* lower_bounds, MonoError *error);
|
||||||
|
|
||||||
|
[LinkName(.C)]
|
||||||
|
public static extern void mono_array_full_copy(MonoArray* src, MonoArray* dest);
|
||||||
|
|
||||||
|
[LinkName(.C)]
|
||||||
|
public static extern MonoArray* mono_array_clone(MonoArray* array);
|
||||||
|
|
||||||
|
[LinkName(.C)]
|
||||||
|
public static extern MonoArray* mono_array_clone_checked(MonoArray* array, MonoError* error);
|
||||||
|
|
||||||
|
[LinkName(.C)]
|
||||||
|
public static extern void* mono_array_addr_with_size(MonoArray *array, int32 size, uintptr_t idx);
|
||||||
|
|
||||||
|
[LinkName(.C)]
|
||||||
|
public static extern uintptr_t mono_array_length(MonoArray *array);
|
||||||
|
|
||||||
|
[Inline]
|
||||||
|
public static T* mono_array_addr<T>(MonoArray* array, uintptr_t index)
|
||||||
|
{
|
||||||
|
return (T*)mono_array_addr_with_size(array, sizeof(T), index);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Inline]
|
||||||
|
public static T mono_array_get<T>(MonoArray* array, uintptr_t index)
|
||||||
|
{
|
||||||
|
return *mono_array_addr<T>(array, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Inline]
|
||||||
|
public static void mono_array_set<T>(MonoArray* array, uintptr_t index, T value)
|
||||||
|
{
|
||||||
|
T* entryPtr = mono_array_addr<T>(array, index);
|
||||||
|
*entryPtr = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
|
|
||||||
struct MonoDomain;
|
struct MonoDomain;
|
||||||
@@ -293,6 +349,8 @@ struct MonoThread;
|
|||||||
|
|
||||||
struct MonoVTable;
|
struct MonoVTable;
|
||||||
|
|
||||||
|
struct MonoArray;
|
||||||
|
|
||||||
struct MonoException
|
struct MonoException
|
||||||
{
|
{
|
||||||
void* _bla;
|
void* _bla;
|
||||||
|
|||||||
+170
-22
@@ -1,26 +1,55 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Xml.Linq;
|
|
||||||
using GlitchyEngine.Core;
|
using GlitchyEngine.Core;
|
||||||
using GlitchyEngine.Math;
|
using GlitchyEngine.Extensions;
|
||||||
|
|
||||||
namespace GlitchyEngine;
|
namespace GlitchyEngine;
|
||||||
|
|
||||||
internal struct EntityHandle
|
|
||||||
{
|
|
||||||
public uint Version;
|
|
||||||
public uint Index;
|
|
||||||
}
|
|
||||||
|
|
||||||
public class Entity : EngineObject
|
public class Entity : EngineObject
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Empty constructor not used. Do NOT USE!
|
/// Only used by the Engine. Don't use!
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected Entity()
|
protected Entity()
|
||||||
{ }
|
{
|
||||||
|
// We don't do anything here.
|
||||||
|
// This constructor will be called by the Engine to initialize the scripts fields.
|
||||||
|
// Especially don't call Create here! Because Create would try and create a new entity.
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new Entity.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name">The name of the new entity.</param>
|
||||||
|
public Entity(string name = null)
|
||||||
|
{
|
||||||
|
Create(name, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new Entity.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name">The name of the new entity.</param>
|
||||||
|
/// <param name="components">The components that the entity shall have.</param>
|
||||||
|
public Entity(string name, params Type[] components)
|
||||||
|
{
|
||||||
|
Create(name, components);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Create(string name, Type[] components)
|
||||||
|
{
|
||||||
|
ScriptGlue.Entity_Create(this, name, components, out _uuid);
|
||||||
|
|
||||||
|
if (_uuid == UUID.Zero)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Failed to create the Entity. Received UUID.Zero from the engine.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates an instance that represents the Entity with the given id.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="uuid">The ID of the entity that belongs to this instance.</param>
|
||||||
internal Entity(UUID uuid)
|
internal Entity(UUID uuid)
|
||||||
{
|
{
|
||||||
_uuid = uuid;
|
_uuid = uuid;
|
||||||
@@ -68,9 +97,11 @@ public class Entity : EngineObject
|
|||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#region Add Components
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the component with the specified component type.
|
/// Adds the component with the specified type.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="T">The type of the component to add.</typeparam>
|
/// <typeparam name="T">The type of the component to add.</typeparam>
|
||||||
/// <returns>The component.</returns>
|
/// <returns>The component.</returns>
|
||||||
@@ -85,22 +116,85 @@ public class Entity : EngineObject
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the component with the given component type.
|
/// Adds the component with the given type.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="componentType">The type of the component to add.</param>
|
/// <param name="componentType">The type of the component to add.</param>
|
||||||
/// <returns>The component or null, if the given type is not a valid component type.</returns>
|
/// <returns>The component or null, if the given type is not a valid component type.</returns>
|
||||||
/// <remarks>For performance reasons it is recommended to use <see cref="AddComponent{T}"/> if possible.</remarks>
|
/// <remarks>For performance reasons it is recommended to use <see cref="AddComponent{T}"/> if possible.</remarks>
|
||||||
public Component AddComponent(Type componentType)
|
public Component AddComponent(Type componentType)
|
||||||
{
|
{
|
||||||
// TODO: probably throw!
|
if (!componentType.IsSubclassOf(typeof(Component)))
|
||||||
if (!componentType.IsSubclassOf(typeof(Component))) return null;
|
{
|
||||||
|
throw new ArgumentException($"Invalid component type \"{componentType}\". Must be a subclass of \"Component\".", nameof(componentType));
|
||||||
|
}
|
||||||
|
|
||||||
ScriptGlue.Entity_AddComponent(_uuid, componentType);
|
ScriptGlue.Entity_AddComponent(_uuid, componentType);
|
||||||
|
|
||||||
Component component = Activator.CreateInstance(componentType) as Component;
|
Component component = Activator.CreateInstance(componentType) as Component;
|
||||||
|
if (component != null)
|
||||||
|
component.Entity = this;
|
||||||
|
|
||||||
return component;
|
return component;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds components with the specified types to the entity.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="componentTypes">The types of the components to add.</param>
|
||||||
|
/// <returns>An array containing the components.</returns>
|
||||||
|
public Component[] AddComponents(params Type[] componentTypes)
|
||||||
|
{
|
||||||
|
foreach ((Type componentType, int index) in componentTypes.WithIndex())
|
||||||
|
{
|
||||||
|
if (!componentType.IsSubclassOf(typeof(Component)))
|
||||||
|
{
|
||||||
|
throw new ArgumentException($"Invalid component type \"{componentType}\" at index {index}. Must be a subclass of \"Component\".", nameof(componentTypes));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ScriptGlue.Entity_AddComponents(_uuid, componentTypes);
|
||||||
|
|
||||||
|
Component[] components = new Component[componentTypes.Length];
|
||||||
|
|
||||||
|
foreach ((Type componentType, int index) in componentTypes.WithIndex())
|
||||||
|
{
|
||||||
|
components[index] = Activator.CreateInstance(componentType) as Component;
|
||||||
|
components[index].Entity = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
return components;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds the specified components to the entity.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A tuple containing the added components.</returns>
|
||||||
|
public (T1, T2) AddComponents<T1, T2>()
|
||||||
|
where T1 : Component, new()
|
||||||
|
where T2 : Component, new()
|
||||||
|
{
|
||||||
|
ScriptGlue.Entity_AddComponents(_uuid, new []{typeof(T1), typeof(T2)});
|
||||||
|
|
||||||
|
return (new T1 { Entity = this }, new T2 { Entity = this });
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds the specified components to the entity.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A tuple containing the added components.</returns>
|
||||||
|
public (T1, T2, T3) AddComponents<T1, T2, T3>()
|
||||||
|
where T1 : Component, new()
|
||||||
|
where T2 : Component, new()
|
||||||
|
where T3 : Component, new()
|
||||||
|
{
|
||||||
|
ScriptGlue.Entity_AddComponents(_uuid, new []{typeof(T1), typeof(T2), typeof(T3)});
|
||||||
|
|
||||||
|
return (new T1 { Entity = this }, new T2 { Entity = this }, new T3 { Entity = this });
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion Add Components
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Removes the component with the specified component type.
|
/// Removes the component with the specified component type.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -117,12 +211,32 @@ public class Entity : EngineObject
|
|||||||
/// <remarks>For performance reasons it is recommended to use <see cref="RemoveComponent{T}"/> if possible.</remarks>
|
/// <remarks>For performance reasons it is recommended to use <see cref="RemoveComponent{T}"/> if possible.</remarks>
|
||||||
public void RemoveComponent(Type componentType)
|
public void RemoveComponent(Type componentType)
|
||||||
{
|
{
|
||||||
// TODO: probably throw!
|
if (!componentType.IsSubclassOf(typeof(Component)))
|
||||||
if (!componentType.IsSubclassOf(typeof(Component))) return;
|
{
|
||||||
|
throw new ArgumentException($"Invalid component type \"{componentType}\". Must be a subclass of \"Component\".", nameof(componentType));
|
||||||
|
}
|
||||||
|
|
||||||
ScriptGlue.Entity_RemoveComponent(_uuid, componentType);
|
ScriptGlue.Entity_RemoveComponent(_uuid, componentType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the script of the entity to the specified type.
|
||||||
|
/// If necessary removes the existing script.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The type of the script.</typeparam>
|
||||||
|
public T SetScript<T>() where T : Entity
|
||||||
|
{
|
||||||
|
return ScriptGlue.Entity_SetScript(_uuid, typeof(T)) as T;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Removes the script from the entity.
|
||||||
|
/// </summary>
|
||||||
|
public void RemoveScript()
|
||||||
|
{
|
||||||
|
ScriptGlue.Entity_RemoveScript(_uuid);
|
||||||
|
}
|
||||||
|
|
||||||
public Transform Transform => GetComponent<Transform>();
|
public Transform Transform => GetComponent<Transform>();
|
||||||
|
|
||||||
//public Vector3 Translation
|
//public Vector3 Translation
|
||||||
@@ -145,6 +259,17 @@ public class Entity : EngineObject
|
|||||||
|
|
||||||
return new Entity(entityId);
|
return new Entity(entityId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns true if the entity has a script component of the given type; false if the entity either has no script or the script is not of the specified type.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The type of the script.</typeparam>
|
||||||
|
public bool Is<T>() where T : Entity
|
||||||
|
{
|
||||||
|
object scriptInstance = ScriptGlue.Entity_GetScriptInstance(_uuid);
|
||||||
|
|
||||||
|
return scriptInstance is T;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns the script of the given type, or null, if the entity has no script of the given type.
|
/// Returns the script of the given type, or null, if the entity has no script of the given type.
|
||||||
@@ -158,7 +283,30 @@ public class Entity : EngineObject
|
|||||||
return scriptInstance as T;
|
return scriptInstance as T;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Will be executed once after the entity as be created.
|
/// <summary>
|
||||||
|
/// Destroys the entity and all it's children.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// The destruction will not take place immediately. It will happen at the end of the current frame.
|
||||||
|
/// </remarks>
|
||||||
|
public void Destroy()
|
||||||
|
{
|
||||||
|
ScriptGlue.Entity_Destroy(_uuid);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Instantiates a copy of the given entity and it's children.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="entity">The entity to copy.</param>
|
||||||
|
/// <returns>The new entity.</returns>
|
||||||
|
public static Entity CreateInstance(Entity entity)
|
||||||
|
{
|
||||||
|
ScriptGlue.Entity_CreateInstance(entity.UUID, out UUID newEntityId);
|
||||||
|
|
||||||
|
return new Entity(newEntityId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Will be executed once after the entity has be created.
|
||||||
// void OnCreate();
|
// void OnCreate();
|
||||||
|
|
||||||
// Will be executed every frame.
|
// Will be executed every frame.
|
||||||
|
|||||||
@@ -11,10 +11,22 @@ namespace GlitchyEngine;
|
|||||||
internal static class ScriptGlue
|
internal static class ScriptGlue
|
||||||
{
|
{
|
||||||
#region Entity
|
#region Entity
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||||
|
internal static extern void Entity_Create(object scriptInstance, string entityName, Type[] componentTypes, out UUID entityId);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||||
|
internal static extern void Entity_Destroy(UUID entityId);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||||
|
internal static extern void Entity_CreateInstance(UUID entityId, out UUID newEntityId);
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||||
internal static extern void Entity_AddComponent(UUID entityId, Type componentType);
|
internal static extern void Entity_AddComponent(UUID entityId, Type componentType);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||||
|
internal static extern void Entity_AddComponents(UUID entityId, Type[] componentTypes);
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||||
internal static extern bool Entity_HasComponent(UUID entityId, Type componentType);
|
internal static extern bool Entity_HasComponent(UUID entityId, Type componentType);
|
||||||
|
|
||||||
@@ -27,6 +39,12 @@ internal static class ScriptGlue
|
|||||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||||
internal static extern object Entity_GetScriptInstance(UUID entityId);
|
internal static extern object Entity_GetScriptInstance(UUID entityId);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||||
|
internal static extern object Entity_SetScript(UUID entityId, Type scriptType);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||||
|
internal static extern void Entity_RemoveScript(UUID entityId);
|
||||||
|
|
||||||
#endregion Entity
|
#endregion Entity
|
||||||
|
|
||||||
#region TransformComponent
|
#region TransformComponent
|
||||||
|
|||||||
Reference in New Issue
Block a user