Setting Scene coordinates - part 3

I replaced


if Player.WalkLeft then
  begin
    Player.X := Player.X - 4;
    PlayerTransform.Translation := Vector3(Player.X, Player.Y, Player.S);
  end;    

with

if Player.WalkLeft then
  begin
    Player.X := Player.X - 4;
   PlayerTransform.Translation := Vector3(MainViewport.PositionTo2DWorld(Vector2(Player.X, 
 Player.Y), false), 1);
end;

but noticed the animation movement is not smooth anymore; it tremors.

I now have:

if Player.WalkLeft then Player.X := Player.X - 2;
if Player.WalkRight then Player.X := Player.X + 2;

PlayerTransform.Translation := Vector3(MainViewport.PositionTo2DWorld(Vector2(Player.X, Player.Y), false), 10);

The “tremor” in motion (the legs of the character) is only when moving in the X axes.
I use 60 frames of animation. Probably the X + 2 and X - 2 does not correspond to a smooth movement when I translate to viewportto2D. The movement is only smooth when I use the “normal” translation.
So how can I get smooth animation movement with the translation to 2D?

Sorry for not being present on the forum lately. I’ll catch up soon.

Please post a reproducible example of the problem. When do you change the player position? Why do you hardcode it to the shift by “4”? Usually, when you change something, e.g. in Update, you should scale movement by SecondsPassed like Quick 2D game (basic window events) | Manual | Castle Game Engine shows (I’m aware this documentation page needs an update, but still it shows the correct approach to moving something in the update).

It worked okay when I use this in the update procedure:

procedure TStatePlay.Update(const SecondsPassed: Single; var HandleInput: Boolean);  
begin
  PlayerTransform.Translation := Vector3(Player.X, Player.Y, Player.S);
  
  if Player.WalkLeft then Player.X := Player.X - Container.Fps.SecondsPassed * 100.0; 
end;

But not if I want it to convert to screen coordinates; then the character sprite moves with “shaky feet”.
This is an old problem that you and Eugene had helped me with before and it was solved when I changed my number of frames to 60. (I was using Window Update then).
But with the Stateplay the problem is here again, when the world coordinates are translated to screen coordinates:

procedure TStatePlay.Update(const SecondsPassed: Single; var HandleInput: Boolean);  
begin
  PlayerTransform.Translation := Vector3(MainViewport.PositionTo2DWorld(Vector2(Player.X, Player.Y), false), 10);  
  
  if Player.WalkLeft then Player.X := Player.X - Container.Fps.SecondsPassed * 100.0; 
end;

On the first glance I don’t see any problem with this approach, I mean you do correctly multiply with Container.Fps.SecondsPassed in the Update. Please submit a full testcase (full application that I can compile and run) and I’ll be happy to check it out.

I’m sorry if you already did that, I know you already send some versions of the application on the forum and I have a backlog to answer. If you did already, just point me to it :slight_smile:

Rescue_Island 14-07-2021.zip (66.9 MB)

Ok, see attachment.
Lines 302 (out-commented) and 303.
When enlarging the window to maximum it is most notable that the animation movement is not smooth.
The animation is only smooth when not converting to Viewportto2D (using line 302).

(also notice the black borders left and right of the background; I mentioned this in other topic with attachment, I cannot get the picture fill the entire viewport).

I looked at your code. Things unrelated to the issue at hand (shaky feet):

  1. You leak memory.

    Remember that you have to free the objects you create. There are various ways to do this – you can free explicity by FreeAndNil(xxx), you can make objects descend from TComponent and create with an “owner”. But you have to do something, otherwise the game at the end will leak memory. And it’s better to fix it, otherwise in more complicated scenarios the game will also leak memory during longer execution, eventually exhausting the available RAM.

    See our Pascal introduction on Modern Object Pascal Introduction for Programmers about “Freeing classes”.

    See our docs on how to be notified about these memory leaks: Detecting Memory Leaks Using HeapTrc · castle-engine/castle-engine Wiki · GitHub . I use HeapTrc by default, so I noticed these leaks.

    For starters, I added Stop method to the state and freed there some things you allocate in Start:

// declare inside TStatePlay like this
      procedure Stop; override;

// implement like this
procedure TStatePlay.Stop;
begin
  FreeAndNil(Player);
  FreeAndNil(PlayerMouse);
  inherited;
end;

You also need TAvatar destructor, to free what you allocated in TAvatar constructor.

// declare like this in TAvatar
destructor Destroy; override;

// implement like this
destructor TAvatar.Destroy;
begin
  FreeAndNil(Personalia);
  FreeAndNil(Appearance);
  inherited;
end;
  1. Creating

    StandIdleScene := TCastleScene.Create(Application);
    WalkLeftScene := TCastleScene.Create(Application);
    WalkRightScene := TCastleScene.Create(Application);
    

    inside the TAvatar.Create is not useful – later you override the StandIdleScene etc. values by Player.StandIdleScene := DesignedComponent ('StandIdleScene') as TCastleScene;.

  2. Doing PlayerMouse := TGamemouse.Create(PlayerMouse); works, but is confusing. It is actually equivalent in this case to PlayerMouse := TGamemouse.Create(nil); since PlayerMouse is not yet initialized before it is created. Change it to just PlayerMouse := TGamemouse.Create(nil); to be less confusing.

  3. You have somewhat random indentation, TAvatar and all it’s fields are indented by 3 spaces. It is more standard to have TAvatar indented by 2 spaces, and the inside (like fields) by 4. In general any indentation is OK, but use it consistently, your TLocation, TStatePlay use a different indentation.

  4. Your code and data is not prepared for the case-sensitive system, like on Unix. I mentioned this before, and eventually I just did now a mechanism in CGE to accept such names: use unit CastleURIUtils and set CastleDataIgnoreCase := true; early (may be in ApplicationInitialize in gameinitialize.pas).

