Smooth Sprite Direction Change?

Hello there! Hope you are all doing great.

It's been a long time since i asked around forums about this specific issue i had but sadly those posts most likely no longer exist and my project was lost as well so deja vu i guess. Anyways straight to the topic.

Is it possible for anyone to help out with a solution for smooth transition of images within the sprite after you determine the direction of the object? What i mean by that is, let's say i have 36 rendered images of a spaceship in isometric view ( each defining a direction for it ). All the data for each of the image_index is stored properly and works well within each degree by 10 so total of 360 degrees while every 10 = 1 image_index.

What i aim to do is to avoid having the ship turn from it's initial image_index to the direction its facing instantly, but instead gradually switch from one to another with certain speed until it reaches the final one which would be the point of direction it travels to, giving it a more slick look. ( mp_potential_step does solve the issue only partially as it goes through all the degrees and images as planned but it does a U turn, which is not the most pleasing result for eyes when camera follows the player's ship ) instead it should turn gradually on a spot regardless if it moves or not.

In case of need i will provide some example of a game where this system works and what i am trying to mimic HERE: https://streamable.com/zo5mt1

Really hope someone can help with this, thank ya.

Stay safe!
 

quattj

Member
Create a "direction facing" variable in your object that you base your image index off of. Then every step/alarm count/however you choose to time it, check if it it close to the current actual direction. If not, make it closer to the actual direction until they are close enough to give you the final image angle.

So, for example
Code:
if (direction_facing > direction + 10) 
    direction_facing -= 10;
else if (direction_facing < direction - 10)
    direction_facing += 10;
image_index = direction_facing/10;
Place something like that in the step or an alarm, depending on how fast you want it to react. Also take in to account going from 359 -> 0 degrees and vice versa.
 
Thank you so much! This definitely pushes it to the final result i am looking for especially after changing the "Draw_Begin" to "Draw_End" for the images.

The only issue now is exactly what you stated ( the 359 and 0 Degrees transition ) it always had the problem of transitioning all the way through all images and never reach the right facing despite the fact closest would be to simply switch from one image index to another, but it does not making it impossible to move to right without seeing the spaceship trying to reach the image index above 359 but also 0 and shuffles through the whole cycle left and right. I know there is a work around that for sure if you have any idea how to fix this, it will be absolutely perfect.
 

Nocturne

Friendly Tyrant
Forum Staff
Admin
OKay, you need to first calculate the direction to the nearest sprite using the shortest possible rotation, and then add/subtract from the image Index. So, something like this:
GML:
var _dif = angle_difference(direction_facing, direction); // might need top reverse these arguments... I always forget the order!
var _index = direction div 10;
if image_index != _index
{
image_index += clamp(_dif / 360, 0.5, -0.5)
}
So, here we are getting the difference between the current direction and the facing direction. Then we get the corresponding image_index that we want the sprite to have. We then check that against the ACTUAL image_index, and if they aren't the same, we "rotate" the sprite to the correct direction. You can modify the speed of the change by editing the 0.5 values in the "clamp" function.

:)
 
This is awesome, thank you! Both of the codes seem to do its work but one issue still remains and that is the correct direction fix. With the "+" it will go clockwise, but when the ship needs to go counter-clockwise, it ignores the shortest distance and cycles through all the images even if the difference of direction is 10

Gonna share example of what is happening, if it's possible to fix with the same code maybe by adding on top of it, then that would literally solve my headache for good ^_^

Current Result: https://streamable.com/gs7ifr
 

Nocturne

Friendly Tyrant
Forum Staff
Admin
The code I posted shouldn't do that... I wonder if either of the direction variables are over 360? Could you check that? You can add a show_debug_message() call into the event that simply outputs the direction and direction_facing values to the console.
 

Liquid

Member
for smoothing something i often use :mixing the old value with the new value

x = (x*88 + goalx*12) / 100
(88+12=100)
this moves fast for bigger differences of x and goalx and slows down if x gets close to goalx and finally makes x=goalx (well i would cutoff the fraction and make them even if difference is smaller 1)

if you want a fast transition than multiply goalx with bigger value
x = (x*1 + goalx*1) / 2
(1+1=2)
this will make it half way with one change.

ok
 
