I looked at your code. Things unrelated to the issue at hand (shaky feet):
-
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 | Castle Game Engine 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;
-
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;
.
-
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.
-
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.
-
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.