Examples work again.

This commit is contained in:
Kapendev 2024-08-13 09:59:40 +03:00
parent 4850a99236
commit d2d269b658
11 changed files with 150 additions and 130 deletions

View file

@ -7,7 +7,7 @@ It focuses on providing a simple foundation for building 2D games.
import popka; import popka;
bool gameLoop() { bool gameLoop() {
draw("Hello world!"); drawDebugText("Hello world!");
return false; return false;
} }

View file

@ -2,7 +2,6 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
/// This example shows how to use the camera structure of Popka. /// This example shows how to use the camera structure of Popka.
import popka; import popka;
// The game variables. // The game variables.
@ -12,19 +11,20 @@ auto cameraSpeed = Vec2(120);
bool gameLoop() { bool gameLoop() {
// Move the camera. // Move the camera.
cameraTarget += wasd * cameraSpeed * deltaTime; cameraTarget += wasd * cameraSpeed * Vec2(deltaTime);
camera.followPosition(cameraTarget); camera.followPosition(cameraTarget, Vec2(deltaTime));
// Draw the game world. // Draw the game world.
camera.attach(); auto cameraArea = Rect(camera.position, resolution).area(camera.hook).subAll(3);
draw("Move with arrow keys."); attachCamera(camera);
draw(camera.area.subAll(3), Color(50, 50, 40, 130)); drawDebugText("Move with arrow keys.");
camera.detach(); drawRect(cameraArea, Color(50, 50, 40, 130));
detachCamera(camera);
// Draw the game UI. // Draw the game UI.
draw("I am UI!"); drawDebugText("I am UI!");
draw("+", resolution * 0.5); drawDebugText("+", resolution * Vec2(0.5));
draw("+", resolution * 0.5 + (cameraTarget - camera.position)); drawDebugText("+", resolution * Vec2(0.5) + (cameraTarget - camera.position));
return false; return false;
} }

View file

