From cfa4a85f3cf90438e51492ac92b0e48cfa8f716f Mon Sep 17 00:00:00 2001 From: Doprez <73259914+Doprez@users.noreply.github.com> Date: Sat, 3 Jun 2023 12:00:53 -0600 Subject: [PATCH 01/10] Removed FSM is now in Doprez.Stride.AI --- .../StateMachines/AsyncFiniteStateMachine.cs | 76 ------------------- .../StateMachines/FiniteStateMachine.cs | 76 ------------------- Doprez.Stride/StateMachines/IAsyncState.cs | 19 ----- Doprez.Stride/StateMachines/IState.cs | 19 ----- 4 files changed, 190 deletions(-) delete mode 100644 Doprez.Stride/StateMachines/AsyncFiniteStateMachine.cs delete mode 100644 Doprez.Stride/StateMachines/FiniteStateMachine.cs delete mode 100644 Doprez.Stride/StateMachines/IAsyncState.cs delete mode 100644 Doprez.Stride/StateMachines/IState.cs diff --git a/Doprez.Stride/StateMachines/AsyncFiniteStateMachine.cs b/Doprez.Stride/StateMachines/AsyncFiniteStateMachine.cs deleted file mode 100644 index d132d3e..0000000 --- a/Doprez.Stride/StateMachines/AsyncFiniteStateMachine.cs +++ /dev/null @@ -1,76 +0,0 @@ -using Stride.Core; -using Stride.Engine; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Doprez.Stride.StateMachines -{ - - [ComponentCategory("FSM")] - public class AsyncFiniteStateMachine : AsyncScript - { - /// - /// All states that can be accessed by the FSM - /// - [DataMemberIgnore] - public Dictionary States = new Dictionary(); - - protected IAsyncState _currentState; - - public void Add(int id, IAsyncState state) - { - States.Add(id, state); - } - - public override async Task Execute() - { - while (Game.IsRunning) - { - await _currentState.UpdateState(); - } - } - - public IAsyncState GetActiveState() - { - return _currentState; - } - - public IAsyncState GetState(int id) - { - return States[id]; - } - - public void SetCurrentState(IAsyncState state) - { - if (_currentState != null) - { - _currentState.ExitState(); - } - - _currentState = state; - - if (_currentState != null) - { - _currentState.EnterState(); - } - } - - public void SetCurrentState(int stateIndex) - { - if (_currentState != null) - { - _currentState.ExitState(); - } - - _currentState = GetState(stateIndex); - - if (_currentState != null) - { - _currentState.EnterState(); - } - } - } -} diff --git a/Doprez.Stride/StateMachines/FiniteStateMachine.cs b/Doprez.Stride/StateMachines/FiniteStateMachine.cs deleted file mode 100644 index 731e95d..0000000 --- a/Doprez.Stride/StateMachines/FiniteStateMachine.cs +++ /dev/null @@ -1,76 +0,0 @@ -using Stride.Core; -using Stride.Engine; -using System.Collections.Generic; - -namespace Doprez.Stride.StateMachines -{ - - [ComponentCategory("FSM")] - public class FiniteStateMachine : SyncScript - { - /// - /// All states that can be accessed by the FSM - /// - [DataMemberIgnore] - public Dictionary States = new Dictionary(); - - protected IState _currentState; - - public FiniteStateMachine() - { - } - - public void Add(int id, IState state) - { - States.Add(id, state); - } - - public IState GetActiveState() - { - return _currentState; - } - - public IState GetState(int id) - { - return States[id]; - } - - public void SetCurrentState(IState state) - { - if (_currentState != null) - { - _currentState.ExitState(); - } - - _currentState = state; - - if (_currentState != null) - { - _currentState.EnterState(); - } - } - - public void SetCurrentState(int stateIndex) - { - if (_currentState != null) - { - _currentState.ExitState(); - } - - _currentState = GetState(stateIndex); - - if (_currentState != null) - { - _currentState.EnterState(); - } - } - - public override void Update() - { - if (_currentState != null) - { - _currentState.UpdateState(); - } - } - } -} diff --git a/Doprez.Stride/StateMachines/IAsyncState.cs b/Doprez.Stride/StateMachines/IAsyncState.cs deleted file mode 100644 index 1a75a12..0000000 --- a/Doprez.Stride/StateMachines/IAsyncState.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Doprez.Stride.StateMachines; -public interface IAsyncState -{ - - public string Name { get; set; } - public AsyncFiniteStateMachine FiniteStateMachine { get; set; } - public bool IsDefaultState { get; set; } - - public Task EnterState(); - public Task ExitState(); - public Task UpdateState(); - -} diff --git a/Doprez.Stride/StateMachines/IState.cs b/Doprez.Stride/StateMachines/IState.cs deleted file mode 100644 index f1ace9d..0000000 --- a/Doprez.Stride/StateMachines/IState.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Doprez.Stride.StateMachines -{ - public interface IState - { - public string Name { get; set; } - public FiniteStateMachine FiniteStateMachine { get; set; } - public bool IsDefaultState { get; set; } - - public void EnterState(); - public void ExitState(); - public void UpdateState(); - } -} From ac0a4df47db5c3a794e58a072931312e4bab2c5f Mon Sep 17 00:00:00 2001 From: Doprez <73259914+Doprez@users.noreply.github.com> Date: Sat, 3 Jun 2023 12:01:37 -0600 Subject: [PATCH 02/10] moved files for restructure --- .../CustomScriptComponentExtensions.cs | 14 --- Doprez.Stride/ModelCategory/Data/LODData.cs | 19 --- Doprez.Stride/ModelCategory/LOD.cs | 47 -------- Doprez.Stride/Physics/PlayerMover.cs | 109 ------------------ Doprez.Stride/Physics/Raycast.cs | 57 --------- 5 files changed, 246 deletions(-) delete mode 100644 Doprez.Stride/Extensions/CustomScriptComponentExtensions.cs delete mode 100644 Doprez.Stride/ModelCategory/Data/LODData.cs delete mode 100644 Doprez.Stride/ModelCategory/LOD.cs delete mode 100644 Doprez.Stride/Physics/PlayerMover.cs delete mode 100644 Doprez.Stride/Physics/Raycast.cs diff --git a/Doprez.Stride/Extensions/CustomScriptComponentExtensions.cs b/Doprez.Stride/Extensions/CustomScriptComponentExtensions.cs deleted file mode 100644 index c6b6ee8..0000000 --- a/Doprez.Stride/Extensions/CustomScriptComponentExtensions.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace Stride.Engine; - -public static class CustomScriptComponentExtensions -{ - /// - /// Returns delta time in a shorter format. - /// - /// - /// - public static float DeltaTime(this ScriptComponent scriptComponent) - { - return (float)scriptComponent.Game.UpdateTime.Elapsed.TotalSeconds; - } -} diff --git a/Doprez.Stride/ModelCategory/Data/LODData.cs b/Doprez.Stride/ModelCategory/Data/LODData.cs deleted file mode 100644 index fabaafe..0000000 --- a/Doprez.Stride/ModelCategory/Data/LODData.cs +++ /dev/null @@ -1,19 +0,0 @@ -using Stride.Core; -using Stride.Engine; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Stride.Graphics; -using Stride.Graphics.Data; -using Stride.Rendering; - -namespace Doprez.Stride.ModelCategory; - -[DataContract] -public class LODData -{ - public Model MeshLOD = new Model(); - public int DistanceToActivate { get; set; } = 0; -} diff --git a/Doprez.Stride/ModelCategory/LOD.cs b/Doprez.Stride/ModelCategory/LOD.cs deleted file mode 100644 index 44f8bb9..0000000 --- a/Doprez.Stride/ModelCategory/LOD.cs +++ /dev/null @@ -1,47 +0,0 @@ -using Stride.Core; -using Stride.Core.Mathematics; -using Stride.Engine; -using Stride.Rendering.Compositing; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Doprez.Stride.ModelCategory; - -[DataContract("SimpleLODComponent", Inherited = true)] -[ComponentCategory("Model")] -public class SimpleLOD : AsyncScript -{ - [DataMember(0, "LOD's")] - public List LODs = new List(); - - private Entity _camera; - private ModelComponent _modelComponent; - - public override async Task Execute() - { - _modelComponent = Entity.Get(); - _camera = Entity.GetComponentInScene().Entity; - - while (Game.IsRunning) - { - UpdateCurrentLOD(); - - await Script.NextFrame(); - } - } - - private void UpdateCurrentLOD() - { - var distance = Vector3.Distance(_camera.WorldPosition(), Entity.WorldPosition()); - for (int i = LODs.Count - 1; i >= 0; i--) - { - if (distance < LODs[i].DistanceToActivate && _modelComponent.Model != LODs[i].MeshLOD) - { - _modelComponent.Model = LODs[i].MeshLOD; - } - } - } -} diff --git a/Doprez.Stride/Physics/PlayerMover.cs b/Doprez.Stride/Physics/PlayerMover.cs deleted file mode 100644 index 4d7b929..0000000 --- a/Doprez.Stride/Physics/PlayerMover.cs +++ /dev/null @@ -1,109 +0,0 @@ -using Stride.Core.Mathematics; -using Stride.Engine; -using Stride.Physics; -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using static Stride.Engine.PhysicsComponent; - -namespace Doprez.Stride.Physics -{ - [ComponentCategory("Physics")] - public class PlayerMover : StartupScript - { - //Camera stuff - public float MouseSpeed { get; set; } = 2; - public float MaxCameraAngle { get; set; } = 90; - public float MinCameraAngle { get; set; } = -90; - - //Player stuff - public float MovementSpeed { get; set; } = 10; - public float SprintMultiplier { get; set; } = 2; - public float JumpPower { get; set; } = 4; - - //References - public Entity CameraPivot; - - private Vector3 _cameraRotation; - private CharacterComponent _character; - - public override void Start() - { - _character = Entity.Get(); - - if (_character == null) - { - //this doesnt work colliders must be added in stride for now - InitializeCharacterComponent(); - } - - MinCameraAngle = MathUtil.DegreesToRadians(MinCameraAngle); - MaxCameraAngle = MathUtil.DegreesToRadians(MaxCameraAngle); - _cameraRotation = CameraPivot.Transform.RotationEulerXYZ; - } - - /// - /// makes the player jump. - /// power is determined by the JumpPower variable and uses the CharacterComponent.IsGrounded variable to validate if player can jump. - /// - public void Jump() - { - if (_character.IsGrounded) - { - float jump = 0; - jump += JumpPower; - _character.JumpSpeed = jump; - _character.Jump(); - } - } - - /// - /// Moves the player based on local rotation. - /// speed is determined by the MovementSpeed variable. - /// - /// - public void MovePlayer(Vector2 moveDirection, bool isSprinting = false) - { - var velocity = new Vector3(moveDirection.X, 0, moveDirection.Y); - velocity.Normalize(); - velocity = isSprinting ? velocity * SprintMultiplier : velocity; - velocity = Vector3.Transform(velocity * MovementSpeed, Entity.Transform.Rotation); - - _character.SetVelocity(velocity); - } - - /// - /// Rotates the camera transform - /// rotation speed is determined by MouseSpeed and clamps are determined by MinCameraAngle and MaxCameraAngle - /// - /// - public void UpdateCameraRotation(Vector2 mouseMovement) - { - mouseMovement = mouseMovement * MouseSpeed * this.DeltaTime(); - - _cameraRotation.Y -= mouseMovement.X; - _cameraRotation.X -= mouseMovement.Y; - _cameraRotation.X = MathUtil.Clamp(_cameraRotation.X, MinCameraAngle, MaxCameraAngle); - - //in order for the model to properly rotate we update the charactercomponent instead of the entity - _character.Orientation = Quaternion.RotationY(_cameraRotation.Y); - - //for vertical rotation we only update the camera pivot - CameraPivot.Transform.Rotation = Quaternion.RotationX(_cameraRotation.X); - } - - private void InitializeCharacterComponent() - { - _character = new(); - _character.ColliderShapes.Add(new CapsuleColliderShapeDesc() - { - LocalOffset = new Vector3(0,.5f,0), - }); - - Entity.Add(_character); - } - } -} diff --git a/Doprez.Stride/Physics/Raycast.cs b/Doprez.Stride/Physics/Raycast.cs deleted file mode 100644 index bdb9b2f..0000000 --- a/Doprez.Stride/Physics/Raycast.cs +++ /dev/null @@ -1,57 +0,0 @@ -using Doprez.Stride.Interfaces; -using Stride.Core.Mathematics; -using Stride.Engine; -using Stride.Physics; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Doprez.Stride.Physics -{ - [ComponentCategory("Physics")] - public class Raycast : StartupScript - { - public CollisionFilterGroupFlags CollideWith; - public float RaycastRange { get; set; } = 1; - - private Simulation _simulation; - - public override void Start() - { - _simulation = this.GetSimulation(); - } - - /// - /// A Raycast method based on the example in the fps demo - /// Make sure you are using the actual rotating Entity otherwise you will waste hours like I did debuging a non issue - /// - public HitResult RayCast(Entity entityPosition) - { - var raycastStart = entityPosition.Transform.WorldMatrix.TranslationVector; - var forward = entityPosition.Transform.WorldMatrix.Forward; - var raycastEnd = raycastStart + forward * RaycastRange; - - var result = _simulation.Raycast(raycastStart, raycastEnd); - - return result; - } - - /// - /// An async Raycast method based on the example in the fps demo - /// Make sure you are using the actual rotating Entity otherwise you will waste hours like I did debuging a non issue - /// - public async Task RayCastAsync(Entity entityPosition) - { - var raycastStart = entityPosition.Transform.WorldMatrix.TranslationVector; - var forward = entityPosition.Transform.WorldMatrix.Forward; - var raycastEnd = raycastStart + forward * RaycastRange; - - var result = await Task.FromResult(_simulation.Raycast(raycastStart, raycastEnd)); - - return result; - } - - } -} From c83900f004afcf2739000c3d47f13cd4a8dcd18e Mon Sep 17 00:00:00 2001 From: Doprez <73259914+Doprez@users.noreply.github.com> Date: Sat, 3 Jun 2023 12:02:14 -0600 Subject: [PATCH 03/10] cleaned up code --- .../Components/Models/Data/LODData.cs | 11 ++ Doprez.Stride/Components/Models/SimpleLOD.cs | 43 ++++++++ .../Components/Physics/PlayerMover.cs | 104 ++++++++++++++++++ Doprez.Stride/Components/Physics/Raycast.cs | 53 +++++++++ 4 files changed, 211 insertions(+) create mode 100644 Doprez.Stride/Components/Models/Data/LODData.cs create mode 100644 Doprez.Stride/Components/Models/SimpleLOD.cs create mode 100644 Doprez.Stride/Components/Physics/PlayerMover.cs create mode 100644 Doprez.Stride/Components/Physics/Raycast.cs diff --git a/Doprez.Stride/Components/Models/Data/LODData.cs b/Doprez.Stride/Components/Models/Data/LODData.cs new file mode 100644 index 0000000..033b4f8 --- /dev/null +++ b/Doprez.Stride/Components/Models/Data/LODData.cs @@ -0,0 +1,11 @@ +using Stride.Core; +using Stride.Rendering; + +namespace Doprez.Stride.ModelCategory; + +[DataContract] +public class LODData +{ + public Model MeshLOD = new Model(); + public int DistanceToActivate { get; set; } = 0; +} diff --git a/Doprez.Stride/Components/Models/SimpleLOD.cs b/Doprez.Stride/Components/Models/SimpleLOD.cs new file mode 100644 index 0000000..2a57807 --- /dev/null +++ b/Doprez.Stride/Components/Models/SimpleLOD.cs @@ -0,0 +1,43 @@ +using Stride.Core; +using Stride.Core.Mathematics; +using Stride.Engine; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace Doprez.Stride.ModelCategory; + +[DataContract("DoprezSimpleLODComponent", Inherited = true)] +[ComponentCategory("Model")] +public class SimpleLOD : AsyncScript +{ + [DataMember(0, "LOD's")] + public List LODs = new List(); + + private Entity _camera; + private ModelComponent _modelComponent; + + public override async Task Execute() + { + _modelComponent = Entity.Get(); + _camera = Entity.GetComponentInScene().Entity; + + while (Game.IsRunning) + { + UpdateCurrentLOD(); + + await Script.NextFrame(); + } + } + + private void UpdateCurrentLOD() + { + var distance = Vector3.Distance(_camera.WorldPosition(), Entity.WorldPosition()); + for (int i = LODs.Count - 1; i >= 0; i--) + { + if (distance < LODs[i].DistanceToActivate && _modelComponent.Model != LODs[i].MeshLOD) + { + _modelComponent.Model = LODs[i].MeshLOD; + } + } + } +} diff --git a/Doprez.Stride/Components/Physics/PlayerMover.cs b/Doprez.Stride/Components/Physics/PlayerMover.cs new file mode 100644 index 0000000..369d9d8 --- /dev/null +++ b/Doprez.Stride/Components/Physics/PlayerMover.cs @@ -0,0 +1,104 @@ +using Stride.Core; +using Stride.Core.Mathematics; +using Stride.Engine; +using Stride.Physics; + +namespace Doprez.Stride.Physics +{ + [ComponentCategory("Physics")] + [DataContract("DoprezPlayerMover")] + public class PlayerMover : StartupScript + { + //Camera stuff + public float MouseSpeed { get; set; } = 2; + public float MaxCameraAngle { get; set; } = 90; + public float MinCameraAngle { get; set; } = -90; + + //Player stuff + public float MovementSpeed { get; set; } = 10; + public float SprintMultiplier { get; set; } = 2; + public float JumpPower { get; set; } = 4; + + //References + public Entity CameraPivot; + + private Vector3 _cameraRotation; + private CharacterComponent _character; + + public override void Start() + { + _character = Entity.Get(); + + if (_character == null) + { + //this doesnt work colliders must be added in stride for now + InitializeCharacterComponent(); + } + + MinCameraAngle = MathUtil.DegreesToRadians(MinCameraAngle); + MaxCameraAngle = MathUtil.DegreesToRadians(MaxCameraAngle); + _cameraRotation = CameraPivot.Transform.RotationEulerXYZ; + } + + /// + /// makes the player jump. + /// power is determined by the JumpPower variable and uses the CharacterComponent.IsGrounded variable to validate if player can jump. + /// + public void Jump() + { + if (_character.IsGrounded) + { + float jump = 0; + jump += JumpPower; + _character.JumpSpeed = jump; + _character.Jump(); + } + } + + /// + /// Moves the player based on local rotation. + /// speed is determined by the MovementSpeed variable. + /// + /// + public void MovePlayer(Vector2 moveDirection, bool isSprinting = false) + { + var velocity = new Vector3(moveDirection.X, 0, moveDirection.Y); + velocity.Normalize(); + velocity = isSprinting ? velocity * SprintMultiplier : velocity; + velocity = Vector3.Transform(velocity * MovementSpeed, Entity.Transform.Rotation); + + _character.SetVelocity(velocity); + } + + /// + /// Rotates the camera transform + /// rotation speed is determined by MouseSpeed and clamps are determined by MinCameraAngle and MaxCameraAngle + /// + /// + public void UpdateCameraRotation(Vector2 mouseMovement) + { + mouseMovement = mouseMovement * MouseSpeed * this.DeltaTime(); + + _cameraRotation.Y -= mouseMovement.X; + _cameraRotation.X -= mouseMovement.Y; + _cameraRotation.X = MathUtil.Clamp(_cameraRotation.X, MinCameraAngle, MaxCameraAngle); + + //in order for the model to properly rotate we update the charactercomponent instead of the entity + _character.Orientation = Quaternion.RotationY(_cameraRotation.Y); + + //for vertical rotation we only update the camera pivot + CameraPivot.Transform.Rotation = Quaternion.RotationX(_cameraRotation.X); + } + + private void InitializeCharacterComponent() + { + _character = new(); + _character.ColliderShapes.Add(new CapsuleColliderShapeDesc() + { + LocalOffset = new Vector3(0,.5f,0), + }); + + Entity.Add(_character); + } + } +} diff --git a/Doprez.Stride/Components/Physics/Raycast.cs b/Doprez.Stride/Components/Physics/Raycast.cs new file mode 100644 index 0000000..c96eb8a --- /dev/null +++ b/Doprez.Stride/Components/Physics/Raycast.cs @@ -0,0 +1,53 @@ +using Stride.Core; +using Stride.Engine; +using Stride.Physics; +using System.Threading.Tasks; + +namespace Doprez.Stride.Physics +{ + [ComponentCategory("Physics")] + [DataContract("DoprezRaycast")] + public class Raycast : StartupScript + { + public CollisionFilterGroupFlags CollideWith; + public float RaycastRange { get; set; } = 1; + + private Simulation _simulation; + + public override void Start() + { + _simulation = this.GetSimulation(); + } + + /// + /// A Raycast method based on the example in the fps demo + /// Make sure you are using the actual rotating Entity otherwise you will waste hours like I did debuging a non issue + /// + public HitResult RayCast(Entity entityPosition) + { + var raycastStart = entityPosition.Transform.WorldMatrix.TranslationVector; + var forward = entityPosition.Transform.WorldMatrix.Forward; + var raycastEnd = raycastStart + forward * RaycastRange; + + var result = _simulation.Raycast(raycastStart, raycastEnd); + + return result; + } + + /// + /// An async Raycast method based on the example in the fps demo + /// Make sure you are using the actual rotating Entity otherwise you will waste hours like I did debuging a non issue + /// + public async Task RayCastAsync(Entity entityPosition) + { + var raycastStart = entityPosition.Transform.WorldMatrix.TranslationVector; + var forward = entityPosition.Transform.WorldMatrix.Forward; + var raycastEnd = raycastStart + forward * RaycastRange; + + var result = await Task.FromResult(_simulation.Raycast(raycastStart, raycastEnd)); + + return result; + } + + } +} From d29edb66eddc9cc1f8aba0059a261d5e976e2b73 Mon Sep 17 00:00:00 2001 From: Doprez <73259914+Doprez@users.noreply.github.com> Date: Sat, 3 Jun 2023 12:02:28 -0600 Subject: [PATCH 04/10] added animation events assignable from editor --- .../Components/Animations/AnimationEvent.cs | 98 +++++++++++++++++++ .../Animations/Data/AnimationEventData.cs | 22 +++++ 2 files changed, 120 insertions(+) create mode 100644 Doprez.Stride/Components/Animations/AnimationEvent.cs create mode 100644 Doprez.Stride/Components/Animations/Data/AnimationEventData.cs diff --git a/Doprez.Stride/Components/Animations/AnimationEvent.cs b/Doprez.Stride/Components/Animations/AnimationEvent.cs new file mode 100644 index 0000000..2de26e1 --- /dev/null +++ b/Doprez.Stride/Components/Animations/AnimationEvent.cs @@ -0,0 +1,98 @@ +using Doprez.Stride.Animations.Data; +using Stride.Core; +using Stride.Engine; + +namespace Doprez.Stride.Animations; +[DataContract("DoprezAnimationEvent")] +[ComponentCategory("Animation")] +[AllowMultipleComponents] +public class AnimationEvent : SyncScript +{ + [DataMember(0)] + public string EventName { get; set; } + + [DataMember(10)] + public AnimationEventData EventData { get; set; } + [DataMember(11)] + public AnimationTriggerAction TriggerAction { get; set; } = AnimationTriggerAction.Once; + + public delegate void NotifyAnimationEvent(); + + /// + /// Triggered at the beginning of the Animation Event only once by default + /// Will be continuous or single based on + /// + public event NotifyAnimationEvent AnimationEventTriggered; + /// + /// Triggered once the Animation is no longer running + /// + public event NotifyAnimationEvent AnimationEventEnded; + + private AnimationComponent _animations; + private bool _animationWasPlaying = false; + private bool _triggerHasRun = false; + + public override void Start() + { + base.Start(); + _animations = Entity.Get(); + } + + private void CheckAnimationEventTriggered() + { + if (_animations.PlayingAnimations.Count == 0) + return; + if (!_animations.IsPlaying(EventData.AnimationName)) + return; + if (_triggerHasRun) + return; + + _animationWasPlaying = true; + var playing = _animations.PlayingAnimations; + + for (int i = 0; i < playing.Count; i++) + { + if (playing[i].CurrentTime.TotalMilliseconds >= EventData.EventStartTime + && playing[i].Name.Equals(EventData.AnimationName)) + { + AnimationEventTriggered?.Invoke(); + switch (TriggerAction) + { + case AnimationTriggerAction.Once: + _triggerHasRun = true; + break; + case AnimationTriggerAction.Continuous: + break; + } + } + } + } + + private void CheckAnimationEventEnded() + { + if (_animationWasPlaying && !_animations.IsPlaying(EventData.AnimationName)) + { + AnimationEventEnded?.Invoke(); + _animationWasPlaying = false; + _triggerHasRun = false; + } + } + + public override void Update() + { + CheckAnimationEventTriggered(); + CheckAnimationEventEnded(); + } +} + +public enum AnimationTriggerAction +{ + /// + /// Only runs once on the trigger start + /// + Once, + /// + /// Runs every time the animation is played + /// + Continuous, +} \ No newline at end of file diff --git a/Doprez.Stride/Components/Animations/Data/AnimationEventData.cs b/Doprez.Stride/Components/Animations/Data/AnimationEventData.cs new file mode 100644 index 0000000..83b9596 --- /dev/null +++ b/Doprez.Stride/Components/Animations/Data/AnimationEventData.cs @@ -0,0 +1,22 @@ +using Stride.Core; + +namespace Doprez.Stride.Animations.Data; + +[DataContract(nameof(AnimationEventData))] +[Display("EventData")] +public struct AnimationEventData +{ + + /// + /// Set the Name of the Animation in the AnimationComponent + /// + [DataMember(0)] + public string AnimationName { get; set; } + + /// + /// Set the Start Time of the Animation Event in Milliseconds + /// + [DataMember(10)] + [Display("Start Time")] + public double EventStartTime { get; set; } +} From 30c29105a9099e2ee370f88492655de743134050 Mon Sep 17 00:00:00 2001 From: Doprez <73259914+Doprez@users.noreply.github.com> Date: Sat, 3 Jun 2023 12:02:41 -0600 Subject: [PATCH 05/10] added basic pathfinder --- .../Components/Navigation/Pathfinder.cs | 185 ++++++++++++++++++ 1 file changed, 185 insertions(+) create mode 100644 Doprez.Stride/Components/Navigation/Pathfinder.cs diff --git a/Doprez.Stride/Components/Navigation/Pathfinder.cs b/Doprez.Stride/Components/Navigation/Pathfinder.cs new file mode 100644 index 0000000..edb062e --- /dev/null +++ b/Doprez.Stride/Components/Navigation/Pathfinder.cs @@ -0,0 +1,185 @@ +using Stride.Core; +using Stride.Core.Mathematics; +using Stride.Engine; +using Stride.Engine.Design; +using Stride.Navigation; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Doprez.Stride.Navigation; + +[DataContract("DoprezPathfinder")] +[ComponentCategory("Navigation")] +public class Pathfinder : StartupScript +{ + + public float MovementSpeed { get; set; } = 10; + public Vector3 TargetPosition; + public string NavGroupName { get; set; } + public bool HasPath { get; private set; } + + private GameSettings _gameSettings; + private NavigationComponent _navigationComponent = new(); + private List _waypoints = new(); + private int _waypointIndex = 0; + + /// + /// Gets the distance from the current position directly to the Target + /// + public float DistanceToTarget + { + get + { + return Vector3.Distance(Entity.Transform.WorldMatrix.TranslationVector, TargetPosition); + } + } + + /// + /// Returns the distance between the current position and the next waypoint + /// + public float DistanceToNextWaypoint + { + get + { + if (_waypoints.Count == 0) + { + return 0; + } + return Vector3.Distance(Entity.Transform.WorldMatrix.TranslationVector, _waypoints[_waypointIndex]); + } + } + + /// + /// Returns the distance between each node in a path + /// not the most efficient method to use per frame but logically is better than DistanceToTarget + /// + public float CurrentPathDistance + { + get + { + float distance = 0; + if(_waypoints.Count == 0) + { + return distance; + } + for (int i = 0; i < _waypoints.Count - 1; i++) + { + distance += Vector3.Distance(_waypoints[i], _waypoints[i+1]); + } + return distance; + } + } + + public override void Start() + { + _gameSettings = Services.GetService()?.Settings; + + var navSettings = _gameSettings.Configurations.Get(); + + _navigationComponent.GroupId = navSettings.Groups.FirstOrDefault(x => x.Name == NavGroupName).Id; + + Entity.Add(_navigationComponent); + } + + /// + /// Stops the pathfinder and resets some base values + /// + public void Reset() + { + ClearGoal(); + } + + /// + /// Finds a path to the targetWaypoint + /// + /// + public void SetWaypoint(Vector3 targetWaypoint) + { + ClearGoal(); + TargetPosition = targetWaypoint; + if (_navigationComponent.TryFindPath(TargetPosition, _waypoints)) + { + _waypointIndex = 0; + HasPath = true; + } + } + + /// + /// Finds a path to the targetWaypoint asynchronously + /// + /// + public async Task SetWaypointAsync(Vector3 targetWaypoint) + { + ClearGoal(); + TargetPosition = targetWaypoint; + var foundPath = await Task.FromResult(_navigationComponent.TryFindPath(TargetPosition, _waypoints)); + if (foundPath) + { + HasPath = true; + _waypointIndex = 0; + } + } + + /// + /// Moves the unit in the direction of the path created by SetWaypoint or SetWaypointAsync + /// + public void Move() + { + if (_waypoints.Count == 0) + { + return; + } + + var deltaTime = (float)Game.UpdateTime.Elapsed.TotalSeconds; + var curPosition = Entity.Transform.WorldMatrix.TranslationVector; + var nextWaypointPosition = _waypoints[_waypointIndex]; + var distanceToWaypoint = Vector3.Distance(curPosition, nextWaypointPosition); + + // When the distance between the character and the next waypoint is large enough, move closer to the waypoint + if (distanceToWaypoint > 0.1) + { + var direction = nextWaypointPosition - curPosition; + direction.Normalize(); + direction *= MovementSpeed * deltaTime; + + Entity.Transform.Position += direction; + } + else + { + // If we are close enough to the waypoint, set the next waypoint or we are done and we do a final cleanup + if (_waypointIndex + 1 < _waypoints.Count) + { + _waypointIndex++; + } + else + { + ClearGoal(); + } + } + } + + /// + /// Rotates the unit in the direction of the path created by SetWaypoint or SetWaypointAsync + /// + public void Rotate() + { + if (_waypoints.Count == 0) + { + return; + } + + var currentPosition = Entity.Transform.WorldMatrix.TranslationVector; + + var angleInRadians = (float)Math.Atan2(_waypoints[_waypointIndex].Z - currentPosition.Z, _waypoints[_waypointIndex].X - currentPosition.X); + + Entity.Transform.Rotation = Quaternion.RotationY(-angleInRadians); + } + + private void ClearGoal() + { + _waypoints.Clear(); + HasPath = false; + } +} From f586ce370a96f481369475a29efb71bb9da15c0c Mon Sep 17 00:00:00 2001 From: Doprez <73259914+Doprez@users.noreply.github.com> Date: Sat, 3 Jun 2023 12:02:53 -0600 Subject: [PATCH 06/10] added physics based pathfinder --- .../Navigation/AdvancedPathFinder.cs | 185 ++++++++++++++++++ 1 file changed, 185 insertions(+) create mode 100644 Doprez.Stride/Components/Navigation/AdvancedPathFinder.cs diff --git a/Doprez.Stride/Components/Navigation/AdvancedPathFinder.cs b/Doprez.Stride/Components/Navigation/AdvancedPathFinder.cs new file mode 100644 index 0000000..29e311e --- /dev/null +++ b/Doprez.Stride/Components/Navigation/AdvancedPathFinder.cs @@ -0,0 +1,185 @@ +using Stride.Core; +using Stride.Engine.Design; +using Stride.Engine; +using Stride.Navigation; +using Stride.Physics; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Stride.Core.Mathematics; + +namespace Doprez.Stride.Navigation; +[DataContract("DoprezAdvancedNavigation")] +[ComponentCategory("Navigation")] +public class AdvancedPathFinder : StartupScript +{ + public float MovementSpeed { get; set; } = 400; + public string NavGroupName { get; set; } + public CharacterComponent CharacterComponent { get; set; } + + [DataMemberIgnore] + public Vector3 TargetPosition; + [DataMemberIgnore] + public bool HasPath { get; private set; } + + private GameSettings _gameSettings; + private NavigationComponent _navigationComponent = new(); + private List _waypoints = new(); + private int _waypointIndex = 0; + + /// + /// Gets the distance from the current position directly to the Target + /// + public float DistanceToTarget + { + get + { + return Vector3.Distance(Entity.Transform.WorldMatrix.TranslationVector, TargetPosition); + } + } + + /// + /// Returns the distance between the current position and the next waypoint + /// + public float DistanceToNextWaypoint + { + get + { + if (_waypoints.Count == 0) + { + return 0; + } + return Vector3.Distance(Entity.Transform.WorldMatrix.TranslationVector, _waypoints[_waypointIndex]); + } + } + + /// + /// Returns the distance between each node in a path + /// not the most efficient method to use per frame but logically is better than DistanceToTarget + /// + public float CurrentPathDistance + { + get + { + float distance = 0; + if (_waypoints.Count == 0) + { + return distance; + } + for (int i = 0; i < _waypoints.Count - 1; i++) + { + distance += Vector3.Distance(_waypoints[i], _waypoints[i + 1]); + } + return distance; + } + } + + public override void Start() + { + _gameSettings = Services.GetService()?.Settings; + + var navSettings = _gameSettings.Configurations.Get(); + _navigationComponent.GroupId = navSettings.Groups.FirstOrDefault(x => x.Name == NavGroupName).Id; + + Entity.Add(_navigationComponent); + } + + /// + /// Stops the pathfinder and resets some base values + /// + public void Reset() + { + ClearGoal(); + CharacterComponent.SetVelocity(Vector3.Zero); + } + + /// + /// Finds a path to the targetWaypoint + /// + /// + public void SetWaypoint(Vector3 targetWaypoint) + { + ClearGoal(); + TargetPosition = targetWaypoint; + if (_navigationComponent.TryFindPath(TargetPosition, _waypoints)) + { + _waypointIndex = 0; + HasPath = true; + } + } + + /// + /// Finds a path to the targetWaypoint asynchronously + /// + /// + public async Task SetWaypointAsync(Vector3 targetWaypoint) + { + ClearGoal(); + TargetPosition = targetWaypoint; + var foundPath = await Task.FromResult(_navigationComponent.TryFindPath(TargetPosition, _waypoints)); + if (foundPath) + { + HasPath = true; + _waypointIndex = 0; + } + } + + /// + /// Moves the unit in the direction of the path created by or + /// + public void Move() + { + if (_waypoints.Count == 0) + { + return; + } + + var nextWaypointPosition = _waypoints[_waypointIndex]; + var distanceToWaypoint = Vector3.Distance(Entity.WorldPosition(), nextWaypointPosition); + + // When the distance between the character and the next waypoint is large enough, move closer to the waypoint + if (distanceToWaypoint > 0.5) + { + var direction = nextWaypointPosition - Entity.WorldPosition(); + direction.Normalize(); + direction *= MovementSpeed * this.DeltaTime(); + CharacterComponent.SetVelocity(direction); + } + else + { + // If we are close enough to the waypoint, set the next waypoint or we are done and we do a final cleanup + if (_waypointIndex + 1 < _waypoints.Count) + { + _waypointIndex++; + } + else + { + ClearGoal(); + } + } + } + + /// + /// Rotates the unit in the direction of the path created by SetWaypoint or SetWaypointAsync + /// + public void Rotate() + { + if (_waypoints.Count == 0) + { + return; + } + + var currentPosition = Entity.Transform.WorldMatrix.TranslationVector; + + var angleInRadians = (float)Math.Atan2(_waypoints[_waypointIndex].Z - currentPosition.Z, _waypoints[_waypointIndex].X - currentPosition.X); + + CharacterComponent.Orientation = Quaternion.RotationY(-angleInRadians); + } + + private void ClearGoal() + { + _waypoints.Clear(); + HasPath = false; + } +} From 002bcae65c8b48f13966a79ed35b32366fdf4078 Mon Sep 17 00:00:00 2001 From: Doprez <73259914+Doprez@users.noreply.github.com> Date: Sat, 3 Jun 2023 12:03:17 -0600 Subject: [PATCH 07/10] added follow and rotate scripts for new utilities --- .../Components/Utils/SmoothFollow.cs | 24 ++++++++++++++++ .../Components/Utils/SmoothFollowAndRotate.cs | 28 +++++++++++++++++++ .../Components/Utils/SmoothRotate.cs | 24 ++++++++++++++++ 3 files changed, 76 insertions(+) create mode 100644 Doprez.Stride/Components/Utils/SmoothFollow.cs create mode 100644 Doprez.Stride/Components/Utils/SmoothFollowAndRotate.cs create mode 100644 Doprez.Stride/Components/Utils/SmoothRotate.cs diff --git a/Doprez.Stride/Components/Utils/SmoothFollow.cs b/Doprez.Stride/Components/Utils/SmoothFollow.cs new file mode 100644 index 0000000..614b309 --- /dev/null +++ b/Doprez.Stride/Components/Utils/SmoothFollow.cs @@ -0,0 +1,24 @@ +using Stride.Core; +using Stride.Core.Mathematics; +using Stride.Engine; + +namespace Doprez.Stride.Utils; + +[DataContract("DoprezSmoothFollow")] +[ComponentCategory("DoprezUtils")] +public class SmoothFollow : SyncScript +{ + + public Entity EntityToFollow { get; set; } + public Vector3 Speed { get; set; } = new Vector3(1, 1, 1); + + public override void Update() + { + var currentPosition = Entity.Transform.Position; + var otherPosition = EntityToFollow.WorldPosition(); + var x = MathUtil.Lerp(currentPosition.X, otherPosition.X, Speed.X * this.DeltaTime()); + var y = MathUtil.Lerp(currentPosition.Y, otherPosition.Y, Speed.Y * this.DeltaTime()); + var z = MathUtil.Lerp(currentPosition.Z, otherPosition.Z, Speed.Z * this.DeltaTime()); + Entity.Transform.Position = new Vector3(x, y, z); + } +} diff --git a/Doprez.Stride/Components/Utils/SmoothFollowAndRotate.cs b/Doprez.Stride/Components/Utils/SmoothFollowAndRotate.cs new file mode 100644 index 0000000..d5a8273 --- /dev/null +++ b/Doprez.Stride/Components/Utils/SmoothFollowAndRotate.cs @@ -0,0 +1,28 @@ +using Stride.Core; +using Stride.Core.Mathematics; +using Stride.Engine; + +namespace Doprez.Stride.Utils; + +[ComponentCategory("Utils")] +[DataContract("DoprezSmoothFollowAndRotate")] +public class SmoothFollowAndRotate : SyncScript +{ + + public Entity EntityToFollow { get; set; } + public float Speed { get; set; } = 1; + + public override void Update() + { + var currentPosition = Entity.Transform.Position; + var currentRotation = Entity.Transform.Rotation; + + EntityToFollow.Transform.GetWorldTransformation(out var otherPosition, out var otherRotation, out var _); + + var newPosition = Vector3.Lerp(currentPosition, otherPosition, Speed * this.DeltaTime()); + Entity.Transform.Position = newPosition; + + Quaternion.Slerp(ref currentRotation, ref otherRotation, Speed * this.DeltaTime(), out var newRotation); + Entity.Transform.Rotation = newRotation; + } +} diff --git a/Doprez.Stride/Components/Utils/SmoothRotate.cs b/Doprez.Stride/Components/Utils/SmoothRotate.cs new file mode 100644 index 0000000..6ec6a93 --- /dev/null +++ b/Doprez.Stride/Components/Utils/SmoothRotate.cs @@ -0,0 +1,24 @@ +using Stride.Core; +using Stride.Core.Mathematics; +using Stride.Engine; + +namespace Doprez.Stride.Utils; + +[DataContract("DoprezSmoothRotate")] +[ComponentCategory("DoprezUtils")] +public class SmoothRotate : SyncScript +{ + + public Entity EntityToFollow { get; set; } + public Vector3 Speed { get; set; } = new Vector3(1, 1, 1); + + public override void Update() + { + var currentRotation = Entity.Transform.Rotation; + EntityToFollow.Transform.GetWorldTransformation(out var _, out var otherRotation, out var _); + + Quaternion.Slerp(ref currentRotation, ref otherRotation, Speed.X * this.DeltaTime(), out var newRotation); + + Entity.Transform.Rotation = newRotation; + } +} From 2ff7a9c96ee06c7374bb54eba861ee71ab3a94a1 Mon Sep 17 00:00:00 2001 From: Doprez <73259914+Doprez@users.noreply.github.com> Date: Sat, 3 Jun 2023 12:03:42 -0600 Subject: [PATCH 08/10] added extensions to find camera in a scene --- .../Extensions/ScriptComponentExtensions.cs | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 Doprez.Stride/Extensions/ScriptComponentExtensions.cs diff --git a/Doprez.Stride/Extensions/ScriptComponentExtensions.cs b/Doprez.Stride/Extensions/ScriptComponentExtensions.cs new file mode 100644 index 0000000..b13bc87 --- /dev/null +++ b/Doprez.Stride/Extensions/ScriptComponentExtensions.cs @@ -0,0 +1,65 @@ +using Stride.Rendering.Compositing; + +namespace Stride.Engine; + +public static class ScriptComponentExtensions +{ + /// + /// Returns delta time in a shorter format. + /// + /// + /// + public static float DeltaTime(this ScriptComponent scriptComponent) + { + return (float)scriptComponent.Game.UpdateTime.Elapsed.TotalSeconds; + } + + /// + /// Gets the camera from the with the name main + /// + /// + /// + public static CameraComponent GetCamera(this ScriptComponent scriptComponent) + { + SceneCameraSlotCollection cameraCollection = scriptComponent.SceneSystem.GraphicsCompositor.Cameras; + foreach (var sceneCamera in cameraCollection) + { + if (sceneCamera.Name == "Main") + { + return sceneCamera.Camera; + } + } + return null; + } + + /// + /// Gets the camera from the with the given name. + /// + /// + /// + /// + public static CameraComponent GetCamera(this ScriptComponent scriptComponent, string cameraName) + { + SceneCameraSlotCollection cameraCollection = scriptComponent.SceneSystem.GraphicsCompositor.Cameras; + foreach (var sceneCamera in cameraCollection) + { + if (sceneCamera.Name == cameraName) + { + return sceneCamera.Camera; + } + } + return null; + } + + /// + /// Gets the first camera from the + /// + /// + /// + public static CameraComponent GetFirstCamera(this ScriptComponent scriptComponent) + { + SceneCameraSlotCollection cameraCollection = scriptComponent.SceneSystem.GraphicsCompositor.Cameras; + + return cameraCollection[0].Camera; + } +} From cce03cce0e3e8e0d95b76073a74f009e300756d6 Mon Sep 17 00:00:00 2001 From: Doprez <73259914+Doprez@users.noreply.github.com> Date: Sat, 3 Jun 2023 12:05:11 -0600 Subject: [PATCH 09/10] version bump 2.0 --- Doprez.Stride/Doprez.Stride.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doprez.Stride/Doprez.Stride.csproj b/Doprez.Stride/Doprez.Stride.csproj index 28eb739..8c58449 100644 --- a/Doprez.Stride/Doprez.Stride.csproj +++ b/Doprez.Stride/Doprez.Stride.csproj @@ -7,7 +7,7 @@ en-CA https://github.com/Doprez/Doprez.Stride https://github.com/Doprez/Doprez.Stride - 1.2.5 + 2.0.0 A package of helper classes for use in the Stride Game Engine README.md Stride3d;Game Engine;Stride; From bcb52fae70897748da7bcfdc45aa8beaa6f6020a Mon Sep 17 00:00:00 2001 From: Doprez <73259914+Doprez@users.noreply.github.com> Date: Sat, 3 Jun 2023 12:20:59 -0600 Subject: [PATCH 10/10] code cleanup --- .../Components/Animations/Data/AnimationEventData.cs | 1 - Doprez.Stride/Components/Navigation/Pathfinder.cs | 1 - Doprez.Stride/Components/Physics/Raycast.cs | 5 ++--- Doprez.Stride/Components/Utils/SmoothFollow.cs | 1 - Doprez.Stride/Components/Utils/SmoothFollowAndRotate.cs | 1 - Doprez.Stride/Components/Utils/SmoothRotate.cs | 1 - 6 files changed, 2 insertions(+), 8 deletions(-) diff --git a/Doprez.Stride/Components/Animations/Data/AnimationEventData.cs b/Doprez.Stride/Components/Animations/Data/AnimationEventData.cs index 83b9596..60641d1 100644 --- a/Doprez.Stride/Components/Animations/Data/AnimationEventData.cs +++ b/Doprez.Stride/Components/Animations/Data/AnimationEventData.cs @@ -6,7 +6,6 @@ namespace Doprez.Stride.Animations.Data; [Display("EventData")] public struct AnimationEventData { - /// /// Set the Name of the Animation in the AnimationComponent /// diff --git a/Doprez.Stride/Components/Navigation/Pathfinder.cs b/Doprez.Stride/Components/Navigation/Pathfinder.cs index edb062e..be14d16 100644 --- a/Doprez.Stride/Components/Navigation/Pathfinder.cs +++ b/Doprez.Stride/Components/Navigation/Pathfinder.cs @@ -14,7 +14,6 @@ namespace Doprez.Stride.Navigation; [ComponentCategory("Navigation")] public class Pathfinder : StartupScript { - public float MovementSpeed { get; set; } = 10; public Vector3 TargetPosition; public string NavGroupName { get; set; } diff --git a/Doprez.Stride/Components/Physics/Raycast.cs b/Doprez.Stride/Components/Physics/Raycast.cs index c96eb8a..540cc2f 100644 --- a/Doprez.Stride/Components/Physics/Raycast.cs +++ b/Doprez.Stride/Components/Physics/Raycast.cs @@ -27,7 +27,7 @@ public HitResult RayCast(Entity entityPosition) { var raycastStart = entityPosition.Transform.WorldMatrix.TranslationVector; var forward = entityPosition.Transform.WorldMatrix.Forward; - var raycastEnd = raycastStart + forward * RaycastRange; + var raycastEnd = raycastStart + (forward * RaycastRange); var result = _simulation.Raycast(raycastStart, raycastEnd); @@ -42,12 +42,11 @@ public async Task RayCastAsync(Entity entityPosition) { var raycastStart = entityPosition.Transform.WorldMatrix.TranslationVector; var forward = entityPosition.Transform.WorldMatrix.Forward; - var raycastEnd = raycastStart + forward * RaycastRange; + var raycastEnd = raycastStart + (forward * RaycastRange); var result = await Task.FromResult(_simulation.Raycast(raycastStart, raycastEnd)); return result; } - } } diff --git a/Doprez.Stride/Components/Utils/SmoothFollow.cs b/Doprez.Stride/Components/Utils/SmoothFollow.cs index 614b309..734f165 100644 --- a/Doprez.Stride/Components/Utils/SmoothFollow.cs +++ b/Doprez.Stride/Components/Utils/SmoothFollow.cs @@ -8,7 +8,6 @@ namespace Doprez.Stride.Utils; [ComponentCategory("DoprezUtils")] public class SmoothFollow : SyncScript { - public Entity EntityToFollow { get; set; } public Vector3 Speed { get; set; } = new Vector3(1, 1, 1); diff --git a/Doprez.Stride/Components/Utils/SmoothFollowAndRotate.cs b/Doprez.Stride/Components/Utils/SmoothFollowAndRotate.cs index d5a8273..262f58b 100644 --- a/Doprez.Stride/Components/Utils/SmoothFollowAndRotate.cs +++ b/Doprez.Stride/Components/Utils/SmoothFollowAndRotate.cs @@ -8,7 +8,6 @@ namespace Doprez.Stride.Utils; [DataContract("DoprezSmoothFollowAndRotate")] public class SmoothFollowAndRotate : SyncScript { - public Entity EntityToFollow { get; set; } public float Speed { get; set; } = 1; diff --git a/Doprez.Stride/Components/Utils/SmoothRotate.cs b/Doprez.Stride/Components/Utils/SmoothRotate.cs index 6ec6a93..eb6776d 100644 --- a/Doprez.Stride/Components/Utils/SmoothRotate.cs +++ b/Doprez.Stride/Components/Utils/SmoothRotate.cs @@ -8,7 +8,6 @@ namespace Doprez.Stride.Utils; [ComponentCategory("DoprezUtils")] public class SmoothRotate : SyncScript { - public Entity EntityToFollow { get; set; } public Vector3 Speed { get; set; } = new Vector3(1, 1, 1);