Best method for implementing Enemy Variants

Welcome to our brand new Clickteam Community Hub! We hope you will enjoy using the new features, which we will be further expanding in the coming months.

A few features including Passport are unavailable initially whilst we monitor stability of the new platform, we hope to bring these online very soon. Small issues will crop up following the import from our old system, including some message formatting, translation accuracy and other things.

Thank you for your patience whilst we've worked on this and we look forward to more exciting community developments soon!

Clickteam.
  • Hello.

    I'm working on a platformer that will obviously have enemies. Right now I have 4 types of enemies (standard melee, ranged, status etc.). The way I am designing this game, each level enemies can have a different preset health/attack value than before, ramping up the difficulty. In addition to this, I wanted a way to make "variants" of each enemy. For example, melee enemies have standard, more damage and self-healing variants to spice up the game a little since battling the same enemies can be repetitive and boring.

    Here is where I reached my problem: what is the best way to do this? Do I need to make separate actives for each variant or should I use different animation directions to differentiate the variants? I've played around with both but I don't know if one is better than the other or if there is an even better way to go about this. I'm somewhat digging myself into a hole since I have like 5-7 variations of each enemy right now (I like drawing and get carried away), so this isn't like your Mario patrolling turtles that have maybe three variants. I've worked out causing their spawn chances to be more or less rare (I think) I just want to know what may be a good course of action to take.


    Sincerely,
    A newbie "developer" who gets carried away sometimes

  • Hi Ancalagon18,

    I think best practice would be to code each of your basic AI types on a qualifier that handles the basic stuff and then assign this qualifier to individual actives to handle specific modifications and, of course, the visual appearance.
    Although this adds an extra layer of inconvenience at the beginning I personally recommend using indexed alterable values (37 and higher) for the qualifier code, so you can use the renamable alterable values (A - Z) to code your individual behaviours. On the long run, this will save you a lot of headaches, but keep in mind to always make a reference list for indexed AV, or you'll be doomed when you've to revisit your code later.

    If you use one of the values A-Z for qualifier code (which makes sense for stuff you don`t want to set on runtime) make sure you name it exactly the same in all actives you assign this qualifier to (otherwise its name will be changed to default when working on the quaifier code). When naming values that are used by the qualifier code, you should also give them a specific pre- (or) suffix that identifies them as a shared value. I add a "QV" to all qualifier shared values I use to keep in mind that these values cannot be touched when designing the sub-behaviours.

    Another approach would be to use one active for each basic AI and handle its appearance (animations) and additional behaviours using alterable values and flags. I used this method too, but it's complexity raises dramatically with the number of sub-behaviours you code on top of the basic AI. The qualifier approach is likely a lot more flexible if you want to make changes later or add additional stuff on top.

    I think both approaches are equally ok in case of performance. Also, both methods will cause a lot of problems if you don't write a clean and very well commented code including reference for all indexed values.

    Please login to see this link.
    Please login to see this link. | Please login to see this link. | Please login to see this link. | Please login to see this link.

    Edited once, last by Julian82 (July 29, 2017 at 8:44 AM).

  • Okay thanks for the reply. If you don't mind, I want to run an example past you and ask a few questions.

    If I went with the first method, what are indexed alterable values? I'm still new and have no knowledge about those.

    As for the qualifier code, I could have each enemy qualified under an enemy tag and do stuff like gravity and horizontal control (not the actual behavior, just what to do if the associated values are non-zero such as fall, jump or move) but also give each "class" of enemy and its variations a separate tag to be more specific with the behaviors, right?

    Also, I was thinking ahead for potential optimization and since I'm using fastloops right now I know I don't want to do those loops (like gravity, horizontal control or behaviors) for every enemy all the time. So right now I have an object that follows the player that acts as an activation collision, ticking a flag in the enemy thus activating them for the loops. Does this work alright? I'm trying to figure out the best time for a fastloop that cycles through all instances vs foreach loops. I don't fully understand them or when to use which, even though I have done some reading.

    Thanks!

  • "If I went with the first method, what is indexed alterable values? I'm still new and have no knowledge about those."

    --> You can go beyond values A-Z (index 1-36) by setting and accessing values by an expression (just type in the index of the value you want to set/use). This way you can use an unlimited number of values for each active, but the downfall is you cannot rename them or preset them in the frame editor. Everything has to be done on runtime.

    Please login to see this picture.

    "As for the qualifier code, I could have each enemy qualified under an enemy tag and do stuff like gravity and horizontal control (not the actual behavior, just what to do if the associated values are non-zero such as fall, jump or move) but also give each "class" of enemy and its variations a separate tag to be more specific with the behaviors, right?"

    Exactly! I use one general qualifier to scope (activate/deactivate) enemies and handle basic impacts on the player (getting hurt) as well as enemy health. A next qualifier handles the specific move pattern (platformer, crawler, flying enemies etc.) as well as associated attack patterns. So each enemy active will at least be assigned to two qualifiers.

    "Also, I was thinking ahead for potential optimization and since I'm using fast loops right now I know I don't want to do those loops (like gravity, horizontal control or behaviours) for every enemy all the time. So right now I have an object that follows the player that acts as an activation collision, ticking a flag in the enemy thus activating them for the loops. Does this work alright?
    I'm trying to figure out the best time for a fast loop that cycles through all instances vs for each loop. I don't fully understand them or when to use which, even though I have done some reading."

    Doing object scoping right is one of the most difficult parts in Fusion. Volnaiskra wrote an excellent article about it and the glitches that may occur: Please login to see this link.

    As a rule of thumb, I go with for each loop if I want to scope objects but only need to run one loop within one frame time (for example, if it's sufficient to have something tested and executed 60 FPS)
    I would avoid instance cycling fast loops whenever possible as they are in general much more expensive, but there are situations in which this cannot be avoided (for example, if you need to run a complex loop multiple times within one frame time and you also need to scope the objects within the fastloop). I think there are users who also combine fast and for each loops and nest them into each other to do fancy stuff, but I never managed to get this right.

    If you dig AI programming I also highly recommend a tutorial from Chris Burrows about so called "embedded detectors". There's also a good example on fast loops on the site,
    that nails down the essence of when to use a fast loop Please login to see this link.

    Please login to see this link.
    Please login to see this link. | Please login to see this link. | Please login to see this link. | Please login to see this link.

  • Thanks for the information dump! I've learned a lot since I've started but there is so much more to take in. Since reading more I've gone and updated some of my code and "optimized" it a little to save me pain later, even though optimization is a final step later down the road.

  • No problem^^ I think you're asking the right questions and trying to figure out best practices and optimizing early is IMO an essential approach to get a quality product out there. There are always multiple ways to get things right though. One important thing to consider when working with multiple instances that run on a rather complex AI is not only scoping by distance to player (what you already do), but also scheduling tasks that don't have to run at full FPS. While I would always run player controls at maximum possible framerate (60 FPS for most games), enemy detectors can be easily handled at 30 FPS and enemy AI-iterations may even work fine at 5 FPS. You can easily avoid load by putting stuff into groups and activating/ deactivating them based on a simple timer. There are also other approaches like executing certain parts of code only after some other heavy load is finished to make sure certain heavy processes don't run at the same time. There are nearly endless possibilities and you'll work out what's best for you and your game.

    My basic approach is always to make things as simple as possible and I try to visualize what the player will actual experience rather than seeking imperfections in my code. You can fake a lot of things with very little effort and the player will never even notice. I recently watched a speech from the Rain World creators about how they handled their AIs and it was really an eye opener. As you seem to be into the art part of things too, I recommend watching:

    Please login to see this media element.

    Please login to see this link.
    Please login to see this link. | Please login to see this link. | Please login to see this link. | Please login to see this link.

  • I'll certainly take a look at that.

    When you talk about running things at 60, 30 or even 5 FPS how do you go about that? You mention a timer, so would you set a timer or counter to 0 at the start of the code, then each program loop add to it, only executing (activating a group) at certain values?

    Right now my largest load fastloops are player controls/physics and enemy physics. Apart from that, I use ForEach loops to control the AI, which can probably be simplified to run at 5 FPS and even smaller ForEach loops to deal with bullets, pickups and other odds and ends when necessary.

    Also, since I have enemy variants spawning based on probability (50/100 chance to spawn variant 1, 25/100 variant 2 etc) I have some questions about that if you don't mind. Basically, there is an object that acts as a spawn point and then for each of those a random number is generated internally and based on that the enemy variant is spawned. Here is where it gets icky and I have questions: I want to have variable odds for the different variants I preset each level. So I have a value-storing object I use to preset those. When spawning an enemy, it uses random(sum of all the variants probability). So each variant has a different weight value and then I add those up. So if variant 1 is 50, variant 2 is 25 and variant 3 is 25 I get a total of 100. Make sense? I hope I'm explaining myself well. But I can't just test for that value between say 0 and 50 to make variant 1 and between 50 and 75 for variant 2 each time, because those weights change each level. So right now I have very long conditions reading like:

    If value is equal/greater than 0
    also
    value is lower than "weight 1"
    then
    spawn Enemy variant 1

    If value is equal/greater than "weight 1"
    also
    value is lower than "weight 1"+"weight 2"
    then
    spawn Enemy variant 2

    and so on.

    As you can probably see, if I have 5 variants for each enemy right now, this code gets long and kinda messy and annoying to expand. Any thoughts?

Participate now!

Don’t have an account yet? Register yourself now and be a part of our community!