type
TPlayerHud = class(TCastleUserInterface)
private
FSpriteSheet: TCastleSpriteSheet;
FCurrentAnimation: TCastleSpriteSheetAnimation;
FCurrentFrameIndex: Integer;
FTimeAccumulator: Single;
FAtlasImage: TDrawableImage;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure Update(const SecondsPassed: Single; var HandleInput: Boolean); override;
procedure Render; override;
end;
constructor TPlayerHud.Create(AOwner: TComponent);
var
AtlasImage: TCastleImage;
begin
inherited;
FSpriteSheet := TCastleSpriteSheet.Create(False);
FSpriteSheet.Load('player_animations.castle-sprite-sheet');
if FSpriteSheet.AtlasCanBeRegenrated then
begin
FSpriteSheet.RegenerateAtlas;
FAtlasImage := FSpriteSheet.GeneratedAtlasImage;
end;
if FSpriteSheet.AnimationCount > 0 then
begin
FCurrentAnimation := FSpriteSheet.AnimationByIndex(0);
FCurrentFrameIndex := 0;
end;
FTimeAccumulator := 0;
end;
destructor TPlayerHud.Destroy;
begin
FreeAndNil(FAtlasImage);
FreeAndNil(FSpriteSheet);
inherited;
end;
procedure TPlayerHud.Update(const SecondsPassed: Single; var HandleInput: Boolean);
var
FrameDuration: Single;
begin
inherited;
if (FCurrentAnimation <> nil) and (FCurrentAnimation.FrameCount > 0) then
begin
FrameDuration := 1 / FCurrentAnimation.FramesPerSecond;
FTimeAccumulator := FTimeAccumulator + SecondsPassed;
while FTimeAccumulator >= FrameDuration do
begin
FTimeAccumulator := FTimeAccumulator - FrameDuration;
FCurrentFrameIndex := (FCurrentFrameIndex + 1) mod FCurrentAnimation.FrameCount;
end;
end;
end;
procedure TPlayerHud.Render;
var
R: TFloatRectangle;
Frame: TCastleSpriteSheetFrame;
SourceRect: TFloatRectangle;
begin
inherited;
R := FloatRectangle(10, 10, 400, 50);
DrawRectangle(R, Vector4(1, 0, 0, 0.5));
R := R.Grow(-3);
R.Width := R.Width * 100 / 200;
DrawRectangle(R, Vector4(1, 0, 0, 1));
UIFont.Print(20, 20, Yellow, Format('Player life: %f / %f', [
100,200
]));
if (FCurrentAnimation <> nil) and
(FCurrentFrameIndex < FCurrentAnimation.FrameCount) then
begin
Frame := FCurrentAnimation.Frame[FCurrentFrameIndex];
SourceRect := FloatRectangle(
Frame.XInAtlas,
Frame.YInAtlas,
Frame.WidthInAtlas,
Frame.HeightInAtlas
);
FAtlasImage.Draw(420, 10, SourceRect);
end;
end;
See Sprite Sheets | Manual | Castle Game Engine for how to use sprite sheets, with videos and links to examples
In short: Simply place TCastleScene
with sprite sheet loaded in a TCastleViewport
. Then place this TCastleViewport
as a child of UI control, TCastleViewport
can be a child of anything in UI (like TCastleButton
). See engine examples, e.g. examples/component_gallery
shows how to place a TCastleScene
with 2D model (in this case loaded from Spine JSON, but sprite sheets would work equally well) in a TCastleViewport
in a button:
TCastleScene
has features to play animations, control time speed, and do more things you will likely need (like manage drawing order and blending sorting, ev. physics integration etc.). You can set the above components using either Pascal code or by clicking in CGE editor (great for testing, even if later you will want to do it by Pascal code).
Looking at your question and code:
-
Do not use
TCastleSpriteSheetAnimation
, it’s inCastleInternalSpriteSheet
unit (with “internal” in the name) to signal it’s not something you’re supposed to use. -
Usually: Do not draw yourself stuff in
TCastleUserInterface.Render
overrides, this is most often more work than necessary and you end up missing things (like drawing order or physics integration) already done in the engine forTCastleScene
. This is speaking from experience – in the end, most attempts to “manually draw stuff” introduce additional code complication for no gain, from what I’ve seen.That is why our docs (like Advanced: custom drawn 2D controls | Manual | Castle Game Engine , How to render 2D games with images and sprites | Manual | Castle Game Engine ) provide this as an advanced option, but also warn you that it’s not recommended. It’s really not recommended
Better use
TCastleScene
in which case you don’t even need to write any code to do what you describe, it can be configured and tested in the CGE editor
P.S. Forum notes: When posting a long code, use 3 backticks to make it more readable. See Formatting posts using markdown, BBCode, and HTML - Using Discourse - Discourse Meta about Markdown syntax on this forum. You can even use “backtick backtick backtick delphi” to make Pascal syntax colored. For longer code, you can also just point us to the GitHub repo, or GitHub “gist” where you pasted your code.
I edited now your post to show this.
Yes, Thank you very much, castle game engine is the best for Pascal or Delphi. examples\advanced_editor\advanced_loading_designs\ has demo! castle game engine Is great!!!