Files
MovementTests/systems/head/HeadSystem.cs
Minimata 916a6e7153
All checks were successful
Create tag and build when new code gets to main / BumpTag (push) Successful in 33s
Create tag and build when new code gets to main / Test (push) Successful in 6m19s
Create tag and build when new code gets to main / Export (push) Successful in 7m39s
some editor icons and a base level scene
2026-01-27 10:35:35 +01:00

326 lines
13 KiB
C#

using System;
using Godot;
using RustyOptions;
namespace Movementtests.systems;
[GlobalClass, Icon("res://assets/ui/IconGodotNode/node_3D/icon_face.png")]
public partial class HeadSystem : Node3D
{
[Signal]
public delegate void HitboxActivatedEventHandler();
[Signal]
public delegate void HitboxDeactivatedEventHandler();
[Signal]
public delegate void HitTargetEventHandler();
[Signal]
public delegate void GotHitEventHandler();
[Signal]
public delegate void StepFootEventHandler();
public record CameraParameters(
double Delta,
Vector2 LookDir,
Vector3 PlayerInput,
Vector3 PlayerVelocity,
Vector3 WallContactPoint,
float SensitivitMultiplier,
bool WithCameraJitter,
bool WithCameraBobbing,
float BobbingMultiplier,
float FovMultiplier);
private Camera3D _camera;
private Marker3D _cameraAnchor;
private AnimationPlayer _animationPlayer;
private AnimationTree _animationTree;
[Export(PropertyHint.Range, "0,10,0.1,or_greater")]
public float LookSensitivity { get; set; } = 1f;
[ExportGroup("Camera incline")]
[Export(PropertyHint.Range, "0.1,50,0.1,or_greater")]
public double CameraInclineAcceleration { get; set; } = 10f;
[Export(PropertyHint.Range, "0,10,0.1,or_greater")]
public float WallRunCameraIncline { get; set; } = 5f;
[Export(PropertyHint.Range, "0,10,0.1,or_greater")]
public float GroundedCameraIncline { get; set; } = 5f;
[ExportGroup("Sliding")]
[Export(PropertyHint.Range, "0,2,0.1,or_greater")]
public float SlidingCameraHeightOffset { get; set; } = 1.0f;
[Export(PropertyHint.Range, "0,1,0.01,or_greater")]
public float SlidingJitterFrequency { get; set; } = 0.01f;
[Export(PropertyHint.Range, "0,1,0.01,or_greater")]
public float SlidingJitterAmplitude { get; set; } = 0.1f;
private FastNoiseLite _slidingNoise = new FastNoiseLite();
[ExportGroup("Bobbing")]
private float _bobbingAccumulator; // Constantly increases when player moves in X or/and Z axis
[Export(PropertyHint.Range, "0,10,0.01,or_greater")]
public float BobbingFrequency { set; get; } = 2.4f;
[Export(PropertyHint.Range, "0,0.4,0.01,or_greater")]
public float BobbingAmplitude { set; get; } = 0.08f;
[ExportGroup("FOV")]
[Export(PropertyHint.Range, "0,180,0.1,degrees")]
public float BaseFov { get; set; } = 75.0f;
[Export(PropertyHint.Range, "0,10,0.01,or_greater")]
public float FovChangeFactor { get; set; } = 1.2f;
[Export(PropertyHint.Range, "0,10,0.01,or_greater")]
public float FovChangeSpeed { get; set; } = 6.25f;
[Export(PropertyHint.Range, "0,100,1,or_greater")]
public float FovMaxedOutSpeed { get; set; } = 20f;
[ExportGroup("First Person rig")]
private Node3D _fpRig;
private Node3D _fpDisplacedRig;
private Vector3 _fpDisplacedRigInitialRotation;
[Export(PropertyHint.Range, "0,10,0.1,or_greater")]
public float WeaponSway { get; set; } = 5f;
[Export(PropertyHint.Range, "0,10,0.1,or_greater")]
public float WeaponLookRotation { get; set; } = 1f;
[Export(PropertyHint.Range, "0,200,1,or_greater")]
public float WeaponMoveRotation { get; set; } = 80f;
[Export(PropertyHint.Range, "0,20,0.1,or_greater")]
public float WeaponAdjustmentSpeed { get; set; } = 10f;
[Export(PropertyHint.Range, "0,10,0.1,or_greater")]
public float DisplacedWeaponSway { get; set; } = 5f;
[Export(PropertyHint.Range, "0,10,0.1,or_greater")]
public float DisplacedWeaponLookRotation { get; set; } = 1f;
[Export(PropertyHint.Range, "0,1,0.01,or_greater")]
public float DisplacedWeaponMoveRotation { get; set; } = 0.1f;
[Export(PropertyHint.Range, "0,20,0.1,or_greater")]
public float DisplacedWeaponAdjustmentSpeed { get; set; } = 10f;
public void Init()
{
Input.SetMouseMode(Input.MouseModeEnum.Captured);
_camera = GetNode<Camera3D>("CameraSmooth/Camera3D");
_cameraAnchor = GetNode<Marker3D>("CameraAnchor");
_animationPlayer = GetNode<AnimationPlayer>("AnimationPlayer");
_animationTree = GetNode<AnimationTree>("AnimationTree");
_fpRig = GetNode<Node3D>("FPRig");
_fpDisplacedRig = GetNode<Node3D>("FPRig/Sword");
_fpDisplacedRigInitialRotation = _fpDisplacedRig.Rotation;
_slidingNoise.NoiseType = FastNoiseLite.NoiseTypeEnum.Perlin;
_slidingNoise.SetFrequency(SlidingJitterFrequency);
}
public void OnMantle()
{
_animationTree.Set("parameters/OnMantle/request", (int) AnimationNodeOneShot.OneShotRequest.Fire);
}
public void OnJumpStarted()
{
_animationTree.Set("parameters/OnJumpStart/request", (int) AnimationNodeOneShot.OneShotRequest.Fire);
}
public void OnJumpEnded()
{
_animationTree.Set("parameters/OnJumpEnd/request", (int) AnimationNodeOneShot.OneShotRequest.Fire);
}
public void OnHit()
{
_animationTree.Set("parameters/OnHit/request", (int) AnimationNodeOneShot.OneShotRequest.Fire);
}
public void OnHitTarget()
{
EmitSignalHitTarget();
}
public void OnGetHit()
{
EmitSignalGotHit();
}
public void OnHitboxActivated()
{
EmitSignalHitboxActivated();
}
public void OnHitboxDeactivated()
{
EmitSignalHitboxDeactivated();
}
private bool _footstepEmitted;
public void LookAround(CameraParameters inputs)
{
var (delta,
lookDir,
playerInput,
playerVelocity,
wallContactPoint,
sensitivitMultiplier,
withCameraJitter,
withCameraBobbing,
bobbingMultiplier,
fovMultiplier) = inputs;
// Horizontal movement of head
float angleForHorizontalRotation = lookDir.X * LookSensitivity * sensitivitMultiplier;
RotateY(angleForHorizontalRotation);
// Vertical movement of head
Vector3 currentCameraRotation = _cameraAnchor.Rotation;
currentCameraRotation.X += Convert.ToSingle(lookDir.Y * LookSensitivity * sensitivitMultiplier);
currentCameraRotation.X = Mathf.Clamp(currentCameraRotation.X, Mathf.DegToRad(-90f), Mathf.DegToRad(90f));
// Camera incline on Wall and more
var isWallRunning = wallContactPoint.Length() > Mathf.Epsilon;
float cameraIncline;
if (isWallRunning)
{
var directionToWall = (wallContactPoint - GlobalPosition).Normalized();
var cameraInclineFactor = ComputeCameraInclineFactor(directionToWall);
cameraIncline = Mathf.DegToRad(WallRunCameraIncline * cameraInclineFactor);
}
else
{
var cameraInclineFactor = ComputeCameraInclineFactor(playerInput);
cameraIncline = Mathf.DegToRad(GroundedCameraIncline * cameraInclineFactor * -1.0f);
}
currentCameraRotation.Z = (float) Mathf.Lerp(currentCameraRotation.Z, cameraIncline, delta * CameraInclineAcceleration);
_cameraAnchor.Rotation = currentCameraRotation;
if (withCameraJitter)
{
_cameraAnchor.Position = Vector3.Down*SlidingCameraHeightOffset;
float noise1D = _slidingNoise.GetNoise1D(Time.GetTicksMsec());
float noiseAmplitude = SlidingJitterAmplitude*Mathf.Clamp(playerVelocity.Length(), 0f, 1f);
_cameraAnchor.Position += Vector3.Up*noise1D*noiseAmplitude;
}
else
{
_cameraAnchor.Position = Vector3.Zero;
}
Vector3 newPositionForCamera = Vector3.Zero;
Vector3 newPositionForRig = Vector3.Zero;
if (withCameraBobbing)
{
_bobbingAccumulator += (float) delta * playerVelocity.Length();
// As the _bobbingAccumulator increases we're changing values for sin and cos functions.
// Because both of them are just waves, we will be slide up with y and then slide down with y
// creating bobbing effect. The same works for cos. As the _bobbingAccumulator increases the cos decreases and then increases
newPositionForCamera.Y = Mathf.Sin(_bobbingAccumulator * BobbingFrequency) * BobbingAmplitude * bobbingMultiplier;
newPositionForCamera.X = Mathf.Cos(_bobbingAccumulator * BobbingFrequency / 2.0f) * BobbingAmplitude * bobbingMultiplier;
if (newPositionForCamera.Y < -0.07 && !_footstepEmitted) Footstep();
if (newPositionForCamera.Y > 0) _footstepEmitted = false;
// Offset bobbing for weapon rig
newPositionForRig.Y = Mathf.Cos(_bobbingAccumulator * BobbingFrequency) * BobbingAmplitude * bobbingMultiplier * 0.2f;
newPositionForRig.X = Mathf.Sin(_bobbingAccumulator * BobbingFrequency / 2.0f) * BobbingAmplitude * bobbingMultiplier * 0.2f;
}
_cameraAnchor.Position += newPositionForCamera;
_camera.GlobalTransform = _cameraAnchor.GetGlobalTransformInterpolated();
// First person rig adjustments
_fpRig.GlobalTransform = _cameraAnchor.GetGlobalTransformInterpolated();
// Apply bobbing
_fpRig.Position += newPositionForRig;
// Rotate the whole rig based on movement input
var newRigRotation = _fpRig.Rotation;
var camTilt = Mathf.Lerp(_fpRig.Rotation.Z, cameraIncline*WeaponMoveRotation, delta*WeaponAdjustmentSpeed);
newRigRotation.Z = (float) camTilt;
// Rotate the whole rig based on camera rotation input
newRigRotation.X = Mathf.Lerp(newRigRotation.X, -lookDir.Y*WeaponSway, (float) delta*WeaponAdjustmentSpeed);
newRigRotation.Y = Mathf.Lerp(newRigRotation.Y, -lookDir.X*WeaponSway, (float) delta*WeaponAdjustmentSpeed);
// Apply
_fpRig.Rotation = newRigRotation;
// Compute displaced rig adjustments, starting with movement input
var newDisplacedRigRotation = _fpDisplacedRig.Rotation;
var howMuchForward = ComputeHowMuchInputForward(playerInput);
var howMuchSideways = ComputeHowMuchInputSideways(playerInput);
var displacedCamTiltForward = Mathf.Lerp(newDisplacedRigRotation.Z,
_fpDisplacedRigInitialRotation.Z + howMuchForward*DisplacedWeaponMoveRotation,
delta*DisplacedWeaponAdjustmentSpeed);
var displacedCamTiltSide = Mathf.Lerp(newDisplacedRigRotation.X,
_fpDisplacedRigInitialRotation.X - howMuchSideways*DisplacedWeaponMoveRotation,
delta*DisplacedWeaponAdjustmentSpeed);
newDisplacedRigRotation.X = (float) displacedCamTiltSide;
newDisplacedRigRotation.Z = (float) displacedCamTiltForward;
var displacedSwayY = Mathf.Lerp(newDisplacedRigRotation.Y,
_fpDisplacedRigInitialRotation.Y - lookDir.X*DisplacedWeaponSway,
delta*DisplacedWeaponAdjustmentSpeed);
newDisplacedRigRotation.Y = (float) displacedSwayY;
// Apply
_fpDisplacedRig.Rotation = newDisplacedRigRotation;
// Camera adjustments
float velocityClamped = Mathf.Clamp(playerVelocity.Length(), 0.5f, FovMaxedOutSpeed);
float targetFov = BaseFov + FovChangeFactor * velocityClamped * fovMultiplier;
_camera.Fov = Mathf.Lerp(_camera.Fov, targetFov, (float) delta * FovChangeSpeed);
}
public void Footstep()
{
_footstepEmitted = true;
EmitSignalStepFoot();
}
public void HideWeapon()
{
_fpRig.Visible = false;
}
public void ShowWeapon()
{
_fpRig.Visible = true;
}
public float ComputeCameraInclineFactor(Vector3 direction)
{
var forward = GetForwardHorizontalVector().Normalized();
var crossProduct = forward.Cross(direction);
return crossProduct.Length()*Mathf.Sign(crossProduct.Y);
}
public float ComputeHowMuchInputForward(Vector3 playerInput)
{
var forwardAngle = GetForwardHorizontalVector().AngleTo(playerInput);
var forwardRemapped = Mathf.Remap(forwardAngle, 0, Mathf.Pi, -1, 1);
return playerInput.Length() > 0 ? forwardRemapped : 0;
}
public float ComputeHowMuchInputSideways(Vector3 playerInput)
{
var rightAngle = GetForwardHorizontalVector().Cross(Vector3.Up).Normalized().AngleTo(playerInput);
var forwardRemapped = Mathf.Remap(rightAngle, 0, Mathf.Pi, -1, 1);
return playerInput.Length() > 0 ? forwardRemapped : 0;
}
public Vector3 GetForwardHorizontalVector()
{
return GetGlobalTransform().Basis.Z;
}
public Vector3 GetGlobalLookRotation()
{
return new Vector3(
_camera.Rotation.X,
Rotation.Y,
_camera.Rotation.Z);
}
public void SetHeight(float height)
{
Position = new Vector3(Position.X, height, Position.Z);
}
}