rotate toward cursor working (controller untested)

This commit is contained in:
Rowan 2025-05-03 09:13:19 -05:00
parent c9cc6173f0
commit 90dc606009
6 changed files with 95 additions and 89 deletions

View file

@ -1,39 +0,0 @@
import { Camera3D, float64, Vector3 } from 'godot'
export class CameraCache extends Object {
private _camera: Camera3D
get camera() { return this._camera }
private _angle: float64
get angle() {
return this._camera.rotation.y
}
private _dirty: boolean = true
constructor(initial_camera: Camera3D) {
super()
this._camera = initial_camera
this._angle = this._camera.rotation.y
}
update_camera(camera: Camera3D) {
this._camera = camera
this._dirty = true
}
recalculate() {
if (this._dirty) {
this._angle = this._camera.rotation.y
this._dirty = false
}
}
transform_vec(vec: Vector3): Vector3 {
return vec.rotated(Vector3.UP, this._angle)
}
}

View file

@ -1,22 +0,0 @@
import { Area3D, Camera3D, NodePath, PhysicsBody3D, Variant } from 'godot'
import { export_ } from 'godot.annotations'
import Player from './player'
import MessageBus from './message_bus'
export default class CameraTrigger extends Area3D {
@export_(Variant.Type.TYPE_NODE_PATH)
camera!: NodePath
_camera!: Camera3D
_ready(): void {
this._camera = this.get_node(this.camera) as Camera3D
}
_on_body_entered(body: PhysicsBody3D) {
if (body instanceof Player) {
this._camera?.make_current()
console.log(MessageBus.instance.active_camera_changed)
MessageBus.instance.active_camera_changed.emit(this._camera)
}
}
}

15
src/main_camera.ts Normal file
View file

@ -0,0 +1,15 @@
import { Camera3D } from 'godot'
import MessageBus from './message_bus'
export default class MainCamera extends Camera3D {
private static _instance: MainCamera
static get instance() {
return MainCamera._instance
}
_ready(): void {
MainCamera._instance = this
MessageBus.instance.active_camera_changed.emit(this)
}
}

View file

@ -2,16 +2,16 @@ import { Camera3D, Node, Signal1 } from 'godot'
import { signal } from 'godot.annotations'
export default class MessageBus extends Node {
private static _inst: MessageBus
private static _instance: MessageBus
static get instance() {
return MessageBus._inst
return MessageBus._instance
}
@signal()
active_camera_changed!: Signal1<Camera3D>
_ready(): void {
MessageBus._inst = this
MessageBus._instance = this
}
}

View file

