Ranged and Magic Attacks in Third Person

hey guys, quick question. Anyone figured out how to implement ranged and magic attacks in third person?

I did it in my prototype via strategy pattern. depending on your number of ranged actions and effects its viable. This is my old code from the prototype. Ill be refactoring this soon but i have not decided if I’m using a decorator or composite pattern. you could use this strategy as is, but its not going to scale well if you have a lot of abilities or want a huge amount of fine tuning. also probably switch the instantiation out for an object pool.

using System.Collections;

using System.Collections.Generic;

using UnityEngine;

[CreateAssetMenu(fileName = “Ranged Combat Action”, menuName = “Combat Actions/Ranged Combat Action”)]

public class RangedCombatAction : CombatAction

{

[SerializeField] GameObject _ProjectilePrefab;

public override void Cast(Character caster, Character target)

{

    if (caster == null)

    { return; }

    GameObject proj = Instantiate(_ProjectilePrefab, caster.transform.position + new Vector3(0, 0.5f, 0), Quaternion.identity);

    proj.GetComponent<Projectile>().Initialize(target);

}

}

here is the base abstract RangedCombatAction inherits from. i keep the base classes pretty bare bones most of the time, so most of this should have been pretty easy to guess but this might make it easier.

using System.Collections;

using System.Collections.Generic;

using UnityEngine;

public abstract class CombatAction : ScriptableObject

{

[SerializeField] string _displayName;

[SerializeField] string _description;

public abstract void Cast(Character caster, Character target);

}

as much as I’d love to think this is easy, none of this is frankly easy… like, at all!

When I see abstract classes, I literally freak out (I wish I was exaggerating, but I’m not), because the idea of how they work never fully got into my mind… I don’t have enough experience with those as of yet

(btw, what’s the “strategy pattern”?)

The only thing I learned today, was what a singleton (or an “instance”), is, and how to simply code one (someone outside this community was trying to help me program a notification system to burn some time… we didn’t get that to work either as of yet :stuck_out_tongue_winking_eye: ). I still don’t understand why he wants one instead of declaring and caching variables…

OK I know this is probably off-topic (again), but if Brian reads this, do you have any idea what an “Avatar Mask” is…? I asked Malbers about mounted combat (basically the ability to fight whilst mounting an animal), and he referred me to that topic. After a bit of research, I can see what in the world I’m about to face…

The main question is, though, is it easy or hard to implement, or would you recommend I completely skip this idea?

(sorry im working so i don’t have much time lunch almost done)

Ok I’m going to tell you something you might need to hear my friend. I hope it does not come across as rude… im really trying to help you. i think you need some more class time. there are a lot of pitfalls on the road we are taking and the biggest one is going to be scalable and debugable code.I was a bit surprised when i recommended an observer model to you on another thread, and you didn’t know what it was. its totally ok to not know what it is. i didn’t know what it was either like 10 months ago. I’m here to learn too. not knowing the strategy pattern or being intimidated by abstract is two more strikes. without those at your disposal you are going to end up with places with loads of repeated code, a project that is very hard to scale, and incredibly hard to debug. 40 hours of research is going to save you 400 hours of headache. You have made it this far your smart enough to not be beaten by an abstract class.

i would really really recommend taking the design patterns course. and supplementing it with youtube as well. i have some playlists i can link for you that might help, but the GDTV course REALLY REALLY helped it sink in. Don’t let it intimidate you. i literally had to take that course twice. i took it the first time, realized i was in over my head, watched about 1000 hours of youtube videos, did the intermediate courses, and then repeated it.

as for the basic strategy used here with an abstract its really not too bad. its a very nieve implementation but a good basic starting point.

a quick small note on inheriting. its really just telling unity or c# the type of script you are using. don’t over sweat the abstract. do some research on it, as it will use things like protected overrides, but while you are figuring it out just understand that whatever is in the abstract is essentially INVISIBLE in the concrete. even if its the concrete is completely empty its still going to have everything in the abstract. its kind of like the code version of a prefab variant.

  1. in my example we create an abstract class that holds basic information for an attack. not anything special. it might hold damage and the target. you wouldn’t want it holding variations of the attack… just the stuff every attack needs. ( in our case my abstract is the CombatAction class). we will add basic variations when we define concrete variants like RangedCombatAction or HealCombatAction or StatusCombatAction.

  2. you create concrete versions of that class by inheriting from it. these concrete versions are what you use to make your scriptable objects. in our example they are RangedCombatActions. You may notice that in my very basic example its not even a ranged magic action or an arrow. the strategy pattern allows us to manage that via the next step which is creating a scriptable object from the concrete RangedCombatAction.

  3. once you have created the concrete you go into unity and create the asset ( in our example its the it would show up as create>combatactions>rangedCombatAction. This generates the scriptable object. you then input the values you need to for your spell or ability. this makes it to where you can literally build dozens of spells or abilities in minutes without recycling code. if there is a problem with the code and you are keeping yourself relatevely decouppled you will instantly be able to tell where the problem is. if its happening with every combat action its at the abstract level, if its only happening with ranged actions, its in the concrete RangedCombatAction, if its only happening with one ability its in your inputed fields in that abilities scriptable object. it really makes chasing bugs very intuitive.

small note if you use my prototype code to understand. it is reliant on the projectile having a collider and rgidbody. probably not ideal long term depending on performance but if you are testing with it you will need to know.

please give me a few minutes to read and process all that again… (btw I’m not offended. You’re right, I’m no professional programmer, just an Engineer who wants to switch careers to something more fun, but wants to be quick about it because his opportunities in life are running out faster than he can blink)

Am I making a mistake? I’m not so sure, but I am enjoying this project when it’s not a debugging nightmare (I fixed some wild stuff before, I don’t think I’m giving up now… (dear god, PLEASE don’t call me out on this!) :sweat_smile:)

no worries i am back to work. ill check in tonight and try to help you.

1 Like

I get it. Im a global systems engineer, who got turned off of coding in high schooll because i m dyslexic and the IDE we had was basically useless. I have a kid now and I’m watching time disappear. I’m pretty damned tired of engineering other peoples dreams and watching them get rich off of it. This may not work out but if i can buy back time with my son its worth a shot. at least i can say i had the balls to give it a try, i guess that’s good enough for now.

Would a drawIO of the model help you? i didn’t make one for my prototype, but i can smack one together really quick.

1 Like

if you’re okay with that, sure, but please focus on your day job over helping an internet stranger first. No offence, I just want you to watch your back, I don’t mind waiting till you’re available (since I’m the one seeking help, I need to learn how to be patient) :smiley:

but if there’s one thing I know, is that for me sometimes what I learn doesn’t matter as much as how frequently I see something… I can be a master of abstract classes for instance, but if I haven’t used it in 5 years, I won’t remember it, and just blink like I’ve never seen this before (fun fact, that’s actually the case)

and there are other times where I just need to re-digest what I just read and ask a stupid question to actually get what the other person was saying (and that’s literally 99% of the time for me here, and what happened now was no different… I get what that abstract class is for now btw)

This one isn’t a stupid question though, I genuinely need help with this system

I feel you fam. I don’t have a kid, but I know EXACTLY what you’re talking about! (I had a breakup about a year ago, and this project was my therapy tbh :sweat_smile:)

SOO… Long Story Short:

  • Observer Pattern is when you OBSERVE (basically one object observing and telling other “yo, I had a change, guys please act accordingly”)
  • Strategy is… being strategic (you got different behaviours being tuned for a specific task)

That’s new, but I’ll need to be reminded of these as examples as we go for me to freeze the idea, because right now it’s a loose one…

Observer is a subscription model where a class subscribes to the events in another class and does them when the event is called. they are usually given names like OnAbilityActivated. so if we had a class that managed our input lets say. when you press a it might fire OnAbilityActivated. then in other classes like the actuall ability script, or your sound manager or whatever. when OnAbilityActivated fires. they are listening for it. they listen to it by subscribing to it( best practice is to subscribe in oneneable and unsubscribe in ondisable. z

i sped typed out this example. gottta go do kid bathtime but this should help.
when this class calls OnAbilityActivated

using System;

using UnityEngine;

public class AbilityActivator : MonoBehaviour

{

public Action OnAbilityActivated;

public void ActivateAbility()

{

    OnAbilityActivated?.Invoke();

}

}

these two classes listen to it. one actually fires the ability the other plays the sound of the ability. its just an example but i hope it helps.

using UnityEngine;

public class AbilityObserver : MonoBehaviour

{

public AbilityActivator abilityActivator;

private void OnEnable()

{

    abilityActivator.OnAbilityActivated += DoAbility;

}

private void OnDisable()

{

    abilityActivator.OnAbilityActivated -= DoAbility;

}

public void DoAbility()

{

    Debug.Log("Ability activated!");

}

}
using UnityEngine;

public class SoundManager : MonoBehaviour

{

public AbilityActivator abilityActivator;

[SerializeField]


private AudioClip soundClip;

private AudioSource audioSource;

private void Awake()

{

    audioSource = GetComponent<AudioSource>();

}

private void OnEnable()

{

    abilityActivator.OnAbilityActivated += PlaySound;

}

private void OnDisable()

{

    abilityActivator.OnAbilityActivated -= PlaySound;

}

public void PlaySound()

{

    audioSource.PlayOneShot(soundClip);

}

}

Here is a quick Drawing of the strategy pattern used for my projectile from my prototype

After that you would just put that on a prefab with a Projectile class scripts(u can use this one below for testing but it is absolutely dogshit from a code level perspective). i found it following a different course i would not recommend and hated every second of typing it.

makes sure your prefab has a rigidbody and a collider set to trigger or this wont work

using System.Collections;

using System.Collections.Generic;

using UnityEngine;

    public class Projectile : MonoBehaviour

    {
    //refactor note: what psyco just publics everything??
        public int damage;

        public int heal;

        // public Effect effectToApply;

        public float moveSpeed;

        private Character target;

        public Effect effectToApply;

    void Update()

    {

        // Move towards the target.

        if (target != null)

        {

            //REFACTOR NOTE: Should replace the vector 3  offset here to be a serialized field getter on the target that offsets to where we want to aim projectiles

            transform.position = Vector3.MoveTowards(transform.position, target.transform.position + new Vector3(0, 0.5f, 0), moveSpeed * Time.deltaTime);

        }

    }

    public void Initialize(Character targetChar)

    {
    
        target = targetChar;

    }

    void ImpactTarget()

    {

        if (damage > 0)
     // all this kind of garbage should be observer
            target.TakeDamage(damage);

        if (heal > 0)

            target.Heal(heal);

        // Apply effect if we have one

        if (effectToApply != null)

// Refactor Note observer this
            target.GetComponent<CharacterEffects>().AddNewEffect(effectToApply);

    }

    void OnTriggerEnter(Collider other)

    {

        // If we hit the correct target.

        if (target != null && other.gameObject == target.gameObject)

        {

            ImpactTarget();

            Destroy(gameObject);

        }

    }

}

image

I would really recommend Stephen’s class on GameFeel. he does a lot of refactoring and shows different methods of using the observer pattern. it was very approachable and 100% is what got me over the hump of understanding the observer pattern. I would also recommend Sams Design pattern course.

I didn’t really like the way that some of the design pattern course was structured, you aren’t always sure when and how to follow along with him. but IMO its still probably the best Begninner-ish level breakdown of design patterns i have found. if you take it , just go to one of the end lessons and download the project from the link it will have all the lessons projects in it then you can just follow along with the course from the beginning. and have a reference project if you need it.

1 Like

ahh… OK so basically I know exactly what your code is doing, I just didn’t know what their names were… Jeez I have quite a lot of observer patterns plugged around my code (and at one time a missing unsubscription actually gave me a sleepless night :sweat_smile:)

I’m certain you took the Shops and Abilities course… The Strategy Pattern is what Sam used to implement Abilities.

any chance we shall cross paths with these concepts again? Apart from the abilities, that would be helpful for me to reinforce the ideas in my mind :slight_smile:

Edit: Found the video, I’ll go watch it again, brb (I haven’t used the abilities in a very long time, but I know one of them recently got buggy… it was the one with the targeting and area of effect (the blue summoning circle one), but I just got a bit too occupied with more important concepts to think of fixing it now)

Stephen does a bunch of refactoring to observer pattern in the new GameFeel course. Like i said before its what got me over the hump with observer

1 Like

all it took me was one video to realize that Sam did not even force a strategy pattern… he was just drawing, and it happened to just be a strategy pattern for him :stuck_out_tongue_winking_eye: (and he did mention not to force it)

Anyway, no guarantees I can check the course now (I’m stuck in a plethora of bugs as we speak that need to be fixed, before I can think of anything else), but once the game is back to a bug-free state, I’ll have a look at that course :slight_smile:

And I just realized Abstract classes never get implemented, they just hold a bunch of data that ensures whoever implements/inherits from it has everything that comes in that class (pretty much like an old saying in Arabic, “you marry her, you marry her entire family”), basically a foolproof way of making sure you don’t mess things up…


:stuck_out_tongue:

1 Like

lol believe it or not, I’m watching a whole other tutorial about A* Pathfinding as we speak by Code Monkey… I just noticed if we place an obstacle (like a horse, for example) in front of my enemy, the poor guy doesn’t know what to do…

By all means, yes I have the courses mentioned in mind, but I can’t just drop everything (knowing damn well it’ll hunt me forever) and focus on something else. I have to clean this mess up first, and then I can explore other ideas :slight_smile:

Privacy & Terms