diff --git a/Basalt.Core/Basalt.Core.csproj b/Basalt.Core/Basalt.Core.csproj index a031b73..14c8d4d 100644 --- a/Basalt.Core/Basalt.Core.csproj +++ b/Basalt.Core/Basalt.Core.csproj @@ -4,7 +4,7 @@ net8.0 enable enable - 1.9.0 + 1.9.1 True BasaltLogoBg.png README.md diff --git a/Basalt.Raylib/Basalt.Raylib.csproj b/Basalt.Raylib/Basalt.Raylib.csproj index 952ccd7..383038c 100644 --- a/Basalt.Raylib/Basalt.Raylib.csproj +++ b/Basalt.Raylib/Basalt.Raylib.csproj @@ -5,7 +5,7 @@ enable enable true - 1.9.0 + 1.9.1 True BasaltLogoBg.png README.md diff --git a/Basalt.TestField/Components/TestTrigger.cs b/Basalt.TestField/Components/TestTrigger.cs index 0e8e351..4e44b36 100644 --- a/Basalt.TestField/Components/TestTrigger.cs +++ b/Basalt.TestField/Components/TestTrigger.cs @@ -1,6 +1,8 @@ using Basalt.Common.Components; using Basalt.Common.Entities; using Basalt.Raylib.Components; +using System.Drawing; +using System.Numerics; namespace Basalt.TestField.Components { @@ -10,12 +12,19 @@ public TestTrigger(Entity entity) : base(entity) { } + public override void OnStart() + { + Entity.AddComponent(new RaylibParticleSystem(Entity) { Looping = true, ModelCacheKey = "cube" }); + var ps = Entity.GetComponent(); + ps.UpdateDefaults(new() { Color = Color.RebeccaPurple, Velocity = new Vector3(0, 1f, 0) }); + ps.OnStartEvent(this, EventArgs.Empty); + } + public override void OnCollision(Collider other) { if (other.Entity.Id == "entity.player") { - Console.WriteLine($"Trigger collided with {other.Entity.Id}"); - Entity.GetComponent().ModelCacheKey = "sphere"; + } } } diff --git a/Basalt.TestField/Program.cs b/Basalt.TestField/Program.cs index 0650bb7..6633278 100644 --- a/Basalt.TestField/Program.cs +++ b/Basalt.TestField/Program.cs @@ -15,6 +15,7 @@ using Basalt.Raylib.Sound; using Basalt.Raylib.Utils; using Basalt.TestField; +using Basalt.TestField.Components; using Basalt.Types; using Raylib_cs; using System.Numerics; @@ -65,20 +66,13 @@ Engine.CreateEntity(player); -var emitter = new Entity(); -emitter.Transform.Position = new Vector3(0, 10, 0); -emitter.AddComponent(new RaylibParticleSystem(emitter) { ModelCacheKey = "cube" }); -Engine.CreateEntity(emitter); - -var ps = emitter.GetComponent()!; - -ps.SubscribeOnParticleReset((ref Particle p) => -{ - p.Velocity = new(Random.Shared.NextSingle() * 10 - 5, Random.Shared.NextSingle() * 10 - 5, Random.Shared.NextSingle() * 10 - 5); - // Apply random rotation - p.Rotation = Quaternion.CreateFromYawPitchRoll(Random.Shared.NextSingle() * MathF.PI * 2, Random.Shared.NextSingle() * MathF.PI * 2, Random.Shared.NextSingle() * MathF.PI * 2); - -}); +var trigger = new Entity(); +trigger.AddComponent(new ModelRenderer(trigger) { ModelCacheKey = "cube", Size = new Vector3(1f), ColorTint = Color.Green }); +trigger.AddComponent(new BoxCollider(trigger) { Size = new Vector3(1, 1, 1), IsTrigger = true }); +trigger.AddComponent(new TestTrigger(trigger)); +trigger.AddComponent(new Rigidbody(trigger) { IsKinematic = true }); +trigger.Transform.Position = new Vector3(0, 1, 5); +Engine.CreateEntity(trigger); TestingUtils.SetupTestingScene(250); TestingUtils.SetupDebugInfo(); \ No newline at end of file diff --git a/Basalt.Tests/Integration/EntityIntegrationTests.cs b/Basalt.Tests/Integration/EntityIntegrationTests.cs index b4dd029..c0b72ca 100644 --- a/Basalt.Tests/Integration/EntityIntegrationTests.cs +++ b/Basalt.Tests/Integration/EntityIntegrationTests.cs @@ -169,30 +169,6 @@ public void EntitySerializeToJson_WhenComponentHasEntityReference_ShouldSerializ Assert.That(json, Does.Contain("entity.target")); } - [Test] - public void EntityDeserializeFromJson_WhenComponentHasEntityReference_ShouldDeserializeReference() - { - // Arrange - var target = new Entity(); - target.Id = "entity.target"; - var entity = new Entity(); - entity.Id = "entity.testing"; - entity.AddComponent(new TestComponent(entity) { Target = target }); - - // Act - Engine.Instance.Initialize(); - Engine.CreateEntity(target); - var json = entity.SerializeToJson(); - var newEntity = Entity.DeserializeFromJson(json); - Engine.CreateEntity(newEntity); - - // Assert - Assert.IsNotNull(newEntity, "Deserialization failed, result entity was null"); - Assert.IsNotNull(newEntity.GetComponent(), "Test component was null"); - Assert.IsTrue(newEntity.GetComponent().HasStarted, "Component did not initialize"); - Assert.IsNotNull(newEntity.GetComponent().Target, "Target was null"); - Assert.That(newEntity.GetComponent().Target.Id, Is.EqualTo(target.Id), "Ids are different"); - } [Test] public void EntityDeserializeToJson_WhenMissingIdField_ShouldGenerateNew() diff --git a/Basalt/Basalt.csproj b/Basalt/Basalt.csproj index 9a957ea..015cd5b 100644 --- a/Basalt/Basalt.csproj +++ b/Basalt/Basalt.csproj @@ -5,7 +5,7 @@ enable enable true - 1.9.0 + 1.9.1 True Basalt BasaltLogoBg.png diff --git a/Basalt/Common/Components/BaseParticleSystem.cs b/Basalt/Common/Components/BaseParticleSystem.cs index 5b02ffa..dfbae23 100644 --- a/Basalt/Common/Components/BaseParticleSystem.cs +++ b/Basalt/Common/Components/BaseParticleSystem.cs @@ -186,6 +186,7 @@ public void UnsubscribeOnParticleReset(ParticleUpdateDelegate update) /// The new default particle value public void UpdateDefaults(Particle particle) { + particle.Position = Entity.Transform.Position; defaults = particle; } @@ -198,6 +199,7 @@ public void Reset() for (int i = 0; i < _particles.Length; i++) { _particles[i] = defaults; + _particles[i].Lifetime = ParticleLifetime / _length * i; } } diff --git a/Basalt/Common/Entities/Entity.cs b/Basalt/Common/Entities/Entity.cs index 4f3b401..acb43fd 100644 --- a/Basalt/Common/Entities/Entity.cs +++ b/Basalt/Common/Entities/Entity.cs @@ -21,6 +21,11 @@ public class Entity private List componentDtos = new(); private List components = new(); + [JsonIgnore] + internal bool created = false; + [JsonIgnore] + private int componentCount = 0; + [JsonProperty("Id")] public string Id { get; set; } = System.Guid.NewGuid().ToString(); /// @@ -113,6 +118,9 @@ public string SerializeToJson() /// /// The json string to deserialize from /// An entity instance from the JSON string + /// + /// It is not recommended to rely on deserialization to keep references as they will break. + /// public static Entity DeserializeFromJson(string json) { JObject jObject = JObject.Parse(json); @@ -208,6 +216,11 @@ public void AddComponent(Component component, bool overwrite = false) break; } + if(created) + component.OnStartEvent(this, EventArgs.Empty); + + componentCount++; + } /// @@ -244,6 +257,7 @@ private void ForceAddComponent(Component component) // Handle other cases if necessary break; } + componentCount++; } /// @@ -257,6 +271,7 @@ public void RemoveComponent(Component component) if (component.GetType() == typeof(Rigidbody)) Rigidbody = null; + componentCount--; } /// @@ -273,8 +288,9 @@ public void RemoveComponent(Component component) if (typeof(T) == typeof(Collider)) return Collider as T; - foreach (var component in components) + for (int i = 0; i < components.Count; i++) { + Component? component = components[i]; if (component is T match) { return match; @@ -344,13 +360,16 @@ public void Destroy() { Destroyed = true; Engine.RemoveEntity(this); - foreach (var child in Children) + for (int i = 0; i < Children.Count; i++) { + Entity? child = Children[i]; child.Destroy(); } - foreach (var component in components) + var count = componentCount; + for (int i = 0; i < count; i++) { + Component? component = components[i]; component.onDestroy(); } } @@ -361,8 +380,9 @@ public void Destroy() public void Create() { Engine.CreateEntity(this); - foreach (var child in Children) + for (int i = 0; i < Children.Count; i++) { + Entity? child = Children[i]; child.Create(); } } @@ -371,8 +391,10 @@ public void CallOnCollision(Collider other) { if (Destroyed) return; - foreach (var component in components) - component.OnCollision(other); + + var count = componentCount; + for (int i = 0; i < count; i++) + components[i].OnCollision(other); } private static Type? ByName(string name) @@ -394,8 +416,10 @@ public void CallOnCollision(Collider other) internal void CallStart() { - foreach (var component in components) + var count = componentCount; + for (int i = 0; i < count; i++) { + Component? component = components[i]; if (!component.started) { var dependencyAttribute = component.GetType().GetCustomAttribute(); @@ -431,8 +455,9 @@ public Entity Clone() { var result = new Entity(); result.Id = Id + Guid.NewGuid().ToString(); - foreach (var component in components) + for (int i = 0; i < components.Count; i++) { + Component? component = components[i]; var c = Activator.CreateInstance(component.GetType(), result) as Component; foreach (var prop in c.GetType().GetProperties()) { diff --git a/Basalt/Common/Events/EventBus.cs b/Basalt/Common/Events/EventBus.cs index 4e38c28..1591b16 100644 --- a/Basalt/Common/Events/EventBus.cs +++ b/Basalt/Common/Events/EventBus.cs @@ -1,4 +1,5 @@ using Basalt.Core.Common.Abstractions.Engine; +using System.Collections.Concurrent; namespace Basalt.Common.Events { /// @@ -6,17 +7,15 @@ namespace Basalt.Common.Events /// public class EventBus : IEventBus { - private readonly List observers; private readonly object lockObject; // Game Events - private Dictionary eventHandlers = new Dictionary(); + private ConcurrentDictionary eventHandlers = new ConcurrentDictionary(); /// /// Initializes a new instance of the class. /// public EventBus() { - observers = new List(); lockObject = new object(); } diff --git a/Basalt/Engine.cs b/Basalt/Engine.cs index 99628fa..e8a2d77 100644 --- a/Basalt/Engine.cs +++ b/Basalt/Engine.cs @@ -176,6 +176,7 @@ public static void CreateEntity(Entity entity) Instance.Logger?.LogDebug($"Creating entity {entity.Id}..."); Instance.EntityManager.AddEntity(entity); Instance.GetEngineComponent()?.AddEntityToSimulation(entity); + entity.created = true; entity.CallStart(); } diff --git a/CHANGELOG.md b/CHANGELOG.md index 52f11c7..d7e2f2f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,15 @@ All notable changes to this project will be documented in this file. See [versionize](https://github.com/versionize/versionize) for commit guidelines. + +## [1.9.1](https://www.github.com/thiagomvas/Basalt/releases/tag/v1.9.1) (2024-06-18) + +### Bug Fixes + +* Adding components during some events would crash the engine ([c13a6cf](https://www.github.com/thiagomvas/Basalt/commit/c13a6cf34094cee8841c5e2cbace019422ca04f2)) +* Components are now initialized when added after entity creation ([7aab969](https://www.github.com/thiagomvas/Basalt/commit/7aab9696b8da0ddda0076eef0be87d29e28461ec)) +* Particle Lifetime and starting position now work as expected ([7d19af5](https://www.github.com/thiagomvas/Basalt/commit/7d19af5b30834bbce56df88c45e4697cf361e610)) + ## [1.9.0](https://www.github.com/thiagomvas/Basalt/releases/tag/v1.9.0) (2024-06-18)