From 90dc606009f7e617a464274cb0c16e7e412ce4e3 Mon Sep 17 00:00:00 2001 From: rowan Date: Sat, 3 May 2025 09:13:19 -0500 Subject: [PATCH] rotate toward cursor working (controller untested) --- src/camera_cache.ts | 39 ---------------------------- src/camera_trigger.ts | 22 ---------------- src/main_camera.ts | 15 +++++++++++ src/message_bus.ts | 6 ++--- src/player.ts | 43 ++++++++++++++++++------------- src/player_input.ts | 59 ++++++++++++++++++++++++++++++++++++++----- 6 files changed, 95 insertions(+), 89 deletions(-) delete mode 100644 src/camera_cache.ts delete mode 100644 src/camera_trigger.ts create mode 100644 src/main_camera.ts diff --git a/src/camera_cache.ts b/src/camera_cache.ts deleted file mode 100644 index fccc38d..0000000 --- a/src/camera_cache.ts +++ /dev/null @@ -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) - - } -} - - diff --git a/src/camera_trigger.ts b/src/camera_trigger.ts deleted file mode 100644 index 697f1a5..0000000 --- a/src/camera_trigger.ts +++ /dev/null @@ -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) - } - } -} diff --git a/src/main_camera.ts b/src/main_camera.ts new file mode 100644 index 0000000..852db6f --- /dev/null +++ b/src/main_camera.ts @@ -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) + } +} diff --git a/src/message_bus.ts b/src/message_bus.ts index 1ca2305..af3bee0 100644 --- a/src/message_bus.ts +++ b/src/message_bus.ts @@ -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 _ready(): void { - MessageBus._inst = this + MessageBus._instance = this } } diff --git a/src/player.ts b/src/player.ts index aa18cb7..41f334e 100644 --- a/src/player.ts +++ b/src/player.ts @@ -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 private _enable_action_callable!: Callable1 _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 } diff --git a/src/player_input.ts b/src/player_input.ts index 0933888..71301b4 100644 --- a/src/player_input.ts +++ b/src/player_input.ts @@ -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 + _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 }