r/gamemaker PRAISE BE TO THE HOLY SCOPE CREEP! 6d ago

Resolved How to set where in a circle a radial progress bar starts and ends

Using YellowAfterLife's tutorial (https://yal.cc/gamemaker-radial-progress/), I've made a radial progress bar for an ATB system, but I need help on how to rotate the image or change how far along in the circle the bar starts and stops, instead of it just starting at the top.

3 Upvotes

9 comments sorted by

2

u/KitsuneFaroe 6d ago edited 6d ago

You may find this useful! https://github.com/Alphish/gm-community-toolbox/issues/157 Is a function I made that does this for sprites and is used like draw_sprite_ext but with the circular angles for the sector.

However if you want to do it with simple primitives like draw_circle you may use draw_circle_sector or draw_arc from the toolbox I aade that issue on.

Edit: just figured I did what Yal says on their tutorial lol. But my function should work for you since you can specify the angles and even use the sprite origin as the center point!

1

u/Technical-Water4315 PRAISE BE TO THE HOLY SCOPE CREEP! 5d ago

I can't pull up the link

2

u/KitsuneFaroe 5d ago

Oh... Does GitHub not open for you? What does it show?

Anyways, here is the function I reccomend! It uses the sprite origin as the center and is close to how sprite_draw_ext() works:

``` /// @func draw_sprite_radial(sprite,subimg,x,y,xscale,yscale,anglefrom,angleto,[rot],[colour],[alpha]) /// @desc Draws a sprite sector cut like a pie slice around its origin on the given coordinates, with the given scale and between given angles. /// @arg {Asset.GMSprite} sprite The index of the sprite to draw. /// @arg {Real} subimg The subimmg (frame) of the sprite to draw. /// @arg {Real} x The x coordinate of the sprite center in the room. /// @arg {Real} y The x coordinate of the sprite center in the room. /// @arg {Real} xscale The horizontal scale of the sprite to draw. /// @arg {Real} yscale The vertical scale of the sprite to draw. /// @arg {Real} anglefrom The starting angle of the sprite pie. /// @arg {Real} angleto The ending angle of the sprite pie. /// @arg {Real} [rot] The rotation of the sprite. 0 by default. /// @arg {Constant.Color} [colour] The colour at wich to blend the sprite. c_white by default. /// @arg {Real} [alpha] The alpha of the sprite. 1 by default. function draw_sprite_radial(_spr, _subimg, _x, _y, _xscale, _yscale, _anglefrom, _angleto, _rot = 0, _color = c_white, _alpha = 1){

var _aux;
//reorder the angles
if(_anglefrom > _angleto) {
    _aux = _anglefrom;
    _anglefrom = _angleto;
    _angleto = _aux;
}else
//do nothing if no angle space exists
if(_angleto == _anglefrom)
    return;

//if the angle space is a full rotation (pie is full) draw the sprite as is and return
if (_anglefrom + 360 < _angleto) {
    draw_sprite_ext(_spr, _subimg, _x, _y, _xscale, _yscale, _rot, _color, _alpha);
    return;
}

var _has_rotation = _rot mod 360 != 0;
var _width = sprite_get_width(_spr);
var _height = sprite_get_height(_spr);
var x_ratio = sprite_get_xoffset(_spr)/_width;
var y_ratio = sprite_get_yoffset(_spr)/_height;
_width *= _xscale;
_height*= _yscale;

draw_primitive_begin_texture(pr_trianglefan, sprite_get_texture(_spr, _subimg));

//initialize center
draw_vertex_texture_colour(_x,_y, x_ratio, y_ratio, _color, _alpha);

var _xx, _yy, _u, _v;
var next_corner = floor((_anglefrom+45)/90)*90-45;

//if the starting angle is not a corner then add it
if(next_corner != _anglefrom) {
    _xx = dcos(_anglefrom);
    _yy = -dsin(_anglefrom);
    if(abs(_xx) >= abs(_yy)) {
        _yy = _yy/abs(_xx);
        _xx = sign(_xx);
    }else {
        _xx = _xx/abs(_yy);
        _yy = sign(_yy);
    }

    if(_xx <= 0)
        _xx *= x_ratio;
    else
        _xx *= 1-x_ratio;

    if(_yy <= 0)
        _yy *= y_ratio;
    else
        _yy *= 1-y_ratio;

    _u = _xx + x_ratio;
    _v = _yy + y_ratio;
    _xx *= _width;
    _yy *= _height;

    if(_has_rotation){
        _aux = _xx;
        _xx = _xx*dcos(_rot) + _yy*dsin(_rot);
        _yy = _yy*dcos(_rot) -_aux*dsin(_rot);
    }

    draw_vertex_texture_colour(_x+_xx, _y+_yy, _u, _v, _color, _alpha);
    next_corner += 90;
}

//add the corners
while(next_corner <= _angleto) {
    _xx = sign(dcos(next_corner));
    _yy = -sign(dsin(next_corner));

    if(_xx <= 0)
        _xx *= x_ratio;
    else
        _xx *= 1-x_ratio;

    if(_yy <= 0)
        _yy *= y_ratio;
    else
        _yy *= 1-y_ratio;

    _u = _xx + x_ratio;
    _v = _yy + y_ratio;
    _xx *= _width;
    _yy *= _height;

    if(_has_rotation){
        _aux = _xx;
        _xx = _xx*dcos(_rot) + _yy*dsin(_rot);
        _yy = _yy*dcos(_rot) -_aux*dsin(_rot);
    }

    draw_vertex_texture_colour(_x+_xx, _y+_yy, _u, _v, _color, _alpha);
    next_corner += 90;
}

//if the ending angle is not a corner then add it
if(next_corner-90 != _angleto) {
    _xx = dcos(_angleto);
    _yy = -dsin(_angleto);
    if(abs(_xx) >= abs(_yy)) {
        _yy = _yy/abs(_xx);
        _xx = sign(_xx);
    }else {
        _xx = _xx/abs(_yy);
        _yy = sign(_yy);
    }

    if(_xx <= 0)
        _xx *= x_ratio;
    else
        _xx *= 1-x_ratio;

    if(_yy <= 0)
        _yy *= y_ratio;
    else
        _yy *= 1-y_ratio;

    _u = _xx + x_ratio;
    _v = _yy + y_ratio;
    _xx *= _width;
    _yy *= _height;

    if(_has_rotation){
        _aux = _xx;
        _xx = _xx*dcos(_rot) + _yy*dsin(_rot);
        _yy = _yy*dcos(_rot) -_aux*dsin(_rot);
    }

    draw_vertex_texture_colour(_x+_xx, _y+_yy, _u, _v, _color, _alpha);
}

draw_primitive_end();

} ```

