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.

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

Cool. I originally avoided reading the documentation on the official website as I've only had constant access to the internet recently relatively recently and I didn't know where to find a PDF version of it, so that I can download. I'll check it out.

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

Do you know where I can download a pdf of Godot's documentation?

Do you know where I can download a pdf of Godot's documentation?

PDF downloads are no longer offered because the PDF generated by Sphinx was almost unreadable. Instead, you can use the HTML downloads provided here: https://docs.godotengine.org/en/stable/ (see the section about HTML at the top)

@Calinou said:

Do you know where I can download a pdf of Godot's documentation?

PDF downloads are no longer offered because the PDF generated by Sphinx was almost unreadable. Instead, you can use the HTML downloads provided here: https://docs.godotengine.org/en/stable/ (see the section about HTML at the top)

Thanks

@cybereality said: Okay, I downloaded the project and made a bunch of changes. Hopefully you can look through and it will help you understand what I did. https://drive.google.com/file/d/1V6kZ-OjR4GaqWlRqX01LCnVFUEE982zW/view?usp=sharing

Thanks. I'm starting to understand how the changes work the more I make comparisons between what I have and the changes you made. I think I understand what you've down to the Weapon.pd in it's entirety. I never knew that wheel up and wheel down could have been established as inputs, cool. It seems that the more I think about the code, is the more I understand what's going.

The code underfunc setup_weapons(): gets the variable for each weapon in the Weapon.pd script and then set it's string equal to it's respective equip animation. I think I understand the code underfunc switch_weapon(): but, I don't understand what the != does in if current_weapon != weapons[weapon_index]: . Judging by the context, I think it means not or =/= in Godot.

When it comes to func switch weapon(): it seems that all the code under if current_weapon != weapons[weapon_index]: executes itself in a sequential matter, (one after the other) as oppose to all at the same time.

When it comes to the code for switching weapons with mouse wheel, I don't understand what weapon_index = (weapon_index + 1) % weapons.size() works, more specifically it's the % weapons.size() I don't understand. Anyway, I'm going to do my best to understand these changes and then let them inform how I do coding in GDscript in the future.

weapon_index = (weapon_index + 1) % weapons.size()

That's a common way of cycling through a list. The modulus operator (%) means "remainder upon division by".

give me enough time I'll think of a way to use AStar to do it B) lolol

@DaveTheCoder said:

weapon_index = (weapon_index + 1) % weapons.size()

That's a common way of cycling through a list. The modulus operator (%) means "remainder upon division by".

I just discovered that I don't know what the "size" in weapons.size() means. Could you explain that to me?

Okay, so where I'm at now is that I've managed to successfully implement the "wheel up" and "wheel down" Inputs into the mouse wheel code I originally had; replying the unnecessary stuff I previously had and the code looks neater for it.


	if Input.is_action_just_pressed("wheel_up"):
		if current_weapon < 3:
			current_weapon += 1
		else:
			current_weapon = 1
	if Input.is_action_just_pressed("wheel_down"):
		if current_weapon > 1:
			current_weapon -= 1
		else:
			current_weapon = 3

As you can see, I haven't got around to using the arrays yet. Will be right back when I have success with that.

@DaveTheCoder

Okay, so here is my idea for switching weapons with my scroll wheel. Since arrays have to start at zero and I don't want to make any more changes to my scroll wheel code (yet). I've made my array look like this:

var none = 1

var weapons = [none,pistol, shotgun, rifle]

To switch weapons through my array, I'll use a code like this:

weapons[current_weapon]

However, I'm still having problems finding a simple way to use this code to replace my wall of elif statements:

	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

My plan was to do something like this:

weapons[current_weapon].visible.true pistol.shoot()

However, I'm still running into problems. Any advice?

Thanks for the help everyone. I think I'm just going to go and learn how to code in general before returning back to this subject, wish me the best of luck. I will not quit.

10 days later

@Audiobellum said: Thanks for the help everyone. I think I'm just going to go and learn how to code in general before returning back to this subject, wish me the best of luck. I will not quit.

Good, never quit! You seem to be coding already, so that's a great head start, lol. If it makes you feel better, I've messed around with coding for several years and I still have trouble whenever it comes to arrays, although I'm getting much more comfortable now. =) It must be that you never mess with arrays much until you start a real project.

20 days later

@OpinionatedGamer @cybereality @DaveTheCoder Okay, so I've found some sort of solution to my problems. Basically, functions can be attached to any input in GDscript, so I made a function that would manage all my equip animations. In order to avoid using an array, I just used the match to match my current_weapon number with the required animation in the function responsible for switching weapons. After that, I just integrated that function into my code for switching weapons with the mouse wheel and the results are decent. Of course, I haven't found away to implement the unequip animations into the code probably. I know how to use yeild($anim_player. "animation_finished") but, even though it technically worked for making the transition between equip and unequip animations possible, there was always this awkward pause at the beginning of the unequip animations where I'd see what appears to be the final frame of the animation at the beginning of the animation for a single frame. Needless to say, I'm satisfied with just having the equip animations working when switching weapons with the mouse wheel. Of course my code could be more optimized but, as a beginner trying to make a demo, code optimization isn't my top priority (though I am looking into mentions by which I can get it done still).
As I've alluded to, I'm still having problems using arrays. I do know how to store integers, floats and strings into an array. However, I do have a problem with storing other established variables into an array. However, all of that is an issue for another day in another threat and perhaps I can find the solution to that problem somewhere else.

It gets more fun when you start combining arrays and other things. The config file I've set up for saving my game is a Dictionary. One of the dictionary elements is an array of dictionaries. Two of the elements of those dictionaries are also dictionaries. :)

@DaveTheCoder said: It gets more fun when you start combining arrays and other things. The config file I've set up for saving my game is a Dictionary. One of the dictionary elements is an array of dictionaries. Two of the elements of those dictionaries are also dictionaries. :)

What about putting referred nodes into an array? Let's say that I have a bunch of variables similar to this: var thing = $thing. Whenever I put variables like this in array, I don't get any arrays until I try to use the array back trying to get the index of what's in it. What works for me is if I re-establish the array inside of a function by just copying and pasting to code for the array into the function, while removing the part that says var at the end. However, that has it's obvious limitations.