Link
https://jacob-aberdevine.itch.io/the-memory-book
About
The game features three distinct levels, two enemy types, and multiple ways to handle spawning enemies. I programmed the game myself and was involved in designing all the mechanics.
The final version of the game is quite different from its initial form during KomiksJam 2024, as its mechanics were not engaging enough. We realized the game needed major reworks, so we revisited it, hoping to compete in the ZTGK 2024 computer development team competition soon after. Our booth there attracted enough interest to earn us an award!
2D Mouse Input to 3D World Coordinates
The primary way to defend against enemies is by shooting a projectile that follows a parabolic trajectory toward a target position determined by the mouse. Fine-tuning this mechanic took some time to get right.
Below is a section of the player’s script responsible for detecting right mouse button clicks, determining the target position on the ground, and passing the coordinates to the projectile. In summary, the process:
- Reads the 2D mouse position on the screen.
- Casts a ray based on the camera’s position and orientation.
- Detects a collision with the floor and stores the exact impact point in 3D space.
- Spawns a projectile at the player’s position with the ray intersection as its target.
if Input.is_action_just_pressed("right_mouse"):
if not $Cooldown.get_time_left() > 0 and power > 0:
var mousePos = get_viewport().get_mouse_position()
var rayLength = 1000
var from = camera.project_ray_origin(mousePos)
var to = from + camera.project_ray_normal(mousePos) * rayLength
var space = get_world_3d().direct_space_state
var rayQuery = PhysicsRayQueryParameters3D.new()
rayQuery.from = from
rayQuery.to = to
rayQuery.collide_with_areas = true
rayQuery.collision_mask = 1
var result = space.intersect_ray(rayQuery)
if result.get("position"):
power -= 1
stop_move = true
$Cooldown.start()
var init = projectile.instantiate()
get_parent().get_node("Projectiles").add_child(init)
init.global_position = self.global_position
init.end_pos = result.position
To achieve parabolic movement, the projectile updates its position every frame using linear interpolation between two points moving along the parabola, completing the smooth motion in one second.
func _physics_process(delta):
if finished:
return
if count < 1.0:
count += 1.0 * delta
var m1 = lerp(start_pos, height_pos, count)
var m2 = lerp(height_pos, end_pos, count)
global_position = lerp(m1, m2, count)
else:
explode()
finished = true
return