The code I posted shouldn't do that... I wonder if either of the direction variables are over 360? Could you check that? You can add a show_debug_message() call into the event that simply outputs the direction and direction_facing values to the console.
Checked it and all seems to be fine, never exceeding 360. Definitely confused as well why this happens. It basically only rotates one direction and if the value is changed same happens but reversed direction.
 
I'm gonna post all the data necessary to see if anyone can help me out with solving the issue of the result i shown before, it should hopefully show some errors on my side that might be there as i am completely clueless what causes the " one direction rotation only " bug

Creation Code
GML:
///Basic Definitions & Data

image_speed = 0;

//Variables

shooting = 0; //Not Used Yet

on_move = 0;
direction_facing = 0;

_dif = angle_difference(direction_facing, direction);
_index = direction div 10;
Step Event
Code:
///Movement & Direction Setup

show_debug_message(direction_facing)

if on_move = 1
then
    {
    direction_facing = point_direction(x, y, obj_C_Marker.x, obj_C_Marker.y);
    move_towards_point(obj_C_Marker.x,obj_C_Marker.y,4)
}

//Image Correction

//Fixing The Full 360 Degree Cycle

var _dif = angle_difference(direction_facing, direction); // might need top reverse these arguments... I always forget the order!
var _index = direction div 10;

if image_index != _index
{
image_index += clamp(_dif / 360, 1, -1)
}
Global Left Button
Code:
on_move = 1
Draw Begin
Code:
///Determine Image for Each Direction

if direction_facing = 0 then image_index = 0
if direction_facing = 10 then image_index = 1
if direction_facing = 20 then image_index = 2
if direction_facing = 30 then image_index = 3
if direction_facing = 40 then image_index = 4
if direction_facing = 50 then image_index = 5
if direction_facing = 60 then image_index = 6
if direction_facing = 70 then image_index = 7
if direction_facing = 80 then image_index = 8
if direction_facing = 90 then image_index = 9
if direction_facing = 100 then image_index = 10
if direction_facing = 110 then image_index = 11
if direction_facing = 120 then image_index = 12
if direction_facing = 130 then image_index = 13
if direction_facing = 140 then image_index = 14
if direction_facing = 150 then image_index = 15
if direction_facing = 160 then image_index = 16
if direction_facing = 170 then image_index = 17
if direction_facing = 180 then image_index = 18
if direction_facing = 190 then image_index = 19
if direction_facing = 200 then image_index = 20
if direction_facing = 210 then image_index = 21
if direction_facing = 220 then image_index = 22
if direction_facing = 230 then image_index = 23
if direction_facing = 240 then image_index = 24
if direction_facing = 250 then image_index = 25
if direction_facing = 260 then image_index = 26
if direction_facing = 270 then image_index = 27
if direction_facing = 280 then image_index = 28
if direction_facing = 290 then image_index = 29
if direction_facing = 300 then image_index = 30
if direction_facing = 310 then image_index = 31
if direction_facing = 320 then image_index = 32
if direction_facing = 330 then image_index = 33
if direction_facing = 340 then image_index = 34
if direction_facing = 350 then image_index = 35
also - "obj_C_Marker" is basically the point the ship travels to when global left mouse is clicked, think of it as an RTS where a unit moves to that point, which is very helpful to then control. If anyone has a clue what's going on with the weird rotation bug, let me know. Hope this helps to reveal it easier. ^_^
 

Nocturne

Friendly Tyrant
Forum Staff
Admin
Remove all the code in the Draw Begin. It's not required at all, as the code in the step event is already trying to set the image_index, and is probably being overridden by the Draw code.
 
Remove all the code in the Draw Begin. It's not required at all, as the code in the step event is already trying to set the image_index, and is probably being overridden by the Draw code.
Hmmm, i was hoping that would be the solution, sadly no change. Is there another way around this? I am truly lost with this one, seems like no matter what i add on top of the code or change, it can't remove the bug.
 

Nocturne

Friendly Tyrant
Forum Staff
Admin
Hmmm, i was hoping that would be the solution, sadly no change. Is there another way around this? I am truly lost with this one, seems like no matter what i add on top of the code or change, it can't remove the bug.
Let me test this when I get home from work this evening (so, 8 hours from now...). I'm sure my code should workl, so I'll make a wee test myself and see what happens to make sure that I haven't had you using something that is actually broken...
 
