0
\$\begingroup\$

I'm trying to make a start on a Spell/Skill system similar to a Moba or MMO from my project where you would press the skill button and see what type of targeting it has then aim (either in an area or at a target) and then "fire" the skill but I am coming really unstuck rather soon into it.

Please also bare in mind I am not asking how to implement X Spell from Y Game I'm looking to understand the structure of a typical spell in a MOBA/MMO not the spells behavior.

I'll try to describe the flow I'm aiming for

  1. Player presses Skill Button One - for simplicity's sake a Standard Fireball

  2. Cursor Changes to show targeting,

    -if it was an Aoe it would show the aoe, now this part stays like this until the Player casts or cancels.

  3. The Player casts (left click) - spell is spawned targeting stuff goes away.

  4. The Player cancels (right click) - everything reverts targeting stuff goes away.

This may seem like a really simple problem for some but I am really struggling with step 2

I have no code to show as every time I try something I find something fundamentally wrong with it, I'm repeatedly writing awful code because I can't find a starting point for this, in short - 'I don't get it'.

Would I use a state-machine and if-so where? Would it not get gradually more complex each with every new spell type? I'm a fan of doing this in a component based style but that right now is irrelevant if I can't understand the basic concept of it.

Apologies, I don't have the technical vocabulary to ask this is in a straight forward, concise way. If code is really necessary I can copy in my last attempt but I believe that would just lead people to think I want to fix the un-fixable.

edit: Added EntitySKillUser and Base_Skill

EntitySkillUser

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class EntitySkillUser : MonoBehaviour
{
    public GameObject target = null;
    public Skill[] skills;

    private Transform castLocation;
    private Projector projector;

    private void Start()
    {
        castLocation = transform.Find("castLocation");
        projector = castLocation.gameObject.GetComponent<Projector>();

        projector.enabled = false;
        //set up a dummy spell
        skills = new Skill[1];
        skills[0] = new Skill("Test", 2, 5, TargetType.SINGLE);
    }

    void Update ()
    {
        ///problem here only works when Imholding the button down - yes I know about getButtonDown and getButton
        //I want this to go into "targeting" mode when the buttons pressed wait for a left click to confirm THEN cast
        //
        if (InputManager.AbilityButtonOne())
        {
            if (target == null)
            {
                //how do I set each unqie targeting type for each spell without having to do something like this for each in the EntitySkillUser
                projector.enabled = true;

                Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
                RaycastHit hit;
                LayerMask layerMask = LayerMask.NameToLayer("Terrain");

                int layer = (1 << layerMask);

                //set projectorposition
                if (Physics.Raycast(ray, out hit, Mathf.Infinity, layer))
                {
                    Vector3 pos = new Vector3(hit.transform.position.x, 6f, hit.transform.position.z);
                    projector.transform.position = pos;

                }
                RaycastHit hitTwo;


                if (Physics.Raycast(ray, out hitTwo, Mathf.Infinity))
                {

                    if (Input.GetMouseButton(0))
                    {
                        //set target
                        if (hitTwo.collider.GetComponent<Enemy>())
                        {
                            target = hitTwo.collider.gameObject;
                        }
                    }
                    else if (Input.GetMouseButtonDown(1))
                    {
                        //cancel
                    }



                }



            }
            else
            {
                Debug.Log("have target "  + target.name );
            }


        }


    }

}

Base_Skill

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Base_Skill 
{
    public string name;
    public int apCost;
    public int range;
    public TargetType targetType;

    public Base_Skill(string name, int apCost, int range, TargetType targetType)
    {
        this.name = name;
        this.apCost = apCost;
        this.range = range;
        this.targetType = targetType;
    }
}

public enum TargetType
{
    SINGLE,
    AOE,
    MULTI
}
\$\endgroup\$
3
\$\begingroup\$

Sounds like a perfect fit for an event-driven system i.e. the observer design patten. This will allow you to decouple all of the individual components that you need. Each component would then subscribe to an event and react accordingly.

When a skill is clicked, an event is broadcast i.e. SkillClicked. This message would encapsulate all the information required by each individual system to progress the action i.e. CursorManager could change the cursor when a SkillClicked is fired, SpellManager could start a cast timer and your UI could display a cast bar.

The benefit to this is that it is extensible, you can add more components as you need and then enable their behaviour by simply subscribing to the correct event. A basic example implementation is below in implemented .Net Framework 4.6.1, I haven't tested it in Unity, so you may need to change the syntax of various parts (I'm unsure off the top of my head whether it is supported in Mono).

