258 lines
7.4 KiB
C#
258 lines
7.4 KiB
C#
using Godot;
|
|
|
|
public partial class BoardCameraController : Camera3D
|
|
{
|
|
public override void _Ready()
|
|
{
|
|
InitBoardBounds();
|
|
InitOrbit();
|
|
}
|
|
|
|
private void InitBoardBounds()
|
|
{
|
|
if (HexBoard != null && HexBoard.BoardBounds.Size != Vector2.Zero)
|
|
{
|
|
m_BoardRect = HexBoard.BoardBounds;
|
|
var center2D = m_BoardRect.Position + m_BoardRect.Size * 0.5f;
|
|
m_TargetPosition = new(center2D.X - 1, 0f, center2D.Y + 3);
|
|
}
|
|
else
|
|
{
|
|
m_BoardRect = new(-10, -10, 20, 20);
|
|
m_TargetPosition = new(-1, 0f, 3);
|
|
}
|
|
|
|
var radius = Mathf.Max(m_BoardRect.Size.X, m_BoardRect.Size.Y) * 0.5f;
|
|
m_MinDistance = MinZoomDistance;
|
|
m_MaxDistance = Mathf.Max(m_MinDistance * 2f, radius * MaxZoomOutFactor);
|
|
}
|
|
|
|
private void InitOrbit()
|
|
{
|
|
var startYawRad = GlobalTransform.Basis.GetEuler().Y;
|
|
var startYawDeg = Mathf.RadToDeg(startYawRad);
|
|
|
|
m_RotationIndex = Mathf.PosMod(Mathf.RoundToInt(startYawDeg / 60f), 6);
|
|
m_YawDegrees = m_RotationIndex * 60f;
|
|
|
|
m_Distance = (m_MaxDistance - m_MinDistance) * 0.75f + m_MinDistance;
|
|
UpdateTransformFromOrbit();
|
|
|
|
if (HexBoard != null)
|
|
HexBoard.SetCameraRotationIndex(m_RotationIndex);
|
|
}
|
|
|
|
private void UpdateTransformFromOrbit()
|
|
{
|
|
var pitchRad = Mathf.DegToRad(PitchDegrees);
|
|
var yawRad = Mathf.DegToRad(m_YawDegrees);
|
|
|
|
var horizontal = m_Distance * Mathf.Cos(pitchRad);
|
|
var height = m_Distance * Mathf.Sin(pitchRad);
|
|
|
|
var offset = new Vector3(Mathf.Sin(yawRad) * horizontal, height, Mathf.Cos(yawRad) * horizontal);
|
|
|
|
GlobalPosition = m_TargetPosition + offset;
|
|
LookAt(m_TargetPosition, Vector3.Up);
|
|
}
|
|
|
|
public override void _Process(double delta)
|
|
{
|
|
var dt = (float)delta;
|
|
|
|
HandleRotationInput(dt);
|
|
HandleMovementInput(dt);
|
|
HandleZoomInput(dt);
|
|
}
|
|
|
|
public override void _UnhandledInput(InputEvent @event)
|
|
{
|
|
if (@event is InputEventMouseButton mb && mb.Pressed)
|
|
{
|
|
if (mb.ButtonIndex == MouseButton.WheelUp)
|
|
m_PendingWheelSteps -= 1f;
|
|
else if (mb.ButtonIndex == MouseButton.WheelDown)
|
|
m_PendingWheelSteps += 1f;
|
|
}
|
|
}
|
|
|
|
private void HandleMovementInput(float delta)
|
|
{
|
|
var input = Vector2.Zero;
|
|
|
|
input.Y -= Input.GetActionStrength("camera_move_forward");
|
|
input.Y += Input.GetActionStrength("camera_move_back");
|
|
input.X -= Input.GetActionStrength("camera_move_left");
|
|
input.X += Input.GetActionStrength("camera_move_right");
|
|
|
|
if (input.LengthSquared() < 0.0001f)
|
|
return;
|
|
|
|
var yawRad = Mathf.DegToRad(m_YawDegrees);
|
|
var forward = new Vector2(Mathf.Sin(yawRad), Mathf.Cos(yawRad));
|
|
var right = new Vector2(forward.Y, -forward.X);
|
|
|
|
var worldDir = right * input.X + forward * input.Y;
|
|
var deltaPos = worldDir * MoveSpeed * delta;
|
|
|
|
m_TargetPosition.X += deltaPos.X;
|
|
m_TargetPosition.Z += deltaPos.Y;
|
|
|
|
ClampTargetToBoard();
|
|
UpdateTransformFromOrbit();
|
|
}
|
|
|
|
private void HandleZoomInput(float delta)
|
|
{
|
|
var zoomDelta = 0f;
|
|
|
|
var axis = Input.GetActionStrength("camera_zoom_out") - Input.GetActionStrength("camera_zoom_in");
|
|
if (Mathf.Abs(axis) > 0.001f)
|
|
zoomDelta += axis * PadZoomSpeed * delta;
|
|
|
|
if (Mathf.Abs(m_PendingWheelSteps) > 0.001f)
|
|
{
|
|
zoomDelta += m_PendingWheelSteps * WheelZoomStep;
|
|
m_PendingWheelSteps = 0f;
|
|
}
|
|
|
|
if (Mathf.Abs(zoomDelta) < 0.0001f)
|
|
return;
|
|
|
|
m_Distance = Mathf.Clamp(m_Distance + zoomDelta, m_MinDistance, m_MaxDistance);
|
|
UpdateTransformFromOrbit();
|
|
}
|
|
|
|
private void HandleRotationInput(float delta)
|
|
{
|
|
if (Input.IsActionJustPressed("camera_rotate_left"))
|
|
QueueRotate(-1);
|
|
if (Input.IsActionJustPressed("camera_rotate_right"))
|
|
QueueRotate(+1);
|
|
|
|
var axis = Input.GetActionStrength("camera_rotate_right") - Input.GetActionStrength("camera_rotate_left");
|
|
var axisDir = 0;
|
|
if (axis > GamepadDeadZone)
|
|
axisDir = +1;
|
|
else if (axis < -GamepadDeadZone)
|
|
axisDir = -1;
|
|
|
|
if (axisDir != 0)
|
|
{
|
|
if (m_RotateAxisDir != axisDir)
|
|
{
|
|
m_RotateAxisDir = axisDir;
|
|
m_RotateAxisTimer = 0f;
|
|
QueueRotate(axisDir);
|
|
}
|
|
else
|
|
{
|
|
m_RotateAxisTimer += delta;
|
|
if (m_RotateAxisTimer >= GamepadRotateRepeatDelay)
|
|
{
|
|
m_RotateAxisTimer = 0f;
|
|
QueueRotate(axisDir);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_RotateAxisDir = 0;
|
|
m_RotateAxisTimer = 0f;
|
|
}
|
|
}
|
|
|
|
private void QueueRotate(int steps)
|
|
{
|
|
if (steps == 0 || m_RotationTween != null)
|
|
return;
|
|
|
|
m_RotationIndex = Mathf.PosMod(m_RotationIndex + steps, 6);
|
|
var targetYaw = m_RotationIndex * 60f;
|
|
|
|
var current = m_YawDegrees;
|
|
var deltaYaw = Mathf.Wrap(targetYaw - current, -180f, 180f);
|
|
targetYaw = current + deltaYaw;
|
|
|
|
HexBoard?.SetCameraRotationIndex(m_RotationIndex);
|
|
|
|
m_RotationTween = CreateTween();
|
|
m_RotationTween.SetTrans(Tween.TransitionType.Back);
|
|
m_RotationTween.SetEase(Tween.EaseType.Out);
|
|
m_RotationTween.TweenProperty(this, nameof(YawDegrees), targetYaw, RotationDuration);
|
|
m_RotationTween.TweenCallback(Callable.From(() =>
|
|
{
|
|
YawDegrees = m_RotationIndex * 60f;
|
|
m_RotationTween = null;
|
|
}));
|
|
}
|
|
|
|
private void ClampTargetToBoard()
|
|
{
|
|
if (m_BoardRect.Size == Vector2.Zero)
|
|
return;
|
|
|
|
var minX = m_BoardRect.Position.X;
|
|
var maxX = m_BoardRect.Position.X + m_BoardRect.Size.X;
|
|
var minZ = m_BoardRect.Position.Y;
|
|
var maxZ = m_BoardRect.Position.Y + m_BoardRect.Size.Y;
|
|
|
|
m_TargetPosition.X = Mathf.Clamp(m_TargetPosition.X, minX, maxX);
|
|
m_TargetPosition.Z = Mathf.Clamp(m_TargetPosition.Z, minZ, maxZ);
|
|
}
|
|
|
|
public float YawDegrees
|
|
{
|
|
get => m_YawDegrees;
|
|
set
|
|
{
|
|
m_YawDegrees = value;
|
|
UpdateTransformFromOrbit();
|
|
}
|
|
}
|
|
|
|
[Export]
|
|
public HexBoard3D HexBoard { get; set; }
|
|
|
|
[Export]
|
|
public float GamepadDeadZone = 0.4f;
|
|
|
|
[Export]
|
|
public float GamepadRotateRepeatDelay = 1.0f;
|
|
|
|
[Export]
|
|
public float MaxZoomOutFactor = 2.2f;
|
|
|
|
[ExportCategory("Zoom")]
|
|
[Export]
|
|
public float MinZoomDistance = 8f;
|
|
|
|
[ExportCategory("Movement")]
|
|
[Export]
|
|
public float MoveSpeed = 10f;
|
|
|
|
[Export]
|
|
public float PadZoomSpeed = 40f;
|
|
|
|
[ExportCategory("Rotation")]
|
|
[Export]
|
|
public float PitchDegrees = 60f;
|
|
|
|
[Export]
|
|
public float RotationDuration = 0.35f;
|
|
|
|
[Export]
|
|
public float WheelZoomStep = 3f;
|
|
|
|
private Rect2 m_BoardRect;
|
|
private float m_Distance;
|
|
private float m_MaxDistance;
|
|
private float m_MinDistance;
|
|
private float m_PendingWheelSteps;
|
|
private int m_RotateAxisDir;
|
|
private float m_RotateAxisTimer;
|
|
private int m_RotationIndex;
|
|
private Tween m_RotationTween;
|
|
private Vector3 m_TargetPosition = new(-1, 0f, 3);
|
|
private float m_YawDegrees;
|
|
} |