(SOLVED) Rotating drawn sprites from a spritesheet

Hi everyone.
I was wondering about sprite rotation.
It's easy if you have a single sprite, with it's center centered, and then use
draw_sprite_general or draw_sprite_part_ext or something, and change the 'rot" value. This will always pivot a single sprite around the center point the way you want it to be.

However, I have a lot of objects (trees of different kinds, with multiple growth stages) in my game.
They are all nicely drawn on a spritesheet. I once imported each single image from that spritesheet separately, and did the above when my player cuts a tree, or if I want a storm to "swivel" the tree. Cut it => it fell down by rotating along the base center point, no sweat. It worked fine, but I had to adjust the graphical style and had to redraw them. Plus, importing such an amount is just crazy.

So, now I'm using a single spritesheet. I'd like to keep it that way because asset management is a big deal, the less imported files I have the better. Don't like something? Just change something on that one single .png spritesheet and done!. The game handles drawing using draw_sprite_part_ext in which I can choose which area on the spritesheet to draw. A bit more coding, but A LOT LESS tedious work (import => set center, test center, next sprite, import, set center, test center, …….) . In fact, it's a perfect learning tool for me because now I need to think about coding my own coordinate system when it comes to animating objects. Pretty much everything works great when things don't need to rotate. My sprites drawn and the object's mask are 2 different sprites. I don't need to rotate the object, just the coordinates in the objects DRAW event.

So now I'm at that point again: I need to rotate my trees when needed. This needs to happen at their base, not at the center of a sprite(part) drawn. I'm sure this problem is a common one...


So what I need to do is: determine the point to rotate around, the ROTATE POINT. If a sprite is 16x16 pixels, drawn at the object's x = 0 and y = 0, and i want to rotate around the center, then the rotating point is x+8 and x+8. If i want to rotate it around the center of the base, then the rotating point is x+8 and y+16. You get the idea.

So basically, my sprite ORIGIN needs to MOVE on a circle + the sprite needs to ROTATE around the ROTATE POINT.

The rotation and the movement need to be aligned for this, I'm aware of that. It's probably really tricky to do, but if I can do it by code, and not have to have a massive asset library, in which i determine each's sprite center point,... I would love that. It would be the final "hard thing" to learn for me when it comes to animating and drawing sprites.
 
Last edited:
It's unfortunate that draw_sprite_part_pos doesn't have an angle argument.

I can think of two fairly straightforward things you can do.

One is to change the world matrix to allow rotation with draw_sprite_part_pos. The downside of this is that every single time you draw a sprite in this way, it is going to break the current vertex batch. If you do that too much (amount depends on device), then performance will fall off a cliff.

NOTE: If using gms2 substitute d3d functions with gms2 equivalent
Code:
var _c = dcos(image_angle);
var _s = dsin(image_angle);
var _offset_x = 16;
var _offset_y = 32;
var _x = x *  _c + y * -_s - _offset_x;
var _y = x *  _s + y *  _c - _offset_y;
d3d_transform_set_rotation_z(image_angle);
draw_sprite_part_ext(sprite21, 0, 0,0,32,32, _x, _y, 1, 1, c_white, 1);
d3d_transform_set_identity();

An alternative is to draw with primitives or onto a vertex buffer. While this wont cause a lot of vertex batch breaks, it involves a lot more code. Though the really good news is that if your images are static, you can write it to a vertex buffer just once, and then you'll be able to draw that vertex buffer super fast, without having to rewrite all the stuff to it over and over again.

example of drawing using primitives:
Code:
var _tex = sprite_get_texture(sprite21,0);
draw_primitive_begin_texture(pr_trianglelist, _tex);
//cosine and sine of image_angle
var _c = dcos(image_angle);
var _s = dsin(image_angle);
//positions of sides of sprite relative to origin
var _top = -32;
var _bottom = 0;
var _left = -16;
var _right = 16;
//vertex positions
var _x0 = x + _top *  _c + _left * _s;
var _y0 = y + _top * -_s + _left * _c;
var _x1 = x + _top *  _c + _right * _s;
var _y1 = y + _top * -_s + _right * _c;
var _x2 = x + _bottom *  _c + _right * _s;
var _y2 = y + _bottom * -_s + _right * _c;
var _x3 = x + _bottom *  _c + _left * _s;
var _y3 = y + _bottom * -_s + _left * _c;
//texture coords
var _tl = 0;   //left
var _tr = 0.5; //right
var _tt = 0;   //top
var _tb = 0.5; //bottom
//upper-right triangle
draw_vertex_texture(_x0,_y0,_tl,_tt);
draw_vertex_texture(_x1,_y1,_tr,_tt);
draw_vertex_texture(_x2,_y2,_tr,_tb);
//lower-left triangle
draw_vertex_texture(_x0,_y0,_tl,_tt);
draw_vertex_texture(_x2,_y2,_tr,_tb);
draw_vertex_texture(_x3,_y3,_tl,_tb);
draw_primitive_end();

