UML for UE4 Robust Skill System

Started by
3 comments, last by Mark William Nations 7 years, 11 months ago

So I've been wanting to come up with a robust skill system for game development that I can build in Unreal Engine 4 C++. I've been working on the overall design (and trying to improve my UML skills), so I'd appreciate any feedback you all have.

Basically, I've been trying to apply Decorator, Factory Method, and Command patterns to the composition and application of skills.

And for those of you unfamiliar with UE4 who look at this, hint: TScriptInterface is just a wrapper around a reference to an interface type.

EDIT:

I have a feeling that I'll need to add the use of the Visitor pattern as well so that I can decouple any references to a SkillUser or Skill's data from the SkillEffect and SkillMod "Apply_____" methods, respectively. As it stands, the methods have no way of getting to the data of a SkillUser, and I'd like to keep it that way since it can't be templated properly (which would have allowed the user to supply their own class as the SkillUser/Skill data object). Instead, it would be better to have it function like the Visitor pattern to where the SkillEffect/SkillMod is passed to the SkillUser/Skill and the SkillUser/Skill handles HOW the thing influences its data directly (since it's aware of the private data that is hidden from the interface). I have yet to add that to the UML design though.

willnationsdev - Godot Engine Contributor

Advertisement

First of, I openly admit that I am not exactly the best coder out there, and I am not even going to say if your diagram is good or bad.

Anyways what I was thinking when I read your post and quickly glanced your classdiagram was, that you haven't given us any information what you are actually trying to do. Basically there is no information of what it is that you are actually trying to do. And at least I do not know what you mean by skill system, the definition could be pretty much anything, but judging from the class diagram it has something to do with spells/player attacks or something?

So first before asking feedback, I think it would make sense to actually write the use of your system. Like for example I want to make a system that when pressing Space, it attacks a selected target (if this would be turn based?) and uses the skills special attributes to damage the target, and the things that are within certain radius of the target. The skill has a movement speed and when it is fired it creates a certain sound and when it hits the target, it creates explosion sound. Every target that are damaged also grunt and the skills special attribute such as freeze is applied to every one of them.

The example is pretty terrible, but my point is, that if you write a scenario or something that shows how the system is used it probably helps more for people to make it easier to give feedback.

Now the second point and I have to admit that I haven't used unreal engine much, so it might have some underlying classes that one must use in order to accomplish something, but.... Lets say that this is the first time you are building the system, I honestly think that you should try keep things as simple as possible. If on the otherhand you have already made one version and you are trying to improve your previous design, then you can just skip the rest. So if this is your first design at least for me, if I try to create a solution to complex thing, I usually overthink things too much. Then I spend way too much time trying to solve everything on the first go, and... waste a tons of time. Regardless how long I spend on trying to think stuffs beforehand, things just rarely go as I plan. After getting things work, I realise that some of the decisions I made before hand were either bad or I didn't think something. So I myself have tried to learn to try to come up as simple solution as possible, even if it doesn't solve everything, and eventually add/modify it until it is good enough. Some things that I have overcomplicated might actually turn a lot easier or simpler and the most important thing is, that I acoid making things more complex than I need to. I should not add features that I am not actually going to use, by trying to generalize everything for all purpose super system.

Anyways the last chapter thoughts came from seeing so many interfaces, that I cannot comprehend how complex of a system the pdf looked like from first glance. Or highly possibly I just do not understand what an interface is.

Thanks a lot for your feedback Mekamani! I appreciate it.

TL;DR Start

I've done this once before on a student project, but it was limited. Trying to add robustness for usability in as many different types of games as possible.

Interfaces let you disregard what objects/algorithms one uses and define relationships based solely on the type of interactions they have. This would let me define my own framework, supply my own sample set of classes to actually fit the framework (implement the interfaces), but also leave other developers open to build their own versions should they want to.

I want to minimize code duplication and have the capacity to fully dynamically define the behavior of events in a game.

Ex. pressing "A" does (1). Pressing "B" does (2). (2) changes how (1) works without explicitly requiring me to create a new function/object for (1)'s functionality.

Ex. I press "space" to trigger a skill. I have 3 skills (A), (B), ( C ). I have 1 effect (1). I have 3 ways in which (1) is delivered. (A) uses delivery method (a), (B) uses (b), ( C ) uses ( c ). (A), (B), and ( C ) should each rely on the same function for (1), but should have 3 different objects responsible for the delivery based on (a), (b), and ( c ), minimizing code duplication. I should also be able to easily switch out (1) for some other effect (2) and suddenly get 3 new skills that each cause effect (2) in 3 different ways (a), (b), ( c ). I should also be able to make it so that if a skill ( C ) has multiple stages to it or if a skill (A)/(B) is used, I have the capacity to influence the functionality of the (1) or (2) that is in ( C ), all without changing the underlying implementation of (1) or (2).

TL;DR End

Now the second point and I have to admit that I haven't used unreal engine much, so it might have some underlying classes that one must use in order to accomplish something, but.... Lets say that this is the first time you are building the system, I honestly think that you should try keep things as simple as possible. If on the otherhand you have already made one version and you are trying to improve your previous design, then you can just skip the rest.

I actually already worked on a much simpler version of this skill system for a UE4 project at Baylor (it is used in the Steam Greenlight tactical turn-based game SYNCH). While I was able to do some nice things with it in that project, I often found myself in situations where features that we wanted to have in the game turned out to be very difficult to do with the given framework. As such, this is my current attempt to redo the framework's construction to be more viable for a variety of scenarios.

Anyways the last chapter thoughts came from seeing so many interfaces, that I cannot comprehend how complex of a system the pdf looked like from first glance. Or highly possibly I just do not understand what an interface is.

In the simplest terms, an interface just lets you define the behavior you want without concern for the underlying implementation. That is, I can build a framework for the system that I want without making requirements about how anyone actually implements it, i.e. what objects/algorithms they decide to use to fit into the framework. The ubiquitous interfaces were my attempt to decouple a sample implementation I was planning to make from the design that would be publicly visible to the UE4 system. I wanted to build something that anyone would be able to use and, if desired, make their own by creating their own implementations.

Anyways what I was thinking when I read your post and quickly glanced your classdiagram was, that you haven't given us any information what you are actually trying to do. Basically there is no information of what it is that you are actually trying to do. And at least I do not know what you mean by skill system, the definition could be pretty much anything, but judging from the class diagram it has something to do with spells/player attacks or something?

Ah, I see what you mean. I initially thought that people would understand what I meant by a "skill system," but I see that was a poor assumption on my part.

I tried to have a short summation of the thought process for the skill system in the diagram itself (big Note object in the bottom-right), but I you have to look in the diagram to see it, you may not notice it, and it may not even be good enough at explaining things...

The core usage I had envisioned was for RPGs or tactical turn-based games (like the one I helped produce), but I was also intentionally abstracting the concepts to hopefully make it viable for a large variety of game genres like shooter, action/adventure, RTS, MOBA, etc. Fundamentally, I want a system that allows me to modularly put together "abilities", that is, the culmination of a discrete set of manipulations from one game object upon 1 or more other game objects, and which provides a powerful and robust method of delivering these abilities' manipulations ("effects"). In addition, the system must make it possible for the specifications of "effects" to be variable and influenced by other effects.

For example...

My character has the ability to press A and shoot a bullet in a forward-facing direction. They can also press B to "charge", an action which leaves them unable to move/attack for 2 seconds and then it modifies the effect(s) of the "A" skill. It 1) increases range and 2) adds an area-of-effect element to the bullet. So, if the person presses B, charges, and then presses A, they will have a projectile that hits at farther range and hits several enemies in a given radius rather than just a single enemy at shorter range. This would be an example of where one skill's "effect" is variable and can be influenced by another "effect."

