Skip to content

Commit

Permalink
Merge pull request #6 from Doprez/release-2-0
Browse files Browse the repository at this point in the history
Release 2.0
  • Loading branch information
Doprez authored Jun 3, 2023
2 parents 9ae33da + bcb52fa commit d34a00a
Show file tree
Hide file tree
Showing 18 changed files with 635 additions and 235 deletions.
98 changes: 98 additions & 0 deletions Doprez.Stride/Components/Animations/AnimationEvent.cs
Original file line number Diff line number Diff line change
@@ -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();

/// <summary>
/// Triggered at the beginning of the Animation Event only once by default
/// <para>Will be continuous or single based on <see cref="TriggerAction"/></para>
/// </summary>
public event NotifyAnimationEvent AnimationEventTriggered;
/// <summary>
/// Triggered once the Animation is no longer running
/// </summary>
public event NotifyAnimationEvent AnimationEventEnded;

private AnimationComponent _animations;
private bool _animationWasPlaying = false;
private bool _triggerHasRun = false;

public override void Start()
{
base.Start();
_animations = Entity.Get<AnimationComponent>();
}

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
{
/// <summary>
/// Only runs once on the trigger start
/// </summary>
Once,
/// <summary>
/// Runs every time the animation is played
/// </summary>
Continuous,
}
21 changes: 21 additions & 0 deletions Doprez.Stride/Components/Animations/Data/AnimationEventData.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using Stride.Core;

namespace Doprez.Stride.Animations.Data;

[DataContract(nameof(AnimationEventData))]
[Display("EventData")]
public struct AnimationEventData
{
/// <summary>
/// Set the Name of the Animation in the AnimationComponent
/// </summary>
[DataMember(0)]
public string AnimationName { get; set; }

/// <summary>
/// Set the Start Time of the Animation Event in Milliseconds
/// </summary>
[DataMember(10)]
[Display("Start Time")]
public double EventStartTime { get; set; }
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,4 @@
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;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,12 @@
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)]
[DataContract("DoprezSimpleLODComponent", Inherited = true)]
[ComponentCategory("Model")]
public class SimpleLOD : AsyncScript
{
Expand Down
185 changes: 185 additions & 0 deletions Doprez.Stride/Components/Navigation/AdvancedPathFinder.cs
Original file line number Diff line number Diff line change
@@ -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<Vector3> _waypoints = new();
private int _waypointIndex = 0;

/// <summary>
/// <para>Gets the distance from the current position directly to the Target</para>
/// </summary>
public float DistanceToTarget
{
get
{
return Vector3.Distance(Entity.Transform.WorldMatrix.TranslationVector, TargetPosition);
}
}

/// <summary>
/// <para>Returns the distance between the current position and the next waypoint</para>
/// </summary>
public float DistanceToNextWaypoint
{
get
{
if (_waypoints.Count == 0)
{
return 0;
}
return Vector3.Distance(Entity.Transform.WorldMatrix.TranslationVector, _waypoints[_waypointIndex]);
}
}

/// <summary>
/// <para>Returns the distance between each node in a path</para>
/// <para>not the most efficient method to use per frame but logically is better than DistanceToTarget</para>
/// </summary>
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<IGameSettingsService>()?.Settings;

var navSettings = _gameSettings.Configurations.Get<NavigationSettings>();
_navigationComponent.GroupId = navSettings.Groups.FirstOrDefault(x => x.Name == NavGroupName).Id;

Entity.Add(_navigationComponent);
}

/// <summary>
/// Stops the pathfinder and resets some base values
/// </summary>
public void Reset()
{
ClearGoal();
CharacterComponent.SetVelocity(Vector3.Zero);
}

/// <summary>
/// Finds a path to the targetWaypoint
/// </summary>
/// <param name="targetWaypoint"></param>
public void SetWaypoint(Vector3 targetWaypoint)
{
ClearGoal();
TargetPosition = targetWaypoint;
if (_navigationComponent.TryFindPath(TargetPosition, _waypoints))
{
_waypointIndex = 0;
HasPath = true;
}
}

/// <summary>
/// Finds a path to the targetWaypoint asynchronously
/// </summary>
/// <param name="targetWaypoint"></param>
public async Task SetWaypointAsync(Vector3 targetWaypoint)
{
ClearGoal();
TargetPosition = targetWaypoint;
var foundPath = await Task.FromResult(_navigationComponent.TryFindPath(TargetPosition, _waypoints));
if (foundPath)
{
HasPath = true;
_waypointIndex = 0;
}
}

/// <summary>
/// Moves the unit in the direction of the path created by <see cref="SetWaypoint"/> or <see cref="SetWaypointAsync"/>
/// </summary>
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();
}
}
}

/// <summary>
/// Rotates the unit in the direction of the path created by SetWaypoint or SetWaypointAsync
/// </summary>
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;
}
}
Loading

0 comments on commit d34a00a

Please sign in to comment.