Pointer, Particles, Numbers
This commit is contained in:
parent
4bbed6ba6c
commit
f472d031a8
|
|
@ -13,6 +13,9 @@ namespace Rokojori
|
|||
[Export]
|
||||
public string message;
|
||||
|
||||
[Export]
|
||||
public Node node;
|
||||
|
||||
[Export]
|
||||
public bool printPath;
|
||||
|
||||
|
|
@ -20,11 +23,27 @@ namespace Rokojori
|
|||
{
|
||||
if ( printPath )
|
||||
{
|
||||
this.LogInfo( message );
|
||||
if ( node != null )
|
||||
{
|
||||
this.LogInfo( message, node );
|
||||
}
|
||||
else
|
||||
{
|
||||
this.LogInfo( message );
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
RJLog.Log( message );
|
||||
if ( node != null )
|
||||
{
|
||||
RJLog.Log( message, node );
|
||||
}
|
||||
else
|
||||
{
|
||||
RJLog.Log( message );
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@ namespace Rokojori
|
|||
[Export]
|
||||
public Node3D lookTarget;
|
||||
|
||||
[Export]
|
||||
public bool useModelFront = false;
|
||||
|
||||
protected override void _OnTrigger()
|
||||
{
|
||||
|
|
@ -21,7 +23,7 @@ namespace Rokojori
|
|||
return;
|
||||
}
|
||||
|
||||
lookFrom.LookAt( lookTarget.GlobalPosition );
|
||||
lookFrom.LookAt( lookTarget.GlobalPosition, Vector3.Up, useModelFront );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
|
||||
using Godot;
|
||||
|
||||
|
||||
using Rokojori.Extensions;
|
||||
namespace Rokojori
|
||||
{
|
||||
[GlobalClass, Tool ]
|
||||
public partial class SetNode3D : Action
|
||||
{
|
||||
[Export]
|
||||
public Node source;
|
||||
|
||||
[Export]
|
||||
public string sourceMember;
|
||||
|
||||
|
||||
[Export]
|
||||
public Node target;
|
||||
|
||||
[Export]
|
||||
public string targetMember;
|
||||
|
||||
|
||||
|
||||
protected override void _OnTrigger()
|
||||
{
|
||||
if ( target == null || source == null )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var value = ReflectionHelper.GetValue<Node3D>( source, sourceMember );
|
||||
|
||||
ReflectionHelper.SetValue<Node3D>( target, targetMember, value );
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
uid://dnx8jc8lsfwoi
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
|
||||
using Godot;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
using Rokojori.Extensions;
|
||||
namespace Rokojori;
|
||||
|
||||
[Tool]
|
||||
[GlobalClass, Icon("res://addons/rokojori_action_library/Icons/Action.svg")]
|
||||
public partial class NumberMath:Action
|
||||
{
|
||||
public enum Operator
|
||||
{
|
||||
Set,
|
||||
Add,
|
||||
Substract,
|
||||
Multiply,
|
||||
Divide,
|
||||
Modulo,
|
||||
Freeze,
|
||||
Unfreeze,
|
||||
Reset
|
||||
}
|
||||
|
||||
[Export]
|
||||
public NumberValue target;
|
||||
|
||||
[Export]
|
||||
public Operator operation = Operator.Set;
|
||||
|
||||
public enum Order
|
||||
{
|
||||
Target_Op_Source,
|
||||
Source_Op_Target
|
||||
}
|
||||
|
||||
[Export]
|
||||
public Order order = Order.Target_Op_Source;
|
||||
|
||||
[Export]
|
||||
public double sourceValue;
|
||||
|
||||
[Export]
|
||||
public NumberValue sourceReference;
|
||||
|
||||
protected override void _OnTrigger()
|
||||
{
|
||||
var valueRight = ( sourceReference == null ? 0 : sourceReference.value ) + sourceValue;
|
||||
var valueLeft = target.value;
|
||||
|
||||
if ( order == Order.Source_Op_Target )
|
||||
{
|
||||
var buffer = valueLeft;
|
||||
valueLeft = valueRight;
|
||||
valueRight = buffer;
|
||||
}
|
||||
|
||||
switch ( operation )
|
||||
{
|
||||
case Operator.Set: { target.value = valueRight; } break;
|
||||
case Operator.Add: { target.value = valueLeft + valueRight; } break;
|
||||
case Operator.Substract: { target.value = valueLeft - valueRight; } break;
|
||||
case Operator.Multiply: { target.value = valueLeft * valueRight; } break;
|
||||
case Operator.Divide: { target.value = valueLeft / valueRight; } break;
|
||||
case Operator.Modulo: { target.value = valueLeft % valueRight; } break;
|
||||
case Operator.Freeze: { target.FreezeValue(); } break;
|
||||
case Operator.Unfreeze: { target.UnfreezeValue(); } break;
|
||||
case Operator.Reset: { target.ResetValue(); } break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
uid://b7uiy1j04s3fu
|
||||
|
|
@ -0,0 +1,134 @@
|
|||
|
||||
using Godot;
|
||||
|
||||
|
||||
using Rokojori.Extensions;
|
||||
namespace Rokojori;
|
||||
|
||||
[Tool][GlobalClass]
|
||||
public partial class NumberValue : Node
|
||||
{
|
||||
protected double _value = 0;
|
||||
protected bool _frozen;
|
||||
|
||||
[Export]
|
||||
public double value
|
||||
{
|
||||
get => _value;
|
||||
set { _OnValueChange( value ); }
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return GetType().Name + " { value = " + _value._DDD() + ", frozen = " + _frozen + " }";
|
||||
}
|
||||
|
||||
void _OnValueChange( double v )
|
||||
{
|
||||
if ( _frozen )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var setterValue = Mathf.Clamp( v, minValue, maxValue );
|
||||
|
||||
if ( _value == setterValue )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var before = _value;
|
||||
_value = setterValue;
|
||||
|
||||
var belowLowTreshold = before < lowTreshold;
|
||||
var aboveHighTreshold = before > highTreshold;
|
||||
|
||||
onChange?.Trigger();
|
||||
|
||||
if ( ! belowLowTreshold && _value < lowTreshold )
|
||||
{
|
||||
onBelowLowTreshold?.Trigger();
|
||||
}
|
||||
|
||||
if ( ! aboveHighTreshold && _value > highTreshold )
|
||||
{
|
||||
onAboveHighTreshold?.Trigger();
|
||||
}
|
||||
|
||||
if ( _value == minValue )
|
||||
{
|
||||
onMin?.Trigger();
|
||||
|
||||
if ( autoFreezeOnMin )
|
||||
{
|
||||
_frozen = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ( _value == maxValue )
|
||||
{
|
||||
onMax?.Trigger();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void FreezeValue()
|
||||
{
|
||||
this._frozen = true;
|
||||
}
|
||||
|
||||
public void UnfreezeValue()
|
||||
{
|
||||
this._frozen = false;
|
||||
}
|
||||
|
||||
public void ResetValue()
|
||||
{
|
||||
this._frozen = false;
|
||||
this._value = resetValue;
|
||||
}
|
||||
|
||||
[Export]
|
||||
public double resetValue = 100;
|
||||
|
||||
[Export]
|
||||
public double minValue = 0;
|
||||
|
||||
[Export]
|
||||
public double maxValue = 125;
|
||||
|
||||
[Export]
|
||||
public double lowTreshold = 25;
|
||||
|
||||
[Export]
|
||||
public double highTreshold = 75;
|
||||
|
||||
[Export]
|
||||
public bool autoFreezeOnMin = true;
|
||||
|
||||
|
||||
[Export]
|
||||
public Action onChange;
|
||||
|
||||
[Export]
|
||||
public Action onIncrement;
|
||||
|
||||
[Export]
|
||||
public Action onDecrement;
|
||||
|
||||
[Export]
|
||||
public Action onMin;
|
||||
|
||||
[Export]
|
||||
public Action onMax;
|
||||
|
||||
[Export]
|
||||
public Action onBelowLowTreshold;
|
||||
|
||||
[Export]
|
||||
public Action onAboveHighTreshold;
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1 @@
|
|||
uid://bg8bavpgdo0r6
|
||||
|
|
@ -0,0 +1,131 @@
|
|||
using Godot;
|
||||
|
||||
using Rokojori.Extensions;
|
||||
namespace Rokojori
|
||||
{
|
||||
[Tool]
|
||||
[GlobalClass]
|
||||
public partial class LockOnCamera : VirtualCamera
|
||||
{
|
||||
[Export]
|
||||
public Node3D playerTarget;
|
||||
|
||||
[Export]
|
||||
public LockOnPointer source;
|
||||
|
||||
[Export]
|
||||
public Node3D lockTarget;
|
||||
|
||||
[Export]
|
||||
public LockOnCameraSettings settings;
|
||||
|
||||
[Export]
|
||||
public CharacterController.CharacterUpdateMode updateMode = CharacterController.CharacterUpdateMode.Physics_Process;
|
||||
|
||||
[Export]
|
||||
public bool processInEditor = false;
|
||||
|
||||
public bool canProcess => processInEditor || ! Engine.IsEditorHint();
|
||||
|
||||
public void SetLockTarget( Node3D target )
|
||||
{
|
||||
lockTarget = target;
|
||||
}
|
||||
|
||||
Node3D CurrentLockTarget()
|
||||
{
|
||||
if ( source != null && source.pointable != null )
|
||||
{
|
||||
return source.pointable;
|
||||
}
|
||||
|
||||
return lockTarget;
|
||||
}
|
||||
|
||||
public override void _Process( double delta )
|
||||
{
|
||||
if ( canProcess && CharacterController.CharacterUpdateMode.Process == updateMode )
|
||||
{
|
||||
_ProcessCamera( delta );
|
||||
}
|
||||
}
|
||||
|
||||
public override void _PhysicsProcess( double delta )
|
||||
{
|
||||
if ( canProcess && CharacterController.CharacterUpdateMode.Physics_Process == updateMode )
|
||||
{
|
||||
_ProcessCamera( delta );
|
||||
}
|
||||
}
|
||||
|
||||
void _ProcessCamera( double gameDelta )
|
||||
{
|
||||
if ( playerTarget == null || settings == null )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var delta = (float) gameDelta;
|
||||
var target = CurrentLockTarget();
|
||||
|
||||
var playerPosition = Smoothing.Apply( settings.playerFollowSmoothing, playerTarget.GlobalPosition, delta );
|
||||
|
||||
Vector3 axisDirection;
|
||||
Vector3 lookAtPoint;
|
||||
|
||||
if ( target != null )
|
||||
{
|
||||
var targetPosition = Smoothing.Apply( settings.targetFollowSmoothing, target.GlobalPosition, delta );
|
||||
var offset = targetPosition - playerPosition;
|
||||
var flat = new Vector3( offset.X, 0, offset.Z );
|
||||
|
||||
axisDirection = flat.LengthSquared() > 0.0001f ? flat.Normalized() : -playerTarget.GlobalTransform.Basis.Z;
|
||||
lookAtPoint = playerPosition.Lerp( targetPosition, settings.lookAtTargetWeight );
|
||||
}
|
||||
else
|
||||
{
|
||||
axisDirection = -playerTarget.GlobalTransform.Basis.Z;
|
||||
lookAtPoint = playerPosition;
|
||||
}
|
||||
|
||||
var side = axisDirection.Cross( Vector3.Up );
|
||||
|
||||
var desiredPosition = playerPosition - axisDirection * settings.distance + Vector3.Up * settings.height + side * settings.sideOffset;
|
||||
|
||||
cameraPosition = Smoothing.Apply( settings.positionSmoothing, desiredPosition, delta );
|
||||
|
||||
lookAtPoint.Y += settings.lookAtHeight;
|
||||
|
||||
cameraForward = ( lookAtPoint - cameraPosition ).Normalized();
|
||||
|
||||
var basis = Basis.LookingAt( cameraForward, Vector3.Up, true );
|
||||
cameraRotation = new Quaternion( basis );
|
||||
|
||||
FOV = settings.fov;
|
||||
}
|
||||
|
||||
public Vector3 cameraPosition;
|
||||
|
||||
public override Vector3 GetCameraPosition()
|
||||
{
|
||||
return cameraPosition;
|
||||
}
|
||||
|
||||
public Vector3 cameraForward;
|
||||
|
||||
public Quaternion cameraRotation;
|
||||
|
||||
public override Quaternion GetCameraRotation()
|
||||
{
|
||||
return cameraRotation;
|
||||
}
|
||||
|
||||
[Export]
|
||||
public float FOV = 60f;
|
||||
|
||||
public override float GetCameraFOV()
|
||||
{
|
||||
return FOV;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
uid://b5o54ra4cgj2k
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
using Godot;
|
||||
|
||||
using Rokojori.Extensions;
|
||||
namespace Rokojori
|
||||
{
|
||||
[Tool]
|
||||
[GlobalClass]
|
||||
public partial class LockOnCameraSettings : Resource
|
||||
{
|
||||
[Export]
|
||||
public float distance = 6f;
|
||||
|
||||
[Export]
|
||||
public float height = 2f;
|
||||
|
||||
[Export]
|
||||
public float sideOffset = 0f;
|
||||
|
||||
[ExportGroup( "Look At" )]
|
||||
[Export(PropertyHint.Range, "0,1")]
|
||||
public float lookAtTargetWeight = 0.5f;
|
||||
|
||||
[Export]
|
||||
public float lookAtHeight = 0f;
|
||||
|
||||
[ExportGroup( "FOV" )]
|
||||
[Export]
|
||||
public float fov = 60f;
|
||||
|
||||
[ExportGroup( "Smoothing" )]
|
||||
[Export]
|
||||
public Smoothing playerFollowSmoothing = new FrameSmoothing();
|
||||
|
||||
[Export]
|
||||
public Smoothing targetFollowSmoothing = new FrameSmoothing();
|
||||
|
||||
[Export]
|
||||
public Smoothing positionSmoothing = new FrameSmoothing();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
uid://cqa8ktsmoa041
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
using Godot;
|
||||
|
||||
using Rokojori.Extensions;
|
||||
namespace Rokojori
|
||||
{
|
||||
public enum ThirdPersonCameraLockMode
|
||||
{
|
||||
Lock,
|
||||
Release
|
||||
}
|
||||
|
||||
[Tool]
|
||||
[GlobalClass]
|
||||
public partial class SetThirdPersonCameraLockTarget : Action
|
||||
{
|
||||
[Export]
|
||||
public ThirdPersonCamera camera;
|
||||
|
||||
[Export]
|
||||
public Pointer source;
|
||||
|
||||
[Export]
|
||||
public ThirdPersonCameraLockMode mode = ThirdPersonCameraLockMode.Lock;
|
||||
|
||||
protected override void _OnTrigger()
|
||||
{
|
||||
if ( camera == null )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if ( mode == ThirdPersonCameraLockMode.Release )
|
||||
{
|
||||
camera.lockTarget = null;
|
||||
return;
|
||||
}
|
||||
|
||||
camera.lockTarget = source != null && source.pointable != null ? source.pointable.pointerPosition : null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
uid://pesdcfjnpi30
|
||||
|
|
@ -15,7 +15,10 @@ namespace Rokojori
|
|||
{
|
||||
[Export]
|
||||
public Node3D target;
|
||||
|
||||
|
||||
[Export]
|
||||
public Node3D lockTarget;
|
||||
|
||||
[Export]
|
||||
public float yaw;
|
||||
|
||||
|
|
@ -35,6 +38,8 @@ namespace Rokojori
|
|||
|
||||
public bool canProcess => processInEditor || ! Engine.IsEditorHint();
|
||||
|
||||
Vector3 _lastLockTargetPosition;
|
||||
|
||||
public override void _Process( double delta )
|
||||
{
|
||||
if ( canProcess && CharacterController.CharacterUpdateMode.Process == updateMode )
|
||||
|
|
@ -71,6 +76,30 @@ namespace Rokojori
|
|||
|
||||
var targetPosition = Smoothing.Apply( settings.targetFollowSmoothing, target.GlobalPosition, delta );
|
||||
|
||||
var hasLockTarget = lockTarget != null;
|
||||
var lockTargetPosition = _lastLockTargetPosition;
|
||||
|
||||
if ( hasLockTarget )
|
||||
{
|
||||
lockTargetPosition = Smoothing.Apply( settings.lockTargetFollowSmoothing, lockTarget.GlobalPosition, delta );
|
||||
_lastLockTargetPosition = lockTargetPosition;
|
||||
|
||||
if ( settings.lockReleaseDistance > 0f && ( lockTargetPosition - targetPosition ).Length() > settings.lockReleaseDistance )
|
||||
{
|
||||
lockTarget = null;
|
||||
hasLockTarget = false;
|
||||
}
|
||||
}
|
||||
|
||||
// lockBlend fades 0->1 on engage and 1->0 on release instead of snapping, so the focus
|
||||
// point, yaw override, pitch clamp and bounding-distance below all ease in/out together -
|
||||
// lockTargetPosition keeps the last known value through the fade-out tail even after
|
||||
// lockTarget itself has gone null.
|
||||
var lockBlend = Smoothing.Apply( settings.lockBlendSmoothing, hasLockTarget ? 1f : 0f, delta );
|
||||
var isLocked = lockBlend > 0.001f;
|
||||
|
||||
var focusPosition = targetPosition.Lerp( ( targetPosition + lockTargetPosition ) / 2f, lockBlend );
|
||||
|
||||
var yawAxis = 0f;
|
||||
var pitchAxis = 0f;
|
||||
|
||||
|
|
@ -80,20 +109,38 @@ namespace Rokojori
|
|||
pitchAxis = Sensors.PolarPowerAxis( data.pitchNegativeAxis, data.pitchPositiveAxis, 1f, data.pitchDeadZone, data.pitchPower );
|
||||
}
|
||||
|
||||
// this.LogInfo( "YAW:", yawAxis, "PITCH:", pitchAxis );
|
||||
yaw += yawAxis * settings.yawSpeed * data.yawSpeed * delta;
|
||||
|
||||
// this.LogInfo( "YAW:", yawAxis, "PITCH:", pitchAxis );
|
||||
if ( isLocked )
|
||||
{
|
||||
var lockDirection = lockTargetPosition - targetPosition;
|
||||
lockDirection.Y = 0;
|
||||
|
||||
if ( lockDirection.LengthSquared() > 0.0001f )
|
||||
{
|
||||
yaw = Math3D.GlobalYawDegrees( lockDirection );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
yaw += yawAxis * settings.yawSpeed * data.yawSpeed * delta;
|
||||
}
|
||||
|
||||
|
||||
if ( data.pitchIsRelative )
|
||||
{
|
||||
pitch += pitchAxis * settings.relativePitchSpeed * data.relativePitchSpeed * delta;
|
||||
pitch += pitchAxis * settings.relativePitchSpeed * data.relativePitchSpeed * delta;
|
||||
pitch = Mathf.Clamp( pitch, settings.minPitch, settings.maxPitch );
|
||||
}
|
||||
else
|
||||
{
|
||||
{
|
||||
pitch = Mathf.Remap( pitchAxis, -1, 1, settings.minPitch, settings.maxPitch );
|
||||
}
|
||||
|
||||
if ( isLocked )
|
||||
{
|
||||
pitch = Mathf.Clamp( pitch, settings.lockMinPitch, settings.lockMaxPitch );
|
||||
}
|
||||
|
||||
var appliedYaw = yaw;
|
||||
|
||||
var smoothedYaw = Smoothing.ApplyDegrees( settings.yawSmoothing, appliedYaw, delta );
|
||||
|
|
@ -103,16 +150,35 @@ namespace Rokojori
|
|||
|
||||
|
||||
var pitchState = MathX.NormalizeClamped( pitch, settings.minPitch, settings.maxPitch );
|
||||
var distance = settings.distanceForPitch.Sample( pitchState ) * settings.distanceScale;
|
||||
|
||||
var smoothedDistance = Smoothing.Apply( settings.distanceSmoothing, distance, delta );
|
||||
FOV = settings.fovOffset + ( settings.fovForPitch?.Sample( pitchState ) ?? 0f );
|
||||
FOV = settings.fovOffset + ( settings.fovForPitch?.Sample( pitchState ) ?? 0f );
|
||||
|
||||
var desiredDistance = settings.distanceForPitch.Sample( pitchState ) * settings.distanceScale;
|
||||
|
||||
if ( isLocked )
|
||||
{
|
||||
// The camera always looks exactly at focusPosition (cameraForward is -fowardDirection by
|
||||
// construction below), so fitting a bounding sphere around target+lockTarget in the view
|
||||
// cone is pitch-independent - unlike pulling back along a single axis, it cannot
|
||||
// degenerate when the view direction is near-vertical. radius/sin(halfFov) is the
|
||||
// standard distance that makes a sphere of that radius exactly fill the frame.
|
||||
var boundingRadius = ( lockTargetPosition - targetPosition ).Length() * 0.5f + settings.lockBoundsPadding;
|
||||
var halfFovRadians = Mathf.DegToRad( FOV * 0.5f );
|
||||
|
||||
if ( boundingRadius > 0f && halfFovRadians > 0.001f )
|
||||
{
|
||||
var minDistanceForBounds = boundingRadius / Mathf.Sin( halfFovRadians );
|
||||
desiredDistance = Mathf.Lerp( desiredDistance, Mathf.Max( desiredDistance, minDistanceForBounds ), lockBlend );
|
||||
}
|
||||
}
|
||||
|
||||
var smoothedDistance = Smoothing.Apply( settings.distanceSmoothing, desiredDistance, delta );
|
||||
|
||||
var fowardDirection = Math3D.YawPitchRotation( smoothedYaw, smoothedPitch ) * Vector3.Forward;
|
||||
|
||||
cameraPosition = targetPosition + fowardDirection * smoothedDistance;
|
||||
cameraPosition = focusPosition + fowardDirection * smoothedDistance;
|
||||
|
||||
cameraForward = ( targetPosition - cameraPosition ).Normalized();
|
||||
cameraForward = ( focusPosition - cameraPosition ).Normalized();
|
||||
// LookAt( targetPosition, Vector3.Up, true );
|
||||
|
||||
var pitchOffset = settings.offset;
|
||||
|
|
|
|||
|
|
@ -68,11 +68,32 @@ namespace Rokojori
|
|||
|
||||
[Export]
|
||||
public Curve fovForPitch = MathX.Curve( 0, 0 ).WithValueRange( -10, 10 );
|
||||
|
||||
|
||||
[Export]
|
||||
public float fovOffset = 60;
|
||||
|
||||
|
||||
[ExportGroup("Lock")]
|
||||
|
||||
[Export]
|
||||
public Smoothing lockTargetFollowSmoothing = new FrameSmoothing();
|
||||
|
||||
[Export]
|
||||
public Smoothing lockBlendSmoothing = new FrameSmoothing();
|
||||
|
||||
[Export]
|
||||
public float lockReleaseDistance = 0f;
|
||||
|
||||
[Export]
|
||||
public float lockMinPitch = -10;
|
||||
|
||||
[Export]
|
||||
public float lockMaxPitch = 45;
|
||||
|
||||
[Export]
|
||||
public float lockBoundsPadding = 0f;
|
||||
|
||||
|
||||
public float GetNormalizedPitch( float pitch )
|
||||
{
|
||||
return MathX.Normalize( pitch, minPitch, maxPitch );
|
||||
|
|
|
|||
|
|
@ -51,6 +51,13 @@ namespace Rokojori
|
|||
[Export]
|
||||
public CharacterMovementType[] deviceMovementTypes = [];
|
||||
|
||||
[ExportGroup( "Manual Movement Type Switch" )]
|
||||
[Export]
|
||||
public Sensor movementTypeSwitch;
|
||||
|
||||
[Export]
|
||||
public CharacterMovementType switchedMovementType;
|
||||
|
||||
[Export]
|
||||
public Smoothing onFloorMovementSmoothing = new FrameSmoothing();
|
||||
|
||||
|
|
@ -119,6 +126,11 @@ namespace Rokojori
|
|||
|
||||
currentMovementType = deviceMovementTypes.FindNonNull( d => d.deviceSelector.Selects( activeDevice ) );
|
||||
|
||||
if ( movementTypeSwitch != null && movementTypeSwitch.isActive && switchedMovementType != null )
|
||||
{
|
||||
currentMovementType = switchedMovementType;
|
||||
}
|
||||
|
||||
if ( currentMovementType == null )
|
||||
{
|
||||
// this.LogInfo( "No device found:", activeDevice == null ? "null" : activeDevice.GetType().Name );
|
||||
|
|
|
|||
|
|
@ -0,0 +1,16 @@
|
|||
using Godot;
|
||||
|
||||
using Rokojori.Extensions;
|
||||
namespace Rokojori
|
||||
{
|
||||
[Tool]
|
||||
[GlobalClass]
|
||||
public partial class DirectionalCycleInput : Resource
|
||||
{
|
||||
[Export]
|
||||
public Sensor sensor;
|
||||
|
||||
[Export]
|
||||
public Vector2 direction = Vector2.Right;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
uid://dc6l8lylcqdtv
|
||||
|
|
@ -0,0 +1,289 @@
|
|||
using Godot;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using Rokojori.Extensions;
|
||||
namespace Rokojori
|
||||
{
|
||||
public enum LockOnCycleMode
|
||||
{
|
||||
None,
|
||||
ClosestCycle,
|
||||
Directional
|
||||
}
|
||||
|
||||
[Tool]
|
||||
[GlobalClass]
|
||||
public partial class LockOnPointer : Pointer
|
||||
{
|
||||
[Export]
|
||||
public Sensor engageSensor;
|
||||
|
||||
[Export]
|
||||
public bool releaseWhenTargetInvalid = true;
|
||||
|
||||
[Export]
|
||||
public Action onLockEngaged;
|
||||
|
||||
[Export]
|
||||
public Action onLockReleased;
|
||||
|
||||
[ExportGroup( "Cycling" )]
|
||||
[Export]
|
||||
public LockOnCycleMode cycleMode = LockOnCycleMode.None;
|
||||
|
||||
[Export]
|
||||
public Sensor cycleSensor;
|
||||
|
||||
[Export]
|
||||
public DirectionalCycleInput[] directionalCycleInputs = [];
|
||||
|
||||
[Export]
|
||||
public Node3D closestVisualizer;
|
||||
|
||||
[Export]
|
||||
public Action onClosestFound;
|
||||
|
||||
[Export]
|
||||
public Action onClosestLost;
|
||||
|
||||
[Export]
|
||||
public Action onClosestChanged;
|
||||
|
||||
[ExportGroup("Read Only")]
|
||||
[Export]
|
||||
public Pointable closestCandidate;
|
||||
|
||||
|
||||
bool _engaged = false;
|
||||
|
||||
public bool isEngaged => _engaged;
|
||||
|
||||
protected override void _OnTrigger()
|
||||
{
|
||||
if ( caster == null )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
UpdateClosestCandidate();
|
||||
|
||||
var wasLocked = pointable != null;
|
||||
|
||||
if ( visualizer != null && pointable != null )
|
||||
{
|
||||
visualizer.CopyGlobalPoseFrom( pointable.pointerPosition );
|
||||
}
|
||||
|
||||
if ( engageSensor != null )
|
||||
{
|
||||
_engaged = engageSensor.isActive;
|
||||
|
||||
if ( ! _engaged )
|
||||
{
|
||||
if ( pointable != null )
|
||||
{
|
||||
SetPointable( null );
|
||||
}
|
||||
}
|
||||
else if ( pointable == null )
|
||||
{
|
||||
SetPointable( closestCandidate );
|
||||
}
|
||||
else if ( releaseWhenTargetInvalid && ! IsStillValidTarget( pointable ) )
|
||||
{
|
||||
SetPointable( null );
|
||||
}
|
||||
else
|
||||
{
|
||||
HandleCycling();
|
||||
}
|
||||
}
|
||||
|
||||
// Fires off the pointable transition (not the raw engage-sensor edge) so it also covers the
|
||||
// releaseWhenTargetInvalid auto-release path above, and so pointable already holds its new
|
||||
// value (or null) by the time a listener - e.g. SetThirdPersonCameraLockTarget - reads it.
|
||||
var isNowLocked = pointable != null;
|
||||
|
||||
if ( isNowLocked != wasLocked )
|
||||
{
|
||||
Action.TriggerSafe( isNowLocked ? onLockEngaged : onLockReleased );
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateClosestCandidate()
|
||||
{
|
||||
var next = FindClosestCandidate();
|
||||
|
||||
if ( next == closestCandidate )
|
||||
{
|
||||
if ( closestVisualizer != null && closestCandidate != null )
|
||||
{
|
||||
closestVisualizer.CopyGlobalPoseFrom( closestCandidate.pointerPosition );
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
closestCandidate = next;
|
||||
|
||||
onClosestChanged?.Trigger();
|
||||
|
||||
if ( closestCandidate == null )
|
||||
{
|
||||
onClosestLost?.Trigger();
|
||||
}
|
||||
else
|
||||
{
|
||||
onClosestFound?.Trigger();
|
||||
}
|
||||
|
||||
if ( closestVisualizer != null && closestCandidate != null )
|
||||
{
|
||||
closestVisualizer.CopyGlobalPoseFrom( closestCandidate.pointerPosition );
|
||||
}
|
||||
}
|
||||
|
||||
void HandleCycling()
|
||||
{
|
||||
if ( cycleMode == LockOnCycleMode.ClosestCycle )
|
||||
{
|
||||
if ( cycleSensor != null && cycleSensor.isDown )
|
||||
{
|
||||
SetPointable( FindNextClosestCandidate() );
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ( cycleMode != LockOnCycleMode.Directional || directionalCycleInputs == null )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for ( int i = 0; i < directionalCycleInputs.Length; i++ )
|
||||
{
|
||||
var input = directionalCycleInputs[ i ];
|
||||
|
||||
if ( input == null || input.sensor == null || ! input.sensor.isDown )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var next = FindDirectionalCandidate( input.direction );
|
||||
|
||||
if ( next != null )
|
||||
{
|
||||
SetPointable( next );
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Pointable FindClosestCandidate()
|
||||
{
|
||||
for ( int i = 0; i < caster.NumColliders(); i++ )
|
||||
{
|
||||
var p = Nodes.Find<Pointable>( caster.GetCollider( i ) );
|
||||
|
||||
if ( p != null && p.enabled )
|
||||
{
|
||||
return p;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
Pointable FindNextClosestCandidate()
|
||||
{
|
||||
var currentIndex = -1;
|
||||
var candidates = new List<Pointable>();
|
||||
|
||||
for ( int i = 0; i < caster.NumColliders(); i++ )
|
||||
{
|
||||
var p = Nodes.Find<Pointable>( caster.GetCollider( i ) );
|
||||
|
||||
if ( p == null || ! p.enabled )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( p == pointable )
|
||||
{
|
||||
currentIndex = candidates.Count;
|
||||
}
|
||||
|
||||
candidates.Add( p );
|
||||
}
|
||||
|
||||
if ( candidates.Count == 0 )
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if ( currentIndex == -1 )
|
||||
{
|
||||
return candidates[ 0 ];
|
||||
}
|
||||
|
||||
return candidates[ ( currentIndex + 1 ) % candidates.Count ];
|
||||
}
|
||||
|
||||
Pointable FindDirectionalCandidate( Vector2 direction )
|
||||
{
|
||||
if ( pointable == null )
|
||||
{
|
||||
return FindClosestCandidate();
|
||||
}
|
||||
|
||||
var right = caster.GlobalTransform.Basis.X;
|
||||
var up = caster.GlobalTransform.Basis.Y;
|
||||
var currentPosition = pointable.GlobalPosition;
|
||||
|
||||
Pointable best = null;
|
||||
var bestScore = float.NegativeInfinity;
|
||||
|
||||
for ( int i = 0; i < caster.NumColliders(); i++ )
|
||||
{
|
||||
var candidate = Nodes.Find<Pointable>( caster.GetCollider( i ) );
|
||||
|
||||
if ( candidate == null || candidate == pointable || ! candidate.enabled )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var offset = candidate.GlobalPosition - currentPosition;
|
||||
var projected = new Vector2( offset.Dot( right ), offset.Dot( up ) );
|
||||
|
||||
if ( projected.LengthSquared() < 0.0001f )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var score = projected.Normalized().Dot( direction );
|
||||
|
||||
if ( score > bestScore )
|
||||
{
|
||||
bestScore = score;
|
||||
best = candidate;
|
||||
}
|
||||
}
|
||||
|
||||
return best;
|
||||
}
|
||||
|
||||
bool IsStillValidTarget( Pointable p )
|
||||
{
|
||||
for ( int i = 0; i < caster.NumColliders(); i++ )
|
||||
{
|
||||
if ( Nodes.Find<Pointable>( caster.GetCollider( i ) ) == p )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
uid://c0oeivnw2q0a5
|
||||
|
|
@ -33,6 +33,11 @@ namespace Rokojori
|
|||
[Export]
|
||||
public Action onPointerRemoved;
|
||||
|
||||
[Export]
|
||||
public Node3D pointerPositionOverride;
|
||||
|
||||
public Node3D pointerPosition => pointerPositionOverride ?? this;
|
||||
|
||||
[ExportGroup("Highlighting")]
|
||||
[Export]
|
||||
public HighlightEffect highlightEffect;
|
||||
|
|
|
|||
|
|
@ -17,11 +17,25 @@ namespace Rokojori
|
|||
[Export]
|
||||
public HighlightEffect defaultHighlighter;
|
||||
|
||||
[Export]
|
||||
public Node3D visualizer;
|
||||
|
||||
[Export]
|
||||
public Action onPointed;
|
||||
|
||||
[Export]
|
||||
public Action onUnpointed;
|
||||
|
||||
[Export]
|
||||
public Action onPointerChanged;
|
||||
|
||||
[ExportGroup("Read Only")]
|
||||
[Export]
|
||||
public Pointable pointable;
|
||||
|
||||
|
||||
|
||||
|
||||
public readonly EventSlot<Pointable> onPointerChange = new EventSlot<Pointable>();
|
||||
|
||||
|
||||
|
|
@ -29,7 +43,7 @@ namespace Rokojori
|
|||
public bool printPointables = false;
|
||||
|
||||
protected override void _OnTrigger()
|
||||
{
|
||||
{
|
||||
if ( caster == null )
|
||||
{
|
||||
return;
|
||||
|
|
@ -37,32 +51,61 @@ namespace Rokojori
|
|||
|
||||
var currentCollider = caster.NumColliders() == 0 ? null : caster.GetCollider( 0 );
|
||||
|
||||
var currentPointable = currentCollider == null ? null : Nodes.Find<Pointable>( currentCollider );
|
||||
|
||||
var currentPointable = currentCollider == null ? null : Nodes.Find<Pointable>( currentCollider );
|
||||
SetPointable( currentPointable );
|
||||
|
||||
// RJLog.Log( currentCollider, currentPointable );
|
||||
|
||||
}
|
||||
|
||||
if ( currentPointable == pointable )
|
||||
|
||||
|
||||
protected void SetPointable( Pointable next )
|
||||
{
|
||||
if ( next == pointable )
|
||||
{
|
||||
if ( visualizer != null && pointable != null )
|
||||
{
|
||||
var pointable3D = pointable.pointerPosition;
|
||||
visualizer.CopyGlobalPoseFrom( pointable3D );
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if ( pointable != null )
|
||||
{
|
||||
{
|
||||
pointable.UpdatePointerState( this, false );
|
||||
Highlight( HighlightActionType.End, pointable );
|
||||
}
|
||||
|
||||
pointable = currentPointable;
|
||||
pointable = next;
|
||||
|
||||
if ( pointable != null )
|
||||
{
|
||||
pointable.UpdatePointerState( this, true );
|
||||
{
|
||||
pointable.UpdatePointerState( this, true );
|
||||
Highlight( HighlightActionType.Start, pointable );
|
||||
}
|
||||
|
||||
onPointerChange.DispatchEvent( currentPointable );
|
||||
|
||||
|
||||
onPointerChange.DispatchEvent( pointable );
|
||||
onPointerChanged?.Trigger();
|
||||
|
||||
if ( pointable == null )
|
||||
{
|
||||
onUnpointed?.Trigger();
|
||||
}
|
||||
else
|
||||
{
|
||||
onPointed?.Trigger();
|
||||
}
|
||||
|
||||
if ( visualizer != null && pointable != null )
|
||||
{
|
||||
var pointable3D = pointable.pointerPosition;
|
||||
visualizer.CopyGlobalPoseFrom( pointable3D );
|
||||
}
|
||||
|
||||
if ( printPointables )
|
||||
{
|
||||
|
|
|
|||
|
|
@ -0,0 +1,63 @@
|
|||
|
||||
using Godot;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using Rokojori.Extensions;
|
||||
namespace Rokojori
|
||||
{
|
||||
public enum ActivationMode
|
||||
{
|
||||
Hold,
|
||||
Toggle
|
||||
}
|
||||
|
||||
[Tool]
|
||||
[GlobalClass]
|
||||
public partial class ActivationModeSensor : Sensor
|
||||
{
|
||||
[Export]
|
||||
public Sensor button;
|
||||
|
||||
[Export]
|
||||
public ActivationMode mode = ActivationMode.Hold;
|
||||
|
||||
bool _toggledOn = false;
|
||||
|
||||
public override bool IsSensor( InputEvent ie )
|
||||
{
|
||||
return button != null && button.IsSensor( ie );
|
||||
}
|
||||
|
||||
public void Release()
|
||||
{
|
||||
_toggledOn = false;
|
||||
}
|
||||
|
||||
protected override void UpdateValue()
|
||||
{
|
||||
if ( button == null )
|
||||
{
|
||||
SetBoolValue( false );
|
||||
return;
|
||||
}
|
||||
|
||||
if ( mode == ActivationMode.Hold )
|
||||
{
|
||||
SetFloatValue( button.value );
|
||||
return;
|
||||
}
|
||||
|
||||
if ( button.isDown )
|
||||
{
|
||||
_toggledOn = ! _toggledOn;
|
||||
}
|
||||
|
||||
SetBoolValue( _toggledOn );
|
||||
}
|
||||
|
||||
public override List<InputIcon> GetInputIcons()
|
||||
{
|
||||
return button == null ? new List<InputIcon>() : button.GetInputIcons();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
uid://b4eorkinrt8kv
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
|
||||
using Godot;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using Rokojori.Extensions;
|
||||
namespace Rokojori
|
||||
{
|
||||
|
||||
[Tool]
|
||||
[GlobalClass]
|
||||
public partial class ReleaseActivationModeSensor : Action
|
||||
{
|
||||
[Export]
|
||||
public ActivationModeSensor activationModeSensor;
|
||||
|
||||
protected override void _OnTrigger()
|
||||
{
|
||||
activationModeSensor?.Release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
uid://bmhvyxsurpcql
|
||||
|
|
@ -214,7 +214,7 @@ public partial class TimelineManager:NetworkNode
|
|||
{
|
||||
var inManager = timelines.Contains( timeline );
|
||||
|
||||
RJLog.Log( "No runner found for:", timeline, "Is in manager:", inManager );
|
||||
// RJLog.Log( "No runner found for:", timeline, "Is in manager:", inManager );
|
||||
}
|
||||
|
||||
return runner;
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ colors = PackedColorArray(0.96, 0.9211765, 0.24000007, 1, 1, 0.19607843, 0, 1, 0
|
|||
|
||||
[sub_resource type="Resource" id="Resource_w2g18"]
|
||||
script = ExtResource("1_b51fw")
|
||||
baseColor = Color(1, 0.19607843, 0, 1)
|
||||
baseColor = Color(1, 0.5342896, 0, 1)
|
||||
centerAttraction = 0.0
|
||||
temperaturePolarBlendRadius = 30.0
|
||||
startTemperatureShift = 45.0
|
||||
|
|
|
|||
|
|
@ -29,11 +29,11 @@ void fragment()
|
|||
{
|
||||
|
||||
float d = distance( UV, vec2(0.5) );
|
||||
float a = 1.0 - clamp( d / 0.5, 0.0, 1.0);
|
||||
float a = 1.0 - clamp( d / ( 0.5 * COLOR.a ), 0.0, 1.0);
|
||||
|
||||
|
||||
ALBEDO = COLOR.rgb;
|
||||
|
||||
ALPHA *= a * COLOR.a;
|
||||
ALPHA *= a;
|
||||
ALPHA_SCISSOR_THRESHOLD = 0.01;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -70,7 +70,9 @@ public partial class EmberVFXData:ParticleVFXData
|
|||
|
||||
UpdateCoreSettings( preset, particles );
|
||||
|
||||
processMaterial.Color = preset.color * preset.colorIntensity;
|
||||
var color = preset.color.ScaleColor( preset.colorIntensity );
|
||||
|
||||
processMaterial.Color = color;
|
||||
|
||||
|
||||
var rampTexture = processMaterial.ColorRamp as GradientTexture1D;
|
||||
|
|
|
|||
|
|
@ -67,6 +67,11 @@ public partial class ParticleVFX:Node3D
|
|||
_data = data;
|
||||
}
|
||||
|
||||
public void Play()
|
||||
{
|
||||
preset.Play( this );
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
|
||||
using Godot;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using Rokojori.Extensions;
|
||||
namespace Rokojori
|
||||
{
|
||||
[Tool][GlobalClass, Icon("res://addons/rokojori_action_library/Icons/PlayParticles.svg")]
|
||||
public partial class ParticleVFXControl:Action
|
||||
{
|
||||
public enum ControlType
|
||||
{
|
||||
Play
|
||||
}
|
||||
|
||||
[Export]
|
||||
public ParticleVFX particleVFX;
|
||||
|
||||
[Export]
|
||||
public ControlType controlType = ControlType.Play;
|
||||
|
||||
protected override void _OnTrigger()
|
||||
{
|
||||
if ( ControlType.Play == controlType )
|
||||
{
|
||||
particleVFX?.Play();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
uid://cw6q1qxm35l7l
|
||||
Loading…
Reference in New Issue