Table of Contents

  1. Overview
  2. Installing Godot, Importing or Continuing our Player Controller
  3. Quick review and Importing assets
  4. Adding Crosshair
  5. Create Gun Scene
  6. Create bullet
  7. Shooting gun
  8. Correcting the shooting position
  9. Adding objects to shoot
  10. FINISH!1!!11!

Finished Project

Soly Image Caption

Download Godot

Click Here to Dowload Godot

Copy of First Person Player Controller project

Click Here to Dowload our First Person Player Controller

Run the correct Program

setup godot

Import new project

importing project

The Player Controller

Basic Player Controller, allows user to move around and freelook

Import new assets for shooter

Click here to download assets

Create new TextureRect, anchor center, and apply texture

Crosshair

Create new Inhereted Scene from Rifle.fbx

Create Gun 1

Resize Rifle and Make sure front of the gun is facing -Z

Create Gun 2

Create a new Material (#434343)

Create Gun 3

Add Raycast at the end of the barrel and make the Target Position (0,0,-1) [DO NOT ROTATE, change the target pos]

Create Gun 4

Add a new AnimationPlayer Node and create a new animation name it ‘Shoot'

Create Gun 5

Add Mes the mesh's Position property into the animation, change animation duration to 0.1, and Snap to 0.01 seconds

Create Gun 6

Insert Keyframes

Create Gun 7

Change the middle keyframe, move the position in the positive Z direction by 0.1

Create Gun 8

Link the Gun/Rifle Scene (not .fbx) to the Player Scene

Create Gun 9

Move the Gun/Rifle Scene as the child of the Camera3D Node

Create Gun 10

CODE make the gun functional

At the top of the player script with the other variables
make a refernce to the rifle and the raycast

extends CharacterBody3D


const SPEED = 5.0
const JUMP_VELOCITY = 6.5
const MOUSE_SENSETIVITY = 0.1 

# ADDED CODE
@onready var gun_anim = $Camera3D/Rifle/AnimationPlayer		
@onready var gun_barrel = $Camera3D/Rifle/RayCast3D

Inside _physics_process function add this code

	## Handle Shooting
	if Input.is_action_pressed("shoot"):
		if !gun_anim.is_playing():
			gun_anim.play("Shoot")

Create a new 3D scene rename it bullet

CreatingBullet1

Add a cube mesh to the scene

CreatingBullet1

Add a new material to the cube, change the albedo and emission colors

CreatingBullet1

Change the scale of the cube to a more bullet like shape

CreatingBullet1

Add RayCast3D and GPUParticles3D Node to the scene

CreatingBullet1

Have the RayCast3D point towards -0.8 Z, and move it where the tip is barely in front of the shape

CreatingBullet1

Hide all nodes except GPU Particle, Create a new cube particle

CreatingBullet1

Change the Albedo and Emission of the particle

CreatingBullet1

Create a new particle process

CreatingBullet1

In Process Material, change the spawn properties

CreatingBullet1

In Time, change the particle to One Shot and maximize explosiveness

CreatingBullet1

Create a new Bullet script

CreatingBullet1

Add a Timer Node to the bullet

CreatingBullet1

In the ‘player.gd' script

At the top with the gun_anim and gun_barrel variables add two new variables

var bullet = load("res://Scenes/bullet.tscn")
var instance

@onready var gun_anim = $Camera3D/Rifle/AnimationPlayer		
@onready var gun_barrel = $Camera3D/Rifle/RayCast3D

Inside the ‘_physics_process', Handle Shooting section

	## Handle Shooting
	if Input.is_action_pressed("shoot"):
		if !gun_anim.is_playing():
			gun_anim.play("Shoot")
			
			instance = bullet.instantiate()
			instance.position = gun_barrel.global_position
			instance.transform.basis = gun_barrel.global_transform.basis
			
			get_parent().add_child(instance)

In the bullet.gd script

extends Node3D

const SPEED = 40.0

@onready var mesh = $MeshInstance3D
@onready var ray = $RayCast3D

@onready var particle = $GPUParticles3D

# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta: float) -> void:
	position += transform.basis * Vector3(0,0, -SPEED) * delta
	
	if ray.is_colliding():
		mesh.visible = false
		particle.emitting = true
		
		await get_tree().create_timer(1.0).timeout
		queue_free()


func _on_timer_timeout() -> void:
	queue_free()

Add a new RayCast3D to the Player

Add ray to player

Inside ‘player.gd' script

Add a new variable referencing the Player's ray

var bullet = load("res://Scenes/bullet.tscn")
var instance
@onready var gun_anim = $Camera3D/Rifle/AnimationPlayer		
@onready var gun_barrel = $Camera3D/Rifle/barrel

@onready var player_ray = $RayCast3D

Inside ‘_physics_process' in shooting section

Replace the instance.transform.basis = gun_barrel.global_transform.basis piece of code with

	var hit_point = player_ray.global_transform.origin + (-player_ray.global_transform.basis.z * 40.0)
	var dir = (hit_point - gun_barrel.global_position).normalized()
	
	if player_ray.is_colliding():
		hit_point = player_ray.get_collision_point()
		dir = (hit_point - gun_barrel.global_position).normalized()
		
	instance.transform.basis = Basis().looking_at(dir, Vector3.UP)

Add a new RigidBody3D Object with mesh and collider

object to shoot

Give the mesh and collider a box mesh and collider

object to shootobject to shoot

Inside ‘bullet.gd' _process function add the new code

func _process(delta: float) -> void:
	position += transform.basis * Vector3(0,0, -SPEED) * delta
	
	if ray.is_colliding():
		mesh.visible = false
		particle.emitting = true
		
		## NEW CODE ADDED HERE
		var collider = ray.get_collider()
		if collider is RigidBody3D:
			print("Hit RigidBody3D")
			collider.apply_impulse((transform.basis * Vector3(0, 0, -1)).normalized() * 3)
        #elif collider is Enemy:
		#	collider.takeDmg(1)
		### NEW CODE END
		
		await get_tree().create_timer(1.0).timeout
		queue_free()

Adding a simple enemy

Create a new Scene with CharacterBody3D as the base node

object to shoot

Add mesh and collider to the CharacterBody3D and make the shape be capsule on both

object to shootobject to shoot

Create a new enemy.gd script

object to shoot

Code for the simple enemy

class_name Enemy
extends CharacterBody3D

var currentHP = 50

func takeDmg(dmg: int) -> void:
	currentHP = currentHP -1
	print(currentHP)

	if currentHP <= 0:
		queue_free()

In the bullet.gd script uncomment the elif statement with the Enemy in it

The could should look like this

	var collider = ray.get_collider()
	if collider is RigidBody3D:
		print("Hit RigidBody3D")
		collider.apply_impulse((transform.basis * Vector3(0, 0, -1)).normalized() * 3)
	elif collider is Enemy:
		collider.takeDmg(1)

✅ Intro to Godot
✅ Introduction to Game Engines
❓ Checked in?!?

Soly Image Caption

As you know this is Solly the seal. You may also have noticed that Solly is always waving alone at the end of very workshop, it is because he is single, a fate for those working for a Triple A company.

A contributing fact to the fate of those working for a Triple A comanpy, is that they have 0 rizz when it comes to clothing. Look at Solly, other than the depression and mental instability you may have also noticed he is not wearing any clothes. This is because he is both broke and has no time to shop.

So it's time to make a difference, help the make a difference; fight against destiny and break Solly out of the cycle. Check-in on BullsConnect to convice USF we are an impactful club and so they will provide us with funding so we can get Solly some rizz by buying him clothes.