diff --git a/Runtime/Actions/Audio/VelocityToSound.cs b/Runtime/Actions/Audio/VelocityToSound.cs new file mode 100644 index 0000000..61f9994 --- /dev/null +++ b/Runtime/Actions/Audio/VelocityToSound.cs @@ -0,0 +1,73 @@ + +using Godot; +using System.Collections.Generic; + +namespace Rokojori +{ + [Tool][GlobalClass] + public partial class VelocityToSound:Action + { + [Export] + public PhysicsBody3D velocitySource; + + [Export] + public AudioStreamPlayer3D target; + + [Export] + public Curve velocityToVolumeLinear; + + [Export] + public Curve velocityToPitchScale; + + [Export] + public Smoothing velocitySmoothing; + + [Export] + public float inAirMultiply = 0f; + + [Export] + public bool ensurePlaying = true; + + + + public override void _Process( double delta ) + { + if ( velocitySource == null || target == null ) + { + return; + } + + var velocity = Smoothing.Apply( velocitySmoothing, velocitySource.GetVelocity().Length(), (float) delta ); + + if ( velocitySource.IsInAir() ) + { + velocity *= inAirMultiply; + } + + + var amp = velocityToVolumeLinear == null ? 1 : velocityToVolumeLinear.Sample( velocity ); + var pitch = velocityToPitchScale == null ? 1 : velocityToPitchScale.Sample( velocity ); + + // if ( velocity == 0 ) + // { + // this.LogInfo( "Velocity == 0" ); + // } + + // if ( velocity != 0 ) + // { + // this.LogInfo( "Velocity != 0" ); + // } + + // this.LogInfo( "Vel:", (int)(velocity), "Amp:", (int)(amp * 100 ) + "%",(int) (pitch * 100) ); + + if ( ensurePlaying && amp > 0 && ! target.Playing ) + { + target.Playing = true; + } + + target.VolumeLinear = amp; + target.PitchScale = pitch; + } + } + +} \ No newline at end of file diff --git a/Runtime/Actions/Audio/VelocityToSound.cs.uid b/Runtime/Actions/Audio/VelocityToSound.cs.uid new file mode 100644 index 0000000..250679d --- /dev/null +++ b/Runtime/Actions/Audio/VelocityToSound.cs.uid @@ -0,0 +1 @@ +uid://4o4j41hwd4ro diff --git a/Runtime/Actions/Node3D/MoveTowards.cs b/Runtime/Actions/Node3D/MoveTowards.cs new file mode 100644 index 0000000..c31ba51 --- /dev/null +++ b/Runtime/Actions/Node3D/MoveTowards.cs @@ -0,0 +1,51 @@ +using Godot; + + +namespace Rokojori +{ + [Tool][GlobalClass ] + public partial class MoveTowards:Node + { + [Export] + public Node3D target; + + [Export] + public Node3D goal; + + [Export] + public float speed = 5f; + + [Export] + public Smoothing rotationSmoothing; + + [Export] + public float yDirectionMultiply = 1; + + public override void _Process( double delta ) + { + if ( Engine.IsEditorHint() ) + { + return; + } + + if ( goal == null ) + { + return; + } + + var direction = goal.GlobalPosition - target.GlobalPosition; + direction.Y *= yDirectionMultiply; + + direction = direction.Normalized() * speed * (float)delta; + + target.GlobalPosition += direction; + + // this.LogInfo( "Moving:", direction ); + + target.LookAt( goal.GlobalPosition ); + var nextRotation = target.GetGlobalQuaternion(); + var rotation = Smoothing.Apply( rotationSmoothing, nextRotation, (float) delta ); + target.SetGlobalQuaternion( rotation ); + } + } +} \ No newline at end of file diff --git a/Runtime/Actions/Node3D/MoveTowards.cs.uid b/Runtime/Actions/Node3D/MoveTowards.cs.uid new file mode 100644 index 0000000..6b2461b --- /dev/null +++ b/Runtime/Actions/Node3D/MoveTowards.cs.uid @@ -0,0 +1 @@ +uid://de6odd0f8x7ie diff --git a/Runtime/Actions/Node3D/OnCollision.cs b/Runtime/Actions/Node3D/OnCollision.cs index 4e853fc..65bf236 100644 --- a/Runtime/Actions/Node3D/OnCollision.cs +++ b/Runtime/Actions/Node3D/OnCollision.cs @@ -24,6 +24,9 @@ namespace Rokojori [Export] public Action onExit; + public readonly EventSlot onEnteredSlot = new EventSlot(); + public readonly EventSlot onInsideSlot = new EventSlot(); + public readonly EventSlot onExitSlot = new EventSlot(); Dictionary _inside = new Dictionary(); @@ -83,6 +86,7 @@ namespace Rokojori // this.LogInfo( "Selecting Enter", area.GlobalPosition, HierarchyName.Of( n ), ( (Node3D)n ).GlobalPosition ); Action.Trigger( onEntered ); + onEnteredSlot.DispatchEvent( n ); if ( onInside == null ) { @@ -96,7 +100,12 @@ namespace Rokojori return; } - var callback = ()=>{ Action.Trigger( onInside ); }; + var callback = ()=> + { + Action.Trigger( onInside ); + onInsideSlot.DispatchEvent( n ); + }; + _inside[ n ] = callback; tm.AddProcessCallback( callback ); @@ -112,7 +121,7 @@ namespace Rokojori } Action.Trigger( onExit ); - + onExitSlot.DispatchEvent( n ); if ( ! _inside.ContainsKey( n ) ) diff --git a/Runtime/Actions/Node3D/PlaySound.cs b/Runtime/Actions/Node3D/PlaySound.cs index 2b193fb..f554219 100644 --- a/Runtime/Actions/Node3D/PlaySound.cs +++ b/Runtime/Actions/Node3D/PlaySound.cs @@ -10,7 +10,10 @@ namespace Rokojori [Export] public AudioStreamPlayer3D player; + [Export] + public Duration overdrivePreventionDuration; + float _lastSoundPlayed; [ExportGroup("Randomize Playback Position")] [Export] public bool randomizePlaybackPosition = false; @@ -53,6 +56,17 @@ namespace Rokojori protected override void _OnTrigger() { + if ( overdrivePreventionDuration != null ) + { + var now = TimeLine.osTime; + var elapsed = now - _lastSoundPlayed; + + if ( elapsed < overdrivePreventionDuration.GetDurationInSeconds() ) + { + return; + } + } + var offset = 0f; var player = generatePools ? GetFreePlayer() : this.player; @@ -77,7 +91,7 @@ namespace Rokojori var start = tl.position; - TimeLineManager.ScheduleSpanIn( durationPerSound.timeLine, 0, durationPerSound.GetDurationInSeconds(), + TimeLineManager.ScheduleSpanIn( durationPerSound.timeLine, 0, durationPerSound.GetDurationInSeconds() / player.PitchScale, ( span, type )=> { var timeNow = tl.position; diff --git a/Runtime/Actions/OnTick.cs b/Runtime/Actions/OnTick.cs index b79266e..1516b95 100644 --- a/Runtime/Actions/OnTick.cs +++ b/Runtime/Actions/OnTick.cs @@ -61,13 +61,18 @@ namespace Rokojori } else { + this.LogInfo( "Removing tick:", _eventID ); + if ( _eventID == -1 ) { return; } + TimeLineManager.RemoveEvent( timeLine, _eventID ); _eventID = -1; + + } diff --git a/Runtime/Actions/Physics/SetCollisionShape.cs b/Runtime/Actions/Physics/SetCollisionShape.cs new file mode 100644 index 0000000..1997760 --- /dev/null +++ b/Runtime/Actions/Physics/SetCollisionShape.cs @@ -0,0 +1,27 @@ +using Godot; +using System.Collections; +using System.Collections.Generic; +using Godot.Collections; + +namespace Rokojori +{ + [GlobalClass,Tool] + public partial class SetCollisionShape:Action + { + [Export] + public CollisionShape3D shape3D; + + [Export] + public bool disabled = false; + + protected override void _OnTrigger() + { + if ( shape3D == null ) + { + return; + } + + shape3D.Disabled = disabled; + } + } +} \ No newline at end of file diff --git a/Runtime/Actions/Physics/SetCollisionShape.cs.uid b/Runtime/Actions/Physics/SetCollisionShape.cs.uid new file mode 100644 index 0000000..6cdc8a6 --- /dev/null +++ b/Runtime/Actions/Physics/SetCollisionShape.cs.uid @@ -0,0 +1 @@ +uid://cdspv8f8l6dgd diff --git a/Runtime/Actions/RemoveChildNodes.cs b/Runtime/Actions/RemoveChildNodes.cs new file mode 100644 index 0000000..2febb23 --- /dev/null +++ b/Runtime/Actions/RemoveChildNodes.cs @@ -0,0 +1,27 @@ + +using Godot; +using System.Collections.Generic; + +namespace Rokojori +{ + [Tool][GlobalClass] + public partial class RemoveChildNodes : Action + { + [Export] + public Node target; + + [Export] + public bool queue = true; + + protected override void _OnTrigger() + { + if ( target == null ) + { + return; + } + + target.DestroyChildren( true, queue ); + + } + } +} \ No newline at end of file diff --git a/Runtime/Actions/RemoveChildNodes.cs.uid b/Runtime/Actions/RemoveChildNodes.cs.uid new file mode 100644 index 0000000..fbdf2e6 --- /dev/null +++ b/Runtime/Actions/RemoveChildNodes.cs.uid @@ -0,0 +1 @@ +uid://c8l6m0r3ab8iw diff --git a/Runtime/Actions/UI/CountNumber.cs b/Runtime/Actions/UI/CountNumber.cs new file mode 100644 index 0000000..f4fc546 --- /dev/null +++ b/Runtime/Actions/UI/CountNumber.cs @@ -0,0 +1,66 @@ + +using Godot; + + +namespace Rokojori +{ + [Tool] + [GlobalClass ] + public partial class CountNumber : SequenceAction + { + [Export] + public UIText target; + + [Export] + public int startValue = 0; + + [Export] + public int endValue = 100; + + int _actionID = -1; + int _animationID = -1; + + [Export] + public Duration duration; + + protected override void _OnTrigger() + { + if ( target == null ) + { + return; + } + + if ( _actionID != -1 ) + { + CancelAction( _actionID ); + } + + + var _id = DispatchStart(); + + _actionID = _id; + + _animationID = TimeLineManager.ScheduleSpanWith( duration, + ( s, t ) => + { + if ( _animationID != s.id ) + { + return; + } + + var number = startValue + s.phase * ( endValue - startValue ); + var intNumber = Mathf.RoundToInt( number ); + + target.Set( intNumber + "" ); + + if ( TimeLineSpanUpdateType.End == t ) + { + target.Set( endValue + "" ); + DispatchEnd( _id ); + } + } + ).id; + + } + } +} \ No newline at end of file diff --git a/Runtime/Actions/UI/CountNumber.cs.uid b/Runtime/Actions/UI/CountNumber.cs.uid new file mode 100644 index 0000000..05529a4 --- /dev/null +++ b/Runtime/Actions/UI/CountNumber.cs.uid @@ -0,0 +1 @@ +uid://dowx1alvptsrf diff --git a/Runtime/Actions/Visual/TweenFloat.cs b/Runtime/Actions/Visual/TweenFloat.cs new file mode 100644 index 0000000..55f687c --- /dev/null +++ b/Runtime/Actions/Visual/TweenFloat.cs @@ -0,0 +1,100 @@ + +using System; +using Godot; + + +namespace Rokojori +{ + [Tool] + [GlobalClass ] + public partial class TweenFloat:SequenceAction, Animator + { + [Export] + public GodotObject target; + + [Export] + public string targetMember; + + [Export] + public float endValue; + + [Export] + public Duration duration; + + [Export] + public Curve curve; + + public void OnAnimatorStart(){} + public void OnAnimatorEnd(){} + public void OnAnimatorCancel(){} + + int _actionID = -1; + int _timeID = -1; + + protected override void _OnTrigger() + { + this.LogInfo( "Started Float Tween" ); + + + if ( Engine.IsEditorHint() ) + { + return; + } + + if ( _actionID != -1 ) + { + CancelAction( _actionID ); + } + + _actionID = DispatchStart(); + + var startValue = ReflectionHelper.GetValue( target, targetMember ); + + AnimationManager.StartAnimation( this, target, targetMember ); + + // this.LogInfo( "Get Float Tween", startValue ); + + _timeID = TimeLineManager.ScheduleSpanWith( duration, + ( span, type )=> + { + + // this.LogInfo( "Update Float Tween", startValue ); + if ( span.id != _timeID ) + { + return; + } + + if ( ! AnimationManager.IsAnimating( this, target, targetMember ) ) + { + return; + } + + var phase = span.phase; + + if ( curve != null ) + { + phase = curve.Sample( phase ); + } + + var value = Mathf.Lerp( startValue, endValue, phase ); + + // this.LogInfo( "Updating Float Tween", "phase:", phase, "value:", value, target ); + + ReflectionHelper.SetValue( target, targetMember, value ); + + if ( type == TimeLineSpanUpdateType.End ) + { + // this.LogInfo( "End Float Tween", endValue ); + + target._Set( targetMember, endValue ); + AnimationManager.EndAnimation( this, target, targetMember ); + DispatchEnd( _actionID ); + _actionID = -1; + _timeID = -1; + } + } + ).id; + } + + } +} \ No newline at end of file diff --git a/Runtime/Actions/Visual/TweenFloat.cs.uid b/Runtime/Actions/Visual/TweenFloat.cs.uid new file mode 100644 index 0000000..af1c05e --- /dev/null +++ b/Runtime/Actions/Visual/TweenFloat.cs.uid @@ -0,0 +1 @@ +uid://dm4i0s67kio5v diff --git a/Runtime/Animation/AnimationManager.cs b/Runtime/Animation/AnimationManager.cs index 78efea0..b2e746c 100644 --- a/Runtime/Animation/AnimationManager.cs +++ b/Runtime/Animation/AnimationManager.cs @@ -43,35 +43,33 @@ namespace Rokojori public class AnimationManager { - static MultiMap _animatingNodes = new MultiMap(); - static MultiMap _animatingResources = new MultiMap(); + static MultiMap _animatingObjects = new MultiMap(); - - public static Animator GetAnimator( Node node, AnimationMember member ) + public static Animator GetAnimator( GodotObject go, string memberName ) { - return _animatingNodes.Get( node, member.name ); + return _animatingObjects.Get( go, memberName ); } public static Animator GetAnimator( Material material, ShaderPropertyName propertyName ) { - return _animatingResources.Get( material, propertyName.propertyName ); + return GetAnimator( material, propertyName.propertyName ); } - public static bool IsAnimating( Animator animator, Node node, AnimationMember member ) + public static bool IsAnimating( Animator animator, GodotObject go, string memberName ) { - return GetAnimator( node, member ) == animator; + return GetAnimator( go, memberName ) == animator; } public static bool IsAnimating( Animator animator, Material material, ShaderPropertyName propertyName ) { - return GetAnimator( material, propertyName ) == animator; + return IsAnimating( animator, material, propertyName.propertyName ); } public static bool IsAnimating( Animator animator, Node node, params AnimationMember[] members ) { for ( int i = 0; i < members.Length; i++ ) { - if ( ! IsAnimating( animator, node, members[ i ] ) ) + if ( ! IsAnimating( animator, node, members[ i ].name ) ) { return false; } @@ -93,16 +91,28 @@ namespace Rokojori return nodes.Length > 0; } - public static void StartAnimation( Animator animator, Node node, AnimationMember member ) + public static void StartAnimation( Animator animator, GodotObject go, string member ) { - var activeAnimator = GetAnimator( node, member ); + var activeAnimator = GetAnimator( go, member ); if ( activeAnimator != null ) { activeAnimator.OnAnimatorCancel(); } - _animatingNodes.Set( node, member.name, animator ); + _animatingObjects.Set( go, member, animator ); + } + + public static void StartAnimation( Animator animator, Node node, AnimationMember member ) + { + var activeAnimator = GetAnimator( node, member.name ); + + if ( activeAnimator != null ) + { + activeAnimator.OnAnimatorCancel(); + } + + _animatingObjects.Set( node, member.name, animator ); } @@ -115,14 +125,14 @@ namespace Rokojori activeAnimator.OnAnimatorCancel(); } - _animatingResources.Set( material, shaderPropertyName.propertyName, animator ); + _animatingObjects.Set( material, shaderPropertyName.propertyName, animator ); } public static void StartAnimation( Animator animator, Node node, params AnimationMember[] members ) { for ( int i = 0; i < members.Length; i++ ) { - StartAnimation( animator, node, members[ i ] ); + StartAnimation( animator, node, members[ i ].name ); } } @@ -134,9 +144,20 @@ namespace Rokojori } } + public static void EndAnimation( Animator animator, GodotObject go, string member ) + { + var activeAnimator = GetAnimator( go, member ); + + if ( activeAnimator != null ) + { + activeAnimator.OnAnimatorCancel(); + } + } + + public static void EndAnimation( Animator animator, Node node, AnimationMember member ) { - var activeAnimator = GetAnimator( node, member ); + var activeAnimator = GetAnimator( node, member.name ); if ( activeAnimator != null ) { @@ -158,7 +179,7 @@ namespace Rokojori { for ( int i = 0; i < members.Length; i++ ) { - EndAnimation( animator, node, members[ i ] ); + EndAnimation( animator, node, members[ i ].name ); } } diff --git a/Runtime/Animation/Shake/Presets/Small Impact Low FPS - Shake.tres b/Runtime/Animation/Shake/Presets/Small Impact Low FPS - Shake.tres index d3b3b1e..e8dd68e 100644 --- a/Runtime/Animation/Shake/Presets/Small Impact Low FPS - Shake.tres +++ b/Runtime/Animation/Shake/Presets/Small Impact Low FPS - Shake.tres @@ -40,7 +40,7 @@ timeline = ExtResource("3_h08aw") smooth = true smoothingStrength = 0.325 positionShake = Vector3(0.075, 0, 0.075) -globalPosition = true +globalPosition = false repeatAndFlipFirstPosition = true rotationShake = Vector3(1, 1, 20) globalRotation = false diff --git a/Runtime/Animation/Transform/AnimateTransform.cs b/Runtime/Animation/Transform/AnimateTransform.cs index 78ad7dd..f0aac36 100644 --- a/Runtime/Animation/Transform/AnimateTransform.cs +++ b/Runtime/Animation/Transform/AnimateTransform.cs @@ -42,7 +42,7 @@ namespace Rokojori var start = timeline.position; var end = start + animations.GetMaxDuration( _randomizations ); - var sequenceID = DispatchStart(); + var actionID = DispatchStart(); foreach ( var c in animations.curves ) { @@ -52,6 +52,18 @@ namespace Rokojori TimeLineManager.ScheduleSpanIn( timeline, 0, duration, ( span, type )=> { + if ( actionID == -1 ) + { + return; + } + + if ( ! IsInstanceValid( target ) ) + { + DispatchCancelled( actionID ); + actionID = -1; + return; + } + var timeNow = timeline.position; var elapsed = timeNow - start; @@ -74,7 +86,7 @@ namespace Rokojori AnimationManager.EndAnimation( c, target, c.animationMember ); } - DispatchEnd( sequenceID ); + DispatchEnd( actionID ); } } ); diff --git a/Runtime/Godot/Nodes.cs b/Runtime/Godot/Nodes.cs index 61f8b62..480a887 100644 --- a/Runtime/Godot/Nodes.cs +++ b/Runtime/Godot/Nodes.cs @@ -516,12 +516,12 @@ namespace Rokojori } - public static void DestroyChildren( this Node parent, bool includeInternal = false ) + public static void DestroyChildren( this Node parent, bool includeInternal = false, bool queue = true ) { RemoveAndDeleteChildren( parent, includeInternal ); } - public static void RemoveAndDeleteChildren( Node parent, bool includeInternal = false ) + public static void RemoveAndDeleteChildren( Node parent, bool includeInternal = false, bool queue = true ) { if ( parent == null ) { @@ -534,7 +534,15 @@ namespace Rokojori { var node = parent.GetChild( i, includeInternal ); parent.RemoveChild( node ); - node.QueueFree(); + + if ( ! queue ) + { + node.Free(); + } + else + { + node.QueueFree(); + } } } @@ -547,8 +555,8 @@ namespace Rokojori parent.RemoveChild( node ); } - node.LogInfo( "Freeing queued:", queue, node); - + // node.LogInfo( "Freeing queued:", queue, node); + if ( queue ) { node.QueueFree(); diff --git a/Runtime/Interactions/CharacterController/Actions/SetCharacterControllerAction.cs b/Runtime/Interactions/CharacterController/Actions/SetCharacterControllerAction.cs new file mode 100644 index 0000000..70c7f9a --- /dev/null +++ b/Runtime/Interactions/CharacterController/Actions/SetCharacterControllerAction.cs @@ -0,0 +1,28 @@ +using Godot; +using System.Collections; +using System.Collections.Generic; +using Godot.Collections; + +namespace Rokojori +{ + [Tool] + [GlobalClass] + public partial class SetCharacterControllerAction:Action + { + [Export] + public CharacterControllerAction characterControllerAction; + + [Export] + public bool enabled = true; + + protected override void _OnTrigger() + { + if ( characterControllerAction == null ) + { + return; + } + + characterControllerAction.enabled = enabled; + } + } +} \ No newline at end of file diff --git a/Runtime/Interactions/CharacterController/Actions/SetCharacterControllerAction.cs.uid b/Runtime/Interactions/CharacterController/Actions/SetCharacterControllerAction.cs.uid new file mode 100644 index 0000000..7a38f73 --- /dev/null +++ b/Runtime/Interactions/CharacterController/Actions/SetCharacterControllerAction.cs.uid @@ -0,0 +1 @@ +uid://cuqknlygn6vxe diff --git a/Runtime/Interactions/CharacterController/AddImpactForce.cs b/Runtime/Interactions/CharacterController/AddImpactForce.cs new file mode 100644 index 0000000..f063cb3 --- /dev/null +++ b/Runtime/Interactions/CharacterController/AddImpactForce.cs @@ -0,0 +1,53 @@ +using Godot; +using System.Collections; +using System.Collections.Generic; +using Godot.Collections; + +namespace Rokojori +{ + [Tool] + [GlobalClass, Icon("res://addons/rokojori_action_library/Icons/CCGravity.svg")] + public partial class AddImpactForce:Action + { + [Export] + public ImpactForce impactForce; + + [Export] + public ImpactForces target; + + [Export] + public OnCollision collisionDirectionSource; + + protected override void _OnTrigger() + { + this.LogInfo( TimeLine.osTime ); + + if ( target == null || impactForce == null ) + { + return; + } + + var colliders = collisionDirectionSource.GetNodesInside().FilterType(); + + var force = (ImpactForce) impactForce.Duplicate(); + + if ( colliders.Count == 0 ) + { + force.direction = target.controller.body.GlobalForward() * -1; + } + else + { + var positions = colliders.Map( c => c.GlobalPosition ); + var center = Math3D.ComputeAverage( positions ); + force.direction = target.controller.body.GlobalPosition - center; + } + + force.direction = force.direction * new Vector3( 1, 0, 1 ); + + this.LogInfo( force.direction ); + target.AddImpactForce( force ); + } + + + } +} \ No newline at end of file diff --git a/Runtime/Interactions/CharacterController/AddImpactForce.cs.uid b/Runtime/Interactions/CharacterController/AddImpactForce.cs.uid new file mode 100644 index 0000000..62f24f2 --- /dev/null +++ b/Runtime/Interactions/CharacterController/AddImpactForce.cs.uid @@ -0,0 +1 @@ +uid://cd4s5qc66ber3 diff --git a/Runtime/Interactions/CharacterController/CharacterController.cs b/Runtime/Interactions/CharacterController/CharacterController.cs index 91c0371..059f549 100644 --- a/Runtime/Interactions/CharacterController/CharacterController.cs +++ b/Runtime/Interactions/CharacterController/CharacterController.cs @@ -87,7 +87,22 @@ namespace Rokojori { this.delta = (float) delta; var container = actionsContainer == null ? this : actionsContainer; - Nodes.ForEachDirectChild( container, Action.Trigger ); + Nodes.ForEachDirectChild( + container, + ( c ) => + { + if ( + ! c.enabled || + c.condition != null && ! c.condition.Evaluate() || + c.sceneCondition != null && ! c.sceneCondition.Evaluate() + ) + { + return; + } + + Action.Trigger( c ); + } + ); } } } \ No newline at end of file diff --git a/Runtime/Interactions/CharacterController/CharacterControllerAction.cs b/Runtime/Interactions/CharacterController/CharacterControllerAction.cs index 7c46a30..9126c04 100644 --- a/Runtime/Interactions/CharacterController/CharacterControllerAction.cs +++ b/Runtime/Interactions/CharacterController/CharacterControllerAction.cs @@ -9,9 +9,20 @@ namespace Rokojori [GlobalClass, Icon("res://addons/rokojori_action_library/Icons/CharacterControllerAction.svg")] public partial class CharacterControllerAction:Action { + [Export] + public bool enabled = true; + [Export] public CharacterController controller; + [Export] + public Condition condition; + + [Export] + public SceneCondition sceneCondition; + + + public CharacterBody3D body => controller.body; public void AddVelocity( Vector3 velocity ) diff --git a/Runtime/Interactions/CharacterController/ImpactForce.cs b/Runtime/Interactions/CharacterController/ImpactForce.cs new file mode 100644 index 0000000..e2bf917 --- /dev/null +++ b/Runtime/Interactions/CharacterController/ImpactForce.cs @@ -0,0 +1,32 @@ +using Godot; +using System.Collections; +using System.Collections.Generic; +using Godot.Collections; + +namespace Rokojori +{ + [Tool] + [GlobalClass, Icon("res://addons/rokojori_action_library/Icons/CCGravity.svg")] + public partial class ImpactForce:Resource + { + + [Export] + public Curve forceOverTime; + + [Export] + public float maxForce; + + [Export] + public Curve freezeOverTime; + + + + [Export] + public float duration; + + [Export] + public Vector3 direction; + + + } +} \ No newline at end of file diff --git a/Runtime/Interactions/CharacterController/ImpactForce.cs.uid b/Runtime/Interactions/CharacterController/ImpactForce.cs.uid new file mode 100644 index 0000000..e72541a --- /dev/null +++ b/Runtime/Interactions/CharacterController/ImpactForce.cs.uid @@ -0,0 +1 @@ +uid://dc5l1dqn4yi13 diff --git a/Runtime/Interactions/CharacterController/ImpactForces.cs b/Runtime/Interactions/CharacterController/ImpactForces.cs new file mode 100644 index 0000000..73736f9 --- /dev/null +++ b/Runtime/Interactions/CharacterController/ImpactForces.cs @@ -0,0 +1,70 @@ +using Godot; +using System.Collections; +using System.Collections.Generic; +using Godot.Collections; + +namespace Rokojori +{ + [Tool] + [GlobalClass, Icon("res://addons/rokojori_action_library/Icons/CCGravity.svg")] + public partial class ImpactForces:CharacterControllerAction + { + + List _forces = new List(); + List _forcePosition = new List(); + + public void AddImpactForce( ImpactForce impactForce ) + { + _forces.Add( impactForce ); + _forcePosition.Add( 0 ); + } + + + protected override void _OnTrigger() + { + if ( _forces.Count == 0 ) + { + return; + } + + + var combinedForce = Vector3.Zero; + + var removals = new List(); + + var currentVelocity = body.Velocity * controller.delta; + + var maxFreeze = 1f; + + for ( int i = 0; i < _forces.Count; i++ ) + { + + var position = _forcePosition[ i ]; + var duration = _forces[ i ].duration; + + if ( position > duration ) + { + position = duration; + removals.Add( i ); + } + + var force = _forces[ i ].forceOverTime.Sample( position ) * _forces[ i ].maxForce; + var freeze = 1f - _forces[ i ].freezeOverTime.Sample( position ); + + maxFreeze = Mathf.Min( freeze, maxFreeze ); + + combinedForce += force * _forces[ i ].direction; + + _forcePosition[ i ] += controller.delta; + + + } + + _forces.RemoveIncreasingSortedIndices( removals ); + _forcePosition.RemoveIncreasingSortedIndices( removals ); + + // this.LogInfo( combinedForce ); + SetVelocity( combinedForce + currentVelocity * maxFreeze ); + } + } +} \ No newline at end of file diff --git a/Runtime/Interactions/CharacterController/ImpactForces.cs.uid b/Runtime/Interactions/CharacterController/ImpactForces.cs.uid new file mode 100644 index 0000000..0cd984c --- /dev/null +++ b/Runtime/Interactions/CharacterController/ImpactForces.cs.uid @@ -0,0 +1 @@ +uid://cukvurx87647o diff --git a/Runtime/Interactions/CharacterController/Jump.cs b/Runtime/Interactions/CharacterController/Jump.cs index 0b12442..f04d5df 100644 --- a/Runtime/Interactions/CharacterController/Jump.cs +++ b/Runtime/Interactions/CharacterController/Jump.cs @@ -47,12 +47,18 @@ namespace Rokojori bool needsToRelease = false; bool inAir = false; - + float airStart = 0; + public bool IsJumping() { return inAir; } + public float GetAirTime() + { + return airStart == -1 ? 0 : ( TimeLine.osTime - airStart ); + } + protected override void _OnTrigger() { if ( body.IsOnFloor() ) @@ -60,6 +66,7 @@ namespace Rokojori if ( inAir ) { inAir = false; + airStart = -1; } if ( jumpPressing ) @@ -91,6 +98,7 @@ namespace Rokojori jumpPressing = true; jumpedDuration = 0; inAir = true; + airStart = TimeLine.osTime; Trigger( onJump ); diff --git a/Runtime/Math/MathX.cs b/Runtime/Math/MathX.cs index 55fb200..ec1bdc4 100644 --- a/Runtime/Math/MathX.cs +++ b/Runtime/Math/MathX.cs @@ -497,6 +497,26 @@ namespace Rokojori return Curve( y, y ); } + public static float CurveStartPosition( this Curve c ) + { + if ( c.PointCount == 0 ) + { + return 0; + } + + return c.GetPointPosition( 0 ).X; + } + + public static float CurveEndPosition( this Curve c ) + { + if ( c.PointCount == 0 ) + { + return 0; + } + + return c.GetPointPosition( c.PointCount - 1 ).X; + } + public static float CurveAngle( Curve c, float t, float samplingRange = 0.01f ) { var x0 = Mathf.Max( t - samplingRange, 0 ); diff --git a/Runtime/Physics/PhysicsBodies.cs b/Runtime/Physics/PhysicsBodies.cs new file mode 100644 index 0000000..ad1878f --- /dev/null +++ b/Runtime/Physics/PhysicsBodies.cs @@ -0,0 +1,59 @@ + +using Godot; +using System.Collections.Generic; + +namespace Rokojori +{ + public static class PhysicsBodies + { + public static bool IsInAir( this PhysicsBody3D physicsBody3D ) + { + if ( physicsBody3D is RigidBody3D rb) + { + return false; + } + + if ( physicsBody3D is CharacterBody3D cb) + { + return ! cb.IsOnFloor(); + } + + if ( physicsBody3D is PhysicalBone3D pb ) + { + return false; + } + + if ( physicsBody3D is StaticBody3D sb ) + { + return false; + } + + return false; + } + + public static Vector3 GetVelocity( this PhysicsBody3D physicsBody3D ) + { + if ( physicsBody3D is RigidBody3D rb) + { + return rb.LinearVelocity; + } + + if ( physicsBody3D is CharacterBody3D cb) + { + return cb.Velocity; + } + + if ( physicsBody3D is PhysicalBone3D pb ) + { + return pb.LinearVelocity; + } + + if ( physicsBody3D is StaticBody3D sb ) + { + return sb.ConstantLinearVelocity; + } + + return Vector3.Zero; + } + } +} \ No newline at end of file diff --git a/Runtime/Physics/PhysicsBodies.cs.uid b/Runtime/Physics/PhysicsBodies.cs.uid new file mode 100644 index 0000000..e1b0889 --- /dev/null +++ b/Runtime/Physics/PhysicsBodies.cs.uid @@ -0,0 +1 @@ +uid://dart4lmsyvlgt diff --git a/Runtime/Rendering/Compositor/CompositorEffects/GreyScale/GrayScaleShader.glsl b/Runtime/Rendering/Compositor/CompositorEffects/GreyScale/GrayScaleShader.glsl index d7af795..de7f2d0 100644 --- a/Runtime/Rendering/Compositor/CompositorEffects/GreyScale/GrayScaleShader.glsl +++ b/Runtime/Rendering/Compositor/CompositorEffects/GreyScale/GrayScaleShader.glsl @@ -7,29 +7,33 @@ layout( rgba16f, set = 0, binding = 0) uniform image2D color_image; layout(push_constant, std430) -uniform Params +uniform Parameters { vec2 raster_size; - vec2 reserved; + float amount; + float method; -} params; +} parameters; void main() { ivec2 uv = ivec2( gl_GlobalInvocationID.xy ); - ivec2 size = ivec2( params.raster_size ); + ivec2 size = ivec2( parameters.raster_size ); if ( uv.x >= size.x || uv.y >= size.y ) { - return; + return; } vec4 color = imageLoad( color_image, uv ); - float gray = color.r * 0.2125 + color.g * 0.7154 + color.b * 0.0721; + float gray = color.r * 0.2 + color.g * 0.7 + color.b * 0.1; + float avg = ( color.r + color.g + color.b ) / 3.0; + + gray = mix( gray, avg, parameters.method ); - color.rgb = vec3( gray * 0.5); + color.rgb = mix( color.rgb, vec3( gray ), parameters.amount ); imageStore( color_image, uv, color ); } \ No newline at end of file diff --git a/Runtime/Rendering/Compositor/CompositorEffects/GreyScale/GreyScaleEffect.cs b/Runtime/Rendering/Compositor/CompositorEffects/GreyScale/GreyScaleEffect.cs index 128c8af..874ee7e 100644 --- a/Runtime/Rendering/Compositor/CompositorEffects/GreyScale/GreyScaleEffect.cs +++ b/Runtime/Rendering/Compositor/CompositorEffects/GreyScale/GreyScaleEffect.cs @@ -11,6 +11,12 @@ namespace Rokojori { public static readonly string shaderPath = Path( "GreyScale/GrayScaleShader.glsl" ); + [Export( PropertyHint.Range, "0,1" )] + public float amount; + + [Export( PropertyHint.Range, "0,1" )] + public float luminanceMethod; + protected override void OnConfigure() { @@ -23,7 +29,8 @@ namespace Rokojori { constants.Set( (Vector2)context.internalSize, - Vector2.Zero + amount, + luminanceMethod ); } diff --git a/Runtime/Sensors/OnSensor.cs b/Runtime/Sensors/OnSensor.cs index e641d9b..b707e83 100644 --- a/Runtime/Sensors/OnSensor.cs +++ b/Runtime/Sensors/OnSensor.cs @@ -19,20 +19,32 @@ namespace Rokojori [Export] public Action onEnd; - [ExportGroup("Condition")] + [Export] + public bool onlyWhenNotConsumed = true; + + [Export] + public bool consumeEvent = true; + + + [ExportGroup("Condition")] [Export] public Condition condition; [Export] public SceneCondition sceneCondition; - public override void _Process( double delta) + public override void _Process( double delta ) { if ( sensor == null ) { return; } + if ( onlyWhenNotConsumed && sensor.consumed ) + { + return; + } + if ( condition != null && ! condition.Evaluate() ) { return; @@ -44,21 +56,37 @@ namespace Rokojori } - - if ( sensor.isDown ) { Action.Trigger( onStart ); + + if ( consumeEvent && onStart != null ) + { + sensor.Consume(); + return; + } } if ( sensor.isHold ) { Action.Trigger( onActive ); + + if ( consumeEvent && onActive != null ) + { + sensor.Consume(); + return; + } } if ( sensor.isUp ) { - Action.Trigger( onActive ); + Action.Trigger( onEnd ); + + if ( consumeEvent && onEnd != null ) + { + sensor.Consume(); + return; + } } } diff --git a/Runtime/Sensors/Sensor.cs b/Runtime/Sensors/Sensor.cs index d50a8cd..03935d5 100644 --- a/Runtime/Sensors/Sensor.cs +++ b/Runtime/Sensors/Sensor.cs @@ -25,6 +25,8 @@ namespace Rokojori protected float _activeTreshold = 0.5f; + protected bool _consumed = false; + public void ProcessSensor( SensorRunner runner, float delta ) { _wasActive = _active; @@ -50,6 +52,14 @@ namespace Rokojori public virtual bool WasActive( int device ) => isUp; public virtual bool IsActive( int device ) => isUp; + + public bool consumed => _consumed; + + public void Consume() + { + _consumed = true; + } + protected virtual void UpdateValue() { @@ -59,12 +69,14 @@ namespace Rokojori { _active = activeState; _value = activeState ? 1 : 0; + _consumed = false; } protected void SetFloatValue( float value ) { _value = value; _active = value > _activeTreshold; + _consumed = false; } public virtual List GetInputIcons() diff --git a/Runtime/Shading/Library/Colors.gdshaderinc b/Runtime/Shading/Library/Colors.gdshaderinc index 24d064d..149f653 100644 --- a/Runtime/Shading/Library/Colors.gdshaderinc +++ b/Runtime/Shading/Library/Colors.gdshaderinc @@ -96,12 +96,18 @@ vec3 adjustHSL( vec3 color, vec3 hslAdjustment ) } - -vec3 toLinear( vec3 sRGB ) +vec3 SRGBtoLINEAR( vec3 sRGB ) { return mix( pow( (sRGB + vec3( 0.055 )) * ( 1.0 / ( 1.0 + 0.055 )),vec3( 2.4 )),sRGB * ( 1.0 / 12.92 ),lessThan( sRGB,vec3( 0.04045 )) ); } +vec3 LINEARtoSRGB( vec3 linear ) +{ + vec3 color = linear; + const vec3 a = vec3(0.055f); + return mix((vec3(1.0f) + a) * pow(color.rgb, vec3(1.0f / 2.4f)) - a, 12.92f * color.rgb, lessThan(color.rgb, vec3(0.0031308f))); +} + float modPolarDegrees( float value ) { return mod( value + 180.0, 360.0 ) - 180.0; diff --git a/Runtime/Text/Text.cs b/Runtime/Text/Text.cs index 117bd18..dd9af19 100644 --- a/Runtime/Text/Text.cs +++ b/Runtime/Text/Text.cs @@ -8,6 +8,18 @@ namespace Rokojori { public static class Text { + public static string WithLeadingZeros( int value, int minDigits = 2 ) + { + var label = value + ""; + + while ( label.Length < minDigits ) + { + label = "0" + label; + } + + return label; + } + public static string EscapeAsPathForCommandLine( this string path ) { var escaped = path.Replace( "\"", "\\\"" ); diff --git a/Runtime/Time/Duration/Duration.cs b/Runtime/Time/Duration/Duration.cs index 6a2a7d3..1584c71 100644 --- a/Runtime/Time/Duration/Duration.cs +++ b/Runtime/Time/Duration/Duration.cs @@ -19,5 +19,17 @@ namespace Rokojori { return 0; } + + public static string GetTimerLabel( float seconds ) + { + var secondsAmount = seconds % 60; + var minutesAmount = ( seconds - secondsAmount ) / 60f; + + + var secondsLabel = Text.WithLeadingZeros( Mathf.CeilToInt( secondsAmount ) ); + var minutesLabel = Text.WithLeadingZeros( Mathf.CeilToInt( minutesAmount ) ); + + return minutesLabel + ":" + secondsLabel; + } } } \ No newline at end of file diff --git a/Runtime/Time/TImeLineManager_Scheduling.cs b/Runtime/Time/TImeLineManager_Scheduling.cs index 99b27e0..f37c5e5 100644 --- a/Runtime/Time/TImeLineManager_Scheduling.cs +++ b/Runtime/Time/TImeLineManager_Scheduling.cs @@ -12,6 +12,8 @@ namespace Rokojori { public static void RemoveEvent( TimeLine timeline, int id ) { + RJLog.Log( "Removing tick:", timeline, id ); + timeline = TimeLineManager.Ensure( timeline ); var runner = timeline.runner; @@ -38,7 +40,7 @@ namespace Rokojori return runner._ScheduleEvent( position, eventID, false, callback ); } - public static TimeLineEvent ScheduleEventIn( TimeLine timeline, float offset, Action callback ) + public static TimeLineEvent ScheduleEventIn( TimeLine timeline, float offset, Action callback ) { timeline = TimeLineManager.Ensure( timeline ); var runner = timeline.runner; @@ -48,6 +50,11 @@ namespace Rokojori return runner._ScheduleEvent( position, eventID, false, callback ); } + public static TimeLineEvent ScheduleEventWith( Duration duration, Action callback ) + { + return ScheduleEventIn( duration.timeLine, duration.GetDurationInSeconds(), callback ); + } + public static TimeLineEvent ScheduleLoopEvent( TimeLine timeline, float duration, float offset, Action callback ) { timeline = TimeLineManager.Ensure( timeline ); @@ -68,7 +75,12 @@ namespace Rokojori return runner._ScheduleSpan( start, end, spanID, false, callback ); } - public static TimeLineSpan ScheduleSpanIn( TimeLine timeline, float delay, float duration, Action callback ) + public static TimeLineSpan ScheduleSpanWith( Duration duration, Action callback ) + { + return ScheduleSpanIn( duration.timeLine, 0, duration.GetDurationInSeconds(), callback ); + } + + public static TimeLineSpan ScheduleSpanIn( TimeLine timeline, float delay, float duration, Action callback ) { timeline = TimeLineManager.Ensure( timeline ); var runner = timeline.runner; diff --git a/Runtime/Time/TimeLineRunner.cs b/Runtime/Time/TimeLineRunner.cs index f4203b9..bba6dba 100644 --- a/Runtime/Time/TimeLineRunner.cs +++ b/Runtime/Time/TimeLineRunner.cs @@ -129,11 +129,14 @@ namespace Rokojori void ProcessEvents() { - if ( requestedRemovals.Count > 0 ) + if ( requestedRemovalIDs.Count > 0 ) { - requestedRemovals.Sort(); - Lists.RemoveIncreasingSortedIndices( events, requestedRemovals ); - requestedRemovals.Clear(); + requestedRemovalIDs.Sort(); + + RJLog.Log( "Removing:", requestedRemovalIDs, "From:", events ); + //Lists.RemoveIncreasingSortedIndices( events, requestedRemovalIDs ); + events = events.Filter( e => ! requestedRemovalIDs.Contains( e.id ) ); + requestedRemovalIDs.Clear(); } List eventRemovals = null; @@ -257,11 +260,13 @@ namespace Rokojori return list; } - List requestedRemovals = new List(); + List requestedRemovalIDs = new List(); public void RemoveEvent( int eventID ) { - requestedRemovals.Add( eventID ); + requestedRemovalIDs.Add( eventID ); + + RJLog.Log( "requestedRemovals add:", eventID ); } public TimeLineCallback _ScheduleCallback( Action callback, int eventID ) diff --git a/Runtime/Tools/Lists.cs b/Runtime/Tools/Lists.cs index 9ecb5fc..6979de4 100644 --- a/Runtime/Tools/Lists.cs +++ b/Runtime/Tools/Lists.cs @@ -515,11 +515,19 @@ namespace Rokojori return list[ list.Count - 1 - index ]; } - public static void RemoveIncreasingSortedIndices( List list, List increasinglySortedRemovals ) + public static void RemoveIncreasingSortedIndices( this List list, List increasinglySortedRemovals ) { for ( var i = increasinglySortedRemovals.Count - 1; i >= 0; i-- ) { var index = increasinglySortedRemovals[ i ]; + + if ( index < 0 || index >= list.Count ) + { + // RJLog.Log( "Invalid index in removals:", index, list.Count, increasinglySortedRemovals ); + continue; + } + + // RJLog.Log( "requestedRemovals remove:", index ); list.RemoveAt( index ); } } diff --git a/Runtime/Tools/ReflectionHelper.cs b/Runtime/Tools/ReflectionHelper.cs index 1be7002..4bfcb3e 100644 --- a/Runtime/Tools/ReflectionHelper.cs +++ b/Runtime/Tools/ReflectionHelper.cs @@ -336,6 +336,22 @@ namespace Rokojori return list; } + public static T GetValue( object instance, string name ) + { + var mi = GetDataMemberInfo( instance, name ); + + var value = GetDataMemberValue( instance, mi ); + + return value; + } + + public static void SetValue( object instance, string name, T value ) + { + var mi = GetDataMemberInfo( instance, name ); + + SetDataMemberValue( instance, mi, value ); + } + public static List GetDataMemberValues( object instance ) { var infos = GetDataMemberInfos( instance ); @@ -408,6 +424,21 @@ namespace Rokojori return default(T); } + public static void SetDataMemberValue( object instance, MemberInfo info, T value ) + { + + if ( info is FieldInfo fieldInfo ) + { + fieldInfo.SetValue( instance, value ); + } + + if ( info is PropertyInfo propertyInfo ) + { + propertyInfo.SetValue( instance, value ); + } + + } + public static T GetDataMemberValue( object instance, string memberName, BindingFlags flags = ReflectionHelper.defaultBindings ) { return GetDataMemberValue( instance, GetDataMemberInfo( instance, memberName, flags ) ); diff --git a/Runtime/UI/Nodes/UIText.cs b/Runtime/UI/Nodes/UIText.cs index bee5d2f..b39f657 100644 --- a/Runtime/UI/Nodes/UIText.cs +++ b/Runtime/UI/Nodes/UIText.cs @@ -21,6 +21,11 @@ namespace Rokojori set { _locale = value; UpdateLocalization(); } } + public void Set( string value ) + { + locale = LocaleText.Create( value ); + } + [Export] public bool refreshText { diff --git a/Runtime/VirtualCameras/Effects/PlayCameraEffect.cs b/Runtime/VirtualCameras/Effects/PlayCameraEffect.cs index 4302604..03c09ca 100644 --- a/Runtime/VirtualCameras/Effects/PlayCameraEffect.cs +++ b/Runtime/VirtualCameras/Effects/PlayCameraEffect.cs @@ -56,7 +56,7 @@ namespace Rokojori if ( resolvedSlot == null ) { - // this.LogError( "No camera slot found" ); + this.LogError( "No camera slot found" ); return; } diff --git a/Runtime/VirtualCameras/VirtualCamera3DManager.cs b/Runtime/VirtualCameras/VirtualCamera3DManager.cs index 37a11ae..37f338a 100644 --- a/Runtime/VirtualCameras/VirtualCamera3DManager.cs +++ b/Runtime/VirtualCameras/VirtualCamera3DManager.cs @@ -185,7 +185,7 @@ namespace Rokojori return; } - SetSingleCamera( cam ); + SetSingleCamera( cam, delta ); } else @@ -194,8 +194,10 @@ namespace Rokojori } } - void SetSingleCamera( VirtualCamera3DSlot c ) + void SetSingleCamera( VirtualCamera3DSlot c, double delta ) { + c.Update( delta, this ); + var rotation = c.GetCameraRotation(); if ( ! rotation.IsFinite() || rotation.Length() == 0 ) diff --git a/Runtime/VirtualCameras/VirtualCamera3DSlot.cs b/Runtime/VirtualCameras/VirtualCamera3DSlot.cs index 84d308c..e9f9794 100644 --- a/Runtime/VirtualCameras/VirtualCamera3DSlot.cs +++ b/Runtime/VirtualCameras/VirtualCamera3DSlot.cs @@ -36,11 +36,15 @@ namespace Rokojori { if ( cameraEffectRunner.isFinished ) { + this.LogInfo( "finished" ); cameraEffectRunner = null; + } else { + this.LogInfo( "time:", cameraEffectRunner.timePosition, cameraEffectRunner.effect.maxDuration ); cameraEffectRunner.Update(); + } } @@ -69,6 +73,8 @@ namespace Rokojori var offset = camera.GetGlobalOffset( cameraEffectRunner.position ); + this.LogInfo( "Cam Off", offset ); + return camera.GetCameraPosition() + offset; }