Let me test this when I get home from work this evening (so, 8 hours from now...). I'm sure my code should workl, so I'll make a wee test myself and see what happens to make sure that I haven't had you using something that is actually broken...
Thank you very much! Really respect that and appreciate it greatly.
 

Nocturne

Friendly Tyrant
Forum Staff
Admin
Shouldn't this be image_index += clamp(_dif / 360, -1, 1)?
Ha! Good catch! I missed that and was more interested in what was in the draw event than checking fully what they'd done with the code I supplied. :)
 
Sadly this makes it actually not rotate at all, it makes sense those two values should be controlling the direction but it just stops it completely. I tested out changing the positive and negative before the "clamp" and that switches the direction, but it's impossible to make a second version of the code for the other direction as it has the same effect. I swear GameMaker is trolling me at this point x D

That is literally a headache cause the -1 and 1 are suppose to change the direction, so no idea at all why instead it just stops it completely instead of choosing between them.
 
Last edited:

Nidoking

Member
I recommend using the debugger or debug messages to check the values of image_index at this point and see what's happening. For one thing, it looks to me like this method is going to give you negative image_index values if you try to rotate past zero. There might also be rounding happening with the image_index based on some of the other threads I've read about how the variable works. If you see what the actual, no-kidding value is, then maybe you'll get a better idea of what the problem is.

Maybe it would be best to eliminate the use of image_index entirely and just define a Draw event that draws the corresponding frame based on your current direction. Sort of like what you did with the Draw Begin event, but using ranges rather than equality. Equality is just never going to work the way you want it to.
 
I recommend using the debugger or debug messages to check the values of image_index at this point and see what's happening. For one thing, it looks to me like this method is going to give you negative image_index values if you try to rotate past zero. There might also be rounding happening with the image_index based on some of the other threads I've read about how the variable works. If you see what the actual, no-kidding value is, then maybe you'll get a better idea of what the problem is.

Maybe it would be best to eliminate the use of image_index entirely and just define a Draw event that draws the corresponding frame based on your current direction. Sort of like what you did with the Draw Begin event, but using ranges rather than equality. Equality is just never going to work the way you want it to.
That is true, definitely gonna try all that is possible and inform how it goes, as i do remember solving this years ago, if only i backed up the project, it would save the trouble. In fact one thing i do remember is, it might have been solved through " Switch " and " Case " coding, where it was shuffling through the direction through the draw indeed. Definitely appreciate the help and suggestions!
 
Okay, so let's do it different way. I will be using the draw + case with range as Nidoking suggested as i think that type should work perfectly for this. The only help i would need is to create a speed to cycle through the sprites as in their current state they are instant switch. Will be using 8 directions just so we can work around this easy till it's actually solved so then it is simple to add more sprites for complexity.

So right now it looks like this.

STEP EVENT
GML:
show_debug_message(direction_facing)

if on_move = 1
then
    {
    direction_facing = point_direction(x, y, obj_C_Marker.x, obj_C_Marker.y);
    move_towards_point(obj_C_Marker.x,obj_C_Marker.y,4)
}

if on_move = 1 {
    switch ((((direction + 22.5) mod 360) + 360) mod 360) div 45 {
    case 0: sprite_index = spr_Sp_Player_Main_0_D; break;
    case 1: sprite_index = spr_Sp_Player_Main_315_D; break;
    case 2: sprite_index = spr_Sp_Player_Main_270_D; break;
    case 3: sprite_index = spr_Sp_Player_Main_225_D; break;
    case 4: sprite_index = spr_Sp_Player_Main_180_D; break;
    case 5: sprite_index = spr_Sp_Player_Main_135_D; break;
    case 6: sprite_index = spr_Sp_Player_Main_90_D; break;
    case 7: sprite_index = spr_Sp_Player_Main_45_D; break;
    }
}
Draw End
Code:
if direction > 315 && direction < 45 then sprite_index = spr_Sp_Player_Main_0_D
if direction > 45 && direction < 90 then sprite_index = spr_Sp_Player_Main_315_D
if direction > 90 && direction < 135 then sprite_index = spr_Sp_Player_Main_270_D
if direction > 135 && direction < 180 then sprite_index = spr_Sp_Player_Main_225_D
if direction > 180 && direction < 225 then sprite_index = spr_Sp_Player_Main_180_D
if direction > 225 && direction < 270 then sprite_index = spr_Sp_Player_Main_135_D
if direction > 270 && direction < 315 then sprite_index = spr_Sp_Player_Main_90_D
if direction > 315 && direction < 0 then sprite_index = spr_Sp_Player_Main_45_D
So to shuffle through them one by one till it reaches the direction maybe through alarm or variable/global variables?
 
