Object Rotation feature: looking for opinions

Started by Crimson Wizard, Thu 01/04/2021 15:21:28

Previous topic - Next topic

Crimson Wizard

So, I have a object rotation feature in works, as may be seen here.

Currently it adds Rotation property to several object types, which define angle rotation of rotation in degrees. Rotation is performed around the center of the image at the moment, but I also had thought of adding configurable Pivot position at least for some of them.

There are few images demonstrating it in action (includes GIFs, so may take time to load) -
Spoiler







[close]

A working test version:
Spoiler

A working test version, if anyone want to try out how it works:
https://www.mediafire.com/file/zn338ao4mhgtxwu/AGS-3.99.99.0-ROTATION.zip/file

Warning!!!: this is an experimental version of AGS 4 that has number of other changes, and saving game project there will make it incompatible with current official releases.
Also it's not final, so not 100% guaranteed to keep things as they are.


Rotation is done using following properties:
* GUI.Rotation
* Overlay.Rotation
* Camera.Rotation
* Character.GraphicRotation
* Object.GraphicRotation
[close]

Technically all this works, and may be adjusted further. There is a number of problems related to this. I have my own ideas, but would like to know other people opinions too.
But I must explain few things first.



Sprite rotation vs full object rotation

Most of the game objects are more than just a sprite, they contain several "functions".
For example: Room Object displays a sprite, but also can calculate path and move along walkable area, and also block walkable areas and collide with other objects and characters.
Characters can move in walkable areas, they can block and collide, but they may also turn around switching to different loops, have speech, inventory, and so on.

If we take geometrical transformation, such as Scale and Rotation, these may be applied strictly to object's sprite, but they may be also applied to other object's functions where makes sense.
For example, Objects and Characters have a blocking rectangle which tells which part of walkable area they block. When Scaling is applied, this blocking rectangle is also scaled along with the sprite. But what if you don't want blocking to be aligned with the sprite? then you may override that with BlockingWidth/BlockingHeight properties (I'd leave convenience question out in this topic).
So, technically, blocking rectangle and sprite are not same thing, they are separate components of an object, that optionally may be aligned, but not necessarily.
In ideal world we may say that there is a Object's or Character's own scaling which defines its "physical" contact with the room, and there's sprite's scaling which defines only its visual representation.
Spoiler

I say "in ideal world", because in AGS these things are never fully consistent. For example "Object.IsCollidingWithObject" ignores blocking rectangle and test only sprites. "Character.IsCollidingWithChar" tests sprite widths but not heights, and only succeeds if their Y coordinates are close enough.
And "Character.IsCollidingWithObject" is the weirdest of all, because it also tests for object's transparent pixels, which none of above does.
This is why it's difficult to compare what AGS does with contemporary game engines 1:1.
[close]

Now we come to Rotation.
If we take sprite-only rotation, that's seemingly easy case, as we may rotate it as we please and that supposedly should not affect anything but the visuals.
The following image demonstrates rotation of character sprite by 90 degrees around its center.

[imgzoom]https://i.imgur.com/OY6AKAx.png[/imgzoom]
(sorry if it's not precise, I was just making crude mockups to save time)
(NOTE: all images are posted using imgzoom so you may select their zoom multiplier by hovering mouse under them)

Notice the character's "origin" point though - that's the point aligned with Character's own coordinates (x and y). It remains on same place, and if we change character's position that will be position of origin point, not the chacter's feet anymore.
Human-like sprite may not be the simpliest to realize how this works as we used to have its feet aligned to its position in the room. So let's change it to this "wheel" like character that rotates as it moves around (this is just a random example).

[imgzoom]https://i.imgur.com/F19ZfH6.png[/imgzoom]

We may also introduce something called "Pivot". That's a relative point which serves a center of rotation. Good example is this bell object. Compare sprite rotation around its center and around custom pivot:

[imgzoom]https://i.imgur.com/9d3igOQ.png[/imgzoom]
[imgzoom]https://i.imgur.com/AhTsTTv.png[/imgzoom]
Notice still the object's origin does not change, it's positioned same way, then the sprite is positioned relatively to object's origin using pivot and rotation.



Now, above only sprite was rotated. This means that, for example, blocking rectangle won't be. This is a difference between whole object rotation and sprite rotation in practice.
If we rotate whole object then all of its components that have geometric meaning will be affected as well.

But how do we rotate whole object, do we use sprite's center as a point of rotation too? The thing is that sprite's center is an arbitrary point, not really related to object's position.
Previously I mentioned "origin" point - that's the point at object's coordinates. The sprite is aligned to that origin according to engine rules. For example, for Room Object sprite is aligned to origin by its left-bottom corner, for Character sprite is aligned to origin by its middle-bottom point. GUI surface is aligned by left-top corner.

This "origin" is the main point of an object, and all object's geometry is placed around it. Therefore, if we rotate whole object, it should be rotated also around same "origin" point. And there cannot be any custom pivots in this case.
For example, this is how the full rotation of a Character would look like:

[imgzoom]https://i.imgur.com/FPo6Uxz.png[/imgzoom]

Notice blocking rectangle (painted with blue lines) is also rotated.

Continuing, we may actually imagine applying both object rotation and sprite rotation simultaneously. In such case sprite rotation will be applied relative to object rotation.
For example, this is how it would look like if we rotate full object by 90 degrees and then sprite by 90 degrees (using sprite center as a pivot):

[imgzoom]https://i.imgur.com/BhpoWAF.png[/imgzoom]


To sum up, theoretically there may be purpose for separating object own rotation and its sprite rotation, and use cases for both.
In some cases combining these two may look pretty unusual and confusing. One example is GUI. Controls are part of the object itself, therefore their position should depend on GUI own rotation. Thus in case of GUI the "sprite rotation" is perhaps applied not to whole GUI visuals, but only to its background image.
In case of GUI rotation there's another problem though related to its origin, which I discuss separately below.


There's also a question of how to distinct object's own transformation properties and it's sprite transformation properties.
Supposing we already have Character.Scaling and as mentioned above this property results not only in scaled sprite but also in scaled blocking rectangle.
If we keep same naming idea, the Character.Rotation should mean full object rotation.
Then properties for transforming sprite only could be named SpriteScaling and SpriteRotation, or for slightly shorter names - SpriteScale and SpriteRotate (NOTE: actually it could be SpriteScaleX and SpriteScaleY for separate scaling by X and Y axes, but that's another topic).



Object's own pivot problem

As said before each game object has its own "origin" - this is a point that corresponds to its coordinate properties (x,y). Everything else is aligned in relation to this origin somehow.

For historical reasons, and also because AGS was meant to simulate classic P'n'C games, objects are refering to origin like this:
* GUI, gui Controls, Overlays (also Viewport and Camera since AGS 3.5.0): as a left-top corner;
* Room Objects: as a left-bottom corner;
* Characters: as a middle-bottom point.

Or, if we look at it from opposite perspective, object components are positioned:
* GUI etc: right-below from origin;
* Room Objects: right-above from origin;
* Characters: centered horizontally and above origin.

In the example of Character's own rotation posted above I showed what would happen if own rotation was using origin as a pivot. In case of Character, and maybe Object too, that seem to make sense and is convenient at first, as their origin is logically where they touch "floor" in a side-view games. So rotating Character by 90 degrees would make it work like its walking on a vertical wall, and rotating Character by 180 degrees would make it work like its walking on a celing.

Things become bit more complicated if, for instance, we want a top-down game. In such case side-view logic no longer makes sense. Even now without any rotation one would have to adjust sprite relative to character's position to make it centered around it. Vertically this may be done using "z" property which conveniently exists and works as a vertical sprite offset. There's also Character.LockViewOffset that allows to set both X and Y sprite offsets, which would be convenient if we also rotate the object.
Thankfully blocking rectangle is positioned centered on Character's origin, which is very convenient actually (not that rectangle itself is conveniently customized though). So we don't have to worry to offset it also. It even will keep working when rotating. For Room Object situation may be different, as blocking rect is positioned to the right from the origin, just like its sprite.

However, if we take GUI, or Overlay, or even Camera, here rotating around origin becomes quite questionable. Because their origin is at the top-left corner, and this is how we visually imagine them being moved around, rotating around same point would produce... unexpected and inconvenient results. Basically, GUI rotated around origin may suddenly appear above it, or to the opposite side horizontally, which makes its positioning on screen one hell of a job.

This makes me wonder if GUI and GUI-like objects should rather have not their origin, but their geometric center as a pivot of rotation. Conveniently these have explicit Width and Height properties; unlike Character or Room Object which do not have any persistent "size", and depend on their current sprite.

eri0o

#1
About sprite rotation, maybe instead if it's on a viewframe (like flipped), it would work like that? (Say if a viewframe had an angle and pivot property...)

I liked your bell example because I think someone here in the forums actually did something like that in a game - but using the DynamicSprite feature.

If defaults are preferable instead of manual configuration, then geometric center for everything may be the best option, with the Angle being in Radians so it's easy to interface with current Maths functions!

fernewelten

#2
Objects and GUIs live in different, incomparable worlds. As we know, objects are part of a 3-d world, which is "filmed" into a 2-d screen. Then, the GUIs are placed ("overlaid") onto this 2-d screen. In this 2-d screen, we have a "desktop/icons" type of environment whilst in the game proper, we have a 3-d environment (represented in 2-d).

In an analogue world, designing a GUI would be akin to designing a scrap book or photo album page. The "video screen", in which the game proper plays, is akin to the album page background. You can tape paper flowers on top of this or photos or write spiffy texts. Those would be akin to the GUIs or GUI elements. The "origin" of a button is a point to reference the button by; it's a convenient way to specify where the button is located in relation to other buttons on the "album page".

But there's no reason whatsoever to assume that this point of all points should be preferable as a pivot for rotation. It even seems far-fetched and contrived: I can imagine writing some tilted text into a photo album. I can imagine conceptualizing this as writing it upright and then tilting it. But in this case I'd never think of of it as tilting the text by its upper-left corner. Most "natural" for me would probably be the centre point, but I can imagine other situations where I'd want to turn around another pivots. But never around the top-left corner.


A point can be made that designers might prefer the origin of a character as a turning pivot in the 3-d world. But you've already pointed out that it wouldn't be so natural with, e.g., church bells. And even a human character that is turning cartwheels or doing gymnastics at a bar would have another pivot than the middle of their base line.

Probably the simplest and sanest would be to not assume anything and to always ask the user, i.e., make the pivot an argument of the rotation function. If it turns out to be hard to fill this parameter, e.g., because characters can be scaled, then _this_ is the underlying problem that needs to be solved. Instead of assuming that characters would "want" to be rotated around their origin, we should introduce Character.Width and Character.Height attributes so that the pivot parameters of the rotating function can be filled sensibly.

(Ceterum censeo: And we should strive in AGS 4, to make all those pesky differences between objects and characters go away. For instance, Character should have capitalized X, Y, Z fields -- all the fields of all AGS entities should be uniformly in capitalized camel case without exceptions --; Character.on should be capitalized (!) and renamed/repurposed to something like Character.Enabled and Character.Visible -- or else Objects given a Transparency; Object.IgnoreScaling and Character.ManualScaling should be unified to the same field name; the origin of characters and objects should be made the same -- probably middle of base in both cases --, etc. That's been bugging me for years.)

Cassiebsg

Actually I think it should default to center, but have the option to pick x,y relation to the sprite (maybe from it's center, for easy reference), if the dev does not want to use the center.

Why? Cause it's easier to make your sprite ready to rotate in the middle (when designing it) and will of course simplify ones coding. But allowing to specify a x,y will allow the dev to rotate it from wherever he wants/needs it to rotate... even if it's outside the sprite... imagine your object/character got stuck on a giant wheel and it's now rotating around the center of that wheel. It can allow for creating easy rotations with a lot less complicated code.  ;)
There are those who believe that life here began out there...

Crimson Wizard

#4
Thank you for feedback.

Quote from: eri0o on Thu 01/04/2021 17:24:22
About sprite rotation, maybe instead if it's on a viewframe (like flipped), it would work like that? (Say if a viewframe had an angle and pivot property...)

This is an interesting proposal. Such frame configuration may be useful on its own, but there's a reason I doubt this may be an equivalent to having sprite rotation on an object itself. If user will want to just smoothly rotate the object's sprite, with view frames they would have to either:
- make 1 frame and programmatically change that frame's angle; this is kind of clunky approach, plus will complicate things if same view may be shared by several objects.
- make several frames with gradually changing angle; this is extra work (and extra view/loop/frames) compared to simply having a property on object.
Then, Room Objects also have Graphic property for setting single sprite, as opposed to a View.

Still, speaking hypothetically, frames may have rotation and other settings too as a way to accomodate certain sprite for different kinds of situations, similar to how flip works. There may even be kind of inheritance of transformations defined by view -> loop -> frame each adding something.

Quote from: eri0o on Thu 01/04/2021 17:24:22
with the Angle being in Radians so it's easy to interface with current Maths functions!

This is a tough choice...  I did rotation property in degrees now as that seemed more convenient for users to put literal values in editor and with script commands. I guess majority of people who use AGS can imagine angles like 45, 60, 90, 180 degrees etc, but don't think they would like to enter this in radians (i only know 3.14 is half circle and 6.28 is full circle, because Pi). For math calculations in script degress may be still converted to radians and vice versa.
Then again, maybe it's possible to make two-variant property editor for entering either degrees or radians, and make them use DegreesToRadians when setting rotation in script... idk if it's a good idea.


Quote from: fernewelten on Sun 04/04/2021 02:59:33
A point can be made that designers might prefer the origin of a character as a turning pivot in the 3-d world. But you've already pointed out that it wouldn't be so natural with, e.g., church bells. And even a human character that is turning cartwheels or doing gymnastics at a bar would have another pivot than the middle of their base line.

I got bit concerned that there may be misunderstanding here, so would like to clarify just in case. I've been talking about two kinds or types of rotations: full object rotation and sprite-only rotation, with full-object rotation pivoting around origin and sprite rotation pivoting around custom point which defaults to center (at least according to the initial idea).

Quote from: fernewelten on Sun 04/04/2021 02:59:33
Probably the simplest and sanest would be to not assume anything and to always ask the user, i.e., make the pivot an argument of the rotation function.

Well, Rotation is made not as a function but as a property, otherwise it will be impossible to set in editor. Of course if pivot is a property too, then there's always an accessible argument for user to set if they want to. The question is mostly in convenient defaults.
In regards to pivot property, there are other considerations...

Quote from: fernewelten on Sun 04/04/2021 02:59:33
If it turns out to be hard to fill this parameter, e.g., because characters can be scaled, then _this_ is the underlying problem that needs to be solved. Instead of assuming that characters would "want" to be rotated around their origin, we should introduce Character.Width and Character.Height attributes so that the pivot parameters of the rotating function can be filled sensibly.

First of all, this is an interesting question of whether pivot is set in unscaled or scaled sprite coordinates. I did not think about this earlier. Guess if it's set in scaled coordinates then it may be more precise.

But there's another method of setting a pivot, which is used in most modern engines: instead of using pixel position it's set in relative floating-point range from 0,0 to 1,1. For example 0.5,0.5 would mean center, 0.5,0.0 would mean top-center, 1.0,0.5 - right-middle, and so on. Such format would let user to quickly set up most common pivot positions without manually calculating pixels.
We were discussing this with Alan Drake a while ago, and wonder if it's possible to let user enter pivot position in both of these formats (precise pixel and relative). The only issue is to find a good API for this.

Speaking of Character.Width and Character.Height, these may be an interesting addition, and also  would make it easier to manually scale characters by setting wanted final size rather than trying to guess percentage of scaling. The problem with this is that these would require changing how character visual size is determined, because right now their width and height is fully dependent on immediate sprite, and if View frames have sprites of different sizes the character will enlarge and shrink as it animates.


One last thing that I might add, we also spoke about introducing sprite's Anchor property. Basically it's the point in sprite which corresponds to game object's origin. This is what is currently defined as center-bottom for characters, for instance. If this was also a property then user would be able to make character sprites attached from different "side" to its origin (like, make it be below character's coordinates, instead of above).

eri0o

#5
Oh, this anchor point would be useful for scale! (scaling am object in ags4 are weird since left corner is the "origin" in it)

Crimson Wizard

Quote from: eri0o on Tue 06/04/2021 18:20:37
Oh, this anchor point would be useful for scale! (scaling am object in ags4 are weird since left corner is the "origin" in it)

I was actually wondering about fernewelten's proposal to make Room Object have same origin as a character, horizontally centering by default. That would solve autoscaling issue for objects. But on the other hand it will require user to readjust room design process. And in many cases it could be simplier to set up object position by the corner, maybe even top-left corner depending on the scene.

Crimson Wizard

#7
Perhaps, considering idea of division between object rotation and sprite rotation, I could do following for now:

GUI, Overlays and Cameras will have property Rotation, because they are rotated as whole objects. But their default pivot is at object center, because it's just easier to align them that way.
Characters and Room Objects will have property GraphicRotation, because they rotate only their graphical representation at the moment. This is to keep free property name "Rotation" in case we'll support full object rotation someday (with blocking box etc). The graphic rotation of chars and roomobjects will also have default pivot at their center, as that again seems like easiest to expect and accomodate.

I do not add pivot property at the moment, because it's bit complicated, we need to think how to conveniently support two methods of setting pivot (mentioned above). But maybe that will come too after a short term.

Meanwhile it will be still possible to "simulate" rotation around different pivot if you rotate by default and adjust object's position using math. Not the ideal solution, but should work with proper formula.

eri0o

About the origin point, when objects are used for actual small objects in the room, the middle bottom is more convenient. When objects are used for things that are not in the same "plane" of characters, meaning things on the background or in the foreground, then the current left corner is more convenient.

Crimson Wizard

#9
A working test version, if anyone want to try out how it works:
https://www.mediafire.com/file/zn338ao4mhgtxwu/AGS-3.99.99.0-ROTATION.zip/file

Warning!!!: this is an experimental version of AGS 4 that has number of other changes, and saving game project there will make it incompatible with current official releases.
Also it's not final, so not 100% guaranteed to keep things as they are.


Rotation is done using following properties:
* GUI.Rotation
* Overlay.Rotation
* Camera.Rotation
* Character.GraphicRotation
* Object.GraphicRotation


eri0o

#10
Hey, you know how the character scaling has an option to make it pixel perfect? Is there a way to have such option for the rotation too? (Or use the same option for this)

Edit: Scratch that, it's already working like this! Just tested and it's beautiful!

A heads up, the package requires placing the SDL2.dll file in the Compiled/Windows directory on Windows. You can download from here: https://www.libsdl.org/release/SDL2-2.0.14-win32-x86.zip

Crimson Wizard

#11
Quote from: eri0o on Wed 05/05/2021 11:08:15
Hey, you know how the character scaling has an option to make it pixel perfect? Is there a way to have such option for the rotation too? (Or use the same option for this)

"Render sprites at screen resolution" should control this, "smooth scaled sprites" is an option that passes a sprite through linear filter but I haven't tested it for a while. I now wonder if these options duplicate each other by result, and if it may make sense to remove the latter (because "smooth scaled sprites" likely not work well if sprites are rendered in native res).

eri0o

Yeahp, it's Render sprites at screen resolution (render_at_screenres) the right name, and it does work! When I asked I hadn't tried it yet but just after that I correctly used and it worked!

Snarky

Quote from: Crimson Wizard on Wed 05/05/2021 15:03:00
"Render sprites at screen resolution" should control this, "smooth scaled sprites" is an option that passes a sprite through linear filter but I haven't tested it for a while. I now wonder if these options duplicate each other by result, and if it may make sense to remove the latter (because "smooth scaled sprites" likely not work well if sprites are rendered in native res).

Unless I'm misunderstanding, these two options seem rather different to me. If I'm running a 320x200 game in a 960x600 screen-resolution window (so at x3 pixel-size), and I scale a 180x90-pixel sprite (rendered on-screen at 540x270) to 33%, I would expect the "render sprites at screen resolution option" to give me a 180x90 sprite rendered on-screen at x1 pixel size, so without any pixel loss but causing a mixed-resolution result. With this option off but "smooth scaled sprites" on, I would expect it to give me a 60x30 pixel sprite rendered on-screen at x3 pixel size (180x90), and with the pixels blurred together (probably using linear interpolation).

If both options are on, I feel the result in this case should be (but might not be, if nobody has tested how the options interact) the same as the first, but if you then scale it by a further 33%, it should give a 60x30 sprite rendered at x1, with the pixels blurred together.

My point is that they are not equivalent or duplicates. As someone who dislikes mixed-resolution games (certainly when not done in a deliberate way), I do not consider "render sprites at screen resolution" as an acceptable substitute for "smooth scaled sprites." Particularly not in low-res games.

BTW, if you are revisiting this API, it would be great to be able to control the scaling filter (bilinear, nearest-neighbor, etc.) on an operation-by-operation basis, rather than as one game-wide setting.

Crimson Wizard

#14
Ah, yes, smooth sprites is probably anti-aliasing and is not related to the render resolution.

Filter mode may be added to objects as well, technically renderers already has it set on per-sprite basis, so it's mostly a question of adding a property.

In regards to object API, there's a great need for a "base class" imo, because as we keep adding these properties there appear to be a lot of duplication both in API and in the engine code
https://github.com/adventuregamestudio/ags/issues/1228
^ btw, no one even commented on that proposal. Maybe I should've posted on forums instead?

eri0o

#15
Yes, the forums is better.

Edit: ah, have thought about normalizing the Angle? I did on my last mags game, like, in this way it would accept any angle and trying to read back it would return a normalized one (0.0->360.0)

With normalized angles, something rotating in the background becomes simply oFan.GraphicRotation += 5.0; in repeatedly execute always instead of

Code: ags

oFan.GraphicRotation= MathUtils.ClampF(oFan.GraphicRotation + 5.0,0.0,360.0);

Crimson Wizard

#16
Quote from: eri0o on Thu 06/05/2021 16:05:10
Edit: ah, have thought about normalizing the Angle?

This is among TODO points in the ticket.


Code: ags

oFan.GraphicRotation= MathUtils.ClampF(oFan.GraphicRotation + 5.0,0.0,360.0);


There got to be a function which correctly translates any angle value into either (0 - 2PI) or (-PI - +PI) range, similar to these:
https://github.com/ivan-mogilko/ags-lastfurious/blob/master/Last'n'Furious/MathUtil.asc#L68
(that's an example, not all things work optimally with ags script)

Crimson Wizard

I found something which I did not realize before. My "Rotation" property rotates counter-clockwise just because of how 3D renderers rotate (and also that's "mathematically" correct), but then I noticed that DynamicSprite.Rotate rotates clockwise. What is the preferred way? Guess most people would expect clockwise rotation, and also for consistency with existing DynamicSprite.Rotate these properties should also rotate clockwise?

eri0o

That's something really curious that I never thought about. Apparently my agsbox2d plugin uses clockwise too. If in the future (like, someday) the property setting is visible in the room editor, whatever is selected is easy to visualize and work with.

I would think personally I would prefer clockwise but honestly when I played around with the rotation I didn't notice, and was just happily rotating things.

Crimson Wizard

Changed to clockwise, merged to ags4 branch so this may be tested there now.

Software renderer still may have few issues, primarily there may be rounding mistakes as it relies on pixel coordinates to position rotated bitmap, and of course it will work slower in general, but this can be improved later.

SMF spam blocked by CleanTalk