mirror of
https://github.com/aharabada/glitchy-engine-beef.git
synced 2026-09-06 05:01:53 +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:
@@ -51,14 +51,17 @@ namespace GlitchyEngine.World
|
||||
cameraComponent.Camera.SetViewportSize(e.Scene._viewportWidth, e.Scene._viewportHeight);
|
||||
});
|
||||
|
||||
/*_onComponentAddedHandlers.Add(typeof(Rigidbody2DComponent), (e, t, c) => {
|
||||
if ()
|
||||
_onComponentAddedHandlers.Add(typeof(Rigidbody2DComponent), (e, t, c) => {
|
||||
Rigidbody2DComponent* rigidbodyComponent = (.)c;
|
||||
|
||||
Scene scene = e.Scene;
|
||||
|
||||
Rigidbody2DComponent* rigidBodyComponent = (.)c;
|
||||
|
||||
//cameraComponent.Camera.SetViewportSize(e.Scene._viewportWidth, e.Scene._viewportHeight);
|
||||
});*/
|
||||
if (scene._physicsWorld2D != null)
|
||||
{
|
||||
// TODO: Add rigidbody
|
||||
Log.EngineLogger.Error("Should have added a rigidbody");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public ~this()
|
||||
@@ -154,6 +157,14 @@ namespace GlitchyEngine.World
|
||||
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);
|
||||
|
||||
@@ -562,7 +573,7 @@ namespace GlitchyEngine.World
|
||||
Runtime = Scripts | Physics,
|
||||
}
|
||||
|
||||
//private append List<UUID> _destroyQueue = .();
|
||||
private append List<Entity> _destroyQueue = .();
|
||||
|
||||
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.
|
||||
@@ -719,6 +741,46 @@ namespace GlitchyEngine.World
|
||||
|
||||
_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)
|
||||
{
|
||||
|
||||
@@ -22,6 +22,9 @@ class SceneSerializer
|
||||
private Dictionary<UUID, Entity> _parentIdToChild;
|
||||
|
||||
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)
|
||||
{
|
||||
@@ -262,12 +265,13 @@ class SceneSerializer
|
||||
Runtime.NotImplemented();
|
||||
}
|
||||
|
||||
public Result<void> Deserialize(StringView filePath)
|
||||
public Result<void> Deserialize(StringView filePath, bool loadAsPrefab = false)
|
||||
{
|
||||
Debug.Profiler.ProfileResourceFunction!();
|
||||
|
||||
_parentIdToChild = scope Dictionary<UUID, Entity>();
|
||||
_entitiesMissingParent = scope List<(Entity Entity, UUID ParentId)>();
|
||||
_fileToSceneId = scope Dictionary<UUID, UUID>();
|
||||
|
||||
String buffer = scope String();
|
||||
File.ReadAllText(filePath, buffer);
|
||||
@@ -298,7 +302,7 @@ class SceneSerializer
|
||||
Try!(reader.EntryEnd());
|
||||
}
|
||||
|
||||
Try!(DeserializeEntity(reader));
|
||||
Try!(DeserializeEntity(reader, loadAsPrefab));
|
||||
|
||||
first = false;
|
||||
}
|
||||
@@ -325,23 +329,12 @@ class SceneSerializer
|
||||
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
|
||||
{
|
||||
Asset asset = null;
|
||||
@@ -357,11 +350,29 @@ class SceneSerializer
|
||||
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());
|
||||
|
||||
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())
|
||||
{
|
||||
@@ -416,11 +427,13 @@ class SceneSerializer
|
||||
let nextId = Try!(reader.Identifier());
|
||||
if (nextId == "ParentId")
|
||||
{
|
||||
UUID pId;
|
||||
Deserialize.Value(reader, out pId);
|
||||
UUID rawParentId;
|
||||
Deserialize.Value(reader, out rawParentId);
|
||||
reader.EntryEnd();
|
||||
|
||||
var parentEntity = _scene.GetEntityByID(pId);
|
||||
UUID parentId = RemapId(rawParentId);
|
||||
|
||||
var parentEntity = _scene.GetEntityByID(parentId);
|
||||
|
||||
if (parentEntity case .Ok(let parent))
|
||||
{
|
||||
@@ -428,7 +441,7 @@ class SceneSerializer
|
||||
}
|
||||
else
|
||||
{
|
||||
_entitiesMissingParent.Add((entity, pId));
|
||||
_entitiesMissingParent.Add((entity, parentId));
|
||||
}
|
||||
|
||||
Deserialize.Value(reader, "Position", out component.[Friend]_position);
|
||||
|
||||
@@ -10,16 +10,14 @@ namespace GlitchyEngine.World
|
||||
{
|
||||
internal EcsWorld _world;
|
||||
internal BitArray _bitMask;
|
||||
internal EcsWorld.BitmaskEntry* _currentEntry;
|
||||
internal EcsWorld.BitmaskEntry* _endEntry;
|
||||
internal List<EcsWorld.BitmaskEntry>.Enumerator _entitiesEnumerator;
|
||||
|
||||
public bool IsEmpty => _bitMask == null;
|
||||
|
||||
public this(EcsWorld world, Type[] componentTypes)
|
||||
{
|
||||
_world = world;
|
||||
_currentEntry = _world._entities.Ptr;
|
||||
_endEntry = _world._entities.Ptr + _world._entities.Count;
|
||||
_entitiesEnumerator = _world._entities.GetEnumerator();
|
||||
|
||||
_bitMask = new BitArray(_world._componentPools.Count);
|
||||
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.");
|
||||
#endif
|
||||
DeleteAndNullify!(_bitMask);
|
||||
_endEntry = _currentEntry;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -47,9 +44,12 @@ namespace GlitchyEngine.World
|
||||
|
||||
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
|
||||
if(entry.ID.Index == EcsEntity.InvalidEntity.Index)
|
||||
|
||||
Reference in New Issue
Block a user