The process for writing to a vertex buffer is almost exactly the same.

---------------------

A note on spritesheets. If you aren't drawing your sprites 1:1, then you might run into situations where colours from neiboring images on your sprite sheet bleed into the one you're trying to draw. The solution to that is to create a border around each image where the edge colours have been duplicated.
 
Oh, I forgot. I believe there is a way to import spritesheets such that gamemaker will automatically put each image into a seperate sprite sub. Have you looked into that yet?

EDIT: Even though it wouldn't completely solve the problem, it would simplify it.
 
Last edited:
Oh, I forgot. I believe there is a way to import spritesheets such that gamemaker will automatically put each image into a seperate sprite sub. Have you looked into that yet?

EDIT: Even though it wouldn't completely solve the problem, it would simplify it.
Yes I know about that, at least I think. It still requires manually setting the pivot point, and it also saves the images as separate assets, which i don't like. I'd like to pull as much images out of as little assets as possible through code :)
 
I'm sorry, I actually made an embarrassing mistake with the code relating to defining vertex positions with drawing primitives.

I mixed up the x and y axes. The correct code is this:
Code:
var _x0 = x + _left *  _c + _top * _s;
var _y0 = y + _left * -_s + _top * _c;
var _x1 = x + _right *  _c + _top * _s;
var _y1 = y + _right * -_s + _top * _c;
var _x2 = x + _right *  _c + _bottom * _s;
var _y2 = y + _right * -_s + _bottom * _c;
var _x3 = x + _left *  _c + _bottom * _s;
var _y3 = y + _left * -_s + _bottom * _c;
My apologies for wasting some of your time.
 
I'm sorry, I actually made an embarrassing mistake with the code relating to defining vertex positions with drawing primitives.

I mixed up the x and y axes. The correct code is this:
Code:
var _x0 = x + _left *  _c + _top * _s;
var _y0 = y + _left * -_s + _top * _c;
var _x1 = x + _right *  _c + _top * _s;
var _y1 = y + _right * -_s + _top * _c;
var _x2 = x + _right *  _c + _bottom * _s;
var _y2 = y + _right * -_s + _bottom * _c;
var _x3 = x + _left *  _c + _bottom * _s;
var _y3 = y + _left * -_s + _bottom * _c;
My apologies for wasting some of your time.
no worries, understanding this will take me a few days for sure i think :D
 
I can help explain any parts that you might be having difficulty with.

By the way. I want to revisit the idea of using gamemaker's sprite editor to extract frames from a spritestrip. Doesn't it put all the frames into a single sprite, as seperate sub images? It's true that this will cause the game to take longer to compile. But it will simplify (by quite a lot) the process of rotating your sprites.
 
I can help explain any parts that you might be having difficulty with.

By the way. I want to revisit the idea of using gamemaker's sprite editor to extract frames from a spritestrip. Doesn't it put all the frames into a single sprite, as seperate sub images? It's true that this will cause the game to take longer to compile. But it will simplify (by quite a lot) the process of rotating your sprites.
That's true, and it's what I did last time actually. But i thought "maybe it can be done by cutting out the middle man, and code my animations and sprites directly from spritesheets and their coordinates. For my player object, this works absolutely perfect (The player doesn't need to rotate). However with things that need rotating sprites… Problems xD

I really wish we had a function like draw_sprite_general_something, in which the rotation OFFSET x and y also need an input. Any sprite rotation doesn't happen around the object x and y anymore, but around that offset point xD


Wishful thinking I know :p
 
Oh! Okay. I hadn't understood your problem completely.

I can update the code I posted before to accomplish what I think you're aiming for.

EDIT1: The only thing I'm not sure about is the significance of the angle measures you drew.

EDIT2: I'm also assuming your diagram shows the origin in world space, and not in sprite/object space.

EDIT3: Both of the following examples are drawing a part that is 32x32 pixels and size, with the pivot point (aka offset) at the center of the part that is being drawn. And the location (in world space) of the pivot point is the instance position plus the location of the pivot position (in "sprite" space).

