154 lines
4.8 KiB
C#
154 lines
4.8 KiB
C#
using System;
|
|
using System.Linq;
|
|
using Godot;
|
|
|
|
namespace KarrotStarterTemplate.Common.CameraController;
|
|
|
|
public enum ECameraControllerInputType
|
|
{
|
|
None,
|
|
TopDown,
|
|
FirstPerson,
|
|
ThirdPerson
|
|
|
|
}
|
|
|
|
[GlobalClass]
|
|
public partial class CharacterCameraController : Node3D
|
|
{
|
|
[ExportCategory("Nodes")]
|
|
[Export] public Node3D characterToFollow { get; set; }
|
|
|
|
[ExportCategory("Properties")]
|
|
[ExportGroup("Mouse input")]
|
|
[Export] public ECameraControllerInputType inputType = ECameraControllerInputType.None;
|
|
[Export] public float mouseSensitivity { get; set; } = .5f;
|
|
[Export] public Vector2 cameraTiltLimit { get; set; } = new(-45f, 45f);
|
|
|
|
[ExportGroup("Player")]
|
|
[Export] public bool followPlayer = true;
|
|
[Export(PropertyHint.Range, "0, 1")] public float followDelay { get; set; } = .5f;
|
|
[Export] public bool rotatePlayer = true;
|
|
[Export(PropertyHint.Range, "0, 1")] public float rotateDelay { get; set; } = .5f;
|
|
|
|
private Camera3D _camera; // todo
|
|
private SpringArm3D _springArm;
|
|
private Vector2 _mouseInput = Vector2.Zero;
|
|
|
|
public Camera3D camera => _camera;
|
|
public SpringArm3D springArm => _springArm;
|
|
// Called when the node enters the scene tree for the first time.
|
|
public override void _Ready()
|
|
{
|
|
if (Engine.IsEditorHint())
|
|
{
|
|
TopLevel = true;
|
|
return;
|
|
}
|
|
|
|
characterToFollow ??= (CharacterBody3D)GetParent();
|
|
|
|
_springArm = GetChildren().OfType<SpringArm3D>().First();
|
|
_camera = _springArm.GetChildren().OfType<Camera3D>().First();
|
|
|
|
GlobalPosition = characterToFollow.GlobalPosition;
|
|
// Input.SetMouseMode(Input.MouseModeEnum.Captured);
|
|
}
|
|
|
|
public override void _UnhandledInput(InputEvent @event)
|
|
{
|
|
if (Engine.IsEditorHint())
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (inputType == ECameraControllerInputType.None)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (@event is InputEventMouseMotion mouseMotion)
|
|
{
|
|
_mouseInput = -mouseMotion.Relative * mouseSensitivity;
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
public override void _PhysicsProcess(double delta)
|
|
{
|
|
if (Engine.IsEditorHint())
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (followPlayer)
|
|
{
|
|
float weight = Mathf.Clamp(20 * (1 - followDelay) * (float)delta, 0,1 );
|
|
GlobalPosition = followDelay > 0 ? GlobalPosition.Lerp(characterToFollow.GlobalPosition, weight) : characterToFollow.GlobalPosition;
|
|
}
|
|
|
|
if (rotatePlayer)
|
|
{
|
|
float weight = Mathf.Clamp(20 * (1 - rotateDelay) * (float)delta, 0,1 );
|
|
Vector3 v = characterToFollow.GlobalRotation;
|
|
|
|
switch (inputType)
|
|
{
|
|
|
|
case ECameraControllerInputType.None:
|
|
break;
|
|
case ECameraControllerInputType.TopDown:
|
|
var (x, y, z) = GetMouseIntersectedDirection();
|
|
characterToFollow.GlobalRotation = new Vector3(v.X, Mathf.LerpAngle(v.Y, -Mathf.Atan2(-x, z), weight) , v.Z);
|
|
break;
|
|
case ECameraControllerInputType.FirstPerson:
|
|
case ECameraControllerInputType.ThirdPerson:
|
|
characterToFollow.GlobalRotation = new Vector3(v.X, Mathf.LerpAngle(v.Y, _springArm.GlobalRotation.Y, weight) , v.Z);
|
|
break;
|
|
}
|
|
}
|
|
|
|
switch (inputType)
|
|
{
|
|
|
|
case ECameraControllerInputType.FirstPerson:
|
|
case ECameraControllerInputType.ThirdPerson:
|
|
_springArm.Rotation += new Vector3(_mouseInput.Y * (float)delta, 0f, 0f);
|
|
Rotation += new Vector3(0f, _mouseInput.X * (float)delta, characterToFollow.GlobalPosition.Y);
|
|
|
|
var (x, y, z) = _springArm.RotationDegrees;
|
|
_springArm.RotationDegrees = new Vector3(Mathf.Clamp(x, cameraTiltLimit.X, cameraTiltLimit.Y), y, z);
|
|
|
|
_mouseInput = Vector2.Zero;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Called every frame. 'delta' is the elapsed time since the previous frame.
|
|
public override void _Process(double delta)
|
|
{
|
|
if (Engine.IsEditorHint())
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
public virtual Vector3 GetMouseIntersectedPosition()
|
|
{
|
|
var position2D = GetViewport().GetMousePosition();
|
|
var dropPlane = new Plane(new Vector3(0, 10f, 0), 0f);
|
|
var position3D =
|
|
dropPlane.IntersectsRay(camera.ProjectRayOrigin(position2D), camera.ProjectRayNormal(position2D));
|
|
|
|
|
|
return position3D ?? Vector3.Zero;
|
|
}
|
|
|
|
public virtual Vector3 GetMouseIntersectedDirection()
|
|
{
|
|
return (characterToFollow.GlobalPosition - GetMouseIntersectedPosition()).Normalized();
|
|
}
|
|
}
|