Creating a weapon system

Hi folks,

I am trying to make a weapon system for my 2D game. The player will be able to switch between a wide range of weapons, that will all have separate properties (bullet firing rate, sprite, particle effects, etc.).

I currently don’t know how to implement this, and even my research on the forum didn’t help me very much.

Should I create one go per weapon (and one script for each to handle properties, etc), and put each weapon in a collection, and then add every single collection to my player go and handle creating an instance of the selected weapon when needed ( as well as setting the weapon as a child of the player ) ? Or is there another easier and less “messy” (lot of gos, lot of collections) method ?

Thanks for your answers, and happy coding !

2 Likes

If you know all the weapons before hand and don’t need to animate their position you can add them as separate sprites to the character game object and enable/disable as they are used. Store stats for each weapon in a table, like:

self.weapon_stats = { pistol = { damage = 10, range = 30, firerate = 3 },
                      shotgun = { damage = 50, range = 15, firerate = 1 },
                      ... }

If you have lots and lots of weapons and they eat lots of resources you might want to load them dynamically, but the above should work for quite a few sprite weapons.

Also note that if you need weapons placed separately (as pickups) or have other users of these weapons (like enemies) you may want to store the weapon stats elsewhere. One way could be to have a “weapon” game object with all the possible sprites, stats and a script that switches between the used type.

1 Like

Could also have all weapon animations on the same atlas then swap the image/animation used on the sprite instead of using multiple sprites right? Would there be a down side to that?

Could define which sprite to use in the weapon definition. To make the script playing animations cleaner.

self.weapon_stats = { pistol = { damage = 10, range = 30, firerate = 3, fire_anim="pistol_fire" },
                      shotgun = { damage = 50, range = 15, firerate = 1,  fire_anim="shotgun_fire" },
                      ... }
2 Likes

Yes, of course! No downside to that as far as I can see, on the contrary more efficient.

1 Like

@sicher and @Mattias_Hedberg the sprite method won’t work since every weapon will be unique, with its own bullet types, sounds and particle effects.

However, since it was confirmed by @sicher, I will then use lots and lots of collections and gos, one for each weapon. Thanks !

How many weapons are we talking about?

You can have a weapon game object that switches sprite anim and also contains sound and particle fx components for each type. A table containing which sound and fx to play for each weapon.

For the One Room Ludum Dare a while back I created a weapon system and I chose to create a data driven approach similar to what @sicher and @Mattias_Hedberg suggested:

6 Likes

What I would avoid is to spread out the stats in all those game objects. Keep the stats close to where they need to be used so you won’t have to pass that data around a lot.

1 Like

I would also avoid dynamically spawning all these things unless they eat a lot of memory. If they do, you probably should to keep load times down. Do you have memory estimates?

Thanks for the tips @britzl ! Actually doing all in one game object is perfect. @sicher Brought this solution but I wasn’t thinking about it the correct way :slight_smile:

By the way, I won’t have a lot of weapons in the beginning, and the main advantage is that only the player will have weapons, not the enemies. I will try this solution right now !

If you come from an OOP background it’s very easy to start thinking about things in a similar way in Defold. One game object per weapon, one script per weapon and each with a set of weapon properties, one game object per enemy type, and so on.

Sometimes it makes sense to make some separation, but when it comes to the central and essential data that drives your game it rarely makes sense to spread it out into a ton of different scripts. Collect all the data in one place and let that data drive the behaviour of the way to spawn bullets, play sounds, control enemy behaviour, visuals etc.

3 Likes

You made a fair point, I’m coming from an OOP background :smiley: I’ll try to adapt

I don’t have a lot of content made for my game yet so I can’t judge performance very well, but I’m using a game object for each weapon and spawning them dynamically (from a central “weapon_factory” object). If you’re going to have a lot of weapons, then you’re going to have a lot of data to store somewhere. I prefer to set a few properties on a component rather than write an enormous table in a script somewhere. I let the weapon script handle everything, just feeding it “fire pressed” and “fire released” messages, etc. I have one script for each distinct weapon category (normal single-barrel, shotguns, lasers, etc.), with script properties for fire rate, clip size, automatic/single-shot, accuracy, etc., so I can create tons of variations with one script. The bullet type is set in the factory on the game object.