method 1:
Code:
var _c = dcos(image_angle);
var _s = dsin(image_angle);
var _offset_x = 16;
var _offset_y = 16;
var _x = x + _offset_x;
var _y = y + _offset_y;
var _x1 = _x *  _c + _y * -_s - _offset_x;
var _y1 = _x *  _s + _y *  _c - _offset_y;
d3d_transform_set_rotation_z(image_angle);
draw_sprite_part_ext(sprite21, 0, 0,0,32,32, _x1, _y1, 1, 1, c_white, 1);
d3d_transform_set_identity();

method 2:
Code:
var _tex = sprite_get_texture(sprite21,0);
draw_primitive_begin_texture(pr_trianglelist, _tex);
//cosine and sine of image_angle
var _c = dcos(image_angle);
var _s = dsin(image_angle);
//positions of sides of sprite relative to origin
var _offset_x = 16;
var _offset_y = 16;
var _top = -_offset_y;
var _bottom = 32-_offset_y;
var _left = -_offset_x;
var _right = 32-_offset_x;
//vertex positions
var _x = x + _offset_x;
var _y = y + _offset_y;
var _x0 = _x + _left *  _c + _top * _s;
var _y0 = _y + _left * -_s + _top * _c;
var _x1 = _x + _right *  _c + _top * _s;
var _y1 = _y + _right * -_s + _top * _c;
var _x2 = _x + _right *  _c + _bottom * _s;
var _y2 = _y + _right * -_s + _bottom * _c;
var _x3 = _x + _left *  _c + _bottom * _s;
var _y3 = _y + _left * -_s + _bottom * _c;
//texture coords
var _tl = 0;   //left
var _tr = 0.5; //right
var _tt = 0;   //top
var _tb = 0.5; //bottom
//upper-right triangle
draw_vertex_texture(_x0,_y0,_tl,_tt);
draw_vertex_texture(_x1,_y1,_tr,_tt);
draw_vertex_texture(_x2,_y2,_tr,_tb);
//lower-left triangle
draw_vertex_texture(_x0,_y0,_tl,_tt);
draw_vertex_texture(_x2,_y2,_tr,_tb);
draw_vertex_texture(_x3,_y3,_tl,_tb);
draw_primitive_end();

By the way, I have to modify the statement I made before about primitives not causing a vertex batch. That isn't true if you draw using different textures in between. And in that case you'll also get texture swaps as well.
 
Last edited:
Oh! Okay. I hadn't understood your problem completely.

I can update the code I posted before to accomplish what I think you're aiming for.

EDIT1: The only thing I'm not sure about is the significance of the angle measures you drew.

EDIT2: I'm also assuming your diagram shows the origin in world space, and not in sprite/object space.

EDIT3: Both of the following examples are drawing a part that is 32x32 pixels and size, with the pivot point at the center of the part that is being drawn. And the location (in world space) of the pivot point is the instance position plus the location of the pivot position (aka offset).

method 1:
Code:
var _c = dcos(image_angle);
var _s = dsin(image_angle);
var _offset_x = 16;
var _offset_y = 16;
var _x = x + _offset_x;
var _y = y + _offset_y;
var _x1 = _x *  _c + _y * -_s - _offset_x;
var _y1 = _x *  _s + _y *  _c - _offset_y;
d3d_transform_set_rotation_z(image_angle);
draw_sprite_part_ext(sprite21, 0, 0,0,32,32, _x1, _y1, 1, 1, c_white, 1);
d3d_transform_set_identity();

method 2:
Code:
var _tex = sprite_get_texture(sprite21,0);
draw_primitive_begin_texture(pr_trianglelist, _tex);
//cosine and sine of image_angle
var _c = dcos(image_angle);
var _s = dsin(image_angle);
//positions of sides of sprite relative to origin
var _offset_x = 16;
var _offset_y = 16;
var _top = -_offset_y;
var _bottom = 32-_offset_y;
var _left = -_offset_x;
var _right = 32-_offset_x;
//vertex positions
var _x = x + _offset_x;
var _y = y + _offset_y;
var _x0 = _x + _left *  _c + _top * _s;
var _y0 = _y + _left * -_s + _top * _c;
var _x1 = _x + _right *  _c + _top * _s;
var _y1 = _y + _right * -_s + _top * _c;
var _x2 = _x + _right *  _c + _bottom * _s;
var _y2 = _y + _right * -_s + _bottom * _c;
var _x3 = _x + _left *  _c + _bottom * _s;
var _y3 = _y + _left * -_s + _bottom * _c;
//texture coords
var _tl = 0;   //left
var _tr = 0.5; //right
var _tt = 0;   //top
var _tb = 0.5; //bottom
//upper-right triangle
draw_vertex_texture(_x0,_y0,_tl,_tt);
draw_vertex_texture(_x1,_y1,_tr,_tt);
draw_vertex_texture(_x2,_y2,_tr,_tb);
//lower-left triangle
draw_vertex_texture(_x0,_y0,_tl,_tt);
draw_vertex_texture(_x2,_y2,_tr,_tb);
draw_vertex_texture(_x3,_y3,_tl,_tb);
draw_primitive_end();

By the way, I have to modify the statement I made before about primitives not causing a vertex batch. That isn't true if you draw using different textures in between. And in that case you'll also get texture swaps as well.
thanks, that'll keep me busy for quite a while to understand that :D I aven't done anything with verteces yet so...
 
Also: this is not yet the solution to all problems should it be found. The next issue is pixel perfect rotation. Even after I use surface resize correctly, rotation always causes pixels to appear very messed up if the sprite is not in its original angle :(
 
That's a totally different kind of problem. Your options include whether or not to use texture interpoloation, and whether to try try upscaling your sprites.

As it happens, I was working on something experimental that happens to do an amazing job preserving the appearance of pixelart with upscaled rotated sprites.
I don't know if you'd be interested in it in comparison to other techniques. Here's a picture that shows the difference. The pictures on the left are drawn (in-game) at 4x the sprites original size:

EDIT: The experimental method uses a shader to shift the texture coordinates of each fragment. It produces much better results when texture interpolation is also enabled. Disclaimer: The fragment shader below is a heavily simplified version of something I saw a long time ago, and I don't recall where the idea originally came from. Check spoiler if you are interested in seeing the shader code:
Code:
    varying vec2 v_vTexcoord;
    varying vec4 v_vColour;
    //SCALE is scale of sprite on view port.
    //You must take into account both the image scale of the sprite,
    //and the ratio between the view and port sizes.
    //If the sprite appears 4x bigger on screen than that actual sprite size, then SCALE = 4.
    uniform float SCALE;
    //RES is texel width and texel height of texture upon which sprite is located.
    //(texel width) = 1/(width of texture), (texel height) = 1/(height of texture)
    uniform vec2 RES;
    void main() {
        vec2 pixel = v_vTexcoord/RES + 0.5/SCALE;
        vec2 uv = floor(pixel) + min(fract(pixel)*SCALE,1.0) - 0.5;
        gl_FragColor = v_vColour * texture2D( gm_BaseTexture, uv*RES );
    }

EDIT 2: Made a small correction to the "pixel" computation to stop an incorrect shift to the top-left.
 
Last edited:
lol, i JUST accidently did something to my camera and i now have pixel perfect rotation, then i saw your solution xD What are the odds? :D
I was using surface_resize but I did something wrong with my Scale. Just out of frustration I removed Scale from a single calculation and there we have it! Pixel perfect rotation xD

Now for that actual "other pivot point rotation" … :D Gonna try and understand what you wrote in option 1, just to start :D
 
Hmm... i'd really like to know what you did. And if you can, show a screenshot of the pixel perfect rotation. Because I'm wondering if we're talking about different things there.
 
  • Like
Reactions: SEC
Camera create event:


//Initial parameters
Scale = 3;
Display_Height = display_get_height()/Scale;
Display_Width = display_get_width()/Scale;
//Surface resize to cause pixel perfect (even for rotation) surface resize:
surface_resize(application_surface, display_get_width(), display_get_height());
//GUI and full screen:
display_set_gui_size(Display_Width, Display_Height);
window_set_fullscreen(true);

//Camera:
Camera = camera_create();
camera_set_view_size(Camera, Display_Width, Display_Height);
camera_set_view_pos(Camera, x - (Display_Width / 2), y - (Display_Height / 2));
 
There's no "jaggy" edges when an object rotates with that code.
The mistake I made was that, in my surface resize, i used to do:
Scale*display_get_width(), Scale*display_get_height()


So now i just played around with it, removed the scale, and by accident i removed a mistake i didn't know i made, and now things rotate very smoothly without jaggy edges
 
Hmm... i'd really like to know what you did. And if you can, show a screenshot of the pixel perfect rotation. Because I'm wondering if we're talking about different things there.
I worked out such a crazy elegant trigonometric way that I finally took the step and made my first asset package on the game maker marketplace :D The past 4 days I've been drawing circles on paper, working out all the angles, how every sine and cosine, arctan, are related, and suddenly i saw it. 2 formulas, one for the sprite X and one for the sprite Y! I'm really happy about this :eek:

https://marketplace.yoyogames.com/assets/8364/sprite-rotation-toolset
 
Top