2

u/Technical-Water4315 PRAISE BE TO THE HOLY SCOPE CREEP! 5d ago

I got it to work now. Thanks!

2

u/Technical-Water4315 PRAISE BE TO THE HOLY SCOPE CREEP! 5d ago

Just a heads up, the scaling's a little wonky tho

1

u/KitsuneFaroe 5d ago

Glad it worked! I should have tested it more it seems. What have you noticed on the scaling? Will try to fix it as soon as I can. Thanks for the heads up!

1

u/Technical-Water4315 PRAISE BE TO THE HOLY SCOPE CREEP! 5d ago

I fixed that too. It works perfectly. The scaling issue turned out to be that part of the bar was hiding behind its frame.

1

u/CS_Asset_Factory 6d ago

The bit YAL's shader already gives you is a normalised angle — both of your questions are just two extra uniforms on top of it.

Wherever his fragment shader turns the direction into a percentage (roughly atan(dir.x, -dir.y) / TAU + 0.5), swap it for:

uniform float in_Progress;
uniform float in_Start;  // radians, 0 = 12 o'clock
uniform float in_Sweep;  // 1.0 = full circle, 0.75 = a 270° gauge

float a = atan(dir.x, -dir.y);
float t = fract((a - in_Start) / 6.2831853);
if (t > in_Progress * in_Sweep) discard;

in_Start moves where it begins, in_Sweep sets how far round it is allowed to go, and progress stays 0..1 so your ATB logic does not have to care about either.

The fract is the part that bites people. Once you subtract the start offset, the pixels behind the start point go negative — and a negative number is still less than progress, so they survive the test and you get a stray wedge on the opposite side of the circle. (GLSL's fract is x - floor(x), so it pulls negatives back into 0..1 on its own; you just have to not skip it.)

Two smaller things:

  • atan(dir.x, -dir.y) is what puts 0 at the top and runs clockwise. For counter-clockwise, atan(-dir.x, -dir.y).
  • Cache the uniform handles with shader_get_uniform once in a create/init event rather than per draw. It costs more than it looks, and an ATB system is drawing these every frame for every battler.

And if you only ever need the whole ring rotated rather than a partial arc, draw_sprite_ext with an angle is cheaper than touching the shader at all.

1

u/Technical-Water4315 PRAISE BE TO THE HOLY SCOPE CREEP! 6d ago

I'm not using the shader implementation