mirror of
https://github.com/Kapendev/parin.git
synced 2025-04-25 20:49:57 +03:00
Removed dt from move and new example for platformer.
This commit is contained in:
parent
5bde39857c
commit
e8afbd08e8
3 changed files with 102 additions and 101 deletions
44
examples/intro/platformer.d
Normal file
44
examples/intro/platformer.d
Normal file
|
@ -0,0 +1,44 @@
|
|||
/// This example shows how to use the Parin physics engine.
|
||||
|
||||
import parin;
|
||||
|
||||
auto world = BoxWorld();
|
||||
auto platformBoxId = WallId();
|
||||
auto groundBoxId = WallId();
|
||||
auto playerBoxId = ActorId();
|
||||
auto playerMover = BoxMover(2, 4, 0.3, 1);
|
||||
auto groundY = 140;
|
||||
|
||||
void ready() {
|
||||
lockResolution(320, 180);
|
||||
// Add boxes to the world.
|
||||
platformBoxId = world.appendWall(Box(140, groundY - 20, 64, 16));
|
||||
groundBoxId = world.appendWall(Box(0, groundY, resolutionWidth, resolutionHeight - groundY));
|
||||
playerBoxId = world.appendActor(Box(80, groundY - 16, 16, 16), RideSide.top);
|
||||
}
|
||||
|
||||
bool update(float dt) {
|
||||
// Move the platform box.
|
||||
world.moveWallX(platformBoxId, sin(elapsedTime * 4) * 1.7);
|
||||
// Move the player box.
|
||||
playerMover.direction.x = wasd.x;
|
||||
playerMover.direction.y = wasdPressed.y;
|
||||
playerMover.move();
|
||||
world.moveActorX(playerBoxId, playerMover.velocity.x);
|
||||
if (world.moveActorY(playerBoxId, playerMover.velocity.y)) {
|
||||
playerMover.velocity.y = 0;
|
||||
}
|
||||
// Draw the world.
|
||||
foreach (wall; world.walls) {
|
||||
drawRect(wall.toRect(), black.alpha(190));
|
||||
}
|
||||
foreach (actor; world.actors) {
|
||||
drawRect(actor.toRect(), yellow.alpha(190));
|
||||
}
|
||||
drawDebugText("Move with arrow keys.", Vec2(8));
|
||||
return false;
|
||||
}
|
||||
|
||||
void finish() { }
|
||||
|
||||
mixin runGame!(ready, update, finish);
|
|
@ -275,7 +275,7 @@ struct Texture {
|
|||
}
|
||||
|
||||
/// Represents an identifier for a managed engine resource.
|
||||
/// Managed resources are cached by the path they were loaded from and can be safely shared throughout the code.
|
||||
/// Managed resources can be safely shared throughout the code.
|
||||
/// To free these resources, use the `freeResources` function or the `free` method on the identifier.
|
||||
/// The identifier is automatically invalidated when the resource is freed.
|
||||
struct TextureId {
|
||||
|
@ -310,18 +310,18 @@ struct TextureId {
|
|||
|
||||
/// Checks if the resource identifier is valid. It becomes automatically invalid when the resource is freed.
|
||||
bool isValid() {
|
||||
return data && engineState.textures.has(data);
|
||||
return data && engineState.textures.has(GenerationalIndex(data.value - 1, data.generation));
|
||||
}
|
||||
|
||||
/// Retrieves the texture associated with the resource identifier.
|
||||
ref Texture get() {
|
||||
if (!isValid) assert(0, "Index `{}` with generation `{}` does not exist.".format(data.value, data.generation));
|
||||
return engineState.textures.data[data];
|
||||
return engineState.textures[GenerationalIndex(data.value - 1, data.generation)];
|
||||
}
|
||||
|
||||
/// Retrieves the texture associated with the resource identifier or returns a default value if invalid.
|
||||
Texture getOr() {
|
||||
return isValid ? engineState.textures.data[data] : Texture();
|
||||
return isValid ? engineState.textures[GenerationalIndex(data.value - 1, data.generation)] : Texture();
|
||||
}
|
||||
|
||||
/// Frees the resource associated with the identifier.
|
||||
|
@ -374,7 +374,7 @@ struct Font {
|
|||
}
|
||||
|
||||
/// Represents an identifier for a managed engine resource.
|
||||
/// Managed resources are cached by the path they were loaded from and can be safely shared throughout the code.
|
||||
/// Managed resources can be safely shared throughout the code.
|
||||
/// To free these resources, use the `freeResources` function or the `free` method on the identifier.
|
||||
/// The identifier is automatically invalidated when the resource is freed.
|
||||
struct FontId {
|
||||
|
@ -409,18 +409,18 @@ struct FontId {
|
|||
|
||||
/// Checks if the resource identifier is valid. It becomes automatically invalid when the resource is freed.
|
||||
bool isValid() {
|
||||
return data && engineState.fonts.has(data);
|
||||
return data && engineState.fonts.has(GenerationalIndex(data.value - 1, data.generation));
|
||||
}
|
||||
|
||||
/// Retrieves the font associated with the resource identifier.
|
||||
ref Font get() {
|
||||
if (!isValid) assert(0, "Index `{}` with generation `{}` does not exist.".format(data.value, data.generation));
|
||||
return engineState.fonts.data[data];
|
||||
return engineState.fonts[GenerationalIndex(data.value - 1, data.generation)];
|
||||
}
|
||||
|
||||
/// Retrieves the font associated with the resource identifier or returns a default value if invalid.
|
||||
Font getOr() {
|
||||
return isValid ? engineState.fonts.data[data] : Font();
|
||||
return isValid ? engineState.fonts[GenerationalIndex(data.value - 1, data.generation)] : Font();
|
||||
}
|
||||
|
||||
/// Frees the resource associated with the identifier.
|
||||
|
@ -525,7 +525,7 @@ struct Sound {
|
|||
}
|
||||
|
||||
/// Represents an identifier for a managed engine resource.
|
||||
/// Managed resources are cached by the path they were loaded from and can be safely shared throughout the code.
|
||||
/// Managed resources can be safely shared throughout the code.
|
||||
/// To free these resources, use the `freeResources` function or the `free` method on the identifier.
|
||||
/// The identifier is automatically invalidated when the resource is freed.
|
||||
struct SoundId {
|
||||
|
@ -569,18 +569,18 @@ struct SoundId {
|
|||
|
||||
/// Checks if the resource identifier is valid. It becomes automatically invalid when the resource is freed.
|
||||
bool isValid() {
|
||||
return data && engineState.sounds.has(data);
|
||||
return data && engineState.sounds.has(GenerationalIndex(data.value - 1, data.generation));
|
||||
}
|
||||
|
||||
/// Retrieves the sound associated with the resource identifier.
|
||||
ref Sound get() {
|
||||
if (!isValid) assert(0, "Index `{}` with generation `{}` does not exist.".format(data.value, data.generation));
|
||||
return engineState.sounds.data[data];
|
||||
return engineState.sounds[GenerationalIndex(data.value - 1, data.generation)];
|
||||
}
|
||||
|
||||
/// Retrieves the sound associated with the resource identifier or returns a default value if invalid.
|
||||
Sound getOr() {
|
||||
return isValid ? engineState.sounds.data[data] : Sound();
|
||||
return isValid ? engineState.sounds[GenerationalIndex(data.value - 1, data.generation)] : Sound();
|
||||
}
|
||||
|
||||
/// Frees the resource associated with the identifier.
|
||||
|
@ -827,68 +827,7 @@ struct Camera {
|
|||
}
|
||||
}
|
||||
|
||||
struct EngineResourceGroup(T) {
|
||||
GenerationalList!T data;
|
||||
GenerationalList!LStr names;
|
||||
|
||||
@safe @nogc nothrow:
|
||||
|
||||
Sz length() {
|
||||
return data.length;
|
||||
}
|
||||
|
||||
bool has(GenerationalIndex i) {
|
||||
return data.has(i);
|
||||
}
|
||||
|
||||
Result!GenerationalIndex find(IStr name) {
|
||||
foreach (id; engineState.textures.ids) {
|
||||
if (engineState.textures.names[id] == name) return Result!GenerationalIndex(id);
|
||||
}
|
||||
return Result!GenerationalIndex();
|
||||
}
|
||||
|
||||
GenerationalIndex append(T arg, IStr name) {
|
||||
data.append(arg);
|
||||
return names.append(LStr(name));
|
||||
}
|
||||
|
||||
GenerationalIndex appendEmpty() {
|
||||
return append(T(), "");
|
||||
}
|
||||
|
||||
void remove(GenerationalIndex i) {
|
||||
data[i].free();
|
||||
data.remove(i);
|
||||
names[i].free();
|
||||
names.remove(i);
|
||||
}
|
||||
|
||||
void reserve(Sz capacity) {
|
||||
data.reserve(capacity);
|
||||
names.reserve(capacity);
|
||||
}
|
||||
|
||||
void free() {
|
||||
foreach (ref item; data.items) {
|
||||
item.free();
|
||||
}
|
||||
data.free();
|
||||
foreach (ref item; names.items) {
|
||||
item.free();
|
||||
}
|
||||
names.free();
|
||||
}
|
||||
|
||||
auto items() {
|
||||
return data.items;
|
||||
}
|
||||
|
||||
auto ids() {
|
||||
return data.ids;
|
||||
}
|
||||
}
|
||||
|
||||
/// The engine flags.
|
||||
struct EngineFlags {
|
||||
bool isUpdating;
|
||||
bool isPixelSnapped;
|
||||
|
@ -936,9 +875,9 @@ struct EngineState {
|
|||
Camera userCamera;
|
||||
Viewport userViewport;
|
||||
|
||||
EngineResourceGroup!Texture textures;
|
||||
EngineResourceGroup!Font fonts;
|
||||
EngineResourceGroup!Sound sounds;
|
||||
GenerationalList!Texture textures;
|
||||
GenerationalList!Font fonts;
|
||||
GenerationalList!Sound sounds;
|
||||
EngineViewport viewport;
|
||||
Font debugFont;
|
||||
List!IStr envArgsBuffer;
|
||||
|
@ -1201,8 +1140,11 @@ Result!Texture loadRawTexture(IStr path) {
|
|||
/// The resource is managed by the engine and can be freed manually or with the `freeResources` function.
|
||||
/// Supports both forward slashes and backslashes in file paths.
|
||||
TextureId loadTexture(IStr path) {
|
||||
if (auto id = engineState.textures.find(path)) return TextureId(id.get());
|
||||
if (auto resource = loadRawTexture(path)) return TextureId(engineState.textures.append(resource.get(), path));
|
||||
if (auto resource = loadRawTexture(path)) {
|
||||
auto id = TextureId(engineState.textures.append(resource.get()));
|
||||
id.data.value += 1;
|
||||
return id;
|
||||
}
|
||||
return TextureId();
|
||||
}
|
||||
|
||||
|
@ -1227,8 +1169,11 @@ Result!Font loadRawFont(IStr path, int size, int runeSpacing, int lineSpacing, I
|
|||
/// The resource is managed by the engine and can be freed manually or with the `freeResources` function.
|
||||
/// Supports both forward slashes and backslashes in file paths.
|
||||
FontId loadFont(IStr path, int size, int runeSpacing, int lineSpacing, IStr32 runes = "") {
|
||||
if (auto id = engineState.fonts.find(path)) return FontId(id.get());
|
||||
if (auto resource = loadRawFont(path, size, runeSpacing, lineSpacing, runes)) return FontId(engineState.fonts.append(resource.get(), path));
|
||||
if (auto resource = loadRawFont(path, size, runeSpacing, lineSpacing, runes)) {
|
||||
auto id = FontId(engineState.fonts.append(resource.get()));
|
||||
id.data.value += 1;
|
||||
return id;
|
||||
}
|
||||
return FontId();
|
||||
}
|
||||
|
||||
|
@ -1246,8 +1191,11 @@ Result!Font loadRawFontFromTexture(IStr path, int tileWidth, int tileHeight) {
|
|||
/// Supports both forward slashes and backslashes in file paths.
|
||||
// NOTE: The number of items allocated for this font is calculated as: (font width / tile width) * (font height / tile height)
|
||||
FontId loadFontFromTexture(IStr path, int tileWidth, int tileHeight) {
|
||||
if (auto id = engineState.fonts.find(path)) return FontId(id.get());
|
||||
if (auto resource = loadRawFontFromTexture(path, tileWidth, tileHeight)) return FontId(engineState.fonts.append(resource.get(), path));
|
||||
if (auto resource = loadRawFontFromTexture(path, tileWidth, tileHeight)) {
|
||||
auto id = FontId(engineState.fonts.append(resource.get()));
|
||||
id.data.value += 1;
|
||||
return id;
|
||||
}
|
||||
return FontId();
|
||||
}
|
||||
|
||||
|
@ -1272,8 +1220,11 @@ Result!Sound loadRawSound(IStr path, float volume, float pitch) {
|
|||
/// The resource is managed by the engine and can be freed manually or with the `freeResources` function.
|
||||
/// Supports both forward slashes and backslashes in file paths.
|
||||
SoundId loadSound(IStr path, float volume, float pitch) {
|
||||
if (auto id = engineState.sounds.find(path)) return SoundId(id.get());
|
||||
if (auto resource = loadRawSound(path, volume, pitch)) return SoundId(engineState.sounds.append(resource.get(), path));
|
||||
if (auto resource = loadRawSound(path, volume, pitch)) {
|
||||
auto id = SoundId(engineState.sounds.append(resource.get()));
|
||||
id.data.value += 1;
|
||||
return id;
|
||||
}
|
||||
return SoundId();
|
||||
}
|
||||
|
||||
|
@ -1325,9 +1276,6 @@ void openWindow(int width, int height, const(IStr)[] args, IStr title = "Parin")
|
|||
engineState.textures.reserve(defaultEngineResourcesCapacity);
|
||||
engineState.sounds.reserve(defaultEngineResourcesCapacity);
|
||||
engineState.fonts.reserve(defaultEngineFontsCapacity);
|
||||
engineState.textures.appendEmpty();
|
||||
engineState.sounds.appendEmpty();
|
||||
engineState.fonts.appendEmpty();
|
||||
engineState.viewport.color = gray;
|
||||
engineState.loadTextBuffer.reserve(8192);
|
||||
engineState.saveTextBuffer.reserve(8192);
|
||||
|
@ -2093,6 +2041,7 @@ void drawRect(Rect area, Color color = white) {
|
|||
}
|
||||
}
|
||||
|
||||
/// Draws a hollow rectangle with the specified area and color.
|
||||
@trusted
|
||||
void drawHollowRect(Rect area, float thickness, Color color = white) {
|
||||
if (isPixelSnapped || isPixelPerfect) {
|
||||
|
@ -2117,6 +2066,7 @@ void drawCirc(Circ area, Color color = white) {
|
|||
}
|
||||
}
|
||||
|
||||
/// Draws a hollow circle with the specified area and color.
|
||||
@trusted
|
||||
void drawHollowCirc(Circ area, float thickness, Color color = white) {
|
||||
if (isPixelSnapped || isPixelPerfect) {
|
||||
|
|
|
@ -39,13 +39,15 @@ struct BoxMover {
|
|||
float gravityFallFactor = 0.7f;
|
||||
float acceleration = 0.0f;
|
||||
float decelerationFactor = 0.3f;
|
||||
bool isUnnormalized;
|
||||
|
||||
@safe @nogc nothrow:
|
||||
|
||||
this(float speed, float jump, float gravity) {
|
||||
this(float speed, float jump, float gravity, float acceleration) {
|
||||
this.speed = speed;
|
||||
this.jump = jump;
|
||||
this.gravity = gravity;
|
||||
this.acceleration = acceleration;
|
||||
}
|
||||
|
||||
bool isSmooth() {
|
||||
|
@ -56,22 +58,22 @@ struct BoxMover {
|
|||
return gravity == 0.0f;
|
||||
}
|
||||
|
||||
Vec2 move(float dt, bool isUnnormalized = false) {
|
||||
Vec2 move() {
|
||||
if (isTopDown) {
|
||||
auto tempDirection = isUnnormalized ? direction : direction.normalize();
|
||||
if (isSmooth) {
|
||||
if (direction.x > 0.0f) {
|
||||
velocity.x = min(velocity.x + tempDirection.x * acceleration * dt, tempDirection.x * speed);
|
||||
velocity.x = min(velocity.x + tempDirection.x * acceleration, tempDirection.x * speed);
|
||||
} else if (direction.x < 0.0f) {
|
||||
velocity.x = max(velocity.x + tempDirection.x * acceleration * dt, tempDirection.x * speed);
|
||||
velocity.x = max(velocity.x + tempDirection.x * acceleration, tempDirection.x * speed);
|
||||
}
|
||||
if (velocity.x != tempDirection.x * speed) {
|
||||
velocity.x = lerp(velocity.x, 0.0f, decelerationFactor);
|
||||
}
|
||||
if (direction.y > 0.0f) {
|
||||
velocity.y = min(velocity.y + tempDirection.y * acceleration * dt, tempDirection.y * speed);
|
||||
velocity.y = min(velocity.y + tempDirection.y * acceleration, tempDirection.y * speed);
|
||||
} else if (direction.y < 0.0f) {
|
||||
velocity.y = max(velocity.y + tempDirection.y * acceleration * dt, tempDirection.y * speed);
|
||||
velocity.y = max(velocity.y + tempDirection.y * acceleration, tempDirection.y * speed);
|
||||
}
|
||||
if (velocity.y != tempDirection.y * speed) {
|
||||
velocity.y = lerp(velocity.y, 0.0f, decelerationFactor);
|
||||
|
@ -80,14 +82,14 @@ struct BoxMover {
|
|||
velocity.x = tempDirection.x * speed;
|
||||
velocity.y = tempDirection.y * speed;
|
||||
}
|
||||
velocity.x = velocity.x * dt;
|
||||
velocity.y = velocity.y * dt;
|
||||
velocity.x = velocity.x;
|
||||
velocity.y = velocity.y;
|
||||
} else {
|
||||
if (isSmooth) {
|
||||
if (direction.x > 0.0f) {
|
||||
velocity.x = min(velocity.x + acceleration * dt, speed);
|
||||
velocity.x = min(velocity.x + acceleration, speed);
|
||||
} else if (direction.x < 0.0f) {
|
||||
velocity.x = max(velocity.x - acceleration * dt, -speed);
|
||||
velocity.x = max(velocity.x - acceleration, -speed);
|
||||
}
|
||||
if (velocity.x != direction.x * speed) {
|
||||
velocity.x = lerp(velocity.x, 0.0f, decelerationFactor);
|
||||
|
@ -95,10 +97,10 @@ struct BoxMover {
|
|||
} else {
|
||||
velocity.x = direction.x * speed;
|
||||
}
|
||||
velocity.x = velocity.x * dt;
|
||||
velocity.x = velocity.x;
|
||||
|
||||
if (velocity.y > 0.0f) velocity.y += gravity * dt;
|
||||
else velocity.y += gravity * gravityFallFactor * dt;
|
||||
if (velocity.y > 0.0f) velocity.y += gravity;
|
||||
else velocity.y += gravity * gravityFallFactor;
|
||||
if (direction.y < 0.0f) velocity.y = -jump;
|
||||
}
|
||||
return velocity;
|
||||
|
@ -132,6 +134,11 @@ struct Box {
|
|||
this(IVec2(x, y), size);
|
||||
}
|
||||
|
||||
pragma(inline, true)
|
||||
Rect toRect() {
|
||||
return Rect(position.toVec(), size.toVec());
|
||||
}
|
||||
|
||||
bool hasPoint(IVec2 point) {
|
||||
return (
|
||||
point.x > position.x &&
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue