I'm a beginner to Godot and I want to develop the skills necessary for making a decent weapon switching system in Godot for a first person shooter demo I'm trying to make; ideally I'll learn what it takes to make a first person shooter like Wolfenstein 3d or Doom with 3d models. I've already made a first person controller, I know how to make the guns fire and I also made a weapon switching system. However, the weapon switching system seems amateurish and I want it to be more improved. In order to give you guys of where I'm at, I'll say that I've been following tutorials by the Youtuber Garbaj, I've followed his tutorial on hitscans weapons, making a basic first person controller and switching weapons. I've also been following tutorials by Yotuber PandaDev on how to make a hitscan weapon and how to make an ammo system. PandaDev's videos are similiar to that of Garbaj but, he also taught me how to seamlessly integrate animations and interface element into the demo I'm working on. In order to understand where I am at this stage, I'd recommend look at the videos that have influenced me as it might be helpful in helping you to help me. Here is my code for weapon switching (this piece of code is from the player controller script):

    func weapon_select(): #Weapon Button Switching Code
    
    	
    
    	if Input.is_action_just_pressed("weapon1") and not current_weapon == 1:
    		anim_player.play("pistol_equip")
    		current_weapon = 1
    	elif Input.is_action_just_pressed("weapon2")and not current_weapon == 2:
    		anim_player.play("shot_equip")
    		current_weapon = 2
    	elif Input.is_action_just_pressed("weapon3")and not current_weapon == 3:
    		anim_player.play("rifle_equip")
    		current_weapon = 3
    
    	
    	if current_weapon == 1:
    		pistol.visible = true
    		pistol.shoot()
    	else:
    		pistol.visible = false
    
    	if current_weapon == 2:
    		shotgun.visible = true
    		shotgun.shoot()
    	else:
    		shotgun.visible = false
    
    	if current_weapon == 3:
    		rifle.visible = true
    		rifle.shoot()
    	else:
    		rifle.visible = false

Now here is the code I use for shooting the guns (this works well with the weapon switching system code):

extends Spatial

class_name weapon

export var damage = 10
export var ammo = 5
export var max_ammo = 5
export var fire_anim = "pistol_fire"
export var gun_action = "pistol fire"
export var reload_anim = "pistol_reload"

onready var aimcast = $"../../../Head/Camera/AimCast"
onready var anim_player = $"../../../AnimationPlayer"
onready var ammo_text = $"../../../Head/Camera/Control/ammo_count"

func _ready():
	pass
	




func shoot():
	if Input.is_action_pressed("fire"):
		if ammo > 0:
			if not anim_player.is_playing():
				anim_player.play(fire_anim)
				ammo -= 1
				if aimcast.is_colliding():
					var target = aimcast.get_collider()
					if target.is_in_group("Enemy"):
						target.health -= damage
		print(gun_action)
#Reloading Code underneath here!
	ammo_text.text = String(ammo)
	if ammo <= 0:
		anim_player.play(reload_anim)
		ammo = max_ammo
	if Input.is_action_just_pressed("reload") and ammo < 5:
		anim_player.play(reload_anim)
		ammo = max_ammo

What I did is I applied the shooting script to one of the guns(the pistol) and then I make the other guns inherit the exported variables from it in their own respect scripts and then swap out a few things to make each gun feel distinct. So now what do I want improved? Well, I want to implement weapon switching animations to my weapon switching code. However, I don't know how to completely do it. I've managed to implement weapon equipping animation but, only when it comes to switching the weapons with buttons, not when it comes to using the mouse wheel. I've also don't know how I'd integrate weapon unequipping animations into the code. The Youtuber Skyvastern has a tutorial on creating a fps weapon manager that does indeed use equipping and unequipping animations but, I don't want to follow his tutorial again as his weapon manager isn't to my liking (it's not old school as I want). I also don't know how to use Skyvastern's animation code in the weapon switching system created by Garbaj, as I don't really understand Skyvastern's animation code. Any serious help would be greatly appreciated in this endeavor. I've gone elsewhere for help on the internet. However, as I noob, the advice I received previously was difficult to understand. They're other things that I also need help with but, they're a lot of tutorials on those subject that I haven't explored yet. So for now, I'm focusing strictly on getting this weapon switching system improved. Again, any help would be appreciated.

So, the first thing I would do is abstract the weapon into a class object. It can have the data needed for the use of the weapon (weapon stats) as well as a reference to the 3D model and the function that can be performed (shooting, reloading, etc.). When you switch to a new weapons, you can hide the current weapon at that point or do nothing if that weapon is equipped (avoiding all the if statements).

func weapon_select():
    if Input.is_action_just_pressed("weapon1"):
        switch_to_weapon(pistol_weapon)
    elif Input.is_action_just_pressed("weapon2"):
        switch_to_weapon(shotgun_weapon)

func switch_to_weapon(next_weapon):
    if next_weapon == current_weapon:
        return
    current_weapon.model.visible = false
    next_weapon.model.visible = true
    anim_player.play(next_weapon.animation)
    next_weapon.shoot()
    current_weapon = next_weapon

@cybereality said: So, the first thing I would do is abstract the weapon into a class object. It can have the data needed for the use of the weapon (weapon stats) as well as a reference to the 3D model and the function that can be performed (shooting, reloading, etc.). When you switch to a new weapons, you can hide the current weapon at that point or do nothing if that weapon is equipped (avoiding all the if statements).

func weapon_select():
    if Input.is_action_just_pressed("weapon1"):
        switch_to_weapon(pistol_weapon)
    elif Input.is_action_just_pressed("weapon2"):
        switch_to_weapon(shotgun_weapon)

func switch_to_weapon(next_weapon):
    if next_weapon == current_weapon:
        return
    current_weapon.model.visible = false
    next_weapon.model.visible = true
    anim_player.play(next_weapon.animation)
    next_weapon.shoot()
    current_weapon = next_weapon

I tried this code but, I got an error I don't know to fix:

That was just pseudo-code. You have to create a Weapon class and give it those properties.

Here is an example of what I mean. This would be in its own file, like Weapon.gd

class_name Weapon
    var model : MeshInstance
    var animation : String

    func shoot():
        pass
    
    func reload():
        pass

Then in the main class you can create a weapon.

var current_weapon
var pistol_weapon

func _ready():
    pistol_weapon = Weapon.new()
    pistol_weapon.model = get_node("Path/To/Pistol")
    pistol_weapon.animation = "pistol_equip"
    current_weapon = pistol_weapon

@cybereality said: That was just pseudo-code. You have to create a Weapon class and give it those properties.

Luckily for me. I got my unedited code backed up.

@cybereality said: Here is an example of what I mean. This would be in its own file, like Weapon.gd

class_name Weapon
    var model : MeshInstance
    var animation : String

    func shoot():
        pass
    
    func reload():
        pass

So basically, does this mean that I should just break up the code for firing and reloading into two seperate functions within the pistol script I already have?

Then in the main class you can create a weapon.

var current_weapon
var pistol_weapon

func _ready():
    pistol_weapon = Weapon.new()
    pistol_weapon.model = get_node("Path/To/Pistol")
    pistol_weapon.animation = "pistol_equip"
    current_weapon = pistol_weapon

I'm a bit confused by this part. I'm going to try to explain what I understand. If my memory serves me right, pistol_weapon.model means get/add model to the script of the pistol_weapon script right? Also, does the get_node do the same thing as the $? I can understand what this pseudo-code is getting at but, I'm still a bit confused. Perhaps it would help if I showed you all the variables I have for the weapon code I use in the main player script.

var weapon = ["pistol", "shotgun", "rifle"]#this is useless(I really don't know why I have this here)

var current_weapon_index = 0

var current_weapon = 1



onready var head = $Head
onready var aimcast = $Head/Camera/AimCast
onready var pistol = $Head/Hand/Pistol
onready var shotgun = $Head/Hand/Shotgun
onready var rifle = $Head/Hand/Rifle
onready var anim_player = $AnimationPlayer

get_node() is the same as $ ($ is actually just an alias/macro for get_node()). My method is using a class to hold the data, which makes things easier as you can just call functions on the weapon object, rather than having like 3 different if statements with the same code. current_weapon becomes an object (rather than a number) so you can call the function or properties on it. In your case, you can keep the onready variables for getting the node, that you will need either way. But here you can reference the nodes you already saved.

func _ready():
    pistol_weapon = Weapon.new()
    pistol_weapon.model = pistol
    pistol_weapon.animation = "pistol_equip"
    current_weapon = pistol_weapon

The shoot and reload functions were examples. You could also just call that from your main function, but it would require less code if you handled the visual part of the shooting/reloading in the class. You could store another String with the shoot/reload animation and call the animation from within the Weapon class. The actual shooting logic should still be in the main player class (like if you need to shoot a ray and check for collision, the makes sense to keep in the player class).

@cybereality said: get_node() is the same as $ ($ is actually just an alias/macro for get_node()). My method is using a class to hold the data, which makes things easier as you can just call functions on the weapon object, rather than having like 3 different if statements with the same code. current_weapon becomes an object (rather than a number) so you can call the function or properties on it. In your case, you can keep the onready variables for getting the node, that you will need either way. But here you can reference the nodes you already saved.

func _ready():
    pistol_weapon = Weapon.new()
    pistol_weapon.model = pistol
    pistol_weapon.animation = "pistol_equip"
    current_weapon = pistol_weapon

The shoot and reload functions were examples. You could also just call that from your main function, but it would require less code if you handled the visual part of the shooting/reloading in the class. You could store another String with the shoot/reload animation and call the animation from within the Weapon class. The actual shooting logic should still be in the main player class (like if you need to shoot a ray and check for collision, the makes sense to keep in the player class).

