Get Left, Right, Top, Bottom of objects or characters

Started by fernewelten, Thu 09/01/2020 10:39:27

Previous topic - Next topic

fernewelten

Hi,

the only way I know of getting the left, right, top, bottom co-ordinates of characters or objects is a huge hassle:

  • I first have to find out the appropriate sprite, by getting either the graphic or the view and frame as appropriate. This is a hassle by itself
  • Then I can get at the sprite dimensions.
  • Then I have to find out whether scaling applies. This depends on the character or object and also on their position in the room.
  • If it does apply, I then have to modify the aforementioned dimensions according to the scaling that is in effect.
  • Then I can add the coordinates to the object X (!) or character x (!) or object  Y (!) or character y (!) values. (BTW. note the naming consistency.  :-\  )
  • If it happens to be a character, I then need to subtract half the effective sprite width from the x values, which is inexact but the best I can do.

Is there an easier way than all that?

Crimson Wizard

No, this is how everyone does this :(.


We'd need a proper bounding box property(ies), which is essentially a must since there is a request to add rotation to objects, and an idea to support camera rotation, and with multiple rotations manual calculation becomes a nightmare (and slow to do in script).

Snarky

If you're thinking of adding/redoing support for rotations, could I please ask that you do it using floats rather than ints, and preferably in radians (or fractions of a whole rotation) rather than degrees? DynamicSprite.Rotate() only allows rotation by whole degrees, and this ends up being too coarse in some situations (e.g. if rotating a sprite of 300x10 pixels, the corners move by as much as 3 pixels for each degree of rotation).

Crimson Wizard

Quote from: Snarky on Thu 09/01/2020 11:13:31
If you're thinking of adding/redoing support for rotations, could I please ask that you do it using floats rather than ints, and preferably in radians (or fractions of a whole rotation) rather than degrees? DynamicSprite.Rotate() only allows rotation by whole degrees, and this ends up being too coarse in some situations (e.g. if rotating a sprite of 300x10 pixels, the corners move by as much as 3 pixels for each degree of rotation).

Right, definitely it has to be floats. BTW, here's the related ticket, if anyone wants for the reference: https://github.com/adventuregamestudio/ags/issues/995

eri0o

Just a note that sprite corners translated to character position are hardly the corners you want to get for physics or collision purposes, unless your character is a box.

fernewelten

#5
Quote from: Crimson Wizard on Thu 09/01/2020 10:58:07
We'd need a proper bounding box property(ies), which is essentially a must since there is a request to add rotation to objects

I'm not sure whether this results in a solution that is practical. Let's say we have a sequence of rotations on an arbitrary sprite, e.g., by 15°, 30°, 45° and so on. If we clip the turned sprites in such a way that the actual bounding box just fits (lets say, by evaluating the alpha channel), then there's no way from the “outside” to predict just what is clipped from the top, bottom, left, or right to make the bounding box _just_ fit. The sprites will be clipped in an irregular, unpredictable way. Moreover, there's no way to determine the turning pivot in the rotated sprite from the dimensions of that rotated sprite. Thus, the program that uses the sprites will have a hard time aligning the rotated sprites.


(transparent area is in grey)
The pivot point of the original sprite is the tip of the steeple -- so where exactly in the rotated, cropped sprites is the steeple top? It's hard to say. A program aligning the rotated sprites in a straightforward way  (e.g. by aligning the lower left corner) would see the shape "wobble" instead of rotating around the church steeple.

It would be much easier to predict the turning result if, e.g., a circle was circumscribed around the sprite canvas, then that circle was turned, then a square returned that circumscribes the circle. That way, the sprite is turned around the center of its canvas, and it is guaranteed that each rotation still has this pivot point in the center of the canvas -- no matter the amount of rotation and no matter the shape that is on the canvas.



In the original tile, the pivot point (the center around which the tile is turned) is the tip of the church steeple -- observe that in each rotated sprite, the church steeple remains in the middle of the tile. A program that aligns the rotated tiles by the lower left corner will see the church rotate around the steeple tip without wobbling.

If this way is taken, we don't really need the (real) bounding box of the shape; instead, we need the bounding circle of the whole sprite canvas.

Crimson Wizard

#6
Quote from: fernewelten on Thu 09/01/2020 14:20:13
Quote from: Crimson Wizard on Thu 09/01/2020 10:58:07
We'd need a proper bounding box property(ies), which is essentially a must since there is a request to add rotation to objects

I'm not sure whether this results in a solution that is practical. Let's say we have a sequence of rotations on an arbitrary sprite, e.g., by 15°, 30°, 45° and so on. If we clip the turned sprites in such a way that the actual bounding box just fits (lets say, by evaluating the alpha channel), then there's no way from the “outside” to predict just what is clipped from the top, bottom, left, or right to make the bounding box _just_ fit. The sprites will be clipped in an irregular, unpredictable way. Moreover, there's no way to determine the turning pivot in the rotated sprite from the dimensions of that rotated sprite. Thus, the program that uses the sprites will have a hard time aligning the rotated sprites.

I am not sure if we are speaking of same thing.

I mean, in any time it is possible to calculate the minimal rectangle around the object on screen, taking into account all its transformations. This will be a object's bounding box on screen. There will be no clipping.
Similarily, it's possible to calculate BB for any intermediate steps (e.g. object's bounding box inside a room space).