I’m doing a local multiplayer game with weapons that can be picked up and dropped, so maybe my needs are different from yours. At the moment I’m spawning and deleting the weapon each time it’s picked up or dropped, just holding onto its hashed name, but I guess that’s kind of dumb. This thread is making me look more critically at my system, heh.

@britzl What are the advantages of the data-driven vs the object approach? More performant? Or just easier to tweak and maintain?

3 Likes

Ok, so let’s take the case of a grid based game, perhaps a match 3 game like Candy Crush, or maybe a linker game such as Blossom Blast.

Now if you’re an OOP guy you probably decide that you need a base class of some kind to represent the stuff in the grid right? Perhaps we call it Entity. And Entity can either be a Blocker or a Candy. A Blocker can be a Chocolate or a Licorice. A Candy can either be a Colored Candy or a Special Candy. A Colored Candy can be one of Red, Orange, Yellow, Green, Blue, Purple. A Special Candy can be a Color Bomb, Fish, Striped or Wrapped.

The OOP solution would probably be to create a large class hierarchy with a bunch of abstract classes to represent this data and let the grid have references to instances of your base class. And when you manipulate the data you have a bunch of checks such as instanceof, isBlocker, isCandy, getColor, getType and so one. Essentially a lot of queries down to the subclasses and the actual implementations. The data is spread out among and carried by your objects.

If you replicate the above in Defold you might have a bunch of different game objects and factories to represent the different types of candies and other entities on the grid. Each game object might have one or more scripts with a bunch of go.property() declarations to hold the data. The grid would hold only the game object ids and when you need to query the grid for the property of a certain entity you do calls to go.get() to read the properties of the scripts.

In the data driven approach, which I prefer, I’d have the grid and it’s contents stored in a single place. The grid would have a table per entity and each entity table on the grid would hold all the information about it, such as type, color and a game object id. I’d use a single game object with a sprite and let the game object function only as a visual representation of the data, not a carrier of the actual data. Having all of the data in one place makes it very easy to iterate over the grid, filter it and so on. Querying for a specific property is faster than a go.get() call. It does also make it very easy to serialise the game state.

The above doesn’t always hold true but I prefer to try and keep my data in one place. Sometimes it makes sense, sometimes it doesn’t. In the case of a weapons system it’s not as obvious in my mind which is the best approach. What you suggest @ross.grams sounds reasonable as well. Personally I prefer to keep the data in a table since it’s easier to tweak and easier to get an overview of the whole thing. Thinking ahead it also makes it easier to updated the data from some source (a JSON blob downloaded from somewhere perhaps), although that will not be the case when Live Update supports updating of existing content. One benefit of the go.property() approach on the other hand is that the data is exposed as properties in the editor which is more designer friendly than changing stuff in a JSON file or a Lua tabnle.

6 Likes

Hello,

I know this topic is a little bit old but I am one of those guys coming from a OOP background, also if I have used Lua before in the past. I have programmed using the Love2D framework before coming to Defold (that, so far, I really like: thanks! :smiley: ) and I was either using my own simple class implementation or middleclass for OOP.

My question is, in your example of a grid, would the implementation of the grid holding the information be a class, a module or something else?

I am asking because I have read on the forum several posts somehow “against” going with an OOP approach and I would like to understand your perspective. I am not looking for THE answer here (that, by the way, we all know it is 42), I am just trying to be as open-minded as possible :wink:

Thanks!

1 Like

The implementation of the grid would be a plain Lua table, probably in a board/game/grid.script, store on self and operated on using functions declared in the same script or maybe in a Lua module. The Ocean Commotion example game that we have in a couple of different variations is an example of this. The board is declared as a Lua table on self in the init() function.

I’m not against OOP per-se but I often find it bloating my code with unnecessary boiler plate. I do sometimes when I create Lua modules employ something looking like OOP with a create/new function that returns a table with state and methods, such as this.

5 Likes

Nice “explanation + proof of concept” answer, thanks! :smile:

2 Likes