Hi again,
I’ve done as you suggested, as well as properly implemented the signals for _on_player_player_shoot and _on_player_player_reload (They now have the green box next to them), but the code still doesn’t seem to work. Upon shooting once, the game crashes with the following errors:
clipammotext.change_text(clipammo)
totalammotext.change_text(totalammo)
This section in my pistol code fails since clipammotext and totalammotext seem to be null instances. (In Godot’s words, "Attempt to call function ’ change_text’ in base ‘null instance’ on a null instance.")
func _on_player_player_shoot() -> void:
if clipammo > 0:
clipammo -= 1
ammo_changed.emit(clipammo, totalammo)
if clipammo == 0:
player.can_shoot = false
In another part of the pistol script, Godot says “Invalid assignment of property or key ‘can_shoot’ with value of type ‘bool’ on a base object of type ‘null instance’.” Not really sure about this one tbh.
if Input.is_action_pressed("Shoot") and can_shoot:
match weapon:
weapons.PISTOLS:
_shoot_projectile_pistol()
weapons.AUTO:
_shoot_auto()
Finally, in the Player script, the _shoot_projectile_pistol() says “Invalid assignment of property or key ‘can_shoot’ with value of type ‘bool’ on a base object of type ‘null instance’.” Which is odd, since it was working before.
I believe all this may have something to do with the other scripts, so I’ll post them in full below just in case.
Player.GD (Not sure why this one is black and white, sorry)
extends CharacterBody3D
var speed
const WALK_SPEED = 5.0
const SPRINT_SPEED = 8.0
const JUMP_VELOCITY = 4.8
const SENSITIVITY = 0.004
const HIT_STAGGER = 5
#head bobbing variables.
const BOB_FREQ = 2.4
const BOB_AMP = 0.08
var t_bob = 0.0
#fov variables.
const BASE_FOV = 75.0
const FOV_CHANGE = 1.5
#signal for hit registration.
signal player_hit
#signal for ammo registration.
signal player_reload
signal player_shoot
signal player_switchweapon
# Get the gravity from the project settings to be synced with RigidBody nodes.
var gravity = 9.8
#Bullets
var bullet = load("res://Scenes/PistolBullet.tscn")
var bullet_trail = load("res://Scenes/BulletTrail.tscn")
var instance
# Weapon Switching. Add to this if you want more weapons.
enum weapons {
PISTOLS,
AUTO,
}
@export var weapon = weapons.PISTOLS
@export var can_shoot = true
#Identifies the head and camera as variables. Used for Camera.
@onready var head = $Head
@onready var camera = $Head/Camera3D
@onready var aim_ray = $Head/Camera3D/Aimray
@onready var aim_ray_end = $Head/Camera3D/AimrayEnd
# Guns.
@onready var pistol = $Head/Camera3D/Pistol_Projectile
@onready var rifle = $Head/Camera3D/Rifle
@onready var gun_anim = $Head/Camera3D/Pistol_Projectile/GunPlayer
@onready var gun_barrel = $Head/Camera3D/Pistol_Projectile/RayCast3D
@onready var rifle_anim = $Head/Camera3D/Rifle/RiflePlayer
@onready var rifle_barrel = $Head/Camera3D/Rifle/BarrelTracker
@onready var pistol_shoot_sound = $"FutureWeapons2-Blaster-SimpleLaserShot01"
@onready var rifle_shoot_sound = $MachineGunGuns01539
@onready var pistol_reload_sound = $Head/Camera3D/Pistol_Projectile/"FutureWeapons2-Blaster-Reload1"
@onready var rifle_reload_sound = $Head/Camera3D/Rifle/MachineGunFoleyReload
@onready var weapon_switching = $Head/Camera3D/WeaponSwitching
#Gets rid of the mouse cursor on load.
func _ready():
Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
#Camera movement business
func _unhandled_input(event):
if event is InputEventMouseMotion:
head.rotate_y(-event.relative.x * SENSITIVITY)
camera.rotate_x(-event.relative.y * SENSITIVITY)
camera.rotation.x = clamp(camera.rotation.x, deg_to_rad(-40), deg_to_rad(60))
func _physics_process(delta):
# Adds the gravity.
if not is_on_floor():
velocity.y -= gravity * delta
# This is the jump feature.
if Input.is_action_just_pressed("jump") and is_on_floor():
velocity.y = JUMP_VELOCITY
# Handle Sprint.
if Input.is_action_pressed("sprint"):
speed = SPRINT_SPEED
else:
speed = WALK_SPEED
# Get the input direction and handle the movement/deceleration. Make sure the directional var is using the head as a basis.
var input_dir = Input.get_vector("left", "right", "up", "down")
var direction = (head.transform.basis * transform.basis * Vector3(input_dir.x, 0, input_dir.y)).normalized()
if is_on_floor():
if direction:
velocity.x = direction.x * speed
velocity.z = direction.z * speed
else:
velocity.x = lerp(velocity.x, direction.x * speed, delta * 7.0)
velocity.z = lerp(velocity.z, direction.z * speed, delta * 7.0)
else:
velocity.x = lerp(velocity.x, direction.x * speed, delta * 3.0)
velocity.z = lerp(velocity.z, direction.z * speed, delta * 3.0)
# Head bob. Calculates bobbing on how fast the character is moving.
t_bob += delta * velocity.length() * float(is_on_floor())
camera.transform.origin = _headbob(t_bob)
# FOV
var velocity_clamped = clamp(velocity.length(), 0.5, SPRINT_SPEED * 2)
var target_fov = BASE_FOV + FOV_CHANGE * velocity_clamped
camera.fov = lerp(camera.fov, target_fov, delta * 8.0)
if Input.is_action_pressed("Shoot") and can_shoot:
match weapon:
weapons.PISTOLS:
_shoot_projectile_pistol()
weapons.AUTO:
_shoot_auto()
emit_signal("player_shoot")
#Reloading
if Input.is_action_just_pressed("Reload"):
match weapon:
weapons.PISTOLS:
_reload_pistol()
weapons.AUTO:
_reload_rifle()
#Weapon Switching
if Input.is_action_just_pressed("Weapon1") and weapon != weapons.PISTOLS:
_raise_weapon(weapons.PISTOLS)
player_switchweapon.emit()
if Input.is_action_just_pressed("Weapon2") and weapon != weapons.AUTO:
_raise_weapon(weapons.AUTO)
player_switchweapon.emit()
move_and_slide()
# Modifies velocity if there's a collision.
func _headbob(time) -> Vector3:
var pos = Vector3.ZERO
pos.y = sin(time * BOB_FREQ) * BOB_AMP
pos.x = cos(time * BOB_FREQ / 2) * BOB_AMP
return pos
func hit(dir):
emit_signal("player_hit")
velocity += dir * HIT_STAGGER
if velocity.length() > SPRINT_SPEED:
velocity = velocity.normalized() * SPRINT_SPEED
#Shooting
func _shoot_projectile_pistol():
if !gun_anim.is_playing():
gun_anim.play("Shoot")
pistol_shoot_sound.play()
instance = bullet.instantiate()
instance.position = gun_barrel.global_position
get_parent().add_child(instance)
if aim_ray.is_colliding():
instance.set_velocity(aim_ray.get_collision_point())
else:
instance.set_velocity(aim_ray_end.global_position)
func _shoot_auto():
if !rifle_anim.is_playing():
rifle_anim.play("Shoot")
rifle_shoot_sound.play()
instance = bullet_trail.instantiate()
if aim_ray.is_colliding():
instance.init(rifle_barrel.global_position, aim_ray.get_collision_point())
get_parent().add_child(instance)
if aim_ray.get_collider().is_in_group("enemy"):
aim_ray.get_collider().hit()
instance.trigger_particles(aim_ray.get_collision_point(),
rifle_barrel.global_position, true)
else:
instance.trigger_particles(aim_ray.get_collision_point(),
rifle_barrel.global_position, false)
else:
instance.init(rifle_barrel.global_position, aim_ray_end.global_position)
get_parent().add_child(instance)
func _on_camera_3d_visibility_changed() -> void:
pass # Replace with function body.
#Plays the correct weapon lowering animation.
func _lower_weapon():
match weapon:
weapons.AUTO:
weapon_switching.play("Rifle_Lower")
weapons.PISTOLS:
weapon_switching.play("Pistol_Lower")
func _raise_weapon(new_weapon):
can_shoot = false
_lower_weapon()
await get_tree().create_timer(0.3).timeout
match new_weapon:
weapons.AUTO:
weapon_switching.play_backwards("Rifle_Lower")
weapons.PISTOLS:
weapon_switching.play_backwards("Pistol_Lower")
weapon = new_weapon
can_shoot = true
func _reload_pistol():
can_shoot = false
pistol_reload_sound.play()
gun_anim.play("pistol_reload")
await get_tree().create_timer(1.0).timeout
can_shoot = true
player_reload.emit()
func _reload_rifle():
can_shoot = false
rifle_reload_sound.play()
rifle_anim.play("Rifle_reload")
await get_tree().create_timer(1.0).timeout
can_shoot = true
player_reload.emit()
func _out_of_ammo():
can_shoot = false
UIScript.GD:
extends Control
@onready var crosshair = $UI/CrosshairControl/Crosshair
@onready var crosshair_hit = $UI/CrosshairControl/Crosshairhit
@onready var hit_rect = $UI/HitRect
@onready var clip_counter = $Ammocounter/ClipCounter
@onready var total_ammo = $Ammocounter/TotalAmmoCounter
@onready var player: Node = get_node("$../Map/NavigationRegion3D/Player")
var current_connected_weapon: Node = null
# Called when the node enters the scene tree for the first time.
func _ready() -> void:
var weapon = get_node("$Head/Camera3D/Pistol_Projectile") # Adjust this path to the actual weapon node
if weapon:
weapon.connect("ammo_changed", Callable(self, "_on_ammo_changed"))
# clip_counter.text = str(weapon.clipammo)
# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta: float) -> void:
pass
func _on_ammo_updated(clipammo: int) -> void:
clip_counter.text = "Ammo: %d" % clipammo
Thanks for the help so far!