How to make weapon switching system?

Godot Version

4.1.3

Question

I was trying to make a certain weapon system that is explained in the picture. Does anyone have any idea of how to make this? Thanks! (if anyone has any questions about the drawing you can ask too) (also I am not a good drawer sry)

That’s a lot of features in one drawing.

  1. Simplify the problem:
    a) Ignore UI for now. Just listen to Input and switch between 2 weapons in code when some key on keyboard is pressed.
    b) Ignore special moves. Just focus on weapon switching/hitbox.

  2. Player should have zero knowledge of weapon’s hitbox.

  3. Have all weapons inherit from base Weapon class (after base class is done, create specific weapons by using menu Scene → New Inherited Scene…). Adjust size/shape of collision shape in each specific weapon.

Once you have the above done, i.e. 2 different weapons with different collision shapes and you’ve implemented the code to actually do damage to enemies, then you can worry about Special Moves and only after that - about UI to switch weapons and only after that - about adding locked/unlocked weapon state.

That’s how I’d approach it. Hope it helps, cheers!

Also look into animation system for your weapon class, you can do a lot, especially timing for animating a weapon switch.

The core of weapon switch is that you need a parent script that can manage the weapon change. It’s like a hand manager. You tell you manager, with user input, to switch weapon and it triggers animation on the weapon, the animation ends and calls to remove child or delete the current weapon node. Once this is completed the hand manager will get signaled it was removed and it will load a new weapon. It creates, or just references the removed child, and adds it as a child of the hand. Then, if necessary, call an equipped animation.

Thanks for responding! Is it also possible for me to create a duplication of the player (with all the damage hitboxes of the sword), modify the duplication of the player (by changing the hitboxes and damage), and switch between the two somehow?

Also here is a screenshot of what I’ve made so far (sry I probably should have sent this at the start)

Also if my sword hitbox is already attached to the player is there a way I can change it using your method?

That’s a nice-looking hero!

Why would you duplicate the player and not the weapon? If you’re not familiar with the concept of inheritance in programming, this is a good time to look it up/learn. There may be a tutorial specific to Godot, but if not, do this long tutorial, it does go over it at some point: https://www.youtube.com/watch?v=nAh_Kx5Zh5Q&t=1s

  1. Do not add CollisionShape2D to Sword in your Player scene. The collision shape should be part of Sword scene. If you want Player class to handle the event of sword hitting something, just have weapon emit a signal, such as:

signal hit(obj)

where “obj” is the object the weapon hit, and Player then just connects to that signal.

  1. You need to remember reference to currently-equipped weapon. So at the top of Player class, have this:
var weapon: Node2D # if your weapons all inherit from Weapon class or smth (as they should), you can put Weapon instead of Node2D.

Back to your situation - let’s say you have 2 weapon scenes, Sword.tscn and, dunno, Mace.tscn.

For the sake of simplicity let’s say you store paths to weapon scenes in Player class, also declared at the top:

var weapons: Dictionary = { "sword" : preload("res://scenes/weapons/sword.tscn"), 
		"mace": preload(res://scenes/weapons/mace.tscn") }

(Really, though, you definitely should be keeping paths to weapon scenes in a resource file, but that’s a separate issue for later.)

Then in w/e place / event where you pick up the new weapon, just do:

func equip_weapon(wp_key: String):
	var new_weapon = weapons[wp_key].instantiate()

	# unequip current weapon
	if weapon:
		weapon.queue_free()

	# equip new weapon
	add_child(new_weapon)
	new_weapon.hit.connect(_on_weapon_hit)
	weapon = new_weapon # set weapon variable to hold reference to just-equipped weapon

func _on_weapon_hit(obj):
	# your code here to process weapon hitting some object "obj"
	# for example: 
	if obj.is_in_group("enemy"): 
		var dmg = #your code to calculate damage
		obj.take_damage(dmg)
	# this is just an example... multiple ways to solve this, 
	# such as enemy actually detecting a weapon hitting it 
	# and then asking the weapon for dmg it should take. 
	# The weapon can then tell it or ask some singleton to 
	# calculate dmg (If things weapon isn't aware of 
	# need to be taken into account). Anyway, this is 
	# outside the scope of this thread.

Then remove Sword from the scene tree in editor and just equip it programmatically on load in Player.gd:

func _ready():
	equip_weapon("sword")

I was able to make the two weapons and switch between them. I also was able to pass the current weapon equipped using a signal so I can communicate to godot if the player can double jump or not (if the player can only double jump when using a certain weapon). I was able to do damage with the sword as well as add a special move hitbox that activates when a button is pressed.

Thank you so much for your help!

The only other question I have for right now is the timing of the hitbox. When the attack button is pressed, the damage is inflicted immediately instead of waiting for the animation frame where the player actually swings the sword. Is there a way for me to time the “activation” of the hitbox with the animation frame where the player is swinging the sword?

For your suggestion are you basically saying I should create a script that changes the set of animations the player uses based off of the weapon equipped (which is passed through a signal)? So if the player has an axe equipped, use the axe animations?

If you animate the player differently depending on the weapon. That would sound right…? If the weapons animation is independent of the players animation than the animation can be stored on the weapon and I think at that point you could trigger the equip animation on the ready function of the weapon.

Sorry I need to look at your previous posts to understand your application

Ah okay, so I would also leverage the animation player to deal damage. At the point of the animation you think it should hit something. Add a key frame that makes a method call to check if there is an enemy in the area and deal damage to.

I suspect with your area 2d it might feel a little clunky. A more precise hitbox that follows the spite animation would be cleaner, but more effort on your part for each weapon class.

Of course. If you are using an AnimationPlayer to animate the sword, then just add a “call_method” track to it and call the method that deals damage when appropriate.

Alternatively, if you’re using Area2Ds and damage occurs when sword’s area enters enemy’s, then you can do 2 things:

  1. start sword’s collision shape small and grow it along with the sword swing to max size. This is collision_shape.shape.radius for a circle collision shape and …size for a rectangle shape.

  2. start with sword’s collision shape Disabled = true and Monitoring/Monitorable properties on sword’s area2d set to false. Then enable collision shape when sword is mid-swing (AND turn monitoring/monitorable on), and disable again when swing finishes. If using animation player, that’s just adding 3 more property tracks. If using AnimatedSprite2D to animate the sword, connect to its frame_changed and animation_finished signals and write the code there.

And that’s great to hear you got other things working!

Thanks to both of you for helping me!

I was able to use the property tracks in the animation section to add key frames that signal to when the sword should do damage. I will probably do the same thing for the special moves.

Yes, the player will have different animations depending on the weapon (if axe is equipped then player will use axe idle and attack animations rather than the sword animations).
My player currently has the animation player attached to it. Is it better to somehow store the animations on the weapon and somehow switch to them when the weapon is equipped or have all of the animations on the player and trigger them when needed? (I’m planning to have a lot of weapons so I’m not sure how messy it will get when I store all of the animations on the player).

It seems like the player and weapon animation are the same thing with your pixel art. So the only thing I can think of to encapsulate them is the animation sets and hit boxes. These should stay together somehow. You could remove them from the player logic. Where the player is just triggering generic animations (like walk run attack etc.)

But you switch the animation and hitbox set the player interfaces with. I’m not sure how to do this off the top of my head…

I’m thinking along these same lines for my combat situation.

Player can do different damage based on health, attack power, active power-ups, type of weapon, weapon skill, etc. which would combine to produce a maximum “dmg_given” when it strikes.

Conversely, the enemy also has health, speed, defense, active power-ups, type of defenses, defense skill, etc. which would combine to negate a minimum amount of damage when struck.

When the weapon hits, the player should calculate its own “dmg_given” and pass that to the enemy who can then moderate that with its own “dmg_received”.

For example:
If a weak player goes up against an enemy w an attack of 20 dmg_given, the enemy can fend off up to 50 damage resulting is 0 dmg_received.

However, if a strong player goes up against the same enemy w an attack of 200 dmg_given, the enemy can still fend off up to 50 damage resulting in 150 dmg_received. If excess damage goes to health and enemy health is 100, you’ve just one-shot him.

If, however, a player simply strikes the air or misses the enemy altogether, the signal of dmg_given is not received by any enemy. You could also apply the same dmg_given signal to non-enemies like props or set pieces. Break the pot to find food. Break the chest to find coins.