If i'm not mistaken, Unity has built in support for events that you can extend, but I haven't used it enough myself to be sure.

public static class SkillClicked : EventArgs
{
    public string SkillName { get; }
    public SkillType Type { get; }

    public SkillClicked(string skillName, SkillType type)
    {
        SkillName = skillName;
        Type = type;
    }

    public enum SkillType 
    {
        AoE = 0,
        Targeted = 1
        //etc..
    }
}

public static class EventManager
{
    public static EventHandler<SkillClicked> SkillClicked { get; set; }
}



public class CursorManager
{
    public CursorManager()
    {
        EventManager.SkillClicked += OnSkillClick;
    }

    private void OnSkillClick(object sender, SkillClicked e)
    {
        switch (e.Type)
        {
            case SkillType.AoE:
                //set cursor to AoE
                break;
            case SkillType.Targeted:
                //set cursor to Targeted
                break;
        }
    }
}

public class SpellManager
{
    public SpellManager()
    {
        EventManager.SkillClicked += OnSkillClick;
    }

    private void OnSkillClick(object sender, SkillClicked e)
    {
        if(e.SkillName == "Fireball")
            //start casting fireball
    }
}

Once these manager classes are constructed in some Init() method, you need only publish a SkillClicked event, for the correct behaviour to be invoked.

Hope this helps.

EDIT: If you need any further information regarding events and the observer pattern or other useful design patterns, I highly recommend you take a look at GameProgrammingPatterns

\$\endgroup\$
  • \$\begingroup\$ Hi thank you, I'll try this shortly when I'm home :) \$\endgroup\$ – Pheonix2105 Aug 9 '18 at 15:16
  • \$\begingroup\$ Unity does indeed have a fully functional event-handling system. +1 for your suggestion of using eventhandlers, this will massively improve performance vs checking the state constantly. \$\endgroup\$ – Ruadhan2300 Aug 9 '18 at 15:25
2
\$\begingroup\$

You're kinda stepping into territory I know well and have a lot of confidence with here :)

Were I to implement this, it's all about what code gets authority.

If the spell itself controls the cursor behaviour then what happens when your cursor needs to change otherwise? you'd need to control it in another place, and that can't step on what the spell is telling it to do.
You'd have one section of code saying it needs to be the "You can't walk here" icon, and another saying "area of effect blast template" and somewhere something needs to decide which one takes precedence.
Displaying what the character will do when you click is the primary function of the Cursor icon.

The best solution is almost always to centralise a given piece of functionality.
In an ideal world, you'd have a CursorController which talks to your game-state and decides what the best cursor for the situation is.
This cursor-controller can be folded into your existing input manager without too much trouble.

What you need to do is something like an extended if/else statement, you've got a bunch of nested logic to do and a Switch-Case wouldn't be complex enough.

//SpellManager - The central class that manages the update-loops of spells and tracks their data.
//GetCurrentSpell() - a method to retrieve the spell the caster has readied to fire
//MySpell - The individual spell the caster has readied to fire, 
//    MySpell is managed by SpellManager and is a Spell class. 
//    It contains the data and internal logic of a spell, such as its damage, mana cost, cursor icon, visual effects data and so on.
//SetCursor(arg) - Sets the mouse cursor icon, if the argument input is the same as the one it last set to, then it won't make any changes

If(SpellManager.HasSpellReady()){  
  MySpell = SpellManager.GetCurrentSpell();  
  Icon = MySpell.GetCursorIcon();  
  if(MySpell.IsAOE()){  
    AOE = MySpell.GetAOE();
    //Build the AOE overlay using the icon and aoe values
  }  
  SetCursor(Icon);// Set your mouse cursor to the one assigned by the spell.  
}else{  
  SetCursor("Default_Cursor_Icon");  
// You can also include logic here for displaying other information,  
// such as showing the interaction-state of the selected character.

}

Whether you run this code continuously off your main execution loop or trigger it whenever you see a change in the position of your mouse, or any other system is up to you. I favour the second one. There are probably more computationally efficient systems out there for deciding when to update this but it'll work just fine.

