Added an animation struct for sprites.

This commit is contained in:
Kapendev 2024-09-03 06:07:01 +03:00
parent b4b47d8dea
commit dbceb425d5
11 changed files with 57 additions and 39 deletions

View file

@ -11,7 +11,7 @@ void ready() {
} }
bool update(float dt) { bool update(float dt) {
drawDebugText("Hello world!", Vec2(8.0)); drawDebugText("Hello world!", Vec2(8));
return false; return false;
} }

View file

@ -12,7 +12,7 @@ void ready() {
} }
bool update(float dt) { bool update(float dt) {
drawDebugText("Hello world!", Vec2(8.0)); drawDebugText("Hello world!", Vec2(8));
return false; return false;
} }

View file

@ -1,7 +1,7 @@
# Examples # Examples
> [!NOTE] > [!NOTE]
> For examples that use sprites, you must download the [atlas.png](atlas.png) file and save it in your project's assets folder. > For examples that use textures, the [atlas.png](atlas.png) file must be downloaded and saved in the project's assets folder.
## [Hello](hello.d) ## [Hello](hello.d)
@ -23,10 +23,10 @@ This example shows how to use the camera structure of Popka.
This example shows how to use the Popka dialogue system. This example shows how to use the Popka dialogue system.
## [Follower](follower.d)
This example shows how to create an animated character that follows the mouse.
## [Map](map.d) ## [Map](map.d)
This example shows how to use the tile map structure of Popka. This example shows how to use the tile map structure of Popka.
## [Follower](follower.d)
This example shows how to create an animated character that follows the mouse.

View file

@ -18,12 +18,12 @@ bool update(float dt) {
// Draw the game world. // Draw the game world.
auto cameraArea = Rect(camera.position, resolution).area(camera.hook).subAll(3); auto cameraArea = Rect(camera.position, resolution).area(camera.hook).subAll(3);
camera.attach(); camera.attach();
drawDebugText("Move with arrow keys.", Vec2(8.0)); drawDebugText("Move with arrow keys.", Vec2(8));
drawRect(cameraArea, Color(50, 50, 40, 130)); drawRect(cameraArea, Color(50, 50, 40, 130));
camera.detach(); camera.detach();
// Draw the game UI. // Draw the game UI.
drawDebugText("I am UI!", Vec2(8.0)); drawDebugText("I am UI!", Vec2(8));
drawDebugText("+", resolution * Vec2(0.5)); drawDebugText("+", resolution * Vec2(0.5));
drawDebugText("+", resolution * Vec2(0.5) + (cameraTarget - camera.position)); drawDebugText("+", resolution * Vec2(0.5) + (cameraTarget - camera.position));
return false; return false;

View file

@ -48,9 +48,9 @@ bool update(float dt) {
} }
drawRect(player); drawRect(player);
if (coins.length == 0) { if (coins.length == 0) {
drawDebugText("You collected all the coins!", Vec2(8.0)); drawDebugText("You collected all the coins!", Vec2(8));
} else { } else {
drawDebugText("Coins: {}/{}\nMove with arrow keys.".format(maxCoinCount - coins.length, maxCoinCount), Vec2(8.0)); drawDebugText("Coins: {}/{}\nMove with arrow keys.".format(maxCoinCount - coins.length, maxCoinCount), Vec2(8));
} }
return false; return false;
} }

View file

@ -63,9 +63,9 @@ bool update(float dt) {
drawDebugText(" | {}".format(choice), choicePosition); drawDebugText(" | {}".format(choice), choicePosition);
} }
} else if (dialogue.canUpdate) { } else if (dialogue.canUpdate) {
drawDebugText("{}: {}".format(dialogue.actor, dialogue.text), Vec2(8.0)); drawDebugText("{}: {}".format(dialogue.actor, dialogue.text), Vec2(8));
} else { } else {
drawDebugText("The dialogue has ended.", Vec2(8.0)); drawDebugText("The dialogue has ended.", Vec2(8));
} }
// Draw the game info. // Draw the game info.

View file

