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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
|
extends CharacterBody3D
const SPEED = 5.0
const RUN_SPEED = 1.5 * SPEED
const JUMP_VELOCITY = 4.5
const THROW_FORCE = 10.0
const RUN_CONSUMPTION = 20.0
const STAM_REGEN = 10.0
#@export_node_path("ProgressBar") var stam_bar_path = null
@export var stam_bar: ProgressBar = null;
@export var seed_text: RichTextLabel = null;
var stamina: float = 100
var num_seeds: int = 0
# Get the gravity from the project settings to be synced with RigidBody nodes.
var gravity = ProjectSettings.get_setting("physics/3d/default_gravity")
var current_node: Node3D = null
var holding: Node3D = null
var climbing: bool = false
var last_wall_direction: Vector3 = Vector3()
var inital_camera_offset: Vector3 = Vector3()
var camera_rotation: float = 0
var camera_pitch: float = 0
func got_body(body: Node3D):
if current_node == null:
print("Got body ", body)
current_node = body
func leave_body(body: Node3D):
if current_node == body:
current_node = null
func pickup():
if current_node.collision_layer & 0b1000:
num_seeds += 1
var node = current_node
node.get_parent().remove_child(node)
node.queue_free()
elif holding == null:
holding = current_node
current_node.get_parent().remove_child(current_node)
holding.position = Vector3.ZERO
holding.freeze = true
$Holder.add_child(holding)
func get_camera_fwd() -> Vector3:
var fwd: Vector3 = -Vector3(sin(camera_rotation), 0, cos(camera_rotation))
return fwd
func get_camera_right() -> Vector3:
var right: Vector3 = Vector3(cos(camera_rotation), 0, -sin(camera_rotation))
return right
func throw():
var g_pos = holding.global_position
holding.get_parent().remove_child(holding)
get_parent().add_child(holding)
holding.global_position = g_pos
var fwd = get_camera_fwd()
var right = get_camera_right()
var dir = (fwd + right).normalized()
holding.freeze = false
holding.apply_impulse(THROW_FORCE*dir)
holding = null
func _ready():
assert(stam_bar != null, "Stamina bar is null")
assert(seed_text != null, "Seed text is null")
Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
$Picker.connect("body_entered", got_body)
$Picker.connect("body_exited", leave_body)
inital_camera_offset = $Camera3D.global_position - global_position
func _process(delta: float):
stam_bar.value = stamina
seed_text.text = str(num_seeds)
turn_camera(delta)
func turn_camera(delta: float):
var turn_amt = Input.get_axis("turn_right", "turn_left")
camera_rotation += turn_amt * delta
var pitch_amt = Input.get_axis("turn_up", "turn_down")
camera_pitch = clamp(camera_pitch + pitch_amt * delta, deg_to_rad(-45.0), deg_to_rad(45.0))
func _physics_process(delta):
var speed = SPEED
var stam_consumption = 0
if Input.is_action_pressed("run") and stamina > 0:
speed = RUN_SPEED
stam_consumption += RUN_CONSUMPTION
if Input.is_action_just_pressed("pickup"):
if holding == null and current_node != null:
pickup()
elif holding != null:
throw()
# Add the gravity.
if not is_on_floor() and not climbing:
velocity.y -= gravity * delta
var fwd = get_camera_fwd()
var right = get_camera_right()
var valid_climb = false
if is_on_wall():
for col in get_slide_collision_count():
var col_obj = get_slide_collision(col)
#print("Collider layer is ", col_obj.get_collider().collision_layer, " ", col_obj.get_collider())
if col_obj.get_collider().collision_layer & 0b100:
valid_climb = true
if valid_climb and stamina > 0:
last_wall_direction = get_wall_normal()
climbing = true
elif climbing:
climbing = false
var climb_direction_fwd = Vector3()
var climb_direction_right = Vector3()
if climbing:
climb_direction_fwd = (Vector3.UP - Vector3.UP.project(last_wall_direction)).normalized()
# TODO
# This should not be relative to the camera, climbing should be fixed, I think I might need
# to take a cross product here to do that with the wall normal and the fwd/up direction on the wall
climb_direction_right = (right - right.project(last_wall_direction)).normalized()
# Handle Jump.
if Input.is_action_just_pressed("ui_accept") and is_on_floor():
velocity.y = JUMP_VELOCITY
# Make sure player is looking in the forward direction
# TODO player should only start looking at the fwd direction when they start moving
look_at(global_position + fwd)
# Get the input direction and handle the movement/deceleration.
# As good practice, you should replace UI actions with custom gameplay actions.
var input_dir = Input.get_vector("left", "right", "backward", "forward")
var direction = (input_dir.y * fwd + input_dir.x * right).normalized()
var climb_direction: Vector3 = (input_dir.y * climb_direction_fwd + input_dir.x * climb_direction_right).normalized()
if climbing:
velocity = climb_direction * speed
# TODO there should be some way to force the player to be locked to the wall without
# just pushing into the wall slightly.
velocity += -speed * last_wall_direction
if input_dir.length_squared() > 0:
stam_consumption += RUN_CONSUMPTION
# Force player to look toward wall
look_at(global_position - last_wall_direction)
# Set camera rotation so it continues to face the wall
# TODO player should still be able to move the camera, this should probably just nudge the camera to this
# position while climbing and not controling the camera
camera_rotation = Vector2(last_wall_direction.x, last_wall_direction.z).angle_to(Vector2(inital_camera_offset.x, inital_camera_offset.z))
elif direction:
velocity.x = direction.x * speed
velocity.z = direction.z * speed
else:
velocity.x = move_toward(velocity.x, 0, speed)
velocity.z = move_toward(velocity.z, 0, speed)
move_and_slide()
# Position camera
$Camera3D.global_position = global_position + inital_camera_offset.rotated(Vector3.RIGHT, camera_pitch).rotated(Vector3.UP, camera_rotation)
$Camera3D.look_at(global_position)
if stam_consumption == 0 and not Input.is_action_pressed("run") and is_on_floor():
stam_consumption = -STAM_REGEN
stamina = clamp(stamina - stam_consumption * delta, 0, 100)
|