Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Dynamic/Pluggable Ability System

Discussion in 'Scripting' started by Dorscherl, May 6, 2021.

  1. Dorscherl

    Dorscherl

    Joined:
    May 29, 2017
    Posts:
    26
    Hello, I am a hobbyist programmer and I want to see if can create an ability system that is dynamic and pluggable in the inspector. I want to have a system that is AI friendly but I need to focus on making sure everything works with the player first. I know I will being using a virtual controller for my input on my character.

    Abilities to me define what a character can do involving movement, jumping, climbing, swimming, shooting guns, using weapons, casting spells, and skills. Some of these are activated automatically and some are manual. Abilities can use other abilities and so on.


    Classes I think I'm going to use for the abilities:
    • Ability : ScriptableObject (sealed?)
      • this class contains everything is needs to define an ability (i.e. name, description, activator, targeting, enums to label functionality (hide it from player, is it passive/active, can it be assigned by the player)
    • AbilityActivator : ScriptableObject (abstract)
      • tells the ability how to activate
    • AbilityTargeting : ScriptableObject (abstract)
      • tells the ability how and who it should target
    • AbilityBehaviour : ScriptableObject (abstract)
      • performs a specific action upon specified event
    How everything is controlled/maintained:
    • AbilityController : MonoBehaviour
      • references the character script
      • contains list of all character's abilities
      • players can select which ones go on hotbar if ability permits assignment
    I'm not sure how I should make this work with the ambiguous goal in mind. The activator, targeting, and behaviours (depending on it's event) will probably have to run every frame. I have to account for cooldown, channeled spells/skills, and continuous input handling for movement based abilities.

    For cooldowns, I'm thinking I inherit from activator. The cooldown condition activator will have use another activator instance to start it and wont check it until the cooldown timer ends. The alternative is to make the ability class contain data about a cooldown and uses it if specified.

    Channeling abilities will be trickier yet. Have to figure out a way to increase an attack or launch an ability at different intervals. Channeling got interupted so do a fail animation or the player didnt finish channeling so its a weaker attack/spell. It'll have to read the input through out the process. Might have to have the ability class contain this information too though it's not ideal.


    Next thing I'm considering is to not make the ability class sealed and just keep my inheritance narrow. Have a ChannelAbility class inherit from ability. I don't want cooldown to be a child of ability because so many abilitys could be able to use it.

    Anyways, this was my idea. Been tinkering around for a while now and would like to get somewhere soon. Would like to bounce ideas around and get resources. I would like to use scriptable objects and keep things able to be assigned and given at runtime.
     
  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,713
    Certainly seems reasonable. The only parts listed above that seem like you should not care at all about are the silly language stuff like sealed and abstract and whatnot. Your first order should be to iterate while keeping things as loosely coupled and simple as possible, and see what shapes up.

    Another incredibly powerful tool for Unity is C# interfaces. This lets you make lots of different things that share common traits, such as "IAttackable" or "IDamageable," stuff like that.

    Using Interfaces in Unity3D:

    https://forum.unity.com/threads/how...rereceiver-error-message.920801/#post-6028457

    https://forum.unity.com/threads/manager-classes.965609/#post-6286820

    Check Youtube for other tutorials about interfaces and working in Unity3D. It's a pretty powerful combination.

    But beyond that the biggest deal as you stand this up is to keep your brain in flexible mode so that if you find yourself starting to make something awkward or inflexible, you can stop and back up and refactor. ALWAYS use source control (I use git) so that you can instantly see past history and revert to previous versions to find issues you might have introduced along the way.

    And as always, iterate, iterate, iterate... make the smallest simple hard-wired single-ability system first, then make it able to handle 2 abilities, the make it able to hand 2 abilities and 2 attacks, etc. You will NEVER be able to just code it all at once, press play and go "Yep, there we go." It has to be built in increments and iterations.
     
  3. Dorscherl

    Dorscherl

    Joined:
    May 29, 2017
    Posts:
    26
    My biggest fault is thinking too far into what I want. There are things I know I'll need eventually then I don't know how to get it all working. It's something I need to work on.

    I am uncertain on how I would utilize interfaces in this manner of pluggable behaviour. I want to keep things flexable and uncoupled. I know there will be some abilities that need to be hard coded but when I think of other, smaller abilities it gets more complicated. My goal is to have abilities able to be designed and pieced together in the inspector alone. I am probably thinking of this little project to be TOO ambiguous though, so for now I'll take your advice and start one ability at a time. I'll definitely check out interfaces more too
     
  4. Lurking-Ninja

    Lurking-Ninja

    Joined:
    Jan 20, 2015
    Posts:
    9,904
    As an advice: buy a whiteboard if you have some little space. Stand up to it, draw up your architecture and take notes what connected to what and why. Draw up all types of things you want to do, even if you don't yet know how.
    Visualizing usually helps and good deterrent against feature-creep. Eventually you will trash half of it. :)
    Also you can take a photo of it and pull up on your screen when you're coding or print it out or draw it on paper or whatever, to see during coding. Or you can keep it on the board if you don't want to use it for other features for the time being.
     
    Dorscherl and Kurt-Dekker like this.
  5. felipemullen

    felipemullen

    Joined:
    Mar 4, 2017
    Posts:
    44
    @Dorscherl have you found any decent solutions to this? I have a similar need and there aren't many good approaches out there:

    I want pluggable logic, similar to this sytem, but I don't like having to create an asset for every scriptable type https://learn.unity.com/tutorial/pluggable-ai-with-scriptable-objects

    A system like this also creates a big problem: If you aren't careful, you may end up with bugs that come from reused instances of ScriptableObject assets across different GameObjects.

    My current solution is to create custom dropdowns for classes I intend to use in this way. My editor script searches for all subclasses of a type, and presents a dropdown in the editor. On deserialization, I then create an instance of that class using reflection.

    Surely someone has a better system for flexible traits/logic related to characters?
     
  6. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    5,862
    You can use SerializeReference to serialise direct references to plain classes as a good way to do this compositional design. I use it a lot over scriptable objects because I use this pattern a lot.

    Unity can't draw these fields by default, but various addons can, or you can write your own property drawers. It sounds like you already have a handle on editor coding so it shouldn't be an issue for you.
     
  7. felipemullen

    felipemullen

    Joined:
    Mar 4, 2017
    Posts:
    44
    @spiney199 Thank you for that tip!
    I had come across SerializeReference before but didn't realize how well if fits the current scenario. I have created an attribute that can be used to choose a class type, and when the dropdown value is changed, an instance is created and then applied to the underlying SerializedProperty:

    upload_2022-4-23_8-0-49.png

    With this, I am one step closer to avoid ScriptableObjects for scripts whose intention is to have configurable logic
     
    spiney199 likes this.