@ -2,7 +2,6 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
/// This example shows how to create a simple game with Popka. /// This example shows how to create a simple game with Popka.
import popka; import popka;
// The game variables. // The game variables.
@ -27,7 +26,7 @@ bool gameLoop() {
if (Keyboard.down.isDown || 's'.isDown) { if (Keyboard.down.isDown || 's'.isDown) {
playerDirection.y = 1; playerDirection.y = 1;
} }
player.position += playerDirection * playerSpeed * deltaTime; player.position += playerDirection * playerSpeed * Vec2(deltaTime);
// Check if the player is touching some coins and remove those coins. // Check if the player is touching some coins and remove those coins.
foreach (id; coins.ids) { foreach (id; coins.ids) {
@ -36,29 +35,25 @@ bool gameLoop() {
} }
} }
// Draw the coins. // Draw the coins and the player.
foreach (coin; coins.items) { foreach (coin; coins.items) {
draw(coin, gray2); drawRect(coin);
} }
drawRect(player);
// Draw the player.
draw(player, gray2);
// Draw the game info. // Draw the game info.
if (coins.length == 0) { if (coins.length == 0) {
draw("You collected all the coins!"); drawDebugText("You collected all the coins!");
} else { } else {
draw("Coins: {}/{}\nMove with arrow keys.".fmt(maxCoinCount - coins.length, maxCoinCount)); drawDebugText("Coins: {}/{}\nMove with arrow keys.".format(maxCoinCount - coins.length, maxCoinCount));
} }
return false; return false;
} }
void gameStart() { void gameStart() {
lockResolution(320, 180); lockResolution(320, 180);
changeBackgroundColor(gray1);
// Place the player and create the coins. // Place the player and create the coins.
player.position = resolution * 0.5; player.position = resolution * Vec2(0.5);
foreach (i; 0 .. maxCoinCount) { foreach (i; 0 .. maxCoinCount) {
auto minPosition = Vec2(0, 40); auto minPosition = Vec2(0, 40);
auto maxPosition = resolution - coinSize - minPosition; auto maxPosition = resolution - coinSize - minPosition;

View file

@ -1,19 +1,18 @@
// Copyright 2024 Alexandros F. G. Kapretsos // Copyright 2024 Alexandros F. G. Kapretsos
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
/// This example shows how to use the Popka dialogue system. /// This example shows how to use the Popka chat system.
import popka; import popka;
// The game variables. // The game variables.
auto dialogue = Dialogue(); auto chat = Chat();
auto script = " auto script = "
# This is a comment. # This is a comment.
! choiceCount ! choiceCount
* menuPoint * menuPoint
^ Select first choice. ^ Select second choice. ^ End dialogue. ^ Select first choice. ^ Select second choice. ^ End chat.
* choice1 * choice1
> Bob > Bob
@ -38,51 +37,47 @@ auto script = "
bool gameLoop() { bool gameLoop() {
// Update the game. // Update the game.
if (dialogue.hasText) { if (chat.canUpdate) {
if (dialogue.hasChoices) { if (chat.hasChoices) {
auto keys = digitChars[1 .. 1 + dialogue.choices.length]; auto keys = digitChars[1 .. 1 + chat.choices.length];
foreach (i, key; keys) { foreach (i, key; keys) {
if (key.isPressed) { if (key.isPressed) {
dialogue.select(i); chat.pick(i);
break; break;
} }
} }
} else if (Keyboard.space.isPressed) { } else if (Keyboard.space.isPressed) {
dialogue.update(); chat.update();
} }
} }
// Draw the dialogue. // Draw the chat.
if (dialogue.hasChoices) { if (chat.hasChoices) {
foreach (i, choice; dialogue.choices) { foreach (i, choice; chat.choices) {
auto choicePosition = Vec2(8, 8 + i * 14); auto choicePosition = Vec2(8, 8 + i * 14);
draw("{}".fmt(i + 1), choicePosition); drawDebugText("{}".format(i + 1), choicePosition);
draw(" | {}".fmt(choice), choicePosition); drawDebugText(" | {}".format(choice), choicePosition);
} }
} else if (dialogue.hasText) { } else if (chat.canUpdate) {
draw("{}: {}".fmt(dialogue.actor, dialogue.text)); drawDebugText("{}: {}".format(chat.actor, chat.text));
} else { } else {
draw("The dialogue has ended."); drawDebugText("The chat has ended.");
} }
// Draw the game info/ // Draw the game info/
auto infoPosition = Vec2(8, resolution.y - 2 - 14 * 2); auto infoPosition = Vec2(8, resolution.y - 2 - 14 * 2);
draw(Rect(0, resolution.y * 0.8, resolution.x, resolution.y), gray1); drawRect(Rect(0, resolution.y * 0.8, resolution.x, resolution.y), gray1);
draw("Press 1, 2 or 3 to select a choice.", infoPosition); drawDebugText("Press 1, 2 or 3 to select a choice.", infoPosition);
draw("\nPress space to continue.", infoPosition); drawDebugText("\nPress space to continue.", infoPosition);
return false; return false;
} }
void gameStart() { void gameStart() {
lockResolution(320, 180); lockResolution(320, 180);
changeBackgroundColor(Color(50, 60, 75)); // Parse the chat script of the game.
// The first update makes the chat go to the first available line.
// Parse the dialogue script of the game. chat.parse(script);
dialogue.parse(script); chat.update();
// The first update makes the dialogue go to the first available line.
dialogue.update();
updateWindow!gameLoop(); updateWindow!gameLoop();
} }

View file

@ -11,10 +11,8 @@ auto frameDirection = 1;
auto frameSlowdown = 0.2; auto frameSlowdown = 0.2;
bool gameLoop() { bool gameLoop() {
// Move the frame around in a smooth way. // Move the frame around in a smooth way and update the current frame.
framePosition = framePosition.moveTo(mouseScreenPosition, Vec2(deltaTime), frameSlowdown); framePosition = framePosition.moveToWithSlowdown(mouseScreenPosition, Vec2(deltaTime), frameSlowdown);
// Update the current frame.
frame = wrap(frame + deltaTime * frameSpeed, 0, frameCount); frame = wrap(frame + deltaTime * frameSpeed, 0, frameCount);
// Check the mouse move direction and make the sprite look at that direction. // Check the mouse move direction and make the sprite look at that direction.
@ -33,27 +31,27 @@ bool gameLoop() {
options.flip = frameDirection == 1 ? Flip.x : Flip.none; options.flip = frameDirection == 1 ? Flip.x : Flip.none;
options.scale = Vec2(2); options.scale = Vec2(2);
// Draw the frame. // Draw the frame and the mouse position.
draw(atlas, Rect(frameSize.x * floor(frame), 128, frameSize), framePosition, options); drawTexture(atlas, framePosition, Rect(frameSize.x * floor(frame), 128, frameSize), options);
drawVec2(mouseScreenPosition, 8, frame == 0 ? blank : white.alpha(150));
// Draw the mouse position.
draw(mouseScreenPosition, 8, frame == 0 ? blank : white.alpha(150));
return false; return false;
} }
void gameStart() { void gameStart() {
lockResolution(320, 180); lockResolution(320, 180);
changeBackgroundColor(toRGB(0x0b0b0b)); setBackgroundColor(toRgb(0x0b0b0b));
hideCursor();
togglePixelPerfect(); togglePixelPerfect();
hideCursor();
// Loads the `atlas.png` texture from the assets folder. // Loads the `atlas.png` texture from the assets folder.
atlas.load("atlas.png"); auto result = loadTexture("atlas.png");
if (result.isSome) {
atlas = result.unwrap();
} else {
printfln("Can not load texture. Fault: `{}`", result.fault);
}
updateWindow!gameLoop(); updateWindow!gameLoop();
// Frees the loaded texture.
atlas.free(); atlas.free();
} }

View file

@ -2,13 +2,12 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
/// This example serves as a classic hello-world program, introducing the fundamental structure of a Popka program. /// This example serves as a classic hello-world program, introducing the fundamental structure of a Popka program.
import popka; import popka;
// The game loop. This is called every frame. // The game loop. This is called every frame.
// If true is returned, then the game will stop running. // If true is returned, then the game will stop running.
bool gameLoop() { bool gameLoop() {
draw("Hello world!"); drawDebugText("Hello world!");
return false; return false;
} }

View file

@ -2,7 +2,6 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
/// This example shows how to use the tile map structure of Popka. /// This example shows how to use the tile map structure of Popka.
import popka; import popka;
// The game variables. // The game variables.
@ -12,34 +11,36 @@ auto camera = Camera();
auto cameraSpeed = Vec2(120); auto cameraSpeed = Vec2(120);
bool gameLoop() { bool gameLoop() {
// Move the camera. // Move the camera and set up the drawing options of the game.
camera.position += wasd * cameraSpeed * deltaTime; camera.position += wasd * cameraSpeed * Vec2(deltaTime);
// Set up the drawing options of the game.
auto options = DrawOptions(); auto options = DrawOptions();
options.scale = Vec2(2); options.scale = Vec2(2);
// Passing a camera to the tile map drawing function allows for efficient rendering by only drawing the tiles that are currently in view. // Passing a camera to the tile map drawing function allows for efficient rendering by only drawing the tiles that are currently in view.
camera.attach(); attachCamera(camera);
draw(atlas, map, camera, Vec2(0), options); drawTileMap(atlas, Vec2(), map, camera, options);
camera.detach(); detachCamera(camera);
return false; return false;
} }
void gameStart() { void gameStart() {
lockResolution(320, 180); lockResolution(320, 180);
changeBackgroundColor(toRGB(0x0b0b0b)); setBackgroundColor(toRgb(0x0b0b0b));
atlas.load("atlas.png"); // Loads the `atlas.png` texture from the assets folder.
auto result = loadTexture("atlas.png");
if (result.isSome) {
atlas = result.unwrap();
} else {
printfln("Can not load texture. Fault: `{}`", result.fault);
}
// Parse the tile map data. // Parse the tile map data and set the tile size.
map.parse("145,0,65\n21,22,23\n37,38,39\n53,54,55"); map.parse("145,0,65\n21,22,23\n37,38,39\n53,54,55");
// Set the tile size. map.tileWidth = 16;
map.tileSize = Vec2(16); map.tileHeight = 16;
updateWindow!gameLoop(); updateWindow!gameLoop();
atlas.free(); atlas.free();
} }

View file

@ -2,13 +2,12 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
/// This example shows how to create a pong-like game with Popka. /// This example shows how to create a pong-like game with Popka.
import popka; import popka;
// The game variables. // The game variables.
auto gameCounter = 0; auto gameCounter = 0;
auto ballDirection = Vec2(1, 1); auto ballDirection = Vec2(1, 1);
auto ballSpeed = 120; auto ballSpeed = Vec2(120);
auto ball = Rect(5, 5); auto ball = Rect(5, 5);
auto paddle1 = Rect(5 * 0.35, 5 * 5); auto paddle1 = Rect(5 * 0.35, 5 * 5);
auto paddle2 = Rect(5 * 0.35, 5 * 5); auto paddle2 = Rect(5 * 0.35, 5 * 5);
@ -21,12 +20,12 @@ bool gameLoop() {
// Move the ball. // Move the ball.
if (ball.centerArea.leftPoint.x < 0) { if (ball.centerArea.leftPoint.x < 0) {
ball.position = resolution * 0.5; ball.position = resolution * Vec2(0.5);
paddle1.position.y = resolution.y * 0.5; paddle1.position.y = resolution.y * 0.5;
paddle2.position.y = resolution.y * 0.5; paddle2.position.y = resolution.y * 0.5;
gameCounter = 0; gameCounter = 0;
} else if (ball.centerArea.rightPoint.x > resolution.x) { } else if (ball.centerArea.rightPoint.x > resolution.x) {
ball.position = resolution * 0.5; ball.position = resolution * Vec2(0.5);
paddle1.position.y = resolution.y * 0.5; paddle1.position.y = resolution.y * 0.5;
paddle2.position.y = resolution.y * 0.5; paddle2.position.y = resolution.y * 0.5;
gameCounter = 0; gameCounter = 0;
@ -38,17 +37,17 @@ bool gameLoop() {
ballDirection.y *= -1; ballDirection.y *= -1;
ball.position.y = resolution.y - ball.size.y * 0.5; ball.position.y = resolution.y - ball.size.y * 0.5;
} }
ball.position += ballDirection * ballSpeed * deltaTime; ball.position += ballDirection * ballSpeed * Vec2(deltaTime);
// Move paddle1. // Move paddle1.
paddle1.position.y = clamp(paddle1.position.y + wasd.y * ballSpeed * deltaTime, paddle1.size.y * 0.5, resolution.y - paddle1.size.y * 0.5); paddle1.position.y = clamp(paddle1.position.y + wasd.y * ballSpeed.y * deltaTime, paddle1.size.y * 0.5, resolution.y - paddle1.size.y * 0.5);
// Move paddle2. // Move paddle2.
auto paddle2Target = ball.position.y; auto paddle2Target = ball.position.y;
if (ballDirection.x < 1) { if (ballDirection.x < 1) {
paddle2Target = paddle2.position.y; paddle2Target = paddle2.position.y;
} }
paddle2.position.y = paddle2.position.y.moveTo(clamp(paddle2Target, paddle2.size.y * 0.5, resolution.y - paddle2.size.y * 0.5), ballSpeed * deltaTime); paddle2.position.y = paddle2.position.y.moveTo(clamp(paddle2Target, paddle2.size.y * 0.5f, resolution.y - paddle2.size.y * 0.5f), ballSpeed.y * deltaTime);
// Check for collisions. // Check for collisions.
if (paddle1.centerArea.hasIntersection(ball.centerArea)) { if (paddle1.centerArea.hasIntersection(ball.centerArea)) {
@ -63,29 +62,25 @@ bool gameLoop() {
} }
// Draw the objects. // Draw the objects.
draw(ball.centerArea); drawRect(ball.centerArea);
draw(paddle1.centerArea); drawRect(paddle1.centerArea);
draw(paddle2.centerArea); drawRect(paddle2.centerArea);
// Draw the counter. // Draw the counter.
auto textOptions = DrawOptions(); auto textOptions = DrawOptions();
textOptions.scale = Vec2(2); textOptions.scale = Vec2(2);
textOptions.hook = Hook.center; textOptions.hook = Hook.center;
draw("{}".fmt(gameCounter), Vec2(resolution.x * 0.5, 16), textOptions); drawDebugText("{}".format(gameCounter), Vec2(resolution.x * 0.5, 16), textOptions);
return false; return false;
} }
void gameStart() { void gameStart() {
lockResolution(320, 180); lockResolution(320, 180);
changeBackgroundColor(Color(202,178,106));
// Place the game objects. // Place the game objects.
auto paddleOffset = Vec2(resolution.x * 0.4, 0); auto paddleOffset = Vec2(resolution.x * 0.4, 0);
ball.position = resolution * 0.5; ball.position = resolution * Vec2(0.5);
paddle1.position = resolution * 0.5 - paddleOffset; paddle1.position = resolution * Vec2(0.5) - paddleOffset;
paddle2.position = resolution * 0.5 + paddleOffset; paddle2.position = resolution * Vec2(0.5) + paddleOffset;
updateWindow!gameLoop(); updateWindow!gameLoop();
} }

View file

@ -66,7 +66,13 @@ struct Chat {
return isKind(ChatUnitKind.pause); return isKind(ChatUnitKind.pause);
} }
auto choices() { bool canUpdate() {
return !hasEnded;
}
IStr[] choices() {
static IStr[16] buffer;
struct Range { struct Range {
IStr menu; IStr menu;
@ -84,7 +90,13 @@ struct Chat {
} }
} }
return hasChoices ? Range(units[unitIndex].text.items) : Range(""); auto length = 0;
auto range = hasChoices ? Range(units[unitIndex].text.items) : Range("");
foreach (item; range) {
buffer[length] = item;
length += 1;
}
return buffer[0 .. length];
} }
IStr[] args() { IStr[] args() {

View file

@ -23,12 +23,13 @@ ray.Camera2D _toRay(Camera camera) {
/// Returns a random integer between 0 and float.max (inclusive). /// Returns a random integer between 0 and float.max (inclusive).
@trusted @trusted
int randi() { int randi() {
return ray.GetRandomValue(0, cast(int) float.max); return ray.GetRandomValue(0, int.max);
} }
/// Returns a random floating point number between 0.0f and 1.0f (inclusive). /// Returns a random floating point number between 0.0f and 1.0f (inclusive).
@trusted
float randf() { float randf() {
return randi() / float.max; return ray.GetRandomValue(0, cast(int) float.max) / cast(float) cast(int) float.max;
} }
/// Sets the seed for the random number generator to something specific. /// Sets the seed for the random number generator to something specific.
@ -90,7 +91,7 @@ Result!IStr loadTempText(IStr path) {
@trusted @trusted
Result!Texture loadTexture(IStr path) { Result!Texture loadTexture(IStr path) {
auto value = ray.LoadTexture(path.toAssetsPath().toCStr().unwrapOr()).toPopka(); auto value = ray.LoadTexture(path.toAssetsPath().toCStr().unwrapOr()).toPopka();
return Result!Texture(value, value.isEmpty.toFault()); return Result!Texture(value, value.isEmpty.toFault(Fault.cantFind));
} }
@trusted @trusted
@ -102,7 +103,7 @@ Result!Viewport loadViewport(int width, int height) {
@trusted @trusted
Result!Font loadFont(IStr path, uint size, const(dchar)[] runes = []) { Result!Font loadFont(IStr path, uint size, const(dchar)[] runes = []) {
auto value = ray.LoadFontEx(path.toAssetsPath.toCStr().unwrapOr(), size, cast(int*) runes.ptr, cast(int) runes.length).toPopka(); auto value = ray.LoadFontEx(path.toAssetsPath.toCStr().unwrapOr(), size, cast(int*) runes.ptr, cast(int) runes.length).toPopka();
return Result!Font(value, value.isEmpty.toFault()); return Result!Font(value, value.isEmpty.toFault(Fault.cantFind));
} }
/// Saves a text file to the assets folder. /// Saves a text file to the assets folder.
@ -176,10 +177,11 @@ void updateWindow(alias loopFunc)() {
// The lockResolution and unlockResolution queue. // The lockResolution and unlockResolution queue.
if (engineState.viewport.isLockResolutionQueued) { if (engineState.viewport.isLockResolutionQueued) {
engineState.viewport.isLockResolutionQueued = false; engineState.viewport.isLockResolutionQueued = false;
// engineState.viewport.load(engineState.targetViewportSize); // TODO engineState.viewport.free();
engineState.viewport.data = loadViewport(engineState.viewport.targetWidth, engineState.viewport.targetHeight).unwrapOr();
} else if (engineState.viewport.isUnlockResolutionQueued) { } else if (engineState.viewport.isUnlockResolutionQueued) {
engineState.viewport.isUnlockResolutionQueued = false; engineState.viewport.isUnlockResolutionQueued = false;
// engineState.viewport.free(); // TODO engineState.viewport.free();
} }
// Fullscreen code to fix a bug on KDE. // Fullscreen code to fix a bug on KDE.
if (engineState.fullscreenState.isToggleQueued) { if (engineState.fullscreenState.isToggleQueued) {
@ -239,6 +241,10 @@ void closeWindow() {
engineState = EngineState(); engineState = EngineState();
} }
void setBackgroundColor(Color color) {
engineState.backgroundColor = color;
}
/// Returns true if the FPS of the game is locked. /// Returns true if the FPS of the game is locked.
bool isFpsLocked() { bool isFpsLocked() {
return engineState.flags.isFpsLocked; return engineState.flags.isFpsLocked;
@ -286,6 +292,14 @@ void unlockResolution() {
} }
} }
void toggleResolution(int width, int height) {
if (isResolutionLocked) {
unlockResolution();
} else {
lockResolution(width, height);
}
}
/// Returns true if the system cursor is hidden. /// Returns true if the system cursor is hidden.
bool isCursorHidden() { bool isCursorHidden() {
return engineState.flags.isCursorHidden; return engineState.flags.isCursorHidden;
@ -432,6 +446,30 @@ Vec2 deltaMouse() {
return toPopka(ray.GetMouseDelta()); return toPopka(ray.GetMouseDelta());
} }
@trusted
void attachCamera(ref Camera camera) {
if (camera.isAttached) {
return;
}
camera.isAttached = true;
auto temp = camera._toRay();
if (isPixelPerfect) {
temp.target.x = floor(temp.target.x);
temp.target.y = floor(temp.target.y);
temp.offset.x = floor(temp.offset.x);
temp.offset.y = floor(temp.offset.y);
}
ray.BeginMode2D(temp);
}
@trusted
void detachCamera(ref Camera camera) {
if (camera.isAttached) {
camera.isAttached = false;
ray.EndMode2D();
}
}
@trusted @trusted
Vec2 measureTextSize(Font font, IStr text, DrawOptions options = DrawOptions()) { Vec2 measureTextSize(Font font, IStr text, DrawOptions options = DrawOptions()) {
if (font.isEmpty || text.length == 0) { if (font.isEmpty || text.length == 0) {

View file

@ -185,6 +185,14 @@ struct Camera {
@safe @nogc nothrow: @safe @nogc nothrow:
this(Vec2 position) {
this.position = position;
}
this(float x, float y) {
this(Vec2(x, y));
}
Hook hook() { Hook hook() {
return isCentered ? Hook.center : Hook.topLeft; return isCentered ? Hook.center : Hook.topLeft;
} }
@ -436,24 +444,3 @@ Flip opposite(Flip flip, Flip fallback) {
return fallback; return fallback;
} }
} }
// void attach() {
// if (!isAttached) {
// isAttached = true;
// auto temp = toRay(this);
// if (isPixelPerfect) {
// temp.target.x = floor(temp.target.x);
// temp.target.y = floor(temp.target.y);
// temp.offset.x = floor(temp.offset.x);
// temp.offset.y = floor(temp.offset.y);
// }
// ray.BeginMode2D(temp);
// }
// }
// void detach() {
// if (isAttached) {
// isAttached = false;
// ray.EndMode2D();
// }
// }