mirror of
https://gitlab.com/MaddoScientisto/cirnogodot.git
synced 2026-06-18 18:33:48 +00:00
Rewritten camera to follow cursor
This commit is contained in:
parent
2bfc35d48c
commit
869a3b4c06
7 changed files with 232 additions and 9 deletions
|
|
@ -4,10 +4,10 @@
|
||||||
[ext_resource type="PackedScene" uid="uid://c4pr2707hbeph" path="res://Scenes/Actors/fsm_player.tscn" id="2_3fyis"]
|
[ext_resource type="PackedScene" uid="uid://c4pr2707hbeph" path="res://Scenes/Actors/fsm_player.tscn" id="2_3fyis"]
|
||||||
[ext_resource type="Resource" uid="uid://6ek4lmtuij4t" path="res://Resources/Maps/Roguelite.tres" id="2_k5t51"]
|
[ext_resource type="Resource" uid="uid://6ek4lmtuij4t" path="res://Resources/Maps/Roguelite.tres" id="2_k5t51"]
|
||||||
[ext_resource type="Script" uid="uid://bt2qjgnf1wc2r" path="res://Scripts/Controllers/RogueliteRoomManager.cs" id="4_jtlua"]
|
[ext_resource type="Script" uid="uid://bt2qjgnf1wc2r" path="res://Scripts/Controllers/RogueliteRoomManager.cs" id="4_jtlua"]
|
||||||
[ext_resource type="Script" uid="uid://cfya7sndh7vy2" path="res://Scenes/CameraController.gd" id="8_y651a"]
|
[ext_resource type="Script" uid="uid://dd535g2nxqpg1" path="res://Scripts/Misc/CameraController.cs" id="6_gwtv6"]
|
||||||
[ext_resource type="Script" uid="uid://c5nxsq3tyxcx6" path="res://Scripts/InventoryManager.cs" id="9_vhvs2"]
|
[ext_resource type="Script" uid="uid://c5nxsq3tyxcx6" path="res://Scripts/InventoryManager.cs" id="9_vhvs2"]
|
||||||
|
[ext_resource type="Script" uid="uid://upq0b4sx7nhw" path="res://Scripts/Misc/CameraTarget.cs" id="9_wbqvu"]
|
||||||
[ext_resource type="PackedScene" uid="uid://dkwi1hu1bixoe" path="res://Scenes/HUD/HUD.tscn" id="10_6gk3e"]
|
[ext_resource type="PackedScene" uid="uid://dkwi1hu1bixoe" path="res://Scenes/HUD/HUD.tscn" id="10_6gk3e"]
|
||||||
[ext_resource type="Script" uid="uid://bdshph801ac2i" path="res://Scenes/CameraTarget.gd" id="11_4gy5m"]
|
|
||||||
[ext_resource type="Script" uid="uid://cnkipcolyj61w" path="res://Scripts/AlarmManager.cs" id="12_eoca5"]
|
[ext_resource type="Script" uid="uid://cnkipcolyj61w" path="res://Scripts/AlarmManager.cs" id="12_eoca5"]
|
||||||
[ext_resource type="PackedScene" uid="uid://b3tyacxxw88lx" path="res://Scenes/Utils/StreamPlayerWithName.tscn" id="13_4n7t6"]
|
[ext_resource type="PackedScene" uid="uid://b3tyacxxw88lx" path="res://Scenes/Utils/StreamPlayerWithName.tscn" id="13_4n7t6"]
|
||||||
[ext_resource type="Script" uid="uid://3v6q0p5krqn7" path="res://Scripts/UI/Minimap.cs" id="16_pfafs"]
|
[ext_resource type="Script" uid="uid://3v6q0p5krqn7" path="res://Scripts/UI/Minimap.cs" id="16_pfafs"]
|
||||||
|
|
@ -30,9 +30,11 @@ MapThemes = Array[Object]([ExtResource("25_7gtqx")])
|
||||||
|
|
||||||
[node name="CameraController" type="Camera2D" parent="."]
|
[node name="CameraController" type="Camera2D" parent="."]
|
||||||
process_mode = 1
|
process_mode = 1
|
||||||
script = ExtResource("8_y651a")
|
script = ExtResource("6_gwtv6")
|
||||||
pixel_snap = false
|
PixelSnap = false
|
||||||
enable_smoothing = false
|
EnableSmoothing = false
|
||||||
|
AimLerpSpeed = 0.8
|
||||||
|
DebugCamera2ActionName = &"debug_camera_1"
|
||||||
|
|
||||||
[node name="ReferenceRect" type="ReferenceRect" parent="."]
|
[node name="ReferenceRect" type="ReferenceRect" parent="."]
|
||||||
visible = false
|
visible = false
|
||||||
|
|
@ -48,7 +50,7 @@ script = ExtResource("9_vhvs2")
|
||||||
|
|
||||||
[node name="CameraTarget" type="Node2D" parent="."]
|
[node name="CameraTarget" type="Node2D" parent="."]
|
||||||
position = Vector2(154, 103)
|
position = Vector2(154, 103)
|
||||||
script = ExtResource("11_4gy5m")
|
script = ExtResource("9_wbqvu")
|
||||||
|
|
||||||
[node name="PlayerStartPosition" type="Marker2D" parent="."]
|
[node name="PlayerStartPosition" type="Marker2D" parent="."]
|
||||||
position = Vector2(150, 80)
|
position = Vector2(150, 80)
|
||||||
|
|
|
||||||
|
|
@ -18,9 +18,8 @@ position = Vector2(-4.685, 3.52)
|
||||||
[node name="ShootTimer" type="Timer" parent="."]
|
[node name="ShootTimer" type="Timer" parent="."]
|
||||||
one_shot = true
|
one_shot = true
|
||||||
|
|
||||||
[node name="SoundModule" type="Node2D" parent="." node_paths=PackedStringArray("Weapon", "ShootSound", "ReloadSound", "EmptySound")]
|
[node name="SoundModule" type="Node2D" parent="." node_paths=PackedStringArray("ShootSound", "ReloadSound", "EmptySound")]
|
||||||
script = ExtResource("2_uwnyl")
|
script = ExtResource("2_uwnyl")
|
||||||
Weapon = NodePath("")
|
|
||||||
ShootSound = NodePath("ShootSound")
|
ShootSound = NodePath("ShootSound")
|
||||||
ReloadSound = NodePath("ReloadSound")
|
ReloadSound = NodePath("ReloadSound")
|
||||||
EmptySound = NodePath("EmptySound")
|
EmptySound = NodePath("EmptySound")
|
||||||
|
|
|
||||||
172
Scripts/Misc/CameraController.cs
Normal file
172
Scripts/Misc/CameraController.cs
Normal file
|
|
@ -0,0 +1,172 @@
|
||||||
|
using System;
|
||||||
|
using Cirno.Scripts.Utils;
|
||||||
|
using Godot;
|
||||||
|
|
||||||
|
namespace Cirno.Scripts.Misc;
|
||||||
|
|
||||||
|
public partial class CameraController : Camera2D
|
||||||
|
{
|
||||||
|
[Export] public bool PixelSnap { get; set; } = true;
|
||||||
|
[Export] public bool EnableSmoothing { get; set; } = true;
|
||||||
|
|
||||||
|
[Export] public bool FollowTargeting { get; set; } = true;
|
||||||
|
|
||||||
|
[Export] public float SmoothTime { get; set; } = 0.2f;
|
||||||
|
|
||||||
|
[Export] public float MaxAimOffsetDistance { get; set; } = 64f;
|
||||||
|
[Export] public float AimLerpSpeed { get; set; } = 10f; // How fast the offset adapts
|
||||||
|
[Export] public float AimDeadzone { get; set; } = 0.2f;
|
||||||
|
|
||||||
|
[ExportGroup("Name Strings")]
|
||||||
|
[Export] public StringName CameraControllersGroupName { get; private set; } = "camera_controllers";
|
||||||
|
[Export] public StringName DebugCamera1ActionName { get; private set; } = "debug_camera_1";
|
||||||
|
[Export] public StringName DebugCamera2ActionName { get; private set; } = "debug_camera_2";
|
||||||
|
|
||||||
|
[Export] public StringName AimUpName { get; private set; } = "aim_up";
|
||||||
|
[Export] public StringName AimDownName { get; private set; } = "aim_down";
|
||||||
|
[Export] public StringName AimLeftName { get; private set; } = "aim_left";
|
||||||
|
[Export] public StringName AimRightName { get; private set; } = "aim_right";
|
||||||
|
|
||||||
|
private CameraTarget _activeTarget;
|
||||||
|
|
||||||
|
private Vector2 _previousPixelSnapDelta = Vector2.Zero;
|
||||||
|
|
||||||
|
// The current camera velocity, for smooth damping.
|
||||||
|
private Vector2 _currentVelocity = Vector2.Zero;
|
||||||
|
|
||||||
|
// The current exact position of the camera
|
||||||
|
private Vector2 _currentPosition;
|
||||||
|
|
||||||
|
private Vector2 _currentAimOffset = Vector2.Zero;
|
||||||
|
|
||||||
|
public override void _Ready()
|
||||||
|
{
|
||||||
|
AddToGroup(CameraControllersGroupName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void _UnhandledInput(InputEvent @event)
|
||||||
|
{
|
||||||
|
if (Input.IsActionJustPressed(DebugCamera1ActionName))
|
||||||
|
{
|
||||||
|
PixelSnap = !PixelSnap;
|
||||||
|
GD.Print($"Camera Pixel Snap: {PixelSnap}");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Input.IsActionJustPressed(DebugCamera2ActionName))
|
||||||
|
{
|
||||||
|
EnableSmoothing = !EnableSmoothing;
|
||||||
|
GD.Print($"Camera Smoothing: {EnableSmoothing}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void _Process(double delta)
|
||||||
|
{
|
||||||
|
float deltaTime = (float)delta;
|
||||||
|
// Update position
|
||||||
|
Vector2 nextPosition;
|
||||||
|
|
||||||
|
Vector2 target;
|
||||||
|
if (FollowTargeting)
|
||||||
|
{
|
||||||
|
// Adding the offset
|
||||||
|
Vector2 aimDirection = GetAimDirection(); // We'll define this next
|
||||||
|
Vector2 desiredOffset = aimDirection * MaxAimOffsetDistance;
|
||||||
|
_currentAimOffset = _currentAimOffset.Lerp(desiredOffset, AimLerpSpeed * deltaTime);
|
||||||
|
|
||||||
|
// Clamp final offset
|
||||||
|
if (_currentAimOffset.LengthSquared() > MaxAimOffsetDistance * MaxAimOffsetDistance)
|
||||||
|
_currentAimOffset = _currentAimOffset.Normalized() * MaxAimOffsetDistance;
|
||||||
|
|
||||||
|
target = _activeTarget.GlobalPosition + _currentAimOffset;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
target = _activeTarget.GlobalPosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (EnableSmoothing)
|
||||||
|
{
|
||||||
|
// Handle target movement with SmoothDamp
|
||||||
|
// Replace this with any smooth follow / lerp of your choosing, but be careful to use `delta`
|
||||||
|
// properly -- improper use can result in jitter. Don't do lerp(current, target, delta).
|
||||||
|
var resX = MathFunctions.SmoothDamp(_currentPosition.X, target.X, _currentVelocity.X, SmoothTime,
|
||||||
|
float.PositiveInfinity, deltaTime);
|
||||||
|
var resY = MathFunctions.SmoothDamp(_currentPosition.Y, target.Y, _currentVelocity.Y, SmoothTime,
|
||||||
|
float.PositiveInfinity, deltaTime);
|
||||||
|
|
||||||
|
nextPosition = new Vector2(resX.Item1, resY.Item1);
|
||||||
|
_currentVelocity = new Vector2(resX.Item2, resY.Item2);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Set next camera position to the exact target.
|
||||||
|
nextPosition = target;
|
||||||
|
}
|
||||||
|
|
||||||
|
_currentPosition = nextPosition;
|
||||||
|
|
||||||
|
if (PixelSnap)
|
||||||
|
{
|
||||||
|
// IMPORTANT: perform pixel snap so that the camera movement doesn't interfere with the
|
||||||
|
// sub-pixel "smooth movement" we do in the shader.
|
||||||
|
var snappedPosition = (nextPosition + new Vector2(0.5f, 0.5f)).Floor();
|
||||||
|
_previousPixelSnapDelta = snappedPosition - nextPosition;
|
||||||
|
nextPosition = snappedPosition;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_previousPixelSnapDelta = Vector2.Zero;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the camera position.
|
||||||
|
GlobalPosition = nextPosition;
|
||||||
|
// IMPORTANT: Work around godot bug where camera doesn't update immediately: https://github.com/godotengine/godot/issues/74203
|
||||||
|
ForceUpdateScroll();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Vector2 GetAimDirection()
|
||||||
|
{
|
||||||
|
// Check controller stick input
|
||||||
|
Vector2 stickDir = new Vector2(
|
||||||
|
Input.GetActionStrength(AimRightName) - Input.GetActionStrength(AimLeftName),
|
||||||
|
Input.GetActionStrength(AimDownName) - Input.GetActionStrength(AimUpName)
|
||||||
|
);
|
||||||
|
|
||||||
|
float stickLen = stickDir.Length();
|
||||||
|
if (stickLen > AimDeadzone)
|
||||||
|
{
|
||||||
|
float scaled = (stickLen - AimDeadzone) / (1f - AimDeadzone);
|
||||||
|
return stickDir.Normalized() * Mathf.Clamp(scaled, 0f, 1f);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mouse input
|
||||||
|
Vector2 screenCenter = GetViewportRect().Size / 2f;
|
||||||
|
Vector2 mousePos = GetViewport().GetMousePosition();
|
||||||
|
Vector2 dir = mousePos - screenCenter;
|
||||||
|
float dist = dir.Length();
|
||||||
|
|
||||||
|
// Use a pixel-based deadzone for mouse
|
||||||
|
float mouseDeadzone = AimDeadzone * 100f; // e.g. 0.2 * 100 = 20px
|
||||||
|
|
||||||
|
if (dist > mouseDeadzone)
|
||||||
|
{
|
||||||
|
float scaled = (dist - mouseDeadzone) / (screenCenter.Length() - mouseDeadzone);
|
||||||
|
return dir.Normalized() * Mathf.Clamp(scaled, 0f, 1f);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Vector2.Zero;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Vector2 GetPixelSnapDelta()
|
||||||
|
{
|
||||||
|
return _previousPixelSnapDelta;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RegisterTarget(CameraTarget target)
|
||||||
|
{
|
||||||
|
// assert(not _active_target)
|
||||||
|
_activeTarget = target;
|
||||||
|
_currentPosition = _activeTarget.GlobalPosition;
|
||||||
|
}
|
||||||
|
}
|
||||||
1
Scripts/Misc/CameraController.cs.uid
Normal file
1
Scripts/Misc/CameraController.cs.uid
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
uid://dd535g2nxqpg1
|
||||||
16
Scripts/Misc/CameraTarget.cs
Normal file
16
Scripts/Misc/CameraTarget.cs
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
using Godot;
|
||||||
|
|
||||||
|
namespace Cirno.Scripts.Misc;
|
||||||
|
|
||||||
|
public partial class CameraTarget : Node2D
|
||||||
|
{
|
||||||
|
public override void _Ready()
|
||||||
|
{
|
||||||
|
// register with the controller
|
||||||
|
var res = GetTree().GetFirstNodeInGroup("camera_controllers");
|
||||||
|
if (res is not null && res is CameraController cameraController)
|
||||||
|
{
|
||||||
|
cameraController.RegisterTarget(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
1
Scripts/Misc/CameraTarget.cs.uid
Normal file
1
Scripts/Misc/CameraTarget.cs.uid
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
uid://upq0b4sx7nhw
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
using Godot;
|
using System;
|
||||||
|
using Godot;
|
||||||
|
|
||||||
namespace Cirno.Scripts.Utils;
|
namespace Cirno.Scripts.Utils;
|
||||||
|
|
||||||
|
|
@ -29,4 +30,35 @@ public static class MathFunctions
|
||||||
|
|
||||||
return targetPos + targetVel * t;
|
return targetPos + targetVel * t;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Critically damped spring, based on Game Programming Gems 4 Chapter 1.10. https://archive.org/details/game-programming-gems-4/page/95/mode/2up
|
||||||
|
// Returns a 2-tuple of [next_position, next_velocity].
|
||||||
|
public static Tuple<float, float> SmoothDamp(float current, float target, float currentVelocity, float smoothTime, float maxSpeed, float delta)
|
||||||
|
{
|
||||||
|
smoothTime = MathF.Max(smoothTime, 0.0001f);
|
||||||
|
var omega = 2.0f / smoothTime;
|
||||||
|
|
||||||
|
var x = omega * delta;
|
||||||
|
var xExp = 1.0f / (1.0f + x + 0.48f * x * x + 0.235f * x * x * x);
|
||||||
|
var change = current - target;
|
||||||
|
var originalTarget = target;
|
||||||
|
|
||||||
|
// Clamp max speed
|
||||||
|
var maxChange = maxSpeed * smoothTime;
|
||||||
|
change = Math.Clamp(change, -maxChange, maxChange);
|
||||||
|
target = current - change;
|
||||||
|
|
||||||
|
var temp = (currentVelocity + omega * change) * delta;
|
||||||
|
currentVelocity = (currentVelocity - omega * temp) * xExp;
|
||||||
|
var output = target + (change + temp) * xExp;
|
||||||
|
|
||||||
|
// Prevent Overshooting
|
||||||
|
if ((originalTarget - current > 0.0) == (output > originalTarget))
|
||||||
|
{
|
||||||
|
output = originalTarget;
|
||||||
|
currentVelocity = (output - originalTarget) / delta;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Tuple<float, float>(output, currentVelocity);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue