Portal Rider Devlog 3 : hazards


hazards ⚠️

so the orginal PR that was being built in unity 🤢🤮🤧 the hazards had a lot of hazards that were being implemented. I will be implementing these same hazards in the new version 💪🏾 to stay oragnised I make a scene for each hazard.

Initially I wanted to just use the source files. but before we get into how I actually solved this part of the game. It was time to test.

the test

ok so essentially started off making a placeholder hazard isn't he cute which consisted of :

  • RigidBody3D
    • RayCast3D
    • MeshInstance3D using a CapsuleMesh
    • CollisionShape3D with a CapsuleShape

next a Group called Hazard was added to the RigidBody3D as well hazard.gd :

extends RigidBody3D

@onready var ray: RayCast3D = $RayCast3D
var group:String = "Level"
var function:String = "destroyHazard"

func _ready():
  apply_central_force(Vector3(0,0,-420))

func _on_body_entered(body: Node) -> void:
  #print("this body should be " + body.name)
  if "Deleter" in body.get_groups():
    get_tree().call_group(group, function)
    queue_free()

this is a basic version of the script but eventually I will probably add something like this

func _process(delta):
  if ray.is_colliding():
    pass
    #kill player and reset game
    #queue_free()

so i can fully make use of the RayCast3D which will probably kill | reduce health off the player…haven’t decieded yet. but for now i’ll simply focusing on deleting any spawned hazards with a couple of invisible CSGBox3Ds with a group called Deleter on it. so when my hazard collides with one it then calls another function one i haven’t actually used before

get_tree().call_group()

yuh

ngl this 🤯x💯 frfr… i’ll probably use this more in the future

with this setup i would eventually be able to use this to test the next part

spawning hazards

back in the portal scene where like i said, EVERYTHING IS HAPPENING HERE❗ my root node has a couple functions that deal with hazards.

first thing is to refernce the hazard scene we just setup

@onready var hazard = preload("res://test/hazard.tscn")

then we have a function that will spawn a hazard at a random position on the map

mmhmm

doing it this way allows to have a lot of different spawn points there is also a timer that starts when all spawned items have been deleted.

spawn function

func spawnHazard():
  for i in range(hazards.get(current_level, 1)):
    var h = hazard.instantiate()
    print("Hazard spawning")
    # check the ammount of children within $SpawnHolder
    var spawn_length = $SpawnHolder.get_child_count() - 1
    #print(spawn_length)
    # make a random number within that range
    var rand_num = rand.randi_range(0, spawn_length)
    print("Random number: ", rand_num)
    # randomly use number to select a spawn location
    var spawn_position = $SpawnHolder.get_child(rand_num).position
    #print(spawn_position)
    # add a hazard as a child of the level and set it's position
    h.position = spawn_position
    print(h.position)
    add_child(h)
    await get_tree().create_timer(1.0).timeout
    print("i'm finished spawning hazards for this wave")

oh yeah remember our hazard.gd that has that cool way to call a function on another node via group name ❓ WELL I DO ❗❗❗ this is what the function destroyHazard() looks like:

func destroyHazard():
  print("Hazard destroyed")
  destroyed_hazards += 1
  if destroyed_hazards == hazards.get(current_level, 1):
    $InBetweenWaves.start()
    print($InBetweenWaves.time_left)
    destroyed_hazards = 0

as you can see i’m starting the timer in my hierarchy when the current wave of hazards have all been destroyed.

after i got spawning down it was time to use

the source files

gold palm beer coffee flamingo fishbone grenade

by simply taking my test hazard scene and using it as a scene template i simply changed the CollisionShape3D according to cover the various hazard shapes. once i’ve made all of the hazards (there could be more who knows might add the SPICEX logo) it was time to make a couple changes to my spawning solution.

@onready var hazard_paths = [
  "res://gameplay/hazards/badge.tscn",
  "res://gameplay/hazards/beer.tscn",
  "res://gameplay/hazards/coffee.tscn",
  "res://gameplay/hazards/fishbone.tscn",
  "res://gameplay/hazards/flamingo.tscn",
  "res://gameplay/hazards/gold_palm.tscn",
  "res://gameplay/hazards/grenade.tscn",
  "res://gameplay/hazards/hat.tscn",
  "res://gameplay/hazards/hockey_stick.tscn",
  "res://gameplay/hazards/ninja_star.tscn"
]

this array of hazard paths stores the location of the hazards we just created. now i tried using "res://assets/models/FBX/hazards/Beer.fbx" as the path but because Beer.fbx isn’t setup with a RigidBody3D and all of the other setup from the test hazard it doesn’t move when it’s spawned on RUNTIME.

now that we have a neat reference for all these hazards, now it’s time to pick on randomly and then spawn it instead of our test hazard.

@onready var rand = RandomNumberGenerator.new()

func get_random_hazard():
  var random_index = rand.randi_range(0, hazard_paths.size() - 1)
  var random_hazard = load(hazard_paths[random_index])
  return random_hazard

i added this bit of logic to randomly pic an object to load from the array then in my spawnHazard() function i can replace hazard.instantiate() to get_random_hazard().instantiate() and BAM we have the original assets that psybergames studios curated with the help of cubedrop back to life in this ressurection.

the hazard hole