One math question for you... what number is greater than 315 and less than 45?
Ooops, you are right. When i overthink other issues and miss on the details. ( didn't throw any errors or stopped working so totally missed it )

Also on top of that the Draw doesn't need to be there again, but for some reason i keep thinking it is necessary.
 

quattj

Member
Okay, so let's do it different way. I will be using the draw + case with range as Nidoking suggested as i think that type should work perfectly for this. The
Code:
if direction > 315 && direction < 45 then sprite_index = spr_Sp_Player_Main_0_D
if direction > 45 && direction < 90 then sprite_index = spr_Sp_Player_Main_315_D
if direction > 90 && direction < 135 then sprite_index = spr_Sp_Player_Main_270_D
if direction > 135 && direction < 180 then sprite_index = spr_Sp_Player_Main_225_D
if direction > 180 && direction < 225 then sprite_index = spr_Sp_Player_Main_180_D
if direction > 225 && direction < 270 then sprite_index = spr_Sp_Player_Main_135_D
if direction > 270 && direction < 315 then sprite_index = spr_Sp_Player_Main_90_D
if direction > 315 && direction < 0 then sprite_index = spr_Sp_Player_Main_45_D
A note about his, aside from what was already mentioned about > 315 and < 45 :p

One side of each of these should also include =, you choose which you would prefer.

if direction >= 45 && direction < 90 then sprite_index = spr_Sp_Player_Main_315_D
or
if direction > 45 && direction <= 90 then sprite_index = spr_Sp_Player_Main_315_D

Otherwise you run in to the possibility of unexpected results if it is exactly 45 or 90.

But, that being said, I've never used "angle_difference" so looked it up in the manual, and this is the sample code at the bottom:

var pd = point_direction(x, y, mouse_x, mouse_y);
var dd = angle_difference(image_angle, pd);
image_angle -= min(abs(dd), 10) * sign(dd);


The above code will get the angle of direction from the instance to the mouse cursor, then get the difference between that angle and the current image_angle, using this value to slowly rotate towards the mouse.
Not sure if this would help you out? Instead of setting image_angle, you can use it to change your image index from the angle.
 
A note about his, aside from what was already mentioned about > 315 and < 45 :p

One side of each of these should also include =, you choose which you would prefer.

if direction >= 45 && direction < 90 then sprite_index = spr_Sp_Player_Main_315_D
or
if direction > 45 && direction <= 90 then sprite_index = spr_Sp_Player_Main_315_D

Otherwise you run in to the possibility of unexpected results if it is exactly 45 or 90.

But, that being said, I've never used "angle_difference" so looked it up in the manual, and this is the sample code at the bottom:


Not sure if this would help you out? Instead of setting image_angle, you can use it to change your image index from the angle.
Appreciate the help indeed! Even if it won't help this specific thing was always complicated as i was going through so many approaches even back in the day. Definitely thankful for all the help tho. Gonna see if i figure it out in the end or just move on, but it's one of those moments " I already did manage before so c'mon " urging me x )
 
The code I posted shouldn't do that... I wonder if either of the direction variables are over 360? Could you check that? You can add a show_debug_message() call into the event that simply outputs the direction and direction_facing values to the console.
Been working around other systems in the mean time cause the rotation issue would give me a major headache otherwise, but this is the current code now which turned it into the same issue where 359 to 0 is closest distance but goes through it all instead of just adding 1 degree so it never goes into negative which it should. i checked someone's video where the facing value was always 0 and you could go to negative degree or positive and that way it would never create the bug, but only works for image_angle in that form.