PS. When I mention object rotation, I don't mean editing the sprite to apply rotated image, I mean rotating whole sprite on its parent image (room, screen).

fernewelten

#7
(Don't know what happened -- this post is double for some reason.)

eri0o

I don't agree with the expectation of cutting the alpha of the texture in real time because this will impact performance.

A character already has a "collidable" rectangle of sorts, that is used to draw "non-walkable" area on the walkable areas. Could maybe be evolved to provide a collision box?

You can use the Box2D plug-in if you need shapes on ags for now, you don't need to use the physics simulation and can use only the AABB tests and raycast.

Crimson Wizard

#9
Quote from: eri0o on Thu 09/01/2020 14:55:39
I don't agree with the expectation of cutting the alpha of the texture in real time because this will impact performance.

Erm... each new post makes me even more confused :D Who ever mentioned the cutting of alpha of the texture?
EDIT Oh, I think I understand what you are refering to. Thing is, fernewelten above is talking about software method of rotating an image, but that's just some possible implementation details.


@fernewelten, sorry, I should probably explain myself better, with some demo chart too. I will try to do this when I get free time, maybe this night.

fernewelten

Quote from: Crimson Wizard on Thu 09/01/2020 10:58:07
No, this is how everyone does this :(.

Oh well. I'm going to code some helper functions. Something like
Code: ags

function Top(this Object *) { ... }
function Top(this Character *) { ...}
function Right(this Object *) { ... }
function Right(this Character *) { ...}
...



Snarky

Quote from: Crimson Wizard on Thu 09/01/2020 10:58:07
We'd need a proper bounding box property(ies), which is essentially a must since there is a request to add rotation to objects

I don't really understand this comment, and I think it's the source of the confusion about how sprite rotation should be done.

Quote from: fernewelten on Thu 09/01/2020 14:20:13
If we clip the turned sprites in such a way that the actual bounding box just fits (lets say, by evaluating the alpha channel), then there's no way from the “outside” to predict just what is clipped from the top, bottom, left, or right to make the bounding box _just_ fit. The sprites will be clipped in an irregular, unpredictable way. Moreover, there's no way to determine the turning pivot in the rotated sprite from the dimensions of that rotated sprite. Thus, the program that uses the sprites will have a hard time aligning the rotated sprites.


(transparent area is in grey)
The pivot point of the original sprite is the tip of the steeple -- so where exactly in the rotated, cropped sprites is the steeple top? It's hard to say. A program aligning the rotated sprites in a straightforward way  (e.g. by aligning the lower left corner) would see the shape "wobble" instead of rotating around the church steeple.

Like CW, I don't get where the assumption that the sprite would be cropped based on transparency comes from.

Currently, DynamicSprite.Rotate() simply rotates the whole sprite, without any cropping. However, the size of the rotated sprite it creates is only the bounding rectangle of the particular rotation requested, not the bounding square of the circle of all rotations, and that means that it will vary in size depending on the rotation angle. Because of symmetry, the center of the original sprite will always be in the center of the rotated sprite, so by center-positioning each rotated frame at your desired coordinates you can keep the pivot stable... but only as long as it is in the center. Any other pivot requires trigonometry to work out, and is subject to jittering (due to rounding errors, since the desired pivot pixel will usually end up at non-integer coordinates). The only way around this I know of (in AGS script) is to pad the original sprite before the rotation enough to put the pivot in the center. This can potentially quadruple the size of the sprite, and slow down the rotation correspondingly.

For rotating characters and objects, which don't have their pivots in the center, this is not great, but it would presumably be handled internally.

… Actually, I'm not sure I understand what it means to rotate an object or character. How is that compatible with baselines, for example?

(I also note that there is a similar problem for scaling with characters that have non-zero z-coordinates: because the height and z-coordinate are both rounded to the nearest integer value, the head of the character can jitter up and down when walking along a scaling area.)

Crimson Wizard

#12
Quote from: fernewelten on Thu 09/01/2020 16:43:48
Oh well. I'm going to code some helper functions. Something like

Note, you could also create managed struct Rect and get whole rectangular, to speed things up in case you need several coordinates at once

Code: ags

managed struct Rect {
    int x, int y, int w, int h;
}

void GetAABB(this Object *, Rect *r) {
    // calculate
    .....
    // fill in rect
    r.x = obj_x;
    r.y = obj_y;
    etc
}




Quote from: Snarky on Thu 09/01/2020 16:51:43
Quote from: Crimson Wizard on Thu 09/01/2020 10:58:07
We'd need a proper bounding box property(ies), which is essentially a must since there is a request to add rotation to objects

I don't really understand this comment, and I think it's the source of the confusion about how sprite rotation should be done.

I can make a more elaborate post later, when I have more time, but to make a little clarification: right now if you want to find out where the object's boundaries on screen are, you only need to account for its position and scale, and that's already frustrating to script yourself. If we add also rotation as the object's property and/or camera property, then calculating things in script becomes even more tedious.

Therefore I believe that along with "rotation" property AGS must provide an actual API for getting these boundaries.

For very similar reasons ScreenToRoomPt and RoomToScreenPt functions were added in 3.5.0, because with additions of custom viewports & cameras converting between screen and room coordinates became more serious task than simply adding ViewportX&Y.

SMF spam blocked by CleanTalk