gameplay: player movement and collisions
This commit is contained in:
parent
845517793a
commit
375bec919e
|
@ -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 |
|
@ -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:
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
};
|
42
src/main.cpp
42
src/main.cpp
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
14
src/world.h
14
src/world.h
|
@ -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 {
|
||||
|
|
Loading…
Reference in New Issue