So this is the code right now ( still not solved, still with the bug in place and it drives me nuts )

GML:
Create Event

_pd =  point_direction(x, y, obj_C_Marker.x, obj_C_Marker.y);
_index = direction div 10;
GML:
Step Event

if ta = 0

    {

    var _dif = angle_difference(_pd, direction);
    var _index = direction div 10
    
}

if ta = 0
{
    if image_index > _index
    {
    image_index += clamp(_dif / 180, 0.5, -0.5)
    }
    
    if image_index < _index
    {
    image_index -= clamp(_dif / 180, 0.5, -0.5)
    }
}
The -0.5 really doesn't matter because of the max is + and min is - then it literally doesn't rotate at all, so that is not the issue. I am just thinking whether i need to determine what the rotation should do when it reaches " 0 " degree because that seems to be the ice breaker. Somehow tell it to go to negative and keep rotating and not just go back because as it stands now, 360 is always positive and never negative which creates this bug.

Thank you all for the help btw even if it didn't solve it. It's just so worth to trying to crack this one for good which i am hoping for, just really lost and no matter what i try it fails. Rotation codes were never my favorite as clearly seen.

Stay safe!
 

Nidoking

Member
if image_index > _index
This isn't using the result of angle_difference, so you've just moved the problem to a different spot. Try looking at the sign of _dif instead. Heck, you don't even need an if. Just do image_index += clamp(_dif / 180, -0.5, 0.5).

I have no idea why you're even doing a clamp with the bounds reversed.
 
This isn't using the result of angle_difference, so you've just moved the problem to a different spot. Try looking at the sign of _dif instead. Heck, you don't even need an if. Just do image_index += clamp(_dif / 180, -0.5, 0.5).

I have no idea why you're even doing a clamp with the bounds reversed.
Basically only because one of them alone works only one way and when it reaches 0 it goes all around backwards so i was trying to see if it would fix it but as you said, only makes the issue different, the previous one was 360 so i divided them into two but that only makes a different issue pop up. Appreciate the suggestion!
 
HUGE BUMP! ( Problem Solved )

So after what seemed like an eternity, i finally found a project that was using exactly what i needed and figured how far we all have been from solving the rotation. I am happy to share the code and all it needs for a solution. Basically long story short - it doesn't even need any of the codes in " Step " events, but instead do it all through drawing and running 2 scripts which consist of " Arguments "

Here are the codes necessary, anyone who needs this for 360 rotation using only Image_Index make sure to archive it because i am so happy this nightmare is over. This will be best for anything from RTS projects from isometric perspective or point and click movement with multiple images within a sprite.

Here are the codes necessary to make this magic happen

GML:
scr_set_angle

argument0 = argument0 mod 360;
if argument0 <0 argument0 += 360;

if abs(angle_difference(argument0,argument1))<argument2
    return argument1;
return argument0 + (sign(sin(degtorad(argument1-argument0)))*argument2);
GML:
scr_angle_difference

/*
**  usage:
**      diff = angle_difference(angle1,angle2);
**
**  given:
**      angle1    first direction in degrees, real
**      angle2    second direction in degrees, real
**
**  returns:
**      difference of the given angles in degrees, -180 to 180
**
**  GMLscripts.com
*/
{
    return ((((argument0 - argument1) mod 360) + 540) mod 360) - 180;
}
And finally the actual drawing

GML:
Draw Event

if(attacking == false) {
    directions=direction;
    image_index = round(scr_set_angle(image_index*(360/36),directions,dirspeed)/(360/36));
}else{
    directions=point_direction(obj_Sp_Player.x,obj_Sp_Player.y,round(obj_C_Marker.x),round(obj_C_Marker.y));
    image_index = round(scr_set_angle(image_index*(360/36),directions,dirspeed)/(360/36));
}
draw_sprite_ext(spr_Sp_Player_Main,image_index,round(x),round(y),image_xscale,image_yscale,0,c_white,image_alpha)
can be used for as many images as needed ( dirspeed naturally determines the speed of rotation )

Blows my mind that the step event was not necessary at all for the rotation itself, rather just the movement of spaceship.

Guess we can consider this one solved for good ^w^

Thank you all for the suggestions before and hope this helps others
 
Top