1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
|
# Hurry Curry! - a game about cooking
# Copyright 2024 nokoe
# Copyright 2024 tpart
# Copyright 2024 metamuffin
# Copyright 2024 BigBrotherNii
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, version 3 of the License only.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
#
class_name FollowCamera
extends Camera3D
const ROTATE_SPEED: float = 1.5
const ROTATE_WEIGHT: float = 8.0
const ROTATE_UP_SPEED: float = 0.7
const ROTATE_UP_WEIGHT: float = 4.0
const ANGLE_UP_MIN: float = 0.5
const ANGLE_UP_MAX: float = 0.4999 * PI
const LOOK_WEIGHT: float = 8.0
const MOVE_WEIGHT: float = 2.0
const ZOOM_SPEED: float = 1.0
const DISCRETE_DURATION: float = 0.3
const ZOOM_WEIGHT: float = 10.0
const MAX_ZOOM: float = 20.0
const MIN_ZOOM: float = 2.0
const ZOOM_CURRENT_TO_MAX_RATIO: float = 1.35
@export var target: Node3D
var angle_target: float = 0
var angle: float = 0
var angle_up_target: float = 1
var angle_up: float = 1
var ground: Vector3
var camera_distance: float = 10.
var camera_distance_target: float = camera_distance
var disable_input_menu := false
var disable_input_lobby := false
var _disable_input := false
func _ready():
if target == null:
push_warning("target is not set")
else:
ground = target.position
func _process(delta):
if target != null:
follow(delta)
func _input(_event):
if _disable_input: return
if Input.is_action_just_pressed("reset"):
reset()
func reset():
angle_target = 0
angle_up_target = 1
camera_distance_target = 10
func follow(delta):
if not _disable_input: angle_target += Input.get_axis("rotate_left", "rotate_right") * (
ROTATE_SPEED * delta * (-1 if Global.get_setting("gameplay.invert_camera") else 1)
)
angle = G.interpolate_angle(angle, angle_target, delta * ROTATE_WEIGHT)
if not _disable_input: angle_up_target += Input.get_axis("rotate_down", "rotate_up") * (
ROTATE_UP_SPEED * delta * (-1 if Global.get_setting("gameplay.invert_camera") else 1)
)
angle_up_target = clamp(angle_up_target, ANGLE_UP_MIN, ANGLE_UP_MAX)
angle_up = G.interpolate_angle(angle_up, angle_up_target, delta * ROTATE_UP_WEIGHT)
var offset = Vector3(0, sin(angle_up) * camera_distance, cos(angle_up) * camera_distance).rotated(Vector3.UP, angle)
var new_transform = transform
new_transform.origin = target.position + offset
new_transform = new_transform.looking_at(target.position)
if Global.get_setting("gameplay.interpolate_camera_rotation"):
transform.basis = Basis.from_euler(Vector3(
G.interpolate_angle(transform.basis.get_euler().x, new_transform.basis.get_euler().x, delta * LOOK_WEIGHT),
G.interpolate_angle(transform.basis.get_euler().y, new_transform.basis.get_euler().y, delta * LOOK_WEIGHT),
G.interpolate_angle(transform.basis.get_euler().z, new_transform.basis.get_euler().z, delta * LOOK_WEIGHT)
))
else:
transform.basis = new_transform.basis
# add 0.5, this is the head height
ground = G.interpolate(ground, target.position + Vector3(0., 0.5, 0.), delta * MOVE_WEIGHT)
if not _disable_input:
var zoom_dist = Input.get_axis("zoom_in", "zoom_out") * delta
zoom_dist += float(Input.is_action_just_pressed("zoom_out_discrete")) * DISCRETE_DURATION
zoom_dist -= float(Input.is_action_just_pressed("zoom_in_discrete")) * DISCRETE_DURATION
camera_distance_target *= exp(zoom_dist * ZOOM_SPEED)
camera_distance_target = clamp(camera_distance_target, MIN_ZOOM, MAX_ZOOM)
camera_distance = G.interpolate(camera_distance, camera_distance_target, delta * ZOOM_WEIGHT)
position = ground + offset
func set_ingame(state: bool, in_lobby: bool):
# Disable input in lobby
if state and in_lobby:
reset()
disable_input_lobby = true
else:
disable_input_lobby = false
update_disable_input()
func update_disable_input():
_disable_input = disable_input_lobby or disable_input_menu
|