#Weapon Swapping
1 messages · Page 1 of 1 (latest)
How is your node structure?
Do you have all gun models instantiated at the same time?
uh
So each gun has a camera that holds the script. But the script has specific code for each gun all clumped in the same script?
no its 2 scripts
Different ones?
weaponscript is just what i made the class name for the shotgun script
cus originally it was the only weapon
but now i added the pistol
this and
this
are the same script edited for each weapon
so same but not same
pistol_cam and view_model
This is a pretty odd approach, i'm thinking on how to make this work without changing too much stuff.
sorry ive used godot for only 6 days XD
im surprised i even get to have errors to do with weapons in unity basic movement took me a week or so :P
Honestly i'd start by reworking the scripts. I see the shotgun script still has pistol specific stuff.
You can also use inheritance to avoid having 2 identical scripts.
Like, you can have a "Weapon" class that is not attached to any node, but rather meant to be expanded by each weapon:
##Weapon.gd
class_name Weapon
extends Node3D
@export var fps_rig: Node
@export var animation_player: Node
@export var fire_rate: Node
@export var weapon_sound: Node
@export var weapon_cocking: Node
@export var light: Node
@export var emitter: Node
@export var flash_time : float = 0.02
@export var aim_emitter: Node
var in_use = false;
var aiming = false;
var can_fire : bool = true
signal weapon_fired
signal aim_weapon_fired
func add_muzzle_flash() -> void:
light.visible = true
emitter.emitting = true
await get_tree().create_timer(flash_time).timeout
light.visible = false
##And all the other code you may need.
Then, you can add a new script to the shotgun/pistol and automatically include all of this code just by extending it:
##Pistol.gd
extends Weapon
If, for example, you want the _input() function to work differently for the new script, you can declare it again in said script and it will overwrite the one from the "Weapon" script.
extends Weapon
func _input(event):
##Different code goes here
With @export you can define diferent nodes like "weapon_sound" per scene. Using the inspector.
Or you can just keep using @onready if both will have nodes with the same names.
okok
thankyou!
where would that script go?
like would i put that in the main scene then?
The first one wouldn't be in any node.
It would just be in any folder inside your project.
It just exists to define the "Weapon" class and all its code.
ah okok thanks for clearing that up
Actually, if you add a gun that does not need different code. You can indeed use that script for it.
well
only the shotgun needs different code atm
but eventually both will
because different muzzle flashes
maybe if i add a bow and arrow (very probable) it will just use that
Does the muzzle flash need different code or a different node?
Because if for example you're using an AnimationPlayer for the muzzle flash in all cases, using a different AnimationPlayer wouldn't require any extra code. You just set it to use that node if you're using @export or make it have the same node name if you're using @onready
It would probably need the animations to be called the same too, tho.
Depends on your approach.
and do i delete the second viewports?
@export does not care about names.
@onready would need it to be the case.
I'd delete the viewports yeah. They are a performance hog and complicate things a lot.
but its so the weapon cant clip
Aaah, right. Forgor.
You can use the same viewport.
Just make all weapons a child of the same viewport instead of having one for each.
so put both cameras in the same viewport then?
That should work.
You also only need 1 camera.
Format would be:
SubViewport
|->Camera
|->Pistol
|->Shotgun
oh?
lemme do that
got sidetracked let me do it now
it it like camera and then pistol and shotgun inside it?
or camera and pistol and shotgun are siblings
this ok?
@worthy onyx sorry for tag ur very helpful XD ur helping me understand
and i only have 15 mins left
or this
I went out, sorry.
The first structure was good, but both work
i broke it all
completely and utterly broke it all
the pistol doesnt even exist
but like its there
but its not
ok its slightly better now
ok its still shit tho
class_name Weapon
extends Node3D
@export var fps_rig: Node3D
@export var animation_player: AnimationPlayer
@export var fire_rate: Timer
#@export var weapon_sound: AudioListener3D
#@export var weapon_cocking: AudioListener3D
#@export var light: Node
#@export var emitter: Node
#@export var flash_time : float = 0.02
#@export var aim_emitter: Node
var in_use = false;
var aiming = false;
var can_fire : bool = true
signal weapon_fired
signal aim_weapon_fired
func _ready():
pass
#func sway(sway_ammount):
#fps_rig.position.x -= sway_ammount.x * 0.00005
#fps_rig.position.y += sway_ammount.y * 0.00005
#func add_muzzle_flash() -> void:
#light.visible = true
#emitter.emitting = true
#await get_tree().create_timer(flash_time).timeout
#light.visible = false
extends Weapon
@export var pistol : Node3D
@export var shotgun : Node3D
var pistol_in_use : bool = true
var shotgun_in_use : bool = true
func _input(event):
if can_fire:
if event.is_action_pressed("left_mouse") and pistol_in_use:
fire_rate.start()
can_fire = false
if aiming:
$AnimationPlayer.play("aim_fire")
emit_signal("aim_weapon_fired")
$pistol_sound.play()
else:
$AnimationPlayer.play("fire_pistol")
emit_signal("weapon_fired")
$pistol_sound.play()
if event.is_action_pressed("itemslot2"):
if pistol_in_use:
$AnimationPlayer.play("unequip_pistol")
pistol_in_use = false
pistol.hide()
else:
$AnimationPlayer.play("equip_pistol")
pistol_in_use = true
shotgun_in_use = false
pistol.show()
shotgun.hide()
if event.is_action_pressed("right_mouse") and pistol_in_use:
if not aiming:
aiming = true
$AnimationPlayer.play("aim_down_sight")
elif event.is_action_released("right_mouse") and pistol_in_use:
if aiming:
aiming = false
animation_player.play("stop_aim")
func _on_fire_rate_timeout():
can_fire = true
extends Weapon
@export var pistol : Node3D
@export var shotgun : Node3D
var shotgun_in_use : bool = true
var pistol_in_use : bool = false
func _input(event):
if can_fire:
if event.is_action_pressed("left_mouse") and shotgun_in_use:
fire_rate.start()
can_fire = false
$shotgun_sound.play()
$shotgun_cocking.play()
if aiming:
animation_player.play("aim_fire")
emit_signal("aim_weapon_fired")
else:
animation_player.play("fire_shotgun")
emit_signal("weapon_fired")
if event.is_action_pressed("itemslot1"):
if shotgun_in_use:
animation_player.play("unequip_shotgun")
shotgun_in_use = false
shotgun.hide()
else:
animation_player.play("equip_shotgun")
shotgun.show()
shotgun_in_use = true
pistol_in_use = false
pistol.hide()
if event.is_action_pressed("right_mouse") and shotgun_in_use:
if not aiming:
aiming = true
animation_player.play("aim_down_sight")
elif event.is_action_released("right_mouse") and shotgun_in_use:
if aiming:
aiming = false
animation_player.play("stop_aim")
func _on_fire_rate_timeout():
can_fire = true
weapon,gd
What is the issue currently?
right now its this because im trying to make only one weapon useable at once (for obvious reasons)
I mean, this wouldn't do anything.
This runs once when the node is added and doesn't do anything.
WeaponType.SHOTGUN is the same as just leaving a "1"
What code do you run when changing weapons?
ill show you
this and the inverse for shotgun
Neither pistol nor shotgun have a script
They do, they are a above
#1266152812040028252 message
so which nodes did you export - having these two nodes with the same names is probably not helping you
And this doesn't work?
But that’s $shotgun/shotgun that has the script
My GPU is kinda having issues, anything 3D just renders as random coloured triangles.
Its a weird issue with my cobbled-togheter Windows version, i'll fix it someday :p
its not anything fancy i think it might run
Ok, send it.
the gpu particles are currently disabled
fair warning ESC closes the game
test build exported for my friend tani and i spelt it wrong XD
which scirpt is this in?
I tought you meant the project folder. Sorry but i am not running an exe i found online.
oh yea i can send that too
pistol
and the other in shotgun
how do i export the project? just zip it in file manager?
I wrote a little example on how enums work.
extends Node3D
class_name WeaponSwapExample
enum WeaponType {PISTOL, SHOTGUN, A_GIANT_BANANA}
var selected_weapon: WeaponType
func _ready():
# This sets selected_weapon as pistol
selected_weapon = WeaponType.PISTOL
# This swaps weapon to selected_weapon
swap_weapon(selected_weapon)
func swap_weapon(weapon_to_swap_to:WeaponType):
# These check which weapon was selected
if weapon_to_swap_to == WeaponType.PISTOL:
print("pistol selected")
elif weapon_to_swap_to == WeaponType.SHOTGUN:
print("shotgun selected")
elif weapon_to_swap_to == WeaponType.A_GIANT_BANANA:
print("yum yum banana")```
That would work
swap_weapon doesnt exist in this context though right?
What do you mean? Like in your own code?
Yeah I just wanted to give you an example on how enums work.
no i meant in the example
swap_weapon doesnt exist
It does, it's a func below ready
oh im slow sorry
Enums are just numbers internally btw.
It's a lot like doing int weapon_type except you get to name them so it's much easier to remember what is what. It'd be harder to remember what 0, 1, 2 mean.
yea i read the documentation
its like emum {chicken, banana, orange}
is same as
chicken = 1
banana = 2
orange = 3
righr?
0, 1, 2. but yeah you got the idea!
Ah, i had discord muted, didn't hear the DM
Yeah, basically like that.
Internally it is considered a constant Dictionary off String : int.
i think chicken and banana makes more sense then that
i think architecturally, it doesn't make sense for your pistol script to be responsible for switching to your shotgun., you'd be better off having a common parent thats responsible for switching
also, whats the purpose of your
pistol: Node3D
--- pistol: Node3D structure?
why not just have the script on the parent, and get rid of the second node3d?
i just want it to look tidier XD
turn each weapon into a scene of its own, so its children are hidden
I've honestly done it like this before.
But i always make sure that, in this case the "Weapon" script, has a way to get all other weapons.
there is the > arrow for that
does it?
atm you have to manually keep a variable for each gun and replicate the code in both scripts.
even with this in weapon.gd?
The thing is that you'll have to create a new variable for each new gun you implement.
It is not BAD, but considering the over-reliance on a single class for handling all guns, it is a bit messy.
so i should use enum? or what would be better
I would recommend relying on enums, yeah.
so keep this then
I can't open the project you sent me sadly
because 3d?
Probably my GPU
if you go to my stream its open
your stream?
Oh damn, that's fancy.
its built into discord
Quick question, not strictly about your current issue. but are you using shotgun.hide() in an effort to "turn off" the shotgun?
to make it so they dont both display at the same time
turning off is next on the list
ok, just wanted to check you were aware its code would still run even when hidden 🙂
no problem :)
So, i'd start by implementing a function to get all guns, and also make the script keep track of their type.
##To add to Weapon.gd
const WEAPON_GROUP := "_WEAPONS"
@export var type: WeaponType
## Register this weapon in the group when it enters the scene.
func _ready():
get_tree().add_to_group(WEAPON_GROUP)
## Get all weapons in the group
func get_weapons() -> Array[Node]:
return get_tree().get_nodes_in_group(WEAPON_GROUP)
##Lets you get the specific weapon.
func find_weapon(weapon_type: WeaponType) -> Node:
for weapon: Node in get_weapons():
if weapon.type == weapon_type:
return weapon
You'd also have to set each gun's type on the scene using the @export above.
Ah, right.
Add a "return null" at the bottom of the function.
If it cannot find a weapon of the given type, it needs a fail safe.
same error am i putting it in wrong place?
It has to be at the same level as the "for"
fixed
ty
shotgun in use still broken
should it just be if WeaponType.SHOTGUN
or even just WeaponType.SHOTGUN
How are you deciding which gun is active?
I mean, do you have a variable that says which is active?
oh, shit no i dont
For equipping weapons, you can use a static variable to ensure only one is active at a time and make it accessible to all Weapon nodes.
static var equipped_weapon: Weapon
That way, all guns can just do this to set themselves as equipped:
equipped_weapon = self
in weapon.gd?
Yeah
where does equipped_weapon = self go
Now any gun can check if they are equipped with "if equipped_weapon == self"
Or even know the type of the equipped one with "if equipped_weapon.type == WeaponType.SHOTGUN"
In the function for equipping the gun.
This kind of stuff doesn't do anything tho.
oh
It is the same as doing
1
not 0```
You can just remove that from _ready():
When you do "equipped_weapon = self", you automatically unset that variable from ALL weapons and set it to the new value.
As in, now all weapons know what is equipped, and only one can be equipped at a time.
should i add a 3rd weapon type that is NONE
so both know that nothing is equipped
Ah, you want to set the equipped_weapon as a "WeaponType"?
In that case, change the typing of the variable from "Weapon" to "Weapon.WeaponType"
And instead of "= self". Do "= type"
type.<WEAPONTYPE>?
well technically setting it to self auto sets it as a weapon type no?
No, because self is the node itself, which is a "Weapon" class.
WeaponType is an enum BELONGING to the Weapon class.
You can still use the node to get its type tho.
Ah, i gave the wrong syntax.
It is just "add_to_group(WEAPON_GROUP)"
Apparently the SceneTree doesn't need to be referenced to add them.
thats not the error i think
oh it was maybe?
idk
Did you save before running again? There's no reference to any SceneTree there.
Happens if you save while the game is running. You have to stop it first.
oopsies
STILL INVALID GET INDEX IN USE ON NODE3D
oh i guess it can just be if WeaponType.SHOTGUN
Maybe you set a non Weapon node as the shotgun?
why?
i dont have a non weapon node?
Any node that does not have a script that extends Weapon (or Weapon.gd itself) is not a Weapon node.
Also, ofc. "in_use" must be defined in Weapon.gd
Altho is there any difference between in_use and equipped_weapon?
oh, no there isnt i dont think
all broken again :(
How so?
Worth noting that the changes so far are for a rework of how the gun system works. My changes alone aren't meant to work out of the box.
You still need to modify the existing code to work with the new changes.
That's normal, errors means that you are being told what to do next.
is this correct for unequipping
no errors, just stuff doesnt work how i want it to
Nope, there you're just referencing the value, but you aren't actually changing anything.
You'd have to do "equipped_weapon = WeaponType.EMPTY"
The type of the variable (equipped_weapon) is still Weapon, you have to change it to be Weapon.WeaponType
iirc = if i recall correctly?
yep
hmm
why can weapon type not be weapon variable
oh cus weapon and weapontype arent the same?
Because Weapon is a Node.
WeaponType is an enum (a Dictionary, internally)
so remove weapon.?
Replace it with Weapon.WeaponType (or maybe just WeaponType).
I probably should have mentioned earlier, enum names can be used as types for purposes of type casting.
both give the same error
That's impossible, are you sure you're saving?
Can you show me the code where this error pops up?
And the equipped_weapon definition was modified to be like this?
var equipped_weapon: Weapon.WeaponType
I should have clarified what i meant with "definition".
There's some code further down that is trying to do the opposite you're doing here. Putting a Weapon in the variable instead of a WeaponType
It says line 35
That function is just getting values, it is not setting anything.
Yep, that could simply be replaced with: "equipped_weapon = type"
now its just the cannot get property from enum value
WeaponType still has a "SHOTGUN" value, right?
I'm trying to figure out what is wrong here.
The enum and its value exist. I don't understand what it is complaining about.
aaah, equipped_weapon is not a Weapon anymore. It doesn't have a "type" property.
equipped_weapon is the WeaponType itself.
so that means i just put equipped_weapon = [weapontype here] ?
yeah, that should work (with double =)
Like this:
equipped_weapon == WeaponType.SHOTGUN
equipped_weapon changed to be a WeaponType, but you still need to specify from where "SHOTGUN" is coming from in the right half.
is
Also, you never did "equipped_weapon = WeaponType" right?
this?
I mean after that definition.
no, i didnt
If you where to write 2 lines like this:
equipped_weapon
WeaponType.SHOTGUN```
Does it throw an error?
in ready?
Anywhere, really. I imagine this error is happening without the game running (?)
yea
YAY
now the errors arent in the weapon gd
time for shotgun
extends Weapon
func _input(event):
if can_fire:
if event.is_action_pressed("left_mouse") and equipped_weapon == WeaponType.PISTOL:
fire_rate.start()
can_fire = false
$shotgun_sound.play()
$shotgun_cocking.play()
if aiming:
animation_player.play("aim_fire")
emit_signal("aim_weapon_fired")
else:
animation_player.play("fire_shotgun")
emit_signal("weapon_fired")
if event.is_action_pressed("itemslot1"):
if equipped_weapon == WeaponType.SHOTGUN:
animation_player.play("unequip_shotgun")
equipped_weapon == WeaponType.EMPTY
shotgun.hide()
else:
animation_player.play("equip_shotgun")
shotgun.show()
shotgun.in_use = true
pistol.in_use = false
pistol.hide()
if event.is_action_pressed("right_mouse") and equipped_weapon == WeaponType.SHOTGUN:
if not aiming:
aiming = true
animation_player.play("aim_down_sight")
elif event.is_action_released("right_mouse") and equipped_weapon == WeaponType.SHOTGUN:
if aiming:
aiming = false
animation_player.play("stop_aim")
func _on_fire_rate_timeout():
can_fire = true
this is shotgun.gd
do i need to change or remove anything here
its throwing errors at me about in_use
I'd just remove in_use altogheter. Maybe replace it with a function if you need to.
func is_in_use() -> bool:
return type == equipped_weapon
fixed those now were back to both weapons functioning no matter of visibility or equipped_weapon
Do you have any code that prevents the guns from working when the other is equipped?
In shotgun you only allow firing if the PISTOL is active, idk if that was intentional.
no
it was not
why would that be intentional 😭
maybe it was
past me might me smarter
because i change this to shotgun and now the pistol sounds play when i try to fire
I don't see any code related to the pistol's sound there.
Unless $shotgun_sound has the sound of the pistol by mistake.
no because its pistol fire rate too
Or the AnimationPlayer is in charge of the sound.
its not
extends Weapon
func _input(event):
if can_fire:
if event.is_action_pressed("left_mouse") and equipped_weapon == WeaponType.PISTOL:
fire_rate.start()
can_fire = false
if aiming:
$AnimationPlayer.play("aim_fire")
emit_signal("aim_weapon_fired")
$pistol_sound.play()
else:
$AnimationPlayer.play("fire_pistol")
emit_signal("weapon_fired")
$pistol_sound.play()
if event.is_action_pressed("itemslot2"):
if WeaponType.PISTOL:
$AnimationPlayer.play("unequip_pistol")
equipped_weapon = Weapon.WeaponType.EMPTY
pistol.hide()
else:
$AnimationPlayer.play("equip_pistol")
equipped_weapon == WeaponType.PISTOL
pistol.show()
if event.is_action_pressed("right_mouse") and equipped_weapon == WeaponType.PISTOL:
if not aiming:
aiming = true
$AnimationPlayer.play("aim_down_sight")
elif event.is_action_released("right_mouse") and equipped_weapon == WeaponType.PISTOL:
if aiming:
aiming = false
animation_player.play("stop_aim")
func _on_fire_rate_timeout():
can_fire = true
The pistol is slot 2, right?
correct
It is starting to get a little too bloated in _input()
I'd make a separate function to change weapons, that is called from inside _input()
You could tell the function which WeaponType you are trying to change to, or simply the slot. Then use that to run the rest of the logic.
At least i couldn't find out what is the issue myself without breakpoints, in the current code.
the slots are not connected to any inventory system yet the inputs are named pre-emptively
That is fine, you just need to know what weapon to bring out.
Like:
if event.is_action_pressed("itemslot2"):
change_weapon(WeaponType.Pistol)
i need change_weapon function?
That is an example, altho that would be a good way yo offload some code.
The issue rn is the complexity of the functions you're using. Which is making it too hard to discover what is the culprit.
This is a common issue with bloated code, which is why it is always recommended to break things down into separate functions
ok so how do i make this function
as in format
my code is always way too much in 1 function
here is my player.gd
well part of it
have to make it ,txt
As an example:
func change_weapon( type_to_equip : WeaponType):
##If you select the same weapon already equipped, unequip it.
if type_to_equip == type:
equipped_weapon = Weapon.WeaponType.EMPTY
if type_to_equip == WeaponType.PISTOL:
$AnimationPlayer.play("unequip_pistol")
if type_to_equip == WeaponType.SHOTGUN:
$AnimationPlayer.play("unequip_shotgun")
##If it is different.
else:
equipped_weapon = type_to_equip
if type_to_equip == WeaponType.PISTOL:
$AnimationPlayer.play("equip_pistol")
if type_to_equip == WeaponType.SHOTGUN:
$AnimationPlayer.play("equip_shotgun")
##Rest of the code...
this goes in the pistol?
okok
u silly billy
u put weapon.weapontype.empty instead of just weapontype.empty
im pro game dev i see it from 1 billion light years away
Ya, i don't remember which language complains about not being explicit. So now i do it automatically
ah you know lots?
Aside from GDScript i learned trough Java coding (which i forgor now)
I am sure i can still do stuff in lua and i am currently trying to master C#
I tried JavaScript but it is so bad i prefer not learning it lol
Screenshot?
its in game
Of the code i mean. The one in _input is probably at fault.
You need an _input() function to control stuff with inputs.
if event.is_action_pressed("itemslot2"):
if WeaponType.PISTOL:
$AnimationPlayer.play("unequip_pistol")
equipped_weapon = Weapon.WeaponType.EMPTY
pistol.hide()
else:
$AnimationPlayer.play("equip_pistol")
equipped_weapon == WeaponType.PISTOL
pistol.show()
so this should be in weapon script then?
Ah, i tought you where using the change_weapon() function
no i start game and press 1 (itemslot1) and it equip animation
and i press again... equip animation
and again... same same same u get it
Then what did you change that could cause that issue of constant unequipping?
As i said earlier, you either need to start decoupling code into separate functions or run trough your code line by line and ensure the logic is correct.
Normally i'd use breakpoints to debug this (if i had to make a function this large). But i can't do that remotely.
istg i will open teamviewer rn 😭
this shit is making me insane
I have stuff to do, so i don't think i can keep helping with this for today.
Even with team viewer (which i don't have installed) we would just be trying to untangle spaghetti without fixing any of it.