About shaky feet:

  • This has nothing to do with using PositionTo2DWorld, in my tests. In occurs both with an without. You just accidentally positioned the player at a different place on the screen, on a different background, with PositionTo2DWorld case (line 303). So it just happened to be more noticeable in case of PositionTo2DWorld, because it was on different background then. Change the lines 302-303 inside TStatePlay.Update to test it like this:
  if Container.Pressed[KeyS] then
    PlayerTransform.Translation := Vector3(Player.X - 400, Player.Y - 600, Player.S)  // smooth movement
  else
    PlayerTransform.Translation := Vector3(MainViewport.PositionTo2DWorld(Vector2(Player.X, Player.Y), false), 10);  // tremor feet movement

The hardcoded shifts - 400, - 600 above are just a quick hack to make the test fair, i.e. move the character to a similar place of the background.

Press key “s” for one version, release “s” for another version. You will see that “shaky feet” occur in both versions. Indeed they are more visible when window is larger.

The reasons are the same as in that old thread from 2018, Castle Game Engine / Old Forum / General Discussion: Shaky Sprite move . Your sprite sheet has high number of frames, but not high enough, and “time aliasing” is visible when you render 44 FPS animation on a screen that renders 60 FPS. Some frames just stay on screen longer than others.

And you have 44 FPS – as there are 8 frames per second by default when reading from Starling sprite sheet ( Sprite sheets · castle-engine/castle-engine Wiki · GitHub ) and you set TimePlayingSpeed = 5.5 on WalkLeftScene, WalkRightScene.

Quoting my answer from 2018:

Your sprite sheet has 24 frames per second, and it’s high enough that our eye “thinks it should be smooth animation”, but then it’s not smooth enough.

Now you have 44, but it seems it’s still not high enough.

Solutions from that thread still stand.

  • Move the character slower (or faster) to break the aliasing.
  • Experiment with different TimePlayingSpeed to have more FPS on animation.
  • Or prepare a sprite animation with more FramesPerSecond, e.g. 60.
  • Last, probably something you don’t want as it is a big change: Prepare this animation as a smooth animation (Spine, Blender), not sprite sheets. Sprite sheets are great for retro games with low frames per second and small images, when the animation is obviously not smooth to our eye. Here you created an animaton with high FPS, enough high resolution, so our eye things “this should be smooth” and reacts badly when the illusion fails, because 44 FPS doesn’t match 60 FPS.

I took a look at your sprite sheet example. There you use 24 and 55 frames.
(I must admit I do not see any difference in smoothness between the 24 and 55 frames). :slight_smile:
But you have Playingspeed set on 1 and still the character animates fast.
When I set Playingspeed 1 my sheet animates very slowly. I need 5.5 or 6 to get a realistic speed animating loop.
So should I with starling use a sprite atlas with an amount of images that can be divided by 8, so 24 or even 32 images? Or can I change the standard starling 8 frames per second so it matches with the playingspeed? I already have made dozens of sheets with 60 frames. :flushed:

Many thanks for pointing this out; I will sort this out.

When using .castle-sprite-sheet format, the animation FPS are set and stored in the .castle-sprite-sheet file (and it can even be different for each animation). So indeed I can have any FPS I want while TimePlayingSpeed can remain 1.0 in the simple cases. This is one advantage .castle-sprite-sheet has over Starling format.

Note that you can create a sprite sheet in .castle-sprite-sheet from .starling-xml – just open in sprite sheet editor a .starling-xml file, and it will be saved as .castle-sprite-sheet. I was testing your sprite sheets with that too.

If you want to keep using Starling format directly (loading .starling-xml) then follow docs on Sprite sheets · castle-engine/castle-engine Wiki · GitHub and add #fps:xxx to the URL. That is after assigning TCastleScene.URL to a .starling-xml file, just edit URL and add e.g. #fps:60. Remember that you will probably want to set TimePlayingSpeed back to default 1.0, otherwise it will be 5.5*60 which is very fast :slight_smile:

Thanks.
Now the shaking legs are barely notable though the animation is a bit too fast, but when I lower the #fps or Playingspeed they tremor badly. :slight_smile:
The bottom line is that 60 frames per second is too fast to notice and to enjoy every frame :wink:
I just read that movies have 24 framerate, so why should I make 60 frames, taking lots of time to edit and consuming more memory than neccesary?
Conclusion:
I will make and try out a 24 frame sprite screen to compare. Keep you posted.

btw: The only reason I am still not using the castle-sprite-sheet format is that it can not trim the spaces between the frames. So with 60 frames the .png is very large.
But now with just 24 frames I probably should reconsider…

This makes sense. I mean, you don’t really have to make sprite sheets with 60 FPS in general – we have lots of example sprite sheets with lower FPS, and they look OK.

But in your case, with your larger graphics, and 44 FPS – you have aliasing that is visible. Using lower FPS should solve it as well.

Well… I tried with 24 frames but the result was bad, so I sticked with the 60 frames.
The tremor is barely notable anymore with fps#60. Strange enough it is only visible with the first steps, then it is smooth. Maybe my eyes fool me or the animation is played from cache after looping.
Anyway, I have a 32 inch monitor so when I barely see it at this size most people with smaller screens won’t notice it or just the picky ones (which I am).
You can take a look at my small video now at the end of topic “Coordinates again”.
:wink:

Each run of the animation is really the same, first run and subsequent runs :slight_smile: All the things that are cached (like GPU texture resources) – are already fully in the cache before you see anything of the animation.