RPG-like serverside spell/skill system

Started by
7 comments, last by Enzi 11 years, 1 month ago
Hi,
i'm creating a game which needs Spells like those seen in most Mmorpgs,
meaning area of effect spells and targeted spells/abilities.
About Three years have passed now and i'm still not at a point, where i'm satisfied with the system.
During those years i already had the game in a state where one could "cast spells" with his character.(
).
But the further i got it became obvious that on the server side i had to drop unity for a custom solution.
Now i'm writing the server in visual studio with Lidgren/c#.
And now i'm stuck.
Spells. I can not imagine how i could implement these. I tried many many ways, all of which failed.
In Unity it was simply a matter of instantiating an emtpy gameobject with a script on it which ran as the GO was created(i had many scripts, specific for each spell).

I'm by no means a professional programmer, more a hobbyist, so I'm stuck at translating this drag and drop like design to pure code.
For example at last i thought about having a dictionary where Spell is a Class implementing all the specific logic, and an interface for cast() and so on, and requesting that from the dictionary
class Fireball :Spell
{
}
But that didn't work for many reasons, like i dont know how to retrieve a new instance of this spell
(new (spell.GetType())() didnt work.
I also thought about having an all purpose Spell class with methods and fields for all possible things a spell could do and enabling specific behaviour through bools, enums and so on. But that was too much to keep track of.

I tried a few more things but in the end I'm as clueless as in the beginning, even before using unity as server.

So, how would a basic Spell/ability implementation/system look like? (Interfaces/abstract classes or whatever) when i need the spells to be attachable to any character and be refered to by id and also dont want to hardcode the spells into the player class.

I'm desperate, because i'm sitting on this problem for about 4months now,
so i'd be grateful for anything that helps.

Thank you very much.

P.S I'm not a native english speaker; i may have used strange combinations of words. And as for doing silly things with code, i'm constantly learning new things every day.
Advertisement

it became obvious that on the server side i had to drop unity for a custom solution

Why?

i dont know how to retrieve a new instance of this spell

Is your particular problem "I don't know how to create a particular class instances based on data provided by the player" ?

The simplest solution would be to just use a big switch:

Spell MakeSpell(string name) {
  if (name == "Fireball") { return new Fireball(); }
  if (name == "Icecube") { return new Icecube(); }
  ...
  throw new InvalidDataException("The spell type " + name + " does not exist.");
}
This means that you have to add a new line in this function when you add a new spell type. That's not so bad.

Other ways to make it better may include:

- Build a Dictionary<string, SpellFactory> that has the spell name as key, and a delegate that creates the spell instance as argument. Register spells in this dictionary on start-up.

- Use System.Reflection to walk all classes in a particular assembly, and auto-register all classes that are public and derives from Spell. This removes the need for manual registration.

- Use an Annotation to declare that a particular class is a spell subclass that can be instantiated as part of casting a spell. The annotation class has a static member to keep track of all possible spells. The Dictionary from above would probably be this static member.
enum Bool { True, False, FileNotFound };
Hi,
thank you for such a fast response!

I had to drop Unity for Bandwidth reasons, i already implemented movement sync in my new server and it proved far better.
Unitys sync or rpcs where around 300 byte/s where the new solution is around 70 byte/s, i dont really know Unitys internals to say why.

To the second question: i meant, in my example i had to store an instance of a spell in the dictionary but for every player it needed to be a unique instance(cooldown obviously shouldnt be shared among players), but your idea for the SpellFactory seems very good in that case.
I remember reading about a Factory pattern somewhere, is it related to this?

As stupid as i feel now, now that you mention it, even a big if-else wouldn't be a huge performance impact as a spell would only be retrieved from it once/when a character learns it. I'm just a little concerned, as based on the gameplay players will probably learn and unlearn spells in a rate of about a second at certain times.
I'm estimating at most 15 characters online with about 4-6Spells each which will swap at a 5 second rate, just to be sure. How do you think such a switch will perform when i have about 40 Spells, will it even be noticable(i will probably do an int (id)-comparison instead of a string)?

As for Reflection, i read that its not a good choice for fast code, or is that a myth too?

I'm so focused on performance because now after such a long time coding and failing, i really want to try to make it perfect.

Thank you very much, again.

P.S. i now think i see what you meant with "why?".. i guess i could use Unity AND Lidgren even on the server side.. i actually haven't thought about that (silly me..)

As a side node; is there a general approach to such a system, how do popular mmogs do it? I'm eager to learn.

I'm doing something similar and I'm using Reflection for instancing. The types are saved in a dictionary with the spell Id. I call GetConstructor on this type to get the ConstructorInfo and then Invoke it.

I guess you can even buffer the ConstructorInfo to save some Reflection calls but I've not tested this.

Reflection calls are expansive but it always depends what you do. Saying Reflection is slow per se is wrong though. In this case it's a neglectable performance hit.

Using Reflection is always a trade off in some way, but if you can pick between not being able to implement things as you like and, writing a more complicated, maybe even slower system yourself the answer is quite simple.

thank you for your idea.

I think i will simply use a switch(id), as i guess that will be just as effective. Each spell will have its own class derived from baseclass Spell with its unique Cast() method and will be a simple state machine (BeginCast, WhileCasting, EndCast). Thats similar to what i used in the first place, while still using Unity as Server.

The if() based solution is likely much faster than Reflection, but even with Reflection, you're unlikely to see a problem with 100 players each changing spells every 5 seconds. Other things, like networking, will be much more likely to be a bottleneck.
enum Bool { True, False, FileNotFound };

thank you, i'm halfway through, implementing the old spells now in a switch(id) like i said one post above, so far it performs very well. I guess my real problem is just that i hack in every bit of code that comes in my mind and as far as everything works, ok.

But then , when i stumble upon the first problems i realize that i had to build a completely different base.

My Tip for anyone who works the same way and reads this:

Plan your Systems first, work out how they interact and affect each other. Scribble them on paper and name their parts.

I began to do that lately and it really really helps as you can see possible collisions beforehand.

Plan your Systems first

While a plan certainly helps, there's a danger of going into analysis paralysis.

I think the most important part is to understand that re-factoring code is a natural part of the evolution of a system. Thus, you should make your overall code as easy to refactor as possible. This may mean using interfaces between each subsystem, so that each subsystem is less dependent on everything else. This may mean using consistent code formatting and naming conventions, so that search-replace and code movement is less prone to typos. And it very likely means writing good automated tests for each piece of functionality you develop, so you know that a re-factor doesn't break the functionality!
enum Bool { True, False, FileNotFound };

...

I know that feeling. Over the years I just started to structure on what I work better. For nearly everything I need to design I use a top-bottom approach.

Design how you want to use classes, not how classes should be used. You'll learn a lot during the process of writing your highest level code what you actually need.

The only real downside is that your codebase is often in a state where you can't compile for hours. But when you reach the point where you have implemented everything, press compile and all works as planned you'll be one happy person.

While using bottom-top I often ran into the problem that I forgot something and needed to refactor a lot. Although refactoring is quite common I still want to avoid it where possible.

Also a sidenote that I want to mention. Even though I design most projects alone I always try to make my codebase as API friendly as possible. What if another guy wants to help you out? In the best case you can give him a single project (dll) with interfaces, helper classes, base classes to inherit, virtual methods to override. He shouldn't worry about any low level stuff, that part should all happen in the base classes.

Even though it sounds excessive I'm always proud that I do stuff like that when I come back to parts of a project I completely forgot about. You'll know where everything is and in the case if you just want to extend it you use your simple API calls.

This topic is closed to new replies.

Advertisement