Presumably, ANYTHING would be able to layer on these "modifications" to skill effects though, so power-ups, equipped armor, skill tree bonuses, etc. You could even create a different skill entirely by just having permanent skill modifications to the existing skill. Indeed, if I wanted the second effect to be its own permanent skill, I wouldn't need to actually create a whole different skill that has the exact same effect with a few different stats. I could simply never remove the modifications to the skill (they are essentially added components of the skill that acts as an entity). In this way, I need only have the code that "deals damage to another game object" be in one location: the DealDamage SkillEffect. If I edit the DealDamage object, I can change the way ALL skills deal damage. It is also fully dynamic, so I can add or remove damage dealing from any given skill should I desire to.

Another example...

I have a bullet (deals fixed damage on impact), a fireball (deals fixed damage at regular intervals for a fixed period, i.e. a Damage-Over-Time effect), a lightning bolt (deals damage to one target, then finds another nearby target and deals the same damage to them, triggering up to 3 times, i.e. jumping twice).

The things these each have in common is the singular effect, DealDamage. They each differ in the delivery of that effect. I should be able to make it so that the underlying implementation only differs in the object that is responsible for distributing the effect (as would be possible - ideally - using the SkillDistributor object in the diagram).

In addition, I should be able to fully manipulate the effects and modifications for these skills. For example, I could take the lightning bolt skill and change its effect to be something that slows down enemies (so each hit object has a 50% "Slow" status condition applied to them - 'status conditions' aren't currently a part of the diagram, but it's a viable thing to be added). You could then also make it so that each successive "hit" resulted in an added 10% effectiveness, achieved by having the skill's own effect apply a mod to itself after each hit causing a change to its variable attributes.

I should also be able to switch up the way in which the skill is distributed (a volumetric area, a projectile, just targeting the self, etc.) simply by altering which targeting method is supplied to the delivery object for a skill.

