Legacy GM Enemy AI, if/then or Arrays (or something else)?

B

Blatcho

Guest
edit: modified title to reflect forum rules

Hey everyone, long time lurker first time poster...

I've been playing around with GM for a month or so, and have built a basic top down project, to learn the ropes. What I'm looking for a bit of feedback at some of my code regarding NPC/enemy behaviour. It's basically a bunch of nested if/then, but i've read in a few places that arrays are better at handling enemy actions?

This is my obj_enemy 'Enemy Movement' Step event -

Code:
///Enemy movement
//Check to see whether can see player (within 45 degree angle, no obstruction and within range)
if (abs(angle_difference(varDir,image_angle)) <= 45 && varCanSeePlayer && (varDistanceToPlayer < varRange))
{
    enemyX = obj_player.x;
    enemyY = obj_player.y;
    mp_potential_path(varMyPath, enemyX, enemyY,3,1,0);
    path_start(varMyPath,3,path_action_stop,0);
    //image_angle = point_direction(x,y,enemyX,enemyY);
    image_angle = direction;
}
else
//Check object path state
if path_position != 1
{
    //Ensure new x,y coordinates are empty
    if !place_empty(enemyX, enemyY)
    {
        enemyX += 100;
        enemyY += 100;
    }
    //Check if object is static
    if varMoveSpeed = 0
    {
        //Reset current time
        varTimeCurrent = current_time;
       
        //Check if time lapsed is greated than interval
        if (varTimeCurrent - varTimeStart >= varInterval)
        {
            varMoveSpeed = irandom(2);
            varInterval = irandom(5000);
        }
    }
    //Set path
    mp_potential_path(varMyPath, enemyX, enemyY,3,1,0);
    path_start(varMyPath,varMoveSpeed,path_action_stop,0);
    //image_angle = point_direction(x,y,enemyX,enemyY);
    image_angle = direction;
}
else if path_position = 1
{
    //Reset timer, randomize move speed and x,y
    varTimeStart = current_time;
    varMoveSpeed = irandom(2);
    enemyX = random(1024);
    enemyY = random(768);
    //Ensure new x,y coordinates are empty
    if !place_empty(enemyX, enemyY)
    {
        enemyX += 100;
        enemyY += 100;
    }
    //Set path
    mp_potential_path(varMyPath, enemyX, enemyY,3,1,0)
    path_start(varMyPath,varMoveSpeed,path_action_stop,0);
    //image_angle = point_direction(x,y,enemyX,enemyY);
    image_angle = direction;
}
And this is the obj_enemy 'Enemy shoot' Step event -

Code:
///Enemy shoot

varShootCoolDown-=1;

varCanSeePlayer = !collision_line(x,y,obj_player.x,obj_player.y,obj_wall,false,true);
varDistanceToPlayer = distance_to_object(obj_player);
varDir = point_direction(x,y,obj_player.x,obj_player.y);
//var varIsInsideRadius = distance_to_point(obj_player.x, obj_player.y) < varRadius;

if (abs(angle_difference(varDir,image_angle)) <= 45 && varShootCoolDown<=0 && varCanSeePlayer && (varDistanceToPlayer < varRange))
{
    varShootCoolDown = 10;
    varBullet = instance_create(x,y,obj_projectile_enemy);
    varBullet.speed = 15;
    varBullet.direction = point_direction(x,y,obj_player.x,obj_player.y);
}
So my questions are -

1. Is there a better way to implement the behaviour above? I think array's perhaps?

2. What is 'best practice' in terms of where code should live - in the object step events, or placing in the 'Scripts' folder? (or something else).

3. What's the best way to manage different object behaviour animations i.e. standing idle, moving, moving and shooting, running, running & shooting etc should I separate out individual objects i.e. body, gun or create single animations for each state and use an array to determine which to use?

4. I've read that using the drag/drop wizards can be restricting, so I've avoided using them, is this generally accepted as the way to go?

Finally, as per tradition whenever I enter a new forum, apologies for any newbie mistakes in posting :)
 
Last edited by a moderator:
Q

Quackertree

Guest
First of all: Kudos for the actually readable code! :D

1. The code you're using at the moment is just fine. There aren't many different states, so this is generally accepted. However, if you decide to use a FSM (finite state machine) you should probably make use of enums. Arrays are fine, but enums are slightly better for these kind of things, although not required.

Whether you're going to use an array, enum, or nothing at all, eventually you're going to end up with the same if-then, except for that it looks slightly different. In general, you want to skip over bits of code and seperating bits of code out into multiple code slots (just D&D multiple code blocks under each other). You can name all of these code blocks by using /// at the top of your code block, than the description of the code block.

For example:

Code:
///Init state
if(state == states.init) //states is the enum here
{
//Do stuff
}
Next code block:

Code:
///Pathing state
if(state == states.path)
{
//Do other stuff
}
This will keep the code split between several blocks and will generally keep everything clean.

2. Scripts represent functions. Functions are useful if you need to execute the same code multiple times, so that's when it's useful to create a script.

There are people who create scripts for literally everything, but this leads to confusion most of the time, as you're constantly having to jump between scripts in order to find the piece of code you were looking for. This gets even worse when scripts are being referenced again in scripts...

Always try to let a script stick to one single functionality, which is executable most of the time. That should keep things clean-ish enough. :)

3. I wouldn't seperate out as objects - That's generally considered a "rookie mistake" and is also very unpractical. (Not that I didn't do this back in the day; I made two objects - One for the player moving left, one for the player moving right... uuuuugh..)

Either way, enums could also be your savior in this case. :)

4. D&D isn't super "restricting"; All the advanced stuff is just hidden in GML (actual code). D&D is also mostly for people who start out making games; It helps them to learn about the process and makes it easier for them to learn coding once they've gotten to a point where they want to move on to that.

Generally, most good Game Maker games use code, instead of D&D. Although it sometimes happens that there's a mix between the two of them..

Hopefully that answered all of your questions! :)
 
B

Blatcho

Guest
Thanks sooo much for your reply, it's really helped clear up my thinking. I find I approach gamemaker GML in the sameway i approach creating a new RPG game character i.e. start-over 3-4 times before settling on an approach :-/

So in regard to scripts/functions - if i understand this correctly, if i wrote a piece of code to 'destroy_block' or something, that included animations, assigning variable values, playing a sound etc i could place that in a script, and call it whenever needed? Similarly I could put enemy AI behaviour in a script then call it from within the step event of an 'enemy' object, but for this, it's just as efficient to place the code within object blocks and organize them as you outlined? There's no performance considerations presumably?

I'm off to read-up on enumerations and finite state machines - I think i understand the concept. This looks like a good primer: https://forum.yoyogames.com/index.php?threads/finite-state-machines.366/
 
Q

Quackertree

Guest
Thanks sooo much for your reply, it's really helped clear up my thinking. I find I approach gamemaker GML in the sameway i approach creating a new RPG game character i.e. start-over 3-4 times before settling on an approach :-/

So in regard to scripts/functions - if i understand this correctly, if i wrote a piece of code to 'destroy_block' or something, that included animations, assigning variable values, playing a sound etc i could place that in a script, and call it whenever needed? Similarly I could put enemy AI behaviour in a script then call it from within the step event of an 'enemy' object, but for this, it's just as efficient to place the code within object blocks and organize them as you outlined? There's no performance considerations presumably?

I'm off to read-up on enumerations and finite state machines - I think i understand the concept. This looks like a good primer: https://forum.yoyogames.com/index.php?threads/finite-state-machines.366/
Yes, the link you've put down there is indeed a great start. Fel always has some great tutorials! :)

And yes, that's what I meant about scripts! There is a slight performance loss, that much is true, but it also significantly shrinks the game executable, since there's just one bit of code with a bunch of references to them. There is a way to force a script to be inline, however by using gml_pragma("forceinline"), which will copy-paste the code of scripts right into the reference, and therefor there is no longer a performance loss. I always try to make sure that a script always has multiple places where it'll be used, otherwise there's no point in having a script! :)

Good luck! :)
 
Top