@ -1,4 +1,4 @@
import { Callable, Callable0, Callable1, Camera3D, CharacterBody3D, Color, float64, NodePath, ProjectSettings, Variant, Vector2, Vector3 } from 'godot'
import { AnyCallable, AnySignal, Callable, Callable0, Callable1, Camera3D, CharacterBody3D, Color, float64, NodePath, ProjectSettings, Signal, Signal0, Signal1, Variant, Vector2, Vector3 } from 'godot'
import { export_, help, onready } from 'godot.annotations'
import PlayerInput from './player_input'
import DebugDraw from './debug_draw'
@ -6,33 +6,34 @@ import Interactor from './interactor'
import PlayerAnimation, { PlayerAnimationName } from './player_animation'
export default class Player extends CharacterBody3D {
gravity = ProjectSettings.get_setting('physics/3d/default_gravity')
@export_(Variant.Type.TYPE_NODE_PATH)
camera!: NodePath
private _camera!: Camera3D
readonly gravity = ProjectSettings.get_setting('physics/3d/default_gravity')
@onready("Input")
player_input!: PlayerInput
readonly player_input!: PlayerInput
@onready("AnimationTree")
player_animation!: PlayerAnimation
readonly player_animation!: PlayerAnimation
@onready("Interactor")
interactor!: Interactor
readonly interactor!: Interactor
@help('Forward walk speed in units per second')
@export_(Variant.Type.TYPE_FLOAT)
walk_speed = 3
readonly walk_speed = 3
@help('Run speed in units per second')
@export_(Variant.Type.TYPE_FLOAT)
run_speed = 6
readonly run_speed = 6
@help('Turn speed in rotations per second')
@export_(Variant.Type.TYPE_FLOAT)
turn_speed = 1
_rotation_speed = 2 * Math.PI
readonly turn_speed = 1
private _rotation_speed = 2 * Math.PI
@help('Aim turn speed in rotations per second')
@export_(Variant.Type.TYPE_FLOAT)
readonly aim_turn_speed = 2
private _aim_rotation_speed = 2 * Math.PI * 2
is_moving() {
return !this.velocity.is_zero_approx()
@ -53,15 +54,15 @@ export default class Player extends CharacterBody3D {
private _can_act: boolean = true
private _wants_to_fire: boolean = false
// TODO: abstract this horrible shit away
private _try_interact_callable!: Callable0
private _queue_fire_callable!: Callable0
private _disable_action_callable!: Callable1<string>
private _enable_action_callable!: Callable1<string>
_ready(): void {
this._camera = this.get_node(this.camera) as Camera3D
this._rotation_speed = this.turn_speed * 2 * Math.PI
this._aim_rotation_speed = this.aim_turn_speed * 2 * Math.PI
this._try_interact_callable = Callable.create(
this,
@ -167,8 +168,14 @@ export default class Player extends CharacterBody3D {
this.move_and_slide()
}
private _aim_at_direction() {
this._rotate_toward
// TODO: combine this with _rotate_toward as a general
// rotate_over_time_toward method
// right now, both rotate instantly
private _aim_toward(delta: float64) {
// TODO: this performs a costly sqrt operation. necessary?
const look = this.player_input.look_direction.project(this.global_position)
look.y = this.global_position.y
this.look_at(look, Vector3.UP, true)
}
_physics_process(delta: float64): void {
@ -180,7 +187,7 @@ export default class Player extends CharacterBody3D {
if (this.player_input.is_aiming) {
this.velocity = Vector3.ZERO
this._aim_at_direction()
this._aim_toward(delta)
return
}

View file

@ -1,6 +1,8 @@
import { float64, Input, InputEvent, InputEventJoypadButton, InputEventJoypadMotion, InputEventKey, InputEventMouse, InputEventMouseMotion, int32, Node3D, Signal0, Signal1, Variant, Vector2 } from 'godot'
import { Callable, Callable1, Camera3D, float64, Input, InputEvent, InputEventJoypadButton, InputEventJoypadMotion, InputEventKey, InputEventMouse, InputEventMouseMotion, int32, Node3D, Signal0, Signal1, Variant, Vector2, Vector3 } from 'godot'
import { export_, signal } from 'godot.annotations'
import InputBuffer from './input_buffer'
import MainCamera from './main_camera'
import MessageBus from './message_bus'
export const PlayerMovement = Object.freeze({
MoveLeft: 'move_left',
@ -19,13 +21,37 @@ export const PlayerLook = Object.freeze({
class LookDirection {
static Empty = new LookDirection(Vector2.ZERO, false)
readonly value: Vector2
readonly needs_projection: boolean
_value: Vector2
needs_projection: boolean
camera?: Camera3D
get value() {
return this._value
}
set value(val: Vector2) {
this._value = val
}
constructor(value: Vector2, needs_projection: boolean) {
this.value = value
this._value = value
this.needs_projection = needs_projection
}
accumulate(delta: Vector2) {
this.value = Vector2.ADD(this._value, delta)
}
project(to_position: Vector3): Vector3 {
if (this.camera) {
// TODO: this performance a sqrt operation,
// check if this is necessary
const distance = this.camera.global_position.distance_to(to_position)
return this.camera?.project_position(this._value, distance) ?? Vector3.ZERO
} else {
return Vector3.ZERO
}
}
}
export const PlayerAction = Object.freeze({
@ -112,6 +138,8 @@ export default class PlayerInput extends Node3D {
return this._changed_since_last_frame
}
private _camera!: Camera3D
private _running: boolean = false
private _interacting: boolean = false
private _aiming: boolean = false
@ -181,7 +209,16 @@ export default class PlayerInput extends Node3D {
return n * n
}
private _await_camera_callable!: Callable1<Camera3D>
_ready(): void {
this._camera = MainCamera.instance
this._await_camera_callable = Callable.create(this, this._await_camera)
MessageBus.instance.active_camera_changed.connect(this._await_camera_callable)
this._look_direction.camera = this._camera
this._min_range_sqr = this._sqr(this.min_range)
this._max_range_sqr = this._sqr(this.max_range)
@ -189,6 +226,12 @@ export default class PlayerInput extends Node3D {
this._input_buffer = new InputBuffer(this.input_buffer_size)
}
private _await_camera(camera: Camera3D) {
console.log('got camera', camera)
this._camera = camera
this._look_direction.camera = camera
}
_process(_delta: float64): void {
const next_movement = Input.get_vector(
PlayerMovement.MoveLeft,
@ -207,11 +250,13 @@ export default class PlayerInput extends Node3D {
PlayerLook.LookUp,
PlayerLook.LookDown
)
this._look_direction = new LookDirection(dir, false)
break
this._look_direction.value = dir
this._look_direction.needs_projection = false
case Device.KeyboardMouse:
dir = this.get_viewport().get_mouse_position()
this._look_direction = new LookDirection(dir, true)
this._look_direction.value = dir
this._look_direction.needs_projection = true
break
}