Shooting projectiles

Problem

You want to shoot projectiles from your player/mob/etc..

Solution

For this example, we’ll use the “Mini Tank” that we set up in KinematicBody: Movement.

Setting up the bullet

First, we’ll set up a “bullet” object that we can instance. Here are the nodes we’ll use:

 Area: Bullet
     MeshInstance
     CollisionShape

For your mesh, you can use one of Godot’s built-in primitive shapes, or something like this:

alt alt

Note

If you’d like to use the bullet model pictured here, you can grab it from Kenney’s “Weapon Pack”.

Add your mesh to the MeshInstance and a scale a collision shape to match.

Warning

Remember to align your MeshInstance with the forward direction (-Z) of the Area node, or your bullet won’t look like it’s flying the right way!

Add a script and connect the Area’s body_entered signal.

extends Area

signal exploded

export var muzzle_velocity = 25
export var g = Vector3.DOWN * 20

var velocity = Vector3.ZERO


func _physics_process(delta):
    velocity += g * delta
    look_at(transform.origin + velocity.normalized(), Vector3.UP)
    transform.origin += velocity * delta


func _on_Shell_body_entered(body):
    emit_signal("exploded", transform.origin)
    queue_free()

We’re using a custom gravity vector, g so that we can control how the shell flies from the tank’s cannon, giving it a nice arc effect. If you’d rather your projectiles move in a straight line, you can remove the line that applies it in _physics_process().

Using look_at() each frame turns the bullet to point in its direction of travel.

We’ll also emit an exploded signal, which you can connect up to implement explosion and/or damage effects (but that’s for another recipe).

Shooting

Now in the tank (or whatever object you have doing the shooting), add a Position3D child at the point where you want the bullets to appear. In the case of our tank, we’re placing it at the end of the cannon barrel:

alt alt

Now we can add the code to the tank’s script. First a way to add the bullet scene we’re going to instance:

export (PackedScene) var Bullet

And in _process() or _unhandled_input() (wherever you’re capturing input), add the code to instance the bullet:

if Input.is_action_just_pressed("shoot"):
    var b = Bullet.instance()
    owner.add_child(b)
    b.transform = $Cannon/Muzzle.global_transform
    b.velocity = -b.transform.basis.z * b.muzzle_velocity

That’s it - run your scene and try it out: