We have an existing player character; we want to make him fire at enemies, or throw projectiles.
This Godot 4 recipe solves this problem.
Straight-line vs Parabolic
Some objects such as rocks, daggers or arrows should follow parabolic path (under the influence of gravity).
But sometimes we are not looking for realism and so we want to throw an object in straight line; as in Lep’s World or Dangerous Dave.
For both the cases, we need to:
- Spawn the object in front of player.
- Set its direction to be direction of player.
- Apply velocity to make it move.
- If velocity is not defined, it can be done by changing position with respect to time.
- Once the object has collided with anything; remove it from game world and:
- If the collider has
health
property, decrement it. - Else do nothing.
- If the collider has
So lets do this now.
Creating throwable
In Godot, we make a scene for throwable object. I used RigidBody2D as root node. If object is supposed to go in straight line, just make the gravity_scale
to 0.0.
This is how the scene looks like:
The rigid body physics will be applied to it resulting in a good projectile motion. However, if you don’t want to use rigid-body-physics (for performance reasons), then make Area3D as root and manually update position like this:
# We will have to call this function continously in _process(delta)
# with the user-defined direction and speed parameters
func move(direction: Vector2, speed: float):
position += direction.normalized() * speed
But for for rigid-body-based solution, following is script code for the throwable object:
extends RigidBody2D
class_name Throwable
# How much health reduced if it hit
@export var health_impact: float = 1.0
func _ready():
add_to_group("throwables")
func throw(initial_force: float, direction: Vector2):
var unit_direction := direction.normalized()
var impulse := unit_direction * initial_force
apply_central_impulse(impulse)
func _on_body_entered(body):
if "health" in body:
body.health -= health_impact
queue_free()
The above code also reduces the health
of any object having health
attribute by health_impact
amount.
Spawning throwable
The throw()
function defined above will be called by the player. Following is an example code for throwing dagger. It loads the dagger scene (which is rigid-body object, and sets its initial position to player’s position (with some offset) and sets direction towards the mouse pointer, and finally calls the throw(force, direction)
function on it):
# Throwable weapon (example of dagger)
if Input.is_action_just_pressed("shoot"):
var dagger = load("res://Weapons/Dagger.tscn").instantiate() as Throwable
dagger.position = self.position + Vector2(0, -8)
var direction = (get_global_mouse_position() - self.position).normalized()
dagger.throw(640, direction)
get_parent().add_child(dagger)
Once spawned, the throwable scene must take care of its movement, and impact. After hitting something, it is responsible for reducing health of target and queue_free()
itself and produce smoke or anything to visualize the impact on target site.
Another interesting approach for bullet like things
Raycast (see this). It is great for very fast things such as bullets or lasers. Ray casting is the use of a ray to test if it hits something in given direction, and if it does, we get the collider and reduce its health.
This is especially helpful for FPS games in 3D.
Thank you for reading <3