\$\endgroup\$
  • 1
    \$\begingroup\$ Thanks for the answer, I will give it a try when I get in front of my machine and see if it helps the concept stick bit better. \$\endgroup\$ – Pheonix2105 Aug 9 '18 at 15:15
  • \$\begingroup\$ Whatever solution you go with (and the nature of software development allows for many valid solutions) remember that this system is largely going to be listening to what you do rather than being told what to do explicitly. Your spell shouldn't care what your mouse-icon does. You might not even have a mouse in a future version if you switched to a mobile platform or console. So make it self-contained, make it simple, and don't rely on any more connections to other systems than you have to. \$\endgroup\$ – Ruadhan2300 Aug 9 '18 at 15:21
0
\$\begingroup\$

Not sure how your project is organised, but in theory one way to do this is to include something like target_type to your spells. Each spell, similar to how you save its damage and mana cost, save it's target type as well.

You have a variety of choises of what value to give to it. You can give it a value that represents it's AoE range, with 0 being "single target" (special case). Or you can create an enum, with values single_target and AoE_target, although that may have restrictions on defining range, but you can also add values like allies_only (for healing skills) and enemies_only (for aggressive ones).

So the logic goes: click on skill/press key -> get that skill's data, based on that skill's target_type -> change cursor depending on it

This is one of the ways to do it, I'm sure there are more than enough other ways that may even work better, but it's a start.

\$\endgroup\$
  • \$\begingroup\$ Hi, I understand all that, I have the base spell set up, it's getting each spell depending on its targeting type to display an aoe if its aoe etc. I'll add my code to the question it may be easier to see where I am struggling. \$\endgroup\$ – Pheonix2105 Jul 25 '18 at 15:46
0
\$\begingroup\$

Representing your input system with a state machine seems to be the way to go here. I don't see why it would get "gradually more complex each with every new spell type", because most of the spell-specific logic could be encapsulated in the object representing the spell being cast. The state machine itself could be largely spell-agnostic.

The only thing the "pick target" state needs to know about the current spell is the TargetType. When the player picked a valid target, then the state just needs to pass target and spell on to the "cast spell" state. What the spell actually does is of no concern for the pick target state.

The "cast spell" state wouldn't need to know much about the Spell either. It would just call methods from the spell object. The spell object itself would manage its own execution and call back to the "cast spell" state when it is done. What I would do is have a method public abstract void Execute(GameObject target, PickTargetState callbackState) in my Spell base class. The implementation would then do what the spell is supposed to do and then call callbackState.SpellFinished() when it is done. The method SpellFinished() of PickTargetState.SpellFinishded() would then switch to the next state.

\$\endgroup\$
  • \$\begingroup\$ Would you possibly please be able to show me a little pseudo code for this? Just the overall structure, I don't understand where I'd have the state machine, inside the SkillUser script or within Execute of the base spell itself? \$\endgroup\$ – Pheonix2105 Jul 25 '18 at 16:31
  • \$\begingroup\$ @Pheonix2105 The state machine would be inside your InputController. The script which interprets the player's mouse input. I don't see how pseudo code would help to illustrate this. \$\endgroup\$ – Philipp Jul 25 '18 at 17:05
  • \$\begingroup\$ I asked because I am having a lot of trouble understanding this, I can't find information on implementing something like this so I ask here, I also dont understand where PickTargetState comes from or how it helps me to display a cone or circular aoe range etc. and even if I did how is this turned into a gameobject, would Sphereoverlap reside in the spell? Is it an enum? if so what is the method attached to it? It also looks like an event? Lots of questions. Yes I understand I am very likely misunderstanding a lot but this is why I am asking for pseudo code. \$\endgroup\$ – Pheonix2105 Jul 25 '18 at 17:30
  • \$\begingroup\$ @Pheonix2105 These are all design questions with no right or wrong answers. There is never just one way to do things in software development. If I wanted spells to display an AOE shape, then i would get the kind of shape from the spell. As I said, if I would be designing this, then a spell object would contain all the information which is relevant for that spell. \$\endgroup\$ – Philipp Jul 25 '18 at 22:07
0
\$\begingroup\$

Since you are using Unity I would recommend letting the engine essentially run itself, thats what we had done the last time something like this was needed for a game jam.

In essence, you let the spell deal with its own things, including the rendering and the casting. Your spell could have a simple interface along the lines of Aimfor(point x) where you pass in the point the mouse is pointing at and the spell itself can descide what to render an how to do so since it is the only thing that really knows what to do in this case. It would also possess a second method in the interface Cast(point x) which makes the spell class spawn a new object. This new object then simply has a collider that it uses to detect if/to who it should apply effects and performs the actions it needs.

Using this technique its very easy to add new spells as you only need to define the spell itself as your input manager will know which spell should be used and manage it simply from there

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.