Could someone please link me a tutorial on a system that lets me have different weapon stats (Damage range, attack speed, etc) – What is the best practice? I see videos on using Scriptable Objects, separate scripts per each weapon, etc, etc.
Trying to create a system that covers all possibilities can be difficult, especially when you’re new to programming.
Although it could be said that Unity already has a system. That system is object orientated programming and game objects. So you could just make a weapon in a modeling program and import it into Unity and create a script that’s unique to that weapon. The weapon script can manage everything including attaching itself to the player when the player collects it, and shooting when the player presses the fire button. Taking this approach means you’ll never be trapped by a bad decision you made when coming up with your own system.
The downside to this approach is that you could end up with lots of weapon scripts that are quite similar and if you want to add a new behavior that’s common to all weapons then you’ll need to add the behavior to each script. Although ideally with such a flexible system there shouldn’t be an excuse to make very similar weapons.
Ya, everything that Zulo said and more. Jump in and get your feet wet. Make any trivial game, like “gather all the coins and reach the exit” and then make some coins have different effects:
- give you different amounts of score
- speed you up
- slow you down
- invert your controls
- magically finish the level early
Interfaces to anything in Unity can also be helpful:
Using Interfaces in Unity3D:
Check Youtube for other tutorials about interfaces and working in Unity3D. It’s a pretty powerful combination.
Best practice is just what works for you. A simple game might be fine with a single ScriptableObject per type, but another might want a component-based architecture where you have an SO that behaves as a container for other SOs in the same sense that you have GameObjects with MonoBehaviours.
Thanks, Zulo - Thats kind of where I was heading, I was typing my initial question quickly in between meetings. There seems like quite a few ways to do it, but I was just curious if there was a best practice around that would handle that… I mean all “Swords” would have different damage ranges, and attack speeds, but they all derive from “Sword” so Making a separate script for each seems a little… bad?
Use abstraction and inheritance. Combining this with ScriptableObjects is very powerful.
I would personally suggest composition over inheritance. Such as expressing certain kinds of stats through interfaces. Or having a collection object that encapsulates all the different stats a weapon posses. This is where [SerializeReference]
comes in handy (if it may require custom inspector work).
This way you can avoid getting into an inheritance heirarchy trap, but instead make things more reusable across a diverse number of objects.
Perhaps something like:
[System.Serializabe]
public sealed class StatsContainer
{
[SerializeReference]
private List<StatComponent> _statComponents = new();
public int Count => _statComponents.Count;
public StatComponent this[int index] => _statComponents[index];
// more methods for looking up and such
}
public interface IHasStatsContainer
{
StatsContainer { get; }
}
public class SwordWeapon : ScriptableObject, IHasStatsContainer
{
[SerializeField]
private StatsContainer _statsContainer = new();
public StatsContainer StatsContainer => _statsContainer;
}
This doesn’t have to be weapon specific either. I’m sure a diverse range of objects could also have various stats if your design warrants something like that!
I didn’t detail anything about what a StatComponent
would constitute. But it’d likely be an abstract base class that outlines two primary (but distinct) details:
- The type of stat (as in damage, range, speed, etc)
- The value of said stat
These can be two separate objects that the stat component encapsulates. This is coming from having worked on something recently myself.
If the weapons are very similar then yes, a different script for every weapon would be silly. Although in the situation you’ve described, the stats of the sword (range and speed) could be determined by the length and weight of the sword which in turn can be determined by the scale of the sword and so you wouldn’t necessarily need to store that information. But you could still have a single sword prefab with a few public variables that describe subtle properties like how blunt or brittle the sword is, and then you can change those variables once the prefab has been dropped into the world to help make each sword unique.
I’m a minimalist and so I’m always going to take the simplest approach possible which means I don’t use inheritance or ScriptableObjects. The trouble with programming languages and game engines that have been around for a long time is that they start to become too complex for their own good. I pity new developers that have to learn all the crap that slowly gets added over time. It’s okay for us old timers, we’ve had years to take it all in.