Closed securas closed 3 years ago
Just for reference this is continuing discussion from twitter: https://twitter.com/Securas2010/status/1365255834926604290
Ok I'll take a look and run the numbers! :+1:
I slightly modified your project to use a single centred sprite to eliminate other sources of error: test_character_positioning2.zip
And print out the y coordinate. As you can see what happens is because of physics, the player is at y 57.98 (this may be determined by collision margins etc). This fraction, no matter how small, means with the current transform snapping, this is floored to 57.0.
In Godot 2D, y increases down the screen, so the effect is that the object is raised by a pixel above 'ground' level. The tilemap is not positioned by physics so is exactly on a pixel boundary.
My attempt at a general solution to this problem is to change the rounding from floor
to round
in #43813. In my tests this seems to deal with this common problem, providing that you build your game on whole number coordinates. It also deals with the problem of physics 'vibration' where objects minutely vibrate across a whole number boundary and shift by a whole pixel as a result.
You can alternatively shift your tilemap to deal with this (at least in the y axis). If you set your tilemap Transform->Position->y
to 0.5 you can see the sprite now sits at the right level. Although I think using round
is a more general solution. Or you could apply a small offset to your sprite relative to the collision shape.
With things like this, to debug, always print the numbers, as it will make clear what is happening. All the transform_snapping does currently is apply floor
to the x and y coordinates prior to rendering.
@lawnjelly Thanks for taking the time and effort to check it out. Much appreciated.
It sounds as though we are going to try with the rounding for the next RC which should help these issues. So well worth reporting, as I think others will have had the same problem. :+1:
Fixed by #43813.
@securas Just out of interest, now I have #46569 working, it's helping to form some ideas in my head for recommendations when we finally update the docs.
My initial impressions are that transform_snap
and camera_snap
are best used when window/stretch_mode
is set to 2D
.
When window/stretch_mode
is set to viewport
, I get the impression it is effectively already quantizing to pixels (similar to the effect if you created a viewport
node at a smaller resolution, rendered to this, then blitted it to a texture_rect
(yes I tried this..)). So with transform_snap
etc off, you do get a good result (with the new camera).
The only snag is you still get the pixel gap. This is predictable because of the small gap due to collision margin, and the integer pixels effectively flooring the float coordinate:
i.e. 57.98 becomes 57.0
and the player rises by a pixel because of the godot convention that y is down. This makes the convention somewhat of a pain as it complicates matters. What we really want to do (in a sensible situation) is force rounding in the other direction (downwards in height) which is using ceiling
instead of floor
.
So at the moment I'm playing with the idea of having the transform_snap
do different things depending on the stretch_mode. In 2d
, round seems to work well. But in viewport
mode, because of the weird y convention, it makes sense to treat x and y coordinates differently, and perform a ceiling on the y coordinate. This seems to give stable behaviour and it may not even be necessary to snap the x coordinate at all in this mode.
It is funny, that it is only possible to start making sense of what is going on now the camera is fixed with #46569. There were previously multiple bugs that conflicted with each other. But I'm gradually honing down on a system which I hope will give great results no matter the stretch_mode
. :grin:
Before adding to this discussion... Any chance I can get a compiled version with #46569 to test? I don't have the skill or know how to be able to compile godot by myself...
Before adding to this discussion... Any chance I can get a compiled version with #46569 to test? I don't have the skill or know how to be able to compile godot by myself...
Yup sure, just go to the PR page (#46569), next to the green arrow click 'show all checks'. Click 'details' on the one you want, then 'artifacts' in the top right hand corner and you can download.
The fixed camera helps immensely in all these problems but there are still some more problems, I'm working on fixes right now. With stretch_mode viewport
the setting you are using, the snapping needs to do a slightly different thing that in 2d
mode to get good results, and also I've discovered we also need snapping on the camera smoothing which I'm just working on.
But with the camera PR you (and snapping turned off) should get quite a good result (apart from the pixel gap, I don't recommend turning on snapping till I've got these later fixes merged).
Alright... I tried a version... hopefully the latest though my Github knowledge is very very limited.
Viewport mode is the bread and butter of pixel art games. But not all games are platformers. So the need for flooring or ceiling makes no sense. At best, rounding, as you said. In addition, treating X and Y differently is a very limiting option. I'm not sure if that is the case but take a look at the examples below:
example project, character moving horizontally, fine... moving vertical, much worse than before
example project with camera smoothing, moving vertical or horizontal... seems even worse than before. The gif is too slow to show the character moving back and forward though.
the rounding issues (if those are really the issue) are exacerbated using a parallax background Here without smoothing
And here even worse with smoothing
In the case of the parallax background, after the jumps it seems to reach a given coordinate and then turn back... I can't possibly understand the reasons for it but it for sure is incorrect.
And the new project, if you want to try it for yourself... test_character_positioning.zip
Yup, as I said, the only one you can effectively test so far is with the new camera and snap_transforms
and snap_cameras
off. The rest won't work yet, I'm reworking this entire area currently, in light of the results from the new camera.
Looking forward!
On Tue, Mar 2, 2021 at 9:09 PM lawnjelly notifications@github.com wrote:
Yup, as I said, the only one you can effectively test so far is with the new camera and snap_transforms and snap_cameras off. The rest won't work yet, I'm reworking this entire area currently, in light of the results from the new camera.
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/godotengine/godot/issues/46504#issuecomment-788863188, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAV7RW66NVZYX5WWD5OCAO3TBTIP7ANCNFSM4YK3NY5A .
@securas If you want to give the PR #46569 another try it has now been updated.
Bad news is I could not get satisfied with any of the options for snapping for stretch_mode viewport
. So I've made them all customizable by the user. However I suspect you should be using stretch_mode 2D
, the results are like 100x better.
stretch_mode 2D
works with sub-texel accuracy, i.e it can snap at pixels on screen rather than the blocky texels.With stretch_mode viewport
I suspect you will never get the results you want:
- It works well with the parallax background
Thanks! I'll give it another go.
I choose the 0.9 scale for the parallax background because it is a very common aesthetic and it emphasizes one of the issues that I and some other folks I've talked to would really like to see fixed... The fact that it seems to wander back and forth a little before settling into the final position when the camera is smoothed. I understand that 2d mode would be much better. But that is simply not an option for pure pixel art games. Of course, it is always an aesthetics choice. But pure pixel art games cannot have pixels drawn off grid. Of course, this limits the "smoothness" of cameras and that is understandable. What is not is the wandering back and forth like what you see on the parallax background. Although I'm completely alien to the techniques that are being used here, it is difficult for me to understand what rounding method can produce that effect. It would seem that, under a hypothetical case where the camera has to smoothly transition from X to X+50, at some point the it is showing the coordinate X+51 before returning to X+50. Again... I have no grasp on the complexity that I can only imagine being done there. But it would seem to me that something is amiss.
It's probably due to physics, and the feedback loop inherent in the smoothed camera.
I had a think overnight and I'm now erring towards the idea we should hide these options. Having them available in project settings I can see as being problematic, because most people trying them won't understand them. In general from feedback I get the impression users grossly underestimate how complex snapping is to get right. People want a simple answer to a problem that may not have a simple answer. In the present state I fear we're going to get deluged by bogus support issues.
I'm currently thinking we should either delay the rollout for 3.2.4, or provide all the options, but through script only (e.g. engine calls Engine.set_transform_snap_2d
). The improved camera I think on the other hand is good to go, but I think changing the default to be the old style camera would be wise. So I'm planning to split the PR into 2 today.
Having the snapping available via script-only would provide a barrier to entry, which means users are more likely to have read documentation and understand the issues involved, and actively sought out the functions.
I'm afraid placing barriers has never yielded happy users. Documentation is the way to go IMHO. I have to say that it is a bit disappointing to read that you have essentially given up on the viewport mode. Godot will then remain a not so friendly pixel art engine. And it seems that nobody else is caring too much about it as well. So the current status of thing is likely to remain so for a long time.
On Wed, Mar 3, 2021, 15:22 lawnjelly notifications@github.com wrote:
It's probably due to physics, and the feedback loop inherent in the smoothed camera.
I had a think overnight and I'm now erring towards the idea we should hide these options. Having them available in project settings I can see as being problematic, because most people trying them won't understand them. In general from feedback I get the impression users grossly underestimate how complex snapping is to get right. People want a simple answer to a problem that may not have a simple answer. In the present state I fear we're going to get deluged by bogus support issues.
I'm currently thinking we should either delay the rollout for 3.2.4, or provide all the options, but through script only (e.g. engine calls Engine.set_transform_snap_2d). The improved camera I think on the other hand is good to go, but I think changing the default to be the old style camera would be wise. So I'm planning to split the PR into 2 today.
Having the snapping available via script-only would provide a barrier to entry, which means users are more likely to have read documentation and understand the issues involved, and actively sought out the functions.
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/godotengine/godot/issues/46504#issuecomment-789470268, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAV7RW7CSUP6LQUALCOPJ3TTBXITJANCNFSM4YK3NY5A .
I made two different PRs in the end for the snapping for us to choose between, and Akien is keener on the one with project settings.
Every possible snapping combination will be available to the user in the advanced modes, but I can't say which will be best, you will have to try them out.
I'm happy to try them out. Can you please point those to me?
It's #46615. But you may have to wait a few mins for it to pass CI, before the builds can be downloaded.
I'm off to bed now... I'll test it tomorrow 😜
On Thu, Mar 4, 2021, 01:37 lawnjelly notifications@github.com wrote:
It's #46615 https://github.com/godotengine/godot/pull/46615. But you may have to wait a few mins for it to pass CI, before the builds can be downloaded.
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/godotengine/godot/issues/46504#issuecomment-789854880, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAV7RW7LC7EZUUT2CC2U5CLTBZQULANCNFSM4YK3NY5A .
I'm getting there with doing this in the addon. The jiggle of objects relative to the background was easily solved by snapping to the background (which was pretty much my first suggestion on the subjects, see here: https://github.com/godotengine/godot/issues/35606#issuecomment-578693701 ).
The biggest problem I think is that the current smoothed camera just inherently doesn't work well with pixellated graphics. Instead here I've used a limit camera which locks a maximum range from the target. Using a hard limit removes the judder which I assume is a consequence of fractional feedback causing variation in the distance between the target and camera in the smoothed camera.
There's a bit of side to side with diagonals, I'll have a look at that next, perhaps something isn't matching up leading to a staircase effect. I also suspect physics is likely to be problematic.
Given my limited understanding, the smoothed camera problem should be fixable as it is done extensively in other engines. I did notice that the smoothed camera does show issues when accelerating and decelerating. When it moves with constant velocity, it falls into the same conditions as the non-smoothed camera. This indicates that a part of the issue might be due to the smoothing function. As for the parallax backgrounds, there should also be no reason to jitter. The change of the corresponding canvas transform should be equivalent to using a slower camera, assuming that the parallax layers do not scale the textures within them.
I can't say the current smoothed camera implementation can't be done, just I've had no luck so far making it look good. All this is done in gdscript, so if you or anyone can figure a way of going it, let us know! I'm no expert in this, as I'm really a 3d guy. :grin:
I suspect if it is possible to get working it will use slightly different logic inside the smoothing function.
There's a number of things that make such pixel based stuff more tricky in godot imo:
Got the diagonals working fine now: https://user-images.githubusercontent.com/21999379/110216502-6ab7ca00-7ea7-11eb-823a-aed6fe09c7bf.mp4
The reason for problem on the diagonals is staircasing. If the x and y coordinate are even slightly out of sync (e.g. x is 3.2 and y is 6.4) then on a boundary there will be staircasing - it will cross one axis on one frame, then the other axis the next. To get them to cross at the same time and get nice smooth motion they need to be in lockstep.
The simplest way I found to achieve this was to simply store all coordinates as integers on a grid based on the background bottom left.
There are a number of issues with this though:
An alternative approach to speed is instead of storing a fractional position, you instead store a timer, and allow moves every x ticks. So to half the speed, you allow a pixel move every 2nd tick, something like that. This will give pixel perfect motion, but there are 2 big issues:
stretch_mode viewport
), It would seem easier to not use physics engine. And if you want to use physics, it would seem better to use stretch_mode 2d
, where the screen pixel judder will be less objectionable. Essentially you'll still get staircasing and things like single pixel judder, but most of the time you won't notice it.So the question arises whether the retro games that are being emulated - do they actually use physics engines? Or are they doing per pixel movement / collision detection?
Another thing that you might try is some kind of AI interpolation on top of a physics engine, to smooth out staircasing and judder. Could work, but might add an extra delay on input.
Try also 1:2 diagonals. Those should be more problematic.
Here's one possible solution in GameMaker. Seems to correct the canvas transform on a per frame basis. I'll check this in a little more detail and report back here: https://yal.cc/gamemaker-smooth-pixel-perfect-camera/
That looks like an interesting idea. :+1:
Although there is no player in the centre of the screen. I suspect that with this method you will get player jiggle. But worth a go. You may possibly have to do the viewport longhand, use a viewport node, render to that, then shift e.g. a texture rect that uses the viewport as source texture.
I don't think godot's stretch_mode viewport
inherently would support this, as it clamps to large pixels, and as the article states using 2d
directly loses the pixellation on rotated sprites. So using a custom viewport (which fixes the pixellation) then altering the tex coords / position of a texture rect seems a way to get it to work.
That said, if it were me, I'd just cop out and use stretch_mode 2d
every time, as it skirts around the issue, and just looks better imo. :grin:
As I said before. Using 2D stretch mode is out of the question for pure pixel art games. If Godot cannot support it, it will not be pixel art friendly.
Godot version: 3.2.4 RC3
OS/device including version: Windows 10, NVIDIA GTX 1070
Issue description: Enabling transform snap leads to display of 2d character 1px above and to the right of a pixel platformer, as shown below.
Other relevant settings:
In addition, strong fluctuations of the character on screen can be observed.
Steps to reproduce: run project.
Minimal reproduction project:
test_character_positioning.zip