I hope that made it slightly more clear.

willnationsdev - Godot Engine Contributor

Ok after spending some time looking at the diagram, I think I have grasped most of it. It took some time to find the most centric classes: skillUser and skill, because they were one of the smallest boxes in the pdf.

In simplisity what you have is Skill and SkillUser which to my understanding is essentially something like unit/character. Then when skill is being used it is then being applied to other units/characters that you get from some sort of filtered list, that is determined by the skills characteristics (if skill is aoe or single target, etc.).

Now just some thoughts. How do you apply damage over time? Personally I would add debuffs or skills into the skillUser list that was affected by the skill, and then have some timer on the skillUser that loops through whole list of skills that the skillUser is being affected in certain intervals. For example once every 0.5s if real time, or once every turn change on turn based. Then when the debuff has run out, just remove it from the debuff-list. Although I do not know if this is a good way to do it or not. The dot system also probably needs to take some sort of snap shot from the character stats when the dot was applied to keep the damage same throughout the whole duration.

The debuff list is just bit annoying when you actually have multiple effects on you. If any of the things are additive (power +10, when this effect is on), you have to actually have some sort of back up of your base stats, and recalculate the stats every time you add or remove things. But if everything in your game/system works on multiplying, you can just keep the current value in memory and when effects endy reverse the calculations by just dividing it.

In simplisity what you have is Skill and SkillUser which to my understanding is essentially something like unit/character. Then when skill is being used it is then being applied to other units/characters that you get from some sort of filtered list, that is determined by the skills characteristics (if skill is aoe or single target, etc.).

Yeah, you got the basic gist of it. The only thing to add to that would be that the manner in which a "filtered list" is acquired is based not on the skill's characteristics per se, but on what targeting method you have paired with your SkillDelivery object. A given skill may well specify which TargetingMethod(s) is/are to be used, but that is up to the user's implementation.

Ok after spending some time looking at the diagram, I think I have grasped most of it. It took some time to find the most centric classes: skillUser and skill, because they were one of the smallest boxes in the pdf.

Would you recommend that I move things away from the Skill/ISkillIntf and SkillUser/ISkillUserIntf stuff so that they stand out more, grouping-wise? I want to make it as readable as possible, but I'm also rather inexperienced with UML diagrams and am not sure of the appropriate methods to improve its clarity.

Now just some thoughts. How do you apply damage over time? Personally I would add debuffs or skills into the skillUser list that was affected by the skill, and then have some timer on the skillUser that loops through whole list of skills that the skillUser is being affected in certain intervals. For example once every 0.5s if real time, or once every turn change on turn based. Then when the debuff has run out, just remove it from the debuff-list. Although I do not know if this is a good way to do it or not. The dot system also probably needs to take some sort of snap shot from the character stats when the dot was applied to keep the damage same throughout the whole duration.

The debuff list is just bit annoying when you actually have multiple effects on you. If any of the things are additive (power +10, when this effect is on), you have to actually have some sort of back up of your base stats, and recalculate the stats every time you add or remove things. But if everything in your game/system works on multiplying, you can just keep the current value in memory and when effects endy reverse the calculations by just dividing it.

Yes, status conditions are actually the next step in my analysis! You would need to have some sort of OngoingConditions list of Skills (i.e. SkillStorage) that would be specially designed to pair each Skill with a a starttime, endtime, and triggertime. Perhaps have another interface ITimerIntf that has derived versions IDiscreteTimerIntf (for turns and other discrete advances in time) and IRealTimerIntf (for float-based advances in time, etc.) to use for 1) how long it has been there, 2) much longer it must be there, and 3) the interval for a "trigger" at which point the Skill executes upon the list of targets.

I'm actually not sure if you would need to maintain a snapshot of a character's original stats. I was hoping to create an actual implementation that uses the Skill's as a Command pattern so that each skill is individually responsible for applying or reverting their own influence over the SkillUser (and same for SkillMod/Skill) in which case you can get back to any previous state by running all of the necessary Revert commands on the Skill's that have affected your character. That also means that somewhere you would have to keep track of any relevant information pertaining to Skill's influence on a given SkillUser. That's exactly the reason for the interfaces though. Some people may not want to bother with something so heavy-duty, so the framework itself should be stable under a different implementation.

In the game that I implemented, we actually created a whole copy of a skill with all of its mods already applied whenever we wanted to get information about what WOULD happen if the skill were to be used, i.e. "how much damage would this deal?", "at what range can I hit enemy units?", etc. It was definitely a quick and dirty implementation, but I could imagine it to where the system can just run through the list applying and reverting skill effects to flip flop between states in the Skill's state history, much like running through ctrl-z and ctrl-y on a computer application. Not sure how that is done EFFICIENTLY though...

willnationsdev - Godot Engine Contributor

This topic is closed to new replies.

Advertisement