I tried this could and got another error regarding the "Weapon" identifier. I suspect this error has something to do with the node structure (the code for the shooting and reloading is the pistol script) but, I'm not certain. The Player2 script is just a copy of my original Player script.

You need a Weapon.gd file. With the code from the post which starts with:

class_name Weapon

That defines a class, then you can use the Weapon class elsewhere.

@cybereality said: You need a Weapon.gd file. With the code from the post which starts with:

class_name Weapon

That defines a class, then you can use the Weapon class elsewhere.

I'm not sure if this was the right decision but I created an empty spatial node called Weapon, created a script for it called "Weapon.pd", pasted cut and paste all the code from the pistol script into it (as that was the script that originally had theclass_name weapon)and let all the scripts for all the guns inherit from it. So now I have different error:

The Weapon class is just a text file. You don't need to attach it to anything (and it's better if you don't, it's just for in memory values, not visual objects). At the very least you will need this in the class.

class_name Weapon
var model : MeshInstance
var animation : String

However, I'm not sure what your level of programming experience is, or your familiarity with GDScript. I may be a bit off base with my recommendation. I think this is an okay design, but I can come up with something easier if that makes more sense for you.

@cybereality said: The Weapon class is just a text file. You don't need to attach it to anything (and it's better if you don't, it's just for in memory values, not visual objects). At the very least you will need this in the class.

class_name Weapon
var model : MeshInstance
var animation : String

However, I'm not sure what your level of programming experience is, or your familiarity with GDScript. I may be a bit off base with my recommendation. I think this is an okay design, but I can come up with something easier if that makes more sense for you.

Most of everything I know about using GDscript, I learnt from watching Garbaj's Youtube video on the subject. All of his video's are incredibly short and easy to follow in the sense that almost everyone can follow his instructions and get results. You wouldn't mind if I just gave you a copy of my project if it helps, right? I mean, it's just a demo for teaching purposes only. It would give a better indication of what I understand and what I don't understand where GDscript is concerned. I'll attach my original script to the player but, if you want to see what the ended version looks like ,it's named Player2. Anyway, I'm asking for permission before I send it.

The only Garbaj Youtube tutorials I followed were the one's that I mentioned in first post on this thread; I haven't watched all of them. I've watched other GDscript tutorials but, I find myself going back to Garbaj's tutorials the most and that's why it's easier to recall concepts in his video; other concepts I've learnt elsewhere are harder to recall.

Anyway, here is a zip file of what my demo looks like. It doesn't have any proper character models yet (or ever as it's just a demo I'm using to teach myself with).

@cybereality said: The Weapon class is just a text file. You don't need to attach it to anything (and it's better if you don't, it's just for in memory values, not visual objects). At the very least you will need this in the class.

class_name Weapon
var model : MeshInstance
var animation : String

However, I'm not sure what your level of programming experience is, or your familiarity with GDScript. I may be a bit off base with my recommendation. I think this is an okay design, but I can come up with something easier if that makes more sense for you.

Okay, I've used all the var model: MeshInstance var animation: String

Now I get different errors

To me, getting this weapon switching system right is more important than learning anything else in Godot, as there is an abundance of Youtube tutorials on other Godot topics while there is very view tutorials on Youtube that tackle the issue I've raised specifically.

@cybereality said: The Weapon class is just a text file. You don't need to attach it to anything (and it's better if you don't, it's just for in memory values, not visual objects). At the very least you will need this in the class.

class_name Weapon
var model : MeshInstance
var animation : String

However, I'm not sure what your level of programming experience is, or your familiarity with GDScript. I may be a bit off base with my recommendation. I think this is an okay design, but I can come up with something easier if that makes more sense for you.

Do you know of any tutorial or resource that could teach me how to find the solution on my opinion?

Not sure about that. I haven't really used many tutorials, I've mostly just been reading the documentation and testing features out myself.

@Audiobellum said: Anyway, here is a zip file of what my demo looks like. It doesn't have any proper character models yet (or ever as it's just a demo I'm using to teach myself with).

I downloaded that .rar archive. I was able to view the filenames inside it, but was unable to extract the files. I'm using Linux (Pop!_OS 20.04). Can you provide a .tar.gz or .zip archive?

@DaveTheCoder said:

@Audiobellum said: Anyway, here is a zip file of what my demo looks like. It doesn't have any proper character models yet (or ever as it's just a demo I'm using to teach myself with).

I downloaded that .rar archive. I was able to view the filenames inside it, but was unable to extract the files. I'm using Linux (Pop!_OS 20.04). Can you provide a .tar.gz or .zip archive?

I used a website to convert the rar to a zip file. Anyway, I've went over to Garbaj's discord to ask for some advice on how to fix the problem I had. The advice I got implied that my solution will involve yield(I don't know what does completely); more specifically someone showed me this pseudo-code(yield($animpath, "animation_finished"). It was also recommened that I used an array modifier. You can try and help me to find a solution but, I'll also be trying to find a solution on my own.