rotate toward cursor working (controller untested)
This commit is contained in:
parent
c9cc6173f0
commit
90dc606009
6 changed files with 95 additions and 89 deletions
|
@ -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)
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
|
@ -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
15
src/main_camera.ts
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,16 +2,16 @@ import { Camera3D, Node, Signal1 } from 'godot'
|
||||||
import { signal } from 'godot.annotations'
|
import { signal } from 'godot.annotations'
|
||||||
|
|
||||||
export default class MessageBus extends Node {
|
export default class MessageBus extends Node {
|
||||||
private static _inst: MessageBus
|
private static _instance: MessageBus
|
||||||
|
|
||||||
static get instance() {
|
static get instance() {
|
||||||
return MessageBus._inst
|
return MessageBus._instance
|
||||||
}
|
}
|
||||||
|
|
||||||
@signal()
|
@signal()
|
||||||
active_camera_changed!: Signal1<Camera3D>
|
active_camera_changed!: Signal1<Camera3D>
|
||||||
|
|
||||||
_ready(): void {
|
_ready(): void {
|
||||||
MessageBus._inst = this
|
MessageBus._instance = this
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 { export_, help, onready } from 'godot.annotations'
|
||||||
import PlayerInput from './player_input'
|
import PlayerInput from './player_input'
|
||||||
import DebugDraw from './debug_draw'
|
import DebugDraw from './debug_draw'
|
||||||
|
@ -6,33 +6,34 @@ import Interactor from './interactor'
|
||||||
import PlayerAnimation, { PlayerAnimationName } from './player_animation'
|
import PlayerAnimation, { PlayerAnimationName } from './player_animation'
|
||||||
|
|
||||||
export default class Player extends CharacterBody3D {
|
export default class Player extends CharacterBody3D {
|
||||||
gravity = ProjectSettings.get_setting('physics/3d/default_gravity')
|
readonly gravity = ProjectSettings.get_setting('physics/3d/default_gravity')
|
||||||
|
|
||||||
@export_(Variant.Type.TYPE_NODE_PATH)
|
|
||||||
camera!: NodePath
|
|
||||||
private _camera!: Camera3D
|
|
||||||
|
|
||||||
@onready("Input")
|
@onready("Input")
|
||||||
player_input!: PlayerInput
|
readonly player_input!: PlayerInput
|
||||||
|
|
||||||
@onready("AnimationTree")
|
@onready("AnimationTree")
|
||||||
player_animation!: PlayerAnimation
|
readonly player_animation!: PlayerAnimation
|
||||||
|
|
||||||
@onready("Interactor")
|
@onready("Interactor")
|
||||||
interactor!: Interactor
|
readonly interactor!: Interactor
|
||||||
|
|
||||||
@help('Forward walk speed in units per second')
|
@help('Forward walk speed in units per second')
|
||||||
@export_(Variant.Type.TYPE_FLOAT)
|
@export_(Variant.Type.TYPE_FLOAT)
|
||||||
walk_speed = 3
|
readonly walk_speed = 3
|
||||||
|
|
||||||
@help('Run speed in units per second')
|
@help('Run speed in units per second')
|
||||||
@export_(Variant.Type.TYPE_FLOAT)
|
@export_(Variant.Type.TYPE_FLOAT)
|
||||||
run_speed = 6
|
readonly run_speed = 6
|
||||||
|
|
||||||
@help('Turn speed in rotations per second')
|
@help('Turn speed in rotations per second')
|
||||||
@export_(Variant.Type.TYPE_FLOAT)
|
@export_(Variant.Type.TYPE_FLOAT)
|
||||||
turn_speed = 1
|
readonly turn_speed = 1
|
||||||
_rotation_speed = 2 * Math.PI
|
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() {
|
is_moving() {
|
||||||
return !this.velocity.is_zero_approx()
|
return !this.velocity.is_zero_approx()
|
||||||
|
@ -53,15 +54,15 @@ export default class Player extends CharacterBody3D {
|
||||||
private _can_act: boolean = true
|
private _can_act: boolean = true
|
||||||
private _wants_to_fire: boolean = false
|
private _wants_to_fire: boolean = false
|
||||||
|
|
||||||
|
// TODO: abstract this horrible shit away
|
||||||
private _try_interact_callable!: Callable0
|
private _try_interact_callable!: Callable0
|
||||||
private _queue_fire_callable!: Callable0
|
private _queue_fire_callable!: Callable0
|
||||||
private _disable_action_callable!: Callable1<string>
|
private _disable_action_callable!: Callable1<string>
|
||||||
private _enable_action_callable!: Callable1<string>
|
private _enable_action_callable!: Callable1<string>
|
||||||
|
|
||||||
_ready(): void {
|
_ready(): void {
|
||||||
this._camera = this.get_node(this.camera) as Camera3D
|
|
||||||
|
|
||||||
this._rotation_speed = this.turn_speed * 2 * Math.PI
|
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._try_interact_callable = Callable.create(
|
||||||
this,
|
this,
|
||||||
|
@ -167,8 +168,14 @@ export default class Player extends CharacterBody3D {
|
||||||
this.move_and_slide()
|
this.move_and_slide()
|
||||||
}
|
}
|
||||||
|
|
||||||
private _aim_at_direction() {
|
// TODO: combine this with _rotate_toward as a general
|
||||||
this._rotate_toward
|
// 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 {
|
_physics_process(delta: float64): void {
|
||||||
|
@ -180,7 +187,7 @@ export default class Player extends CharacterBody3D {
|
||||||
|
|
||||||
if (this.player_input.is_aiming) {
|
if (this.player_input.is_aiming) {
|
||||||
this.velocity = Vector3.ZERO
|
this.velocity = Vector3.ZERO
|
||||||
this._aim_at_direction()
|
this._aim_toward(delta)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 { export_, signal } from 'godot.annotations'
|
||||||
import InputBuffer from './input_buffer'
|
import InputBuffer from './input_buffer'
|
||||||
|
import MainCamera from './main_camera'
|
||||||
|
import MessageBus from './message_bus'
|
||||||
|
|
||||||
export const PlayerMovement = Object.freeze({
|
export const PlayerMovement = Object.freeze({
|
||||||
MoveLeft: 'move_left',
|
MoveLeft: 'move_left',
|
||||||
|
@ -19,13 +21,37 @@ export const PlayerLook = Object.freeze({
|
||||||
class LookDirection {
|
class LookDirection {
|
||||||
static Empty = new LookDirection(Vector2.ZERO, false)
|
static Empty = new LookDirection(Vector2.ZERO, false)
|
||||||
|
|
||||||
readonly value: Vector2
|
_value: Vector2
|
||||||
readonly needs_projection: boolean
|
needs_projection: boolean
|
||||||
|
camera?: Camera3D
|
||||||
|
|
||||||
|
get value() {
|
||||||
|
return this._value
|
||||||
|
}
|
||||||
|
|
||||||
|
set value(val: Vector2) {
|
||||||
|
this._value = val
|
||||||
|
}
|
||||||
|
|
||||||
constructor(value: Vector2, needs_projection: boolean) {
|
constructor(value: Vector2, needs_projection: boolean) {
|
||||||
this.value = value
|
this._value = value
|
||||||
this.needs_projection = needs_projection
|
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({
|
export const PlayerAction = Object.freeze({
|
||||||
|
@ -112,6 +138,8 @@ export default class PlayerInput extends Node3D {
|
||||||
return this._changed_since_last_frame
|
return this._changed_since_last_frame
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _camera!: Camera3D
|
||||||
|
|
||||||
private _running: boolean = false
|
private _running: boolean = false
|
||||||
private _interacting: boolean = false
|
private _interacting: boolean = false
|
||||||
private _aiming: boolean = false
|
private _aiming: boolean = false
|
||||||
|
@ -181,7 +209,16 @@ export default class PlayerInput extends Node3D {
|
||||||
return n * n
|
return n * n
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _await_camera_callable!: Callable1<Camera3D>
|
||||||
|
|
||||||
_ready(): void {
|
_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._min_range_sqr = this._sqr(this.min_range)
|
||||||
this._max_range_sqr = this._sqr(this.max_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)
|
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 {
|
_process(_delta: float64): void {
|
||||||
const next_movement = Input.get_vector(
|
const next_movement = Input.get_vector(
|
||||||
PlayerMovement.MoveLeft,
|
PlayerMovement.MoveLeft,
|
||||||
|
@ -207,11 +250,13 @@ export default class PlayerInput extends Node3D {
|
||||||
PlayerLook.LookUp,
|
PlayerLook.LookUp,
|
||||||
PlayerLook.LookDown
|
PlayerLook.LookDown
|
||||||
)
|
)
|
||||||
this._look_direction = new LookDirection(dir, false)
|
|
||||||
break
|
this._look_direction.value = dir
|
||||||
|
this._look_direction.needs_projection = false
|
||||||
case Device.KeyboardMouse:
|
case Device.KeyboardMouse:
|
||||||
dir = this.get_viewport().get_mouse_position()
|
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
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue