gameplay: player movement and collisions

This commit is contained in:
Lephenixnoir 2022-07-17 12:40:35 +01:00
parent 845517793a
commit 375bec919e
Signed by: Lephenixnoir
GPG Key ID: 1BBA026E13FC0495
11 changed files with 165 additions and 22 deletions

View File

@ -9,6 +9,7 @@ find_package(LibProf 2.1 REQUIRED)
set(SOURCES
src/block.cpp
src/camera.cpp
src/game.cpp
src/main.cpp
src/render.cpp
src/world.cpp)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -53,6 +53,8 @@ def mkGlyph(s):
0x2605: 11,
0x25CF: 12,
0x21A5: 13,
0x039B: 14,
# 15 is combined with a diacritic (from CASIO's custom charset)
0x25A1: 0x7f,
}
if ord(s) >= 0x20 and ord(s) <= 0x7f:

View File

@ -10,14 +10,48 @@ WorldRect Camera::validCenters() const
return WorldRect(b.xmin + 1, b.xmax - 1, b.ymin + 1, b.ymax - 1);
}
WorldRect Camera::focusedRegion() const
{
WorldRect b = this->visible;
return WorldRect(b.xmin / 3, b.xmax / 3, b.ymin / 3, b.ymax / 3);
}
void Camera::moveTo(WorldCoord p)
{
this->center = this->validCenters().clampPoint(p);
}
void Camera::moveBy(WorldCoord direction)
{
this->center = this->validCenters().clampPoint(this->center + direction);
this->moveTo(this->center + direction);
}
void Camera::moveToFollow(WorldCoord p)
{
WorldCoord pc = p - this->center;
WorldRect focused = this->focusedRegion();
if(focused.contains(pc))
return;
/* Determine movement required to put pc into the focused region */
WorldCoord diff(0, 0);
if(pc.x < focused.xmin)
diff.x = pc.x - focused.xmin;
if(pc.x > focused.xmax)
diff.x = pc.x - focused.xmax;
if(pc.y < focused.ymin)
diff.y = pc.y - focused.ymin;
if(pc.y > focused.ymax)
diff.y = pc.y - focused.ymax;
this->moveBy(diff);
}
namespace Nooncraft {
void renderCamera(int x, int y, Camera *camera)
void renderCamera(int x, int y, Camera *camera, WorldCoord playerPos,
GlyphCluster playerCluster)
{
if(!camera)
return;
@ -31,6 +65,9 @@ void renderCamera(int x, int y, Camera *camera)
if(!camera->world->worldBorder.contains(WorldCoord(wx, wy))) {
continue;
}
else if(WorldCoord(wx, wy) == playerPos) {
renderCluster(x+2*dx, y+2*dy, playerCluster);
}
else if(camera->world->isPointOnWorldBorder(wx, wy)) {
GlyphCluster c('{', '}', '{', '}');
renderCluster(x+2*dx, y+2*dy, c);

View File

@ -18,13 +18,20 @@ struct Camera
/* Range of possible values for center */
WorldRect validCenters() const;
/* Center rectangle where we allow the player to be */
WorldRect focusedRegion() const;
/* Keep focus on a point */
void moveTo(WorldCoord p);
/* Move the camera by a fixed vector */
void moveBy(WorldCoord direction);
/* Move the camera to ensure `p` is somewhat in frame */
void moveToFollow(WorldCoord p);
};
namespace Nooncraft {
void renderCamera(int x, int y, Camera *camera);
void renderCamera(int x, int y, Camera *camera, WorldCoord playerPos,
GlyphCluster playerCluster);
} /* namespace Nooncraft */

19
src/game.cpp Normal file
View File

@ -0,0 +1,19 @@
#include "game.h"
bool Game::movePlayerBy(WorldCoord dir, bool camera_follow)
{
WorldCoord candidatePos = this->playerPos + dir;
candidatePos = this->world->limits.clampPoint(candidatePos);
if(candidatePos == this->playerPos)
return false;
BlockInfo *info = Nooncraft::getBlockInfo(this->world->cellAt(
candidatePos.x, candidatePos.y));
if(!info || !info->walkable)
return false;
this->playerPos = candidatePos;
if(camera_follow)
this->camera->moveToFollow(this->playerPos);
return true;
}

18
src/game.h Normal file
View File

@ -0,0 +1,18 @@
// nooncraft.game: Most game data randomly thrown in a structure
#pragma once
#include "world.h"
#include "camera.h"
struct Game
{
World *world;
Camera *camera;
WorldCoord playerPos;
/* Move the player by specified amount. */
bool movePlayerBy(WorldCoord dir, bool camera_follow=true);
/* Move the cursor by the specified amount. */
bool moveCursorBy(WorldCoord dir);
};

View File

@ -1,6 +1,7 @@
#include "render.h"
#include "world.h"
#include "camera.h"
#include "game.h"
#include <gint/keyboard.h>
#include <gint/timer.h>
#include <gint/cpu.h>
@ -43,22 +44,29 @@ static bool getkey_global_shortcuts(key_event_t ev)
static char debugMessage[128];
static void renderGame(Camera *camera)
static void renderGame(Game *game)
{
char separator[57];
memset(separator, '-', 56);
separator[56] = 0;
Camera *camera = game->camera;
renderClear();
Nooncraft::renderCamera(26, 12, camera);
GlyphCluster playerCluster(GLYPH_DEGREE_UNDERLINE, ' ',
GLYPH_CAPITAL_LAMBDA, ' ');
Nooncraft::renderCamera(26, 12, camera, game->playerPos, playerCluster);
GlyphCluster invLeft(' ', GLYPH_PARALLEL_TO, ' ', GLYPH_PARALLEL_TO);
GlyphCluster invRight(GLYPH_PARALLEL_TO, ' ', GLYPH_PARALLEL_TO, ' ');
int hudY = 27;
renderText(0, hudY, separator);
renderText(24, hudY+1, "COBBLESTONE");
renderFormat(24, hudY+2, debugMessage);
renderText(22, hudY+1, "HAND: COBBLESTONE");
renderFormat(46, hudY+1, "X%d Y%d", game->playerPos.x, game->playerPos.y);
renderText(22, hudY+2, "CURSOR: COBBLESTONE");
renderFormat(24, hudY+3, debugMessage);
renderCluster(0, hudY+1, invLeft);
renderCluster(2, hudY+1, Nooncraft::getBlockInfo(2)->cluster);
renderCluster(20, hudY+1, invRight);
@ -89,22 +97,29 @@ int main(void)
Camera *camera = new Camera(world);
camera->visible = WorldRect(-13, 14, -6, 6);
Game game;
game.world = world;
game.camera = camera;
game.playerPos = world->findSpawnPoint();
volatile int nextFrame = 1;
int t = timer_configure(TIMER_ANY, 1000000/20, GINT_CALL_SET(&nextFrame));
timer_start(t);
bool runMainLoop = true;
int frameDuration = 0;
int tick = 0;
while(runMainLoop) {
while(!nextFrame)
sleep();
nextFrame = 0;
tick++;
frameDuration = prof_exec({
sprintf(debugMessage, "FRAME: %d MS", frameDuration);
// sprintf(debugMessage, "VORONOI: %d MS", voronoiTime / 1000);
renderGame(camera);
renderGame(&game);
renderUpdate();
}) / 1000;
@ -119,14 +134,15 @@ int main(void)
runMainLoop = false;
}
if(keydown(KEY_LEFT))
camera->moveBy(WorldCoord::Left);
if(keydown(KEY_RIGHT))
camera->moveBy(WorldCoord::Right);
if(keydown(KEY_UP))
camera->moveBy(WorldCoord::Up);
if(keydown(KEY_DOWN))
camera->moveBy(WorldCoord::Down);
/* Clamp speed to 10 blocks/s */
if(keydown(KEY_LEFT) && (tick % 2) == 0)
game.movePlayerBy(WorldCoord::Left);
if(keydown(KEY_RIGHT) && (tick % 2) == 0)
game.movePlayerBy(WorldCoord::Right);
if(keydown(KEY_UP) && (tick % 2) == 0)
game.movePlayerBy(WorldCoord::Up);
if(keydown(KEY_DOWN) && (tick % 2) == 0)
game.movePlayerBy(WorldCoord::Down);
}
timer_stop(t);

View File

@ -30,6 +30,8 @@ enum {
GLYPH_STAR = 11, /* U+2605 BLACK STAR */
GLYPH_CIRCLE = 12, /* U+25CF BLACK CIRCLE */
GLYPH_UP_ARROW_BAR = 13, /* U+21A5 UPWARDS ARROW FROM BAR */
GLYPH_CAPITAL_LAMBDA = 14, /* U+039B GREEK CAPITAL LETTER LAMBDA */
GLYPH_DEGREE_UNDERLINE = 15, /* U+00B0 U+0331 */
};
/* A cluster of 4 glyphs. Can be conversion-constructed from a multi-byte

View File

@ -1,11 +1,8 @@
#include <num/num.h>
#include "world.h"
#include <stdlib.h>
#include <string.h>
#include <math.h>
using namespace libnum;
//---
// Jump-flood Voronoi diagram generation
//---
@ -60,9 +57,6 @@ static void voronoi(int8_t *world, int N, WorldCoord *seeds)
// Perlin noise
//---
struct vec2 {
num x, y;
};
static vec2 perlinGradients[16];
GCONSTRUCTOR
@ -186,6 +180,39 @@ WorldCoord WorldCoord::Down(0, 1);
WorldCoord WorldCoord::Left(-1, 0);
WorldCoord WorldCoord::Right(1, 0);
bool World::validSpawn(int x, int y) const
{
if(!this->limits.contains(WorldCoord(x, y)))
return false;
BlockInfo *info = Nooncraft::getBlockInfo(this->cellAt(x, y));
return info && info->walkable;
}
WorldCoord World::findSpawnPoint()
{
int max_dist_x = max(-this->limits.xmin, this->limits.xmax);
int max_dist_y = max(-this->limits.ymin, this->limits.ymax);
int max_dist = max(max_dist_x, max_dist_y);
for(int d = 0; d <= max_dist; d++) {
/* Look for blocks at distance `d` of (0,0) for somewhere spawnable */
for(int x = -d; x <= d; x++) {
if(this->validSpawn(x, -d))
return WorldCoord(x, -d);
if(this->validSpawn(x, +d))
return WorldCoord(x, +d);
}
for(int y = -d; y <= d; y++) {
if(this->validSpawn(-d, y))
return WorldCoord(-d, y);
if(this->validSpawn(+d, y))
return WorldCoord(+d, y);
}
}
return WorldCoord(0, 0);
}
namespace Nooncraft {
World *mkWorld(int width, int height)

View File

@ -1,11 +1,14 @@
// nooncraft.world: World data and structures
#pragma once
#include <num/num.h>
#include "block.h"
#include <stddef.h>
#include <gint/defs/attributes.h>
#include <gint/defs/util.h>
using namespace libnum;
struct WorldCoord
{
int16_t x, y;
@ -23,6 +26,9 @@ struct WorldCoord
this->y -= other.y;
return *this;
}
inline constexpr bool operator==(WorldCoord const &other) {
return this->x == other.x && this->y == other.y;
}
static WorldCoord Up, Down, Left, Right;
};
@ -64,6 +70,11 @@ struct WorldRect
}
};
/* Don't ask me why this is here */
struct vec2 {
num x, y;
};
enum class Biome: uint8_t {
Plains,
Forest,
@ -107,6 +118,9 @@ struct World
return x == worldBorder.xmin || x == worldBorder.xmax
|| y == worldBorder.ymin || y == worldBorder.ymax;
}
bool validSpawn(int x, int y) const;
WorldCoord findSpawnPoint();
};
namespace Nooncraft {