cirnogodot/Scripts/Misc/CameraController3D.cs

137 lines
4.3 KiB
C#
Raw Normal View History

2025-06-11 15:28:26 +02:00
using Godot;
namespace Cirno.Scripts.Misc;
public partial class CameraController3D : Camera3D
{
2025-06-13 17:46:44 +02:00
public static CameraController3D Instance { get; private set; }
2025-06-11 15:28:26 +02:00
[Export] public bool EnableSmoothing = true;
[Export] public bool FollowTargeting = true;
[Export] public Vector3 DefaultCameraRotation = new Vector3(-36f, 45f, 0f);
2025-06-11 15:28:26 +02:00
[Export] public float SmoothTime = 0.2f;
[Export] public float MaxAimOffsetDistance = 2.0f;
[Export] public float AimLerpSpeed = 8.0f;
[Export] public float AimDeadzone = 0.2f;
[Export] public Vector3 CameraOffset = new Vector3(8, 8.5f, 8); //new Vector3(0, 12, -12); // Relative to target
2025-06-11 15:28:26 +02:00
[Export] public StringName AimUpName = "aim_up";
[Export] public StringName AimDownName = "aim_down";
[Export] public StringName AimLeftName = "aim_left";
[Export] public StringName AimRightName = "aim_right";
[Export] public NodePath TargetPath;
2025-06-18 15:16:43 +02:00
private CameraTarget3D _target;
2025-06-11 15:28:26 +02:00
private Vector3 _currentPosition = Vector3.Zero;
private Vector3 _currentAimOffset = Vector3.Zero;
2025-06-11 15:28:26 +02:00
public override void _Ready()
{
2025-06-13 17:46:44 +02:00
Instance = this;
2025-06-18 15:16:43 +02:00
RotationDegrees = DefaultCameraRotation;
2025-06-18 15:16:43 +02:00
Projection = ProjectionType.Orthogonal;
_target = GetNode<CameraTarget3D>(TargetPath);
2025-06-11 15:28:26 +02:00
if (_target == null)
{
GD.PushError("Camera target not found.");
return;
}
_currentPosition = GlobalPosition;
// Set fixed isometric angle once: -45° X tilt, 45° Y pan
2025-06-18 15:16:43 +02:00
2025-06-11 15:28:26 +02:00
}
2025-06-18 15:16:43 +02:00
public void RegisterTarget(CameraTarget3D target, bool jump = true)
{
// assert(not _active_target)
_target = target;
if (jump)
{
//_currentPosition = _activeTarget.GlobalPosition;
}
}
2025-12-29 17:27:16 +01:00
public override void _Process(double delta)
2025-06-11 15:28:26 +02:00
{
2025-06-18 15:16:43 +02:00
if (_target is null) return;
2025-06-11 15:28:26 +02:00
float dt = (float)delta;
Vector3 targetPos = _target.GlobalTransform.Origin;
// Aim offset
if (FollowTargeting)
{
Vector3 desiredOffset = GetAimOffsetWorldSpace();
_currentAimOffset = _currentAimOffset.Lerp(desiredOffset, AimLerpSpeed * dt);
if (_currentAimOffset.Length() > MaxAimOffsetDistance)
_currentAimOffset = _currentAimOffset.Normalized() * MaxAimOffsetDistance;
}
else
{
_currentAimOffset = Vector3.Zero;
}
// Final target position
Vector3 targetWithOffset = targetPos + _currentAimOffset;
Vector3 desiredCameraPos = targetWithOffset + CameraOffset;
if (EnableSmoothing)
{
float smoothingFactor = 1f - Mathf.Exp(-dt / SmoothTime);
_currentPosition = _currentPosition.Lerp(desiredCameraPos, smoothingFactor);
}
else
{
_currentPosition = desiredCameraPos;
}
2025-12-29 17:39:22 +01:00
GlobalPosition = _currentPosition;
2025-12-29 17:27:16 +01:00
//GlobalPosition = _currentPosition;
2025-06-11 15:28:26 +02:00
// No LookAt or dynamic rotation — angle is fixed
}
private Vector3 GetAimOffsetWorldSpace()
{
2025-12-29 17:39:22 +01:00
var stickDir = new Vector2(
2025-06-11 15:28:26 +02:00
Input.GetActionStrength(AimRightName) - Input.GetActionStrength(AimLeftName),
Input.GetActionStrength(AimDownName) - Input.GetActionStrength(AimUpName)
);
2025-12-29 17:39:22 +01:00
var stickLen = stickDir.Length();
2025-06-11 15:28:26 +02:00
if (stickLen > AimDeadzone)
{
2025-12-29 17:39:22 +01:00
var scaled = (stickLen - AimDeadzone) / (1f - AimDeadzone);
var aimDir2D = stickDir.Normalized() * Mathf.Clamp(scaled, 0f, 1f);
2025-06-11 15:28:26 +02:00
return new Vector3(aimDir2D.X, 0, aimDir2D.Y);
}
// Mouse fallback
2025-12-29 17:39:22 +01:00
var mousePos = GetViewport().GetMousePosition();
var rayOrigin = ProjectRayOrigin(mousePos);
var rayDir = ProjectRayNormal(mousePos) * 1000f;
2025-06-11 15:28:26 +02:00
var plane = new Plane(Vector3.Up, 0);
var hit = plane.IntersectsRay(rayOrigin, rayDir);
2025-12-29 17:39:22 +01:00
if (hit is not { } hitPoint) return Vector3.Zero;
2025-06-11 15:28:26 +02:00
{
2025-12-29 17:39:22 +01:00
var offset = hitPoint - _target.GlobalTransform.Origin;
2025-06-11 15:28:26 +02:00
offset.Y = 0;
2025-12-29 17:39:22 +01:00
var dist = offset.Length();
if (!(dist > 0.01f)) return Vector3.Zero;
var scaled = Mathf.Clamp((dist - AimDeadzone) / (10f - AimDeadzone), 0f, 1f);
return offset.Normalized() * scaled;
2025-06-11 15:28:26 +02:00
}
}
}