@ -3,9 +3,8 @@ import popka;
// The game variables. // The game variables.
auto atlas = TextureId(); auto atlas = TextureId();
auto sprite = Sprite(16, 16, 0, 128, 2, 8); auto sprite = Sprite(16, 16, 0, 128, SpriteAnimation(0, 2, 6));
auto spritePosition = Vec2(); auto spritePosition = Vec2();
auto spriteSlowdown = 0.2;
void ready() { void ready() {
lockResolution(320, 180); lockResolution(320, 180);
@ -18,7 +17,7 @@ void ready() {
bool update(float dt) { bool update(float dt) {
// Move the sprite around in a smooth way. // Move the sprite around in a smooth way.
spritePosition = spritePosition.moveToWithSlowdown(mouseScreenPosition, Vec2(dt), spriteSlowdown); spritePosition = spritePosition.moveToWithSlowdown(mouseScreenPosition, Vec2(dt), 0.2);
// Update the frame of the sprite. // Update the frame of the sprite.
auto isWaiting = spritePosition.distanceTo(mouseScreenPosition) < 0.2; auto isWaiting = spritePosition.distanceTo(mouseScreenPosition) < 0.2;
@ -28,14 +27,23 @@ bool update(float dt) {
sprite.update(dt); sprite.update(dt);
} }
// Check if 1, 2, or 3 is pressed and change the character.
foreach (i, digit; digitChars[1 .. 4]) {
if (digit.isPressed) {
sprite.animation.frameRow = cast(ubyte) i;
}
}
// Set the drawing options for the sprite. // Set the drawing options for the sprite.
auto options = DrawOptions(); auto options = DrawOptions();
options.scale = Vec2(2); options.scale = Vec2(2);
options.hook = Hook.center; options.hook = Hook.center;
options.flip = (spritePosition.directionTo(mouseScreenPosition).x > 0) ? Flip.x : Flip.none; options.flip = (spritePosition.directionTo(mouseScreenPosition).x > 0) ? Flip.x : Flip.none;
// Draw the sprite and the mouse position.
// Draw the sprite, the mouse position and some info.
drawSprite(atlas, sprite, spritePosition, options); drawSprite(atlas, sprite, spritePosition, options);
drawVec2(mouseScreenPosition, 8, isWaiting ? blank : white.alpha(130)); drawVec2(mouseScreenPosition, 8, isWaiting ? blank : white.alpha(150));
drawDebugText("Press 1, 2 or 3 to change the character.", Vec2(8));
return false; return false;
} }

View file

@ -9,7 +9,7 @@ void ready() {
// The update function. This is called every frame while the game is running. // The update function. This is called every frame while the game is running.
// If true is returned, then the game will stop running. // If true is returned, then the game will stop running.
bool update(float dt) { bool update(float dt) {
drawDebugText("Hello world!", Vec2(8.0)); drawDebugText("Hello world!", Vec2(8));
return false; return false;
} }

View file

@ -17,7 +17,8 @@ void ready() {
bool update(float dt) { bool update(float dt) {
// Set the drawing options for the tile map. // Set the drawing options for the tile map.
auto options = DrawOptions(); auto options = DrawOptions();
options.scale = Vec2(2.0f); options.scale = Vec2(2);
// Draw the tile map. // Draw the tile map.
drawTileMap(atlas, tileMap, Vec2(), Camera(), options); drawTileMap(atlas, tileMap, Vec2(), Camera(), options);
return false; return false;

View file

@ -19,7 +19,7 @@ void ready() {
} }
bool update(float dt) { bool update(float dt) {
drawDebugText("Hello world!", Vec2(8.0)); drawDebugText("Hello world!", Vec2(8));
return false; return false;
} }

View file

@ -15,28 +15,36 @@ import popka.engine;
// TODO: Think about gaps in an atlas texture. // TODO: Think about gaps in an atlas texture.
struct Sprite { struct SpriteAnimation {
int width; ubyte frameRow;
int height; ubyte frameCount = 1;
int atlasLeft; ubyte frameSpeed = 1;
int atlasTop;
int frameCount = 1;
float frameSpeed = 1.0f;
float frameProgress = 0.0f;
@safe @nogc nothrow: @safe @nogc nothrow:
this(int width, int height, int atlasLeft, int atlasTop, int frameCount = 1, float frameSpeed = 1.0f) { this(ubyte frameRow, ubyte frameCount, ubyte frameSpeed) {
this.frameRow = frameRow;
this.frameCount = frameCount;
this.frameSpeed = frameSpeed;
}
}
struct Sprite {
int width;
int height;
ushort atlasLeft;
ushort atlasTop;
float frameProgress = 0.0f;
SpriteAnimation animation;
@safe @nogc nothrow:
this(int width, int height, ushort atlasLeft, ushort atlasTop, SpriteAnimation animation = SpriteAnimation()) {
this.width = width; this.width = width;
this.height = height; this.height = height;
this.atlasLeft = atlasLeft; this.atlasLeft = atlasLeft;
this.atlasTop = atlasTop; this.atlasTop = atlasTop;
this.frameCount = frameCount; this.animation = animation;
this.frameSpeed = frameSpeed;
}
this(int width, int height) {
this(width, height, 0, 0);
} }
int frame() { int frame() {
@ -48,19 +56,20 @@ struct Sprite {
} }
void update(float dt) { void update(float dt) {
frameProgress = wrap(frameProgress + frameSpeed * dt, 0.0f, frameCount); frameProgress = wrap(frameProgress + animation.frameSpeed * dt, 0.0f, animation.frameCount);
} }
} }
void drawSprite(Texture texture, Sprite sprite, Vec2 position, DrawOptions options = DrawOptions()) { void drawSprite(Texture texture, Sprite sprite, Vec2 position, DrawOptions options = DrawOptions()) {
auto top = sprite.atlasTop + sprite.animation.frameRow * sprite.height;
auto gridWidth = max(texture.width - sprite.atlasLeft, 0) / sprite.width; auto gridWidth = max(texture.width - sprite.atlasLeft, 0) / sprite.width;
auto gridHeight = max(texture.height - sprite.atlasTop, 0) / sprite.height; auto gridHeight = max(texture.height - top, 0) / sprite.height;
if (gridWidth == 0 || gridHeight == 0) { if (gridWidth == 0 || gridHeight == 0) {
return; return;
} }
auto row = sprite.frame / gridWidth; auto row = sprite.frame / gridWidth;
auto col = sprite.frame % gridWidth; auto col = sprite.frame % gridWidth;
auto area = Rect(sprite.atlasLeft + col * sprite.width, sprite.atlasTop + row * sprite.height, sprite.width, sprite.height); auto area = Rect(sprite.atlasLeft + col * sprite.width, top + row * sprite.height, sprite.width, sprite.height);
drawTextureArea(texture, area, position, options); drawTextureArea(texture, area, position, options);
} }