NPC sprite direction (need help)

D

DevilKap

Guest
I have this test NPC, but it wont change its sprite direction.
I think it has to do with the fact that I'm using an image_index based on direction:
GML:
 direction = point_direction(x,y,other.x,other.y);
            image_index = CARDINAL_DIR
How would I format/structure this into a switch statement like the one I have for my player?:
GML:
//Set Sprite
switch(dir){
    case 0: sprite_index = walkRight; break;
    case 45: sprite_index = walkUpRight; break;
    case 90: sprite_index = walkUp; break;
    case 135: sprite_index = walkUpLeft; break;
    case 180: sprite_index = walkLeft; break;
    case 225: sprite_index = walkDownLeft; break;
    case 270: sprite_index = walkDown; break;
    case 315: sprite_index = walkDownRight; break;
    }
} else {
    image_index = 0
}
Sorry if this is a silly question, I suck at this but I'm slowly improving (I think).
I just can't wrap my head around how I'm supposed to do this.
 

Nidoking

Member
direction = point_direction(x,y,other.x,other.y); image_index = CARDINAL_DIR

How would I format/structure this into a switch statement like the one I have for my player?:
This was your question. If the answer I gave wasn't the one you wanted, then what is the question you intended to ask?
 

TheouAegis

Member
First off, does
Code:
switch { } else { }
even compile? At work right now, but I thought the IDE pops an error if you have else paired with anything other than if.

Secondly, rytan's response was more or less correct. As Nidoking said, you asked a particular question and you got an answer.
var dir = (round(direction / 45) * 45);
(Don't think you needed the modulo there in this case.)
Your enemy code set direction in the enemy; rytan's code takes that direction and converts it to dir, which is what you are already using in the player's code. So now you can just copy that player's code into the enemy and precede it with rytan's code, then change the sprites to the appropriate enemy sprites.
Code:
var dir = (round(direction / 45) * 45);
//Set Sprite
switch(dir){
    case 0: sprite_index = walkRight; break;
    case 45: sprite_index = walkUpRight; break;
    case 90: sprite_index = walkUp; break;
    case 135: sprite_index = walkUpLeft; break;
    case 180: sprite_index = walkLeft; break;
    case 225: sprite_index = walkDownLeft; break;
    case 270: sprite_index = walkDown; break;
    case 315: sprite_index = walkDownRight; break;
    }
 

rytan451

Member
First off, does
Code:
switch { } else { }
even compile? At work right now, but I thought the IDE pops an error if you have else paired with anything other than if.

Secondly, rytan's response was more or less correct. As Nidoking said, you asked a particular question and you got an answer.
var dir = (round(direction / 45) * 45);
(Don't think you needed the modulo there in this case.)
Your enemy code set direction in the enemy; rytan's code takes that direction and converts it to dir, which is what you are already using in the player's code. So now you can just copy that player's code into the enemy and precede it with rytan's code, then change the sprites to the appropriate enemy sprites.
Code:
var dir = (round(direction / 45) * 45);
//Set Sprite
switch(dir){
    case 0: sprite_index = walkRight; break;
    case 45: sprite_index = walkUpRight; break;
    case 90: sprite_index = walkUp; break;
    case 135: sprite_index = walkUpLeft; break;
    case 180: sprite_index = walkLeft; break;
    case 225: sprite_index = walkDownLeft; break;
    case 270: sprite_index = walkDown; break;
    case 315: sprite_index = walkDownRight; break;
    }
You do need the modulo. If the direction is 359, then it rounds to 360, which then needs to be modulo down to 0 (or an if statement will work too).
 

Yal

🐧 *penguin noises*
GMC Elder
Setting image_index directly is a bit buggy in 2.3.0, if you're using it you should use your custom drawimage variable and draw_sprite' your sprite with that image.

GML:
var dir = (round(direction / 45) * 45) % 360;
Adding half a period and flooring is better, you want the point where subimages change to be BETWEEN the cardinal directions, not directly at them. Also GM's modulo is weird for negative numbers so it's a good practice to add a whole period (won't do anything for built-in direction since it's always in the range 0-359.99... but there's a lot of times where you have a interval between -MAXVALUE and +MAXVALUE where this can trip you up)
GML:
var dir = (360 + floor((direction + 22.5) / 45) * 45) % 360;
 
D

DevilKap

Guest
First off, does
Code:
switch { } else { }
even compile? At work right now, but I thought the IDE pops an error if you have else paired with anything other than if.

Secondly, rytan's response was more or less correct. As Nidoking said, you asked a particular question and you got an answer.
var dir = (round(direction / 45) * 45);
(Don't think you needed the modulo there in this case.)
Your enemy code set direction in the enemy; rytan's code takes that direction and converts it to dir, which is what you are already using in the player's code. So now you can just copy that player's code into the enemy and precede it with rytan's code, then change the sprites to the appropriate enemy sprites.
Code:
var dir = (round(direction / 45) * 45);
//Set Sprite
switch(dir){
    case 0: sprite_index = walkRight; break;
    case 45: sprite_index = walkUpRight; break;
    case 90: sprite_index = walkUp; break;
    case 135: sprite_index = walkUpLeft; break;
    case 180: sprite_index = walkLeft; break;
    case 225: sprite_index = walkDownLeft; break;
    case 270: sprite_index = walkDown; break;
    case 315: sprite_index = walkDownRight; break;
    }
This works but now I just realized that this is stored in my PlayerActivateNPC script:
GML:
// Script assets have changed for v2.3.0 see
// https://help.yoyogames.com/hc/en-us/articles/360005277377 for more information
function PlayerActivateNPC(){
    //1. Check for an entity to activate
    //2. If there's nothing, or something without a script, do nothing
    //3. Otherwise, activate diologue
    //4. If the entity is an NPC, make it face towards the player
  
    var _activateX = lengthdir_x(10, direction);
    var _activateY = lengthdir_y(5, direction);

    activate = instance_position(x+_activateX, y+_activateY, pEntity);

    if (activate == noone || activate.entityActivateScript == -1)
    {
        state = PlayerStateFree;
    }
    else
    {
         ScriptExecuteArray(activate.entityActivateScript, activate.entityActivateArgs);
    }
  
    //NPC dir
    if (activate == noone || activate.entityNPC)
    {
        with (activate)
        {
            var dir = (round(direction / 45) * 45);
//Set Sprite
switch(dir){
    case 0: sprite_index = walkRightYellon; break;
    case 45: sprite_index = walkUpRightYellon; break;
    case 90: sprite_index = walkUpYellon; break;
    case 135: sprite_index = walkUpLeftYellon; break;
    case 180: sprite_index = walkLeft; break;
    case 225: sprite_index = walkDownLeft; break;
    case 270: sprite_index = walkDown; break;
    case 315: sprite_index = walkDownRight; break;
    }
        }
    }
}
GML:
    //NPC dir
    if (activate == noone || activate.entityNPC)
    {
        with (activate)
        {
            var dir = (round(direction / 45) * 45);
//Set Sprite
switch(dir){
    case 0: sprite_index = walkRightYellon; break;
    case 45: sprite_index = walkUpRightYellon; break;
    case 90: sprite_index = walkUpYellon; break;
    case 135: sprite_index = walkUpLeftYellon; break;
    case 180: sprite_index = walkLeftYellon; break;
    case 225: sprite_index = walkDownLeftYellon; break;
    case 270: sprite_index = walkDownYellon; break;
    case 315: sprite_index = walkDownRightYellon; break;
    }
The probem here is that I need this to be able to "animate" any NPC, this just makes any and all NPC's to turn into Yellon.
 
D

DevilKap

Guest
To elaborate, I need a script that uses a structure similar to the following:
GML:
switch(dir){
    case 0: sprite_index = walkRightYellon; break;
    case 45: sprite_index = walkUpRightYellon; break;
    case 90: sprite_index = walkUpYellon; break;
    case 135: sprite_index = walkUpLeftYellon; break;
    case 180: sprite_index = walkLeftYellon; break;
    case 225: sprite_index = walkDownLeftYellon; break;
    case 270: sprite_index = walkDownYellon; break;
    case 315: sprite_index = walkDownRightYellon; break;
    }
However, I need it to be capable of working for any NPC, not just a single one
 

chamaeleon

Member
To elaborate, I need a script that uses a structure similar to the following:
GML:
switch(dir){
    case 0: sprite_index = walkRightYellon; break;
    case 45: sprite_index = walkUpRightYellon; break;
    case 90: sprite_index = walkUpYellon; break;
    case 135: sprite_index = walkUpLeftYellon; break;
    case 180: sprite_index = walkLeftYellon; break;
    case 225: sprite_index = walkDownLeftYellon; break;
    case 270: sprite_index = walkDownYellon; break;
    case 315: sprite_index = walkDownRightYellon; break;
    }
However, I need it to be capable of working for any NPC, not just a single one
What makes you think this code could not apply to any NPC, given that a script executing will do so in the context of an instance, and you are in complete control over which set of instances to call it for, be it your player character or one or more NPC characters. The assumption is of course that the current instance does have a dir variable defined.
 
D

DevilKap

Guest
What makes you think this code could not apply to any NPC, given that a script executing will do so in the context of an instance, and you are in complete control over which set of instances to call it for, be it your player character or one or more NPC characters. The assumption is of course that the current instance does have a dir variable defined.
The problem is that the switch statement would make the entities use these sprites instead of having the versatility to be used for all NPC sprites.
I'm sorry if I'm not understanding what you're trying to say here, I think you're implicating that I could assign some kind of instance to this already messed up instance?
I don't think layering a bunch of instances is going to help at all.
 

chamaeleon

Member
The problem is that the switch statement would make the entities use these sprites instead of having the versatility to be used for all NPC sprites.
I'm sorry if I'm not understanding what you're trying to say here, I think you're implicating that I could assign some kind of instance to this already messed up instance?
I don't think layering a bunch of instances is going to help at all.
All that code does is set the sprite of the current instance (and by current, it obviously includes does who become current within a with() statement) to some particular sprite given a direction. Is that what you want to do, or is it not what you want to do, given a particular current instance?

If your reply starts with "Yes it is what I want to do, but ...", you need to write code that correspond to the "but" part.
 
D

DevilKap

Guest
All that code does is set the sprite of the current instance (and by current, it obviously includes does who become current within a with() statement) to some particular sprite given a direction. Is that what you want to do, or is it not what you want to do, given a particular current instance?

If your reply starts with "Yes it is what I want to do, but ...", you need to write code that correspond to the "but" part.
I want a simple method of making NPC's face the player that doesn't lag the game or require a billion scripts.
I'm using some old Shaun Spalding tutorial as a reference point for this, the way he does it is that he makes the direction directly correlate with the image_index.
The only problem with this old script is that it doesn't work anymore, because the mechanisms used in the tutorial don't actually work anymore.
I am new to programming, I'm trying to make an 8-direction RPG game.
Here's a diagram that hopefully shows what I'm trying to do:
1604356485747.png
 

chamaeleon

Member
GML:
global.some_suitable_name = [walkRightYellon, walkUpRightYellon, ..., walkDownRight];
global.some_other_suitable_name = [walkRightFoo, ..., walkDownRightFoo];
GML:
function setSprite(dir,theArray) {
    sprite_index = theArray[dir/45];
}
GML:
var dir = (round(direction / 45) * 45);
if (<the instance is a Yellon>) {
    setSprite(dir, global.some_suitable_name);
} else if (<the instance is a Foo>) {
    setSprite(dir, global.some_other_suitable_name);
}
Edit: Removed enum as I was just making stuff up as I was writing and didn't know if I'd use something like it in other code segments.
 
Last edited:
D

DevilKap

Guest
GML:
global.some_suitable_name = [walkRightYellon, walkUpRightYellon, ..., walkDownRight];
global.some_other_suitable_name = [walkRightFoo, ..., walkDownRightFoo];
enum MySpriteEnum {
    walkRight = 0,
    walkUp = 45,
    ...,
    walkDownRight = 315
}
GML:
function setSprite(dir,theArray) {
    sprite_index = theArray[dir/45];
}
GML:
var dir = (round(direction / 45) * 45);
if (<the instance is a Yellon>) {
    setSprite(dir, global.some_suitable_name);
} else if (<the instance is a Foo>) {
    setSprite(dir, global.some_other_suitable_name);
}
Can I get a rundown of this for future reference? I also need it so I know where I would use this in my program.
 

chamaeleon

Member
Can I get a rundown of this for future reference? I also need it so I know where I would use this in my program.
Rundown? Create an array of sprites, pass appropriate sprite array for the current instance in function call, use direction to look up sprite in the sprite array inside function (for such a short function one could of course just write the same code inside the if statements, if one wish to do so).
 
D

DevilKap

Guest
Rundown? Create an array of sprites, pass appropriate sprite array for the current instance in function call, use direction to look up sprite in the sprite array inside function (for such a short function one could of course just write the same code inside the if statements, if one wish to do so).
By "Array of sprites" do you mean a set of sprites defined within the globals (seen here):
GML:
global.some_suitable_name = [walkRightYellon, walkUpRightYellon, ..., walkDownRight];
global.some_other_suitable_name = [walkRightFoo, ..., walkDownRightFoo];
Or do you mean a set of sprites contained within it's own script array?
Also, assuming these global variables are the "sprite arrays": would I put this in the game operator object, or the script for NPC interaction?

Edit: Some additional questions:
Looking at this:
GML:
var dir = (round(direction / 45) * 45);
if (<the instance is a Yellon>) {
    setSprite(dir, global.some_suitable_name);
} else if (<the instance is a Foo>) {
    setSprite(dir, global.some_other_suitable_name);
}
It looks like this would only be useful if there were only 2, possibly 3 NPC spritesheets. How would I include more NPC spritesheets?
(Assuming that "Foo" means any NPC that isn't Yellon)
 
Last edited by a moderator:

TheouAegis

Member
For sprite issues like this, I like to define all the sprites that something will use inside an array in the create event of that object. And then when you need to change the sprite, just reference that array instead of the sprite indexes directly.
 

chamaeleon

Member
I want to second @TheouAegis suggestion to keep the arrays in instance variables, ideally defined in the create event. I just used the global shortcut to keep down repetition and and show two different arrays, without having to explain too much about different objects. In the end, there's no one correct solution for most problems. The "best" one might be the one one understands most easily, that allows the project to move forward. The next project that comes along might have a better implementation based on experience.
 
D

DevilKap

Guest
You can use else if as many times in a row as you need.
Doesn't that lag the game? I don't wanna pull a YandereDev here.
For sprite issues like this, I like to define all the sprites that something will use inside an array in the create event of that object. And then when you need to change the sprite, just reference that array instead of the sprite indexes directly.
So you're suggesting that I define the arrays in the object's "create" tab and then use it whenever I need it in the program? If this is the case, how would I make it so that the NPC stops and looks at the player when interacted with? Chameleon's script is useful for animating the NPC when it's walking, but what about interaction?
 
Last edited by a moderator:

Nidoking

Member
Doesn't that lag the game? I don't wanna pull a YandereDev here.
I don't know what that is, but that IS the game. That's like saying you don't want graphics in your game because drawing them takes GPU cycles. You can't make a game if you're not willing to put logic in it.
 
Doesn't that lag the game? I don't wanna pull a YandereDev here.
Don't prematurely optimise, especially when you are new to coding. You don't yet have the base of knowledge to know whether something is optimal or not, so you can't make an informed decision about optimisation. Get things to work. Once they work, if they lag the game, then try to consider ways to optimise them/ask us for help. But getting things to work is way more important than getting things to work optimally (at least initially, that is).

Of course, doing this there's always the chance that you'll yandere your way through a game. But that's not important when you are learning. As you build more and more stuff you'll learn more and more optimal ways of doing things. (Also, there's plenty more indie devs outside of yandere that have used sub-optimal code, and while "code experts" might 💩💩💩💩 on their code, at least they produce games, which is way more important in game dev terms than writing optimal lines of code but never finishing a game).
 

TheouAegis

Member
Doesn't that lag the game? I don't wanna pull a YandereDev here.

So you're suggesting that I define the arrays in the object's "create" tab and then use it whenever I need it in the program? If this is the case, how would I make it so that the NPC stops and looks at the player when interacted with? Chameleon's script is useful for animating the NPC when it's walking, but what about interaction?
As chameleon elaborated, you'd actually want to use a global array so you avoid repetition or memory bloating. Your array should have the sprites for every object. You could save each array in a ds_map indexed by object_index. Then when you need to change the sprite, fetch the array from the map and then get the sprite based on dir. However, now instead of multiplying dir by 45, just just stop at the division.

Code:
global.dir_sprites = ds_map_create();
global.dir_sprites[?Yellon] = [walkRightYellon,walkUpRightYellon, walkUpYellon, walkUpLeftYellon, walkLeftYellon, walkDownLeftYellon,walkDownYellon,walkDownRightYellon];
//repeat for all the other objects using 8way movement
Code:
var dir = (direction + 22.5) div 45 mod 8;
var arr = global.dir_sprites[?object_index];
sprite_index = arr[dir];
 
Top