basic smooth controls
This commit is contained in:
parent
a31f56478a
commit
1e9afa5550
|
@ -16,6 +16,7 @@ endif()
|
|||
|
||||
set(SOURCES
|
||||
src/entities.c
|
||||
src/game.c
|
||||
src/geometry.c
|
||||
src/level.c
|
||||
src/main.c
|
||||
|
|
|
@ -1,26 +1,61 @@
|
|||
#include "entities.h"
|
||||
|
||||
void entity_move(entity_t *e, fixed_t dx, fixed_t dy)
|
||||
fpoint_t entity_pos(entity_t const *e)
|
||||
{
|
||||
e->x += dx;
|
||||
e->y += dy;
|
||||
return (fpoint_t){ e->movement.x, e->movement.y };
|
||||
}
|
||||
|
||||
fixed_t entity_distance2(entity_t const *e1, entity_t const *e2)
|
||||
static void update_pos(entity_movement_t *m, fixed_t dt)
|
||||
{
|
||||
fixed_t dx = e2->x - e1->x;
|
||||
fixed_t dy = e2->y - e1->y;
|
||||
|
||||
return fmul(dx, dx) + fmul(dy, dy);
|
||||
m->x += fmul(m->vx, dt);
|
||||
m->y += fmul(m->vy, dt);
|
||||
}
|
||||
|
||||
fpoint_t entity_facing(entity_t const *e)
|
||||
entity_movement_t entity_still(entity_t *e, fixed_t dt)
|
||||
{
|
||||
if(e->type != ENTITY_AGENT) return (fpoint_t){ 0, 0 };
|
||||
entity_movement_t next = e->movement;
|
||||
|
||||
int f = e->agent.facing;
|
||||
return (fpoint_t){
|
||||
.x = ((f == RIGHT) - (f == LEFT)) * fix(1),
|
||||
.y = ((f == DOWN) - (f == UP)) * fix(1),
|
||||
};
|
||||
/* Decrease speed along a quadratic curve */
|
||||
next.vx >>= 1;
|
||||
next.vy >>= 1;
|
||||
next.accel_duration = 0;
|
||||
|
||||
if(next.vx >= -1 && next.vx <= 1) next.vx = 0;
|
||||
if(next.vy >= -1 && next.vy <= 1) next.vy = 0;
|
||||
|
||||
update_pos(&next, dt);
|
||||
return next;
|
||||
}
|
||||
|
||||
entity_movement_t entity_move(entity_t *e, int direction, fixed_t dt)
|
||||
{
|
||||
entity_movement_params_t const *params = e->movement_params;
|
||||
|
||||
entity_movement_t next = e->movement;
|
||||
next.facing = direction;
|
||||
|
||||
/* If direction has changed, reset speed to 0 */
|
||||
if(next.facing != direction) {
|
||||
next.vx = 0;
|
||||
next.vy = 0;
|
||||
next.accel_duration = 0;
|
||||
}
|
||||
|
||||
/* Apply the acceleration curve */
|
||||
next.accel_duration += dt;
|
||||
if(next.accel_duration > params->accel_time)
|
||||
next.accel_duration = params->accel_time;
|
||||
|
||||
fixed_t v = 0;
|
||||
if(params->accel_time > 0) {
|
||||
fixed_t progress = feasein(fdiv(next.accel_duration,params->accel_time));
|
||||
v = fmul(params->max_speed - params->min_speed, progress) + params->min_speed;
|
||||
}
|
||||
|
||||
fpoint_t dir = fdir(next.facing);
|
||||
next.vx = fmul(dir.x, v);
|
||||
next.vy = fmul(dir.y, v);
|
||||
|
||||
update_pos(&next, dt);
|
||||
return next;
|
||||
}
|
||||
|
|
|
@ -6,42 +6,66 @@
|
|||
|
||||
#include "geometry.h"
|
||||
|
||||
/* Entity types */
|
||||
enum {
|
||||
ENTITY_AGENT,
|
||||
ENTITY_PARTICLE,
|
||||
};
|
||||
//---
|
||||
// Agents
|
||||
//---
|
||||
|
||||
typedef struct {
|
||||
/* Entity type */
|
||||
uint16_t type;
|
||||
/* Speed limi */
|
||||
fixed_t max_speed;
|
||||
/* Minimum speed granted immediately when starting a movement */
|
||||
fixed_t min_speed;
|
||||
/* Duration to reach max speed (the curve might be smooth) */
|
||||
fixed_t accel_time;
|
||||
/* Duration to reach min speed */
|
||||
fixed_t decel_time;
|
||||
|
||||
/* Coordinates of the entity's hitbox center on the map */
|
||||
} entity_movement_params_t;
|
||||
|
||||
typedef struct {
|
||||
/* Current position */
|
||||
fixed_t x, y;
|
||||
/* Current speed */
|
||||
fixed_t vx, vy;
|
||||
/* Time spent accelerating */
|
||||
fixed_t accel_duration;
|
||||
/* Current facing */
|
||||
uint8_t facing;
|
||||
|
||||
union {
|
||||
/* ENTITY_AGENT */
|
||||
struct {
|
||||
/* Entity's hitbox */
|
||||
shape_t hitbox;
|
||||
/* Facing */
|
||||
uint8_t facing;
|
||||
} agent;
|
||||
} entity_movement_t;
|
||||
|
||||
/* ENTITY_PARTICLE */
|
||||
struct {
|
||||
fixed_t z;
|
||||
fixed_t vx, vy, vz;
|
||||
} part;
|
||||
};
|
||||
/* Alive agents with hitboxes and movement control */
|
||||
typedef struct {
|
||||
/* Hitbox, centered around (movement.x, movement.y) */
|
||||
shape_t hitbox;
|
||||
/* Current cinematic situation */
|
||||
entity_movement_t movement;
|
||||
/* Cinematic parameters */
|
||||
entity_movement_params_t const *movement_params;
|
||||
|
||||
} entity_t;
|
||||
|
||||
/* Move an entity in the map space, assumes no collisions. */
|
||||
void entity_move(entity_t *e, fixed_t dx, fixed_t dy);
|
||||
/* Entity position */
|
||||
fpoint_t entity_pos(entity_t const *e);
|
||||
|
||||
/* Distance quared between two entities */
|
||||
fixed_t entity_distance2(entity_t const *e1, entity_t const *e2);
|
||||
/* Movement if entity stays still */
|
||||
entity_movement_t entity_still(entity_t *e, fixed_t dt);
|
||||
/* Movement after a normal move (collisions might prevent it) */
|
||||
entity_movement_t entity_move(entity_t *e, int direction, fixed_t dt);
|
||||
/* Position and movement after a dash (collisions might prevent it) */
|
||||
entity_movement_t entity_dash(entity_t *e, int direction, fixed_t dt);
|
||||
|
||||
/* Unit vector in the direction the agent is facing */
|
||||
/* Unit vector in the direction the entity is facing */
|
||||
fpoint_t entity_facing(entity_t const *e);
|
||||
|
||||
//---
|
||||
// Particles
|
||||
//---
|
||||
|
||||
/* Particles with no interaction but visual effects */
|
||||
typedef struct {
|
||||
/* Location and speed */
|
||||
fixed_t x, y;
|
||||
fixed_t vx, vy;
|
||||
|
||||
} particle_t;
|
||||
|
|
25
src/fixed.h
25
src/fixed.h
|
@ -15,11 +15,18 @@ typedef fixed_t map_coord_t;
|
|||
|
||||
static inline fixed_t fmul(fixed_t left, fixed_t right)
|
||||
{
|
||||
/* Could be optimized to use 32x32 -> 64 bit product and xtrct */
|
||||
/* Generally optimized by the compiler to use dmuls.l and xtrct */
|
||||
int64_t p = (int64_t)left * (int64_t)right;
|
||||
return (int32_t)(p >> 16);
|
||||
}
|
||||
|
||||
static inline fixed_t fdiv(fixed_t left, fixed_t right)
|
||||
{
|
||||
/* Pretty slow */
|
||||
uint64_t d = (uint64_t)left << 16;
|
||||
return d / right;
|
||||
}
|
||||
|
||||
static inline fixed_t fix(int constant)
|
||||
{
|
||||
return constant << 16;
|
||||
|
@ -54,3 +61,19 @@ static inline double f2double(fixed_t f)
|
|||
{
|
||||
return (double)f / 65536;
|
||||
}
|
||||
|
||||
static inline fixed_t feasein(fixed_t x)
|
||||
{
|
||||
return fmul(x, x);
|
||||
}
|
||||
|
||||
static inline fixed_t fease(fixed_t x)
|
||||
{
|
||||
if(x <= fix(1) / 2) {
|
||||
return 2 * fmul(x, x);
|
||||
}
|
||||
else {
|
||||
x = fix(1) - x;
|
||||
return fix(1) - 2 * fmul(x, x);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,74 @@
|
|||
#include "game.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
bool game_load(game_t *g, level_t *level)
|
||||
{
|
||||
game_unload(g);
|
||||
|
||||
map_t *m = &g->map;
|
||||
m->width = level->width;
|
||||
m->height = level->height;
|
||||
m->tiles = malloc(m->width * m->height * sizeof *m->tiles);
|
||||
if(!m->tiles) {
|
||||
game_unload(g);
|
||||
return false;
|
||||
}
|
||||
|
||||
for(int y = 0; y < m->height; y++)
|
||||
for(int x = 0; x < m->width; x++) {
|
||||
struct tile *t = map_tile(m, x, y);
|
||||
t->base = level->tiles[level->width * y + x].base;
|
||||
t->decor = level->tiles[level->width * y + x].decor;
|
||||
t->solid = (t->base == 0) || (t->base >= 16);
|
||||
}
|
||||
|
||||
camera_init(&g->camera, m);
|
||||
|
||||
g->entities = NULL;
|
||||
g->entity_count = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
void game_unload(game_t *g)
|
||||
{
|
||||
g->map.width = 0;
|
||||
g->map.height = 0;
|
||||
free(g->map.tiles);
|
||||
|
||||
for(int i = 0; i < g->entity_count; i++)
|
||||
free(g->entities[i]);
|
||||
free(g->entities);
|
||||
}
|
||||
|
||||
void game_add_entity(game_t *g, entity_t *entity)
|
||||
{
|
||||
size_t new_size = (g->entity_count + 1) * sizeof *g->entities;
|
||||
entity_t **new_entities = realloc(g->entities, new_size);
|
||||
if(!new_entities) return;
|
||||
|
||||
g->entities = new_entities;
|
||||
g->entities[g->entity_count] = entity;
|
||||
g->entity_count++;
|
||||
}
|
||||
|
||||
bool game_shape_collides(game_t *g, entity_t const *e, shape_t const *hitbox)
|
||||
{
|
||||
/* Collision against map */
|
||||
if(map_entity_collides(&g->map, hitbox))
|
||||
return true;
|
||||
|
||||
/* Collision against entities */
|
||||
for(int i = 0; i < g->entity_count; i++) {
|
||||
entity_t *other = g->entities[i];
|
||||
if(other == e) continue;
|
||||
|
||||
shape_t other_hitbox = other->hitbox;
|
||||
shape_translate(&other_hitbox, entity_pos(other));
|
||||
|
||||
if(shape_collide(hitbox, &other_hitbox))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
//---
|
||||
// game: Static and dynamic information relating to an unfolding game
|
||||
//---
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "map.h"
|
||||
#include "entities.h"
|
||||
#include "render.h"
|
||||
#include "level.h"
|
||||
|
||||
typedef struct game {
|
||||
/* The map's coordinate system is the primary coordinate system in all of
|
||||
this game's code */
|
||||
map_t map;
|
||||
/* User's camera */
|
||||
camera_t camera;
|
||||
/* List of entities */
|
||||
entity_t **entities;
|
||||
int entity_count;
|
||||
/* Player; this must be one of the entities loaded in the game */
|
||||
entity_t *player;
|
||||
|
||||
} game_t;
|
||||
|
||||
/* Allocate resources to load a level. */
|
||||
bool game_load(game_t *g, level_t *level);
|
||||
|
||||
/* Free resources allocated for the level. */
|
||||
void game_unload(game_t *g);
|
||||
|
||||
/* Add an entity to the game. */
|
||||
void game_add_entity(game_t *g, entity_t *e);
|
||||
|
||||
/* Check whether an entity would collide if it had the given hitbox. */
|
||||
bool game_shape_collides(game_t *g, entity_t const *e, shape_t const *hitbox);
|
|
@ -65,6 +65,23 @@ ipoint_t point_f2i(fpoint_t);
|
|||
irect_t rect_f2i(frect_t);
|
||||
icircle_t circle_f2i(fcircle_t);
|
||||
|
||||
/* Distance-squared between two points */
|
||||
static inline fixed_t dist2(fpoint_t p1, fpoint_t p2)
|
||||
{
|
||||
fixed_t dx = p2.x - p1.x;
|
||||
fixed_t dy = p2.y - p1.y;
|
||||
return fmul(dx, dx) + fmul(dy, dy);
|
||||
}
|
||||
|
||||
/* Unit vector in the specified direction */
|
||||
static inline fpoint_t fdir(int dir)
|
||||
{
|
||||
return (fpoint_t){
|
||||
.x = fix((dir == RIGHT) - (dir == LEFT)),
|
||||
.y = fix((dir == DOWN) - (dir == UP)),
|
||||
};
|
||||
}
|
||||
|
||||
//---
|
||||
// General shapes
|
||||
//---
|
||||
|
|
138
src/main.c
138
src/main.c
|
@ -3,6 +3,7 @@
|
|||
#include "render.h"
|
||||
#include "geometry.h"
|
||||
#include "entities.h"
|
||||
#include "game.h"
|
||||
|
||||
#include <gint/display.h>
|
||||
#include <gint/keyboard.h>
|
||||
|
@ -24,12 +25,6 @@ static bool getkey_global_shortcuts(key_event_t e)
|
|||
return false;
|
||||
}
|
||||
|
||||
#define _ C_BLACK,
|
||||
#define R C_RED,
|
||||
#define G C_GREEN,
|
||||
#define B C_BLUE,
|
||||
#define Y C_RGB(16,16,16),
|
||||
|
||||
extern level_t lv_demo;
|
||||
|
||||
int main(void)
|
||||
|
@ -42,33 +37,29 @@ int main(void)
|
|||
usb_interface_t const *interfaces[] = { &usb_ff_bulk, NULL };
|
||||
usb_open(interfaces, GINT_CALL_NULL);
|
||||
|
||||
//---
|
||||
// Load map
|
||||
//---
|
||||
game_t game = { 0 };
|
||||
map_t *m = &game.map;
|
||||
camera_t *c = &game.camera;
|
||||
|
||||
struct map *m = malloc(sizeof *m);
|
||||
m->width = lv_demo.width;
|
||||
m->height = lv_demo.height;
|
||||
m->entities = NULL;
|
||||
m->entity_count = 0;
|
||||
m->tiles = malloc(m->width * m->height * sizeof *m->tiles);
|
||||
|
||||
for(int y = 0; y < m->height; y++)
|
||||
for(int x = 0; x < m->width; x++) {
|
||||
struct tile *t = map_tile(m, x, y);
|
||||
t->base = lv_demo.tiles[lv_demo.width * y + x].base;
|
||||
t->decor = lv_demo.tiles[lv_demo.width * y + x].decor;
|
||||
t->solid = (t->base == 0) || (t->base >= 16);
|
||||
}
|
||||
|
||||
struct camera camera;
|
||||
struct camera *c = &camera;
|
||||
camera_init(c, m);
|
||||
game_load(&game, &lv_demo);
|
||||
|
||||
//---
|
||||
// Load entities on the map
|
||||
// Load random entities on the map
|
||||
//---
|
||||
|
||||
entity_movement_params_t emp_none = {
|
||||
.min_speed = fix(0),
|
||||
.max_speed = fix(0),
|
||||
.accel_time = fix(0),
|
||||
.decel_time = fix(0),
|
||||
};
|
||||
entity_movement_params_t emp_player = {
|
||||
.min_speed = PLAYER_SPEED / 4,
|
||||
.max_speed = PLAYER_SPEED,
|
||||
.accel_time = fix(1) / 4,
|
||||
.decel_time = fix(1) / 4,
|
||||
};
|
||||
|
||||
for(int i = 0; i < 10; i++) {
|
||||
entity_t *e = malloc(sizeof *e);
|
||||
if(!e) continue;
|
||||
|
@ -81,30 +72,34 @@ int main(void)
|
|||
struct tile *t = map_tile(m, x, y);
|
||||
if(t && !t->solid) break;
|
||||
}
|
||||
e->type = ENTITY_AGENT;
|
||||
e->x = fix(x) + fix(1) / 2;
|
||||
e->y = fix(y) + fix(1) / 2;
|
||||
e->movement.x = fix(x) + fix(1) / 2;
|
||||
e->movement.y = fix(y) + fix(1) / 2;
|
||||
e->movement.vx = 0;
|
||||
e->movement.vy = 0;
|
||||
e->movement.facing = rand() % 4;
|
||||
e->movement.accel_duration = 0;
|
||||
e->movement_params = &emp_none;
|
||||
|
||||
if(rand() % 2) {
|
||||
e->agent.hitbox = (shape_t){ SHAPE_RECT,
|
||||
.rect = { .l=-fix(3)/8, .r=fix(3)/8, .t=-fix(3)/8, .b=fix(3)/8 },
|
||||
e->hitbox = (shape_t){ SHAPE_RECT,
|
||||
.rect = { .l=-fix(1)/4, .r=fix(1)/4, .t=-fix(1)/4, .b=fix(1)/4 },
|
||||
.color = C_GREEN,
|
||||
};
|
||||
e->agent.facing = rand() % 4;
|
||||
}
|
||||
else {
|
||||
e->agent.hitbox = (shape_t){ SHAPE_CIRCLE,
|
||||
.circle = { .x=0, .y=0, .r=fix(3)/8 },
|
||||
e->hitbox = (shape_t){ SHAPE_CIRCLE,
|
||||
.circle = { .x=0, .y=0, .r=fix(1)/4 },
|
||||
.color = C_GREEN,
|
||||
};
|
||||
e->agent.facing = rand() % 4;
|
||||
}
|
||||
|
||||
map_add_entity(m, e);
|
||||
game_add_entity(&game, e);
|
||||
}
|
||||
|
||||
entity_t *player = m->entities[5];
|
||||
player->agent.hitbox.color = C_BLUE;
|
||||
entity_t *player = game.entities[5];
|
||||
game.player = player;
|
||||
player->hitbox.color = C_BLUE;
|
||||
player->movement_params = &emp_player;
|
||||
|
||||
//---
|
||||
// Main loop
|
||||
|
@ -132,7 +127,7 @@ int main(void)
|
|||
|
||||
time_render = prof_exec({
|
||||
dclear(C_RGB(2,1,1));
|
||||
render_map(m, c, player);
|
||||
render_game(&game);
|
||||
dprint(1, 1, C_WHITE, "Frame: %.3j ms", time_render);
|
||||
dupdate();
|
||||
});
|
||||
|
@ -176,52 +171,44 @@ int main(void)
|
|||
}
|
||||
else lock_optn = false;
|
||||
|
||||
/* Player speed */
|
||||
vx = PLAYER_SPEED_X;
|
||||
vy = PLAYER_SPEED_Y;
|
||||
|
||||
/* Player movement */
|
||||
fixed_t dx = 0;
|
||||
fixed_t dy = 0;
|
||||
int new_facing = player->agent.facing;
|
||||
int dir = -1;
|
||||
if(keydown(KEY_UP)) dir = UP;
|
||||
if(keydown(KEY_DOWN)) dir = DOWN;
|
||||
if(keydown(KEY_LEFT)) dir = LEFT;
|
||||
if(keydown(KEY_RIGHT)) dir = RIGHT;
|
||||
|
||||
if(keydown(KEY_UP)) {
|
||||
dy -= fmul(dt, vy);
|
||||
new_facing = UP;
|
||||
}
|
||||
if(keydown(KEY_DOWN)) {
|
||||
dy += fmul(dt, vy);
|
||||
new_facing = DOWN;
|
||||
}
|
||||
if(keydown(KEY_LEFT)) {
|
||||
dx -= fmul(dt, vx);
|
||||
new_facing = LEFT;
|
||||
}
|
||||
if(keydown(KEY_RIGHT)) {
|
||||
dx += fmul(dt, vx);
|
||||
new_facing = RIGHT;
|
||||
}
|
||||
entity_movement_t next = (dir >= 0)
|
||||
? entity_move(player, dir, dt)
|
||||
: entity_still(player, dt);
|
||||
|
||||
entity_move(player, dx, dy);
|
||||
if(map_entity_collides(m, player))
|
||||
entity_move(player, -dx, -dy);
|
||||
shape_t player_hitbox = player->hitbox;
|
||||
shape_translate(&player_hitbox, (fpoint_t){ next.x, next.y });
|
||||
|
||||
player->agent.facing = new_facing;
|
||||
if(!game_shape_collides(&game, player, &player_hitbox)) {
|
||||
player->movement = next;
|
||||
}
|
||||
else {
|
||||
player->movement.facing = next.facing;
|
||||
player->movement.vx = fix(0);
|
||||
player->movement.vy = fix(0);
|
||||
player->movement.accel_duration = fix(0);
|
||||
}
|
||||
|
||||
/* Attack */
|
||||
if(keydown(KEY_SHIFT) && !lock_shift) {
|
||||
//~ Attacks
|
||||
fixed_t reach2 = fmul(ATTACK_REACH, ATTACK_REACH);
|
||||
fixed_t angle_cos2 = fmul(ATTACK_ANGLE_COS, ATTACK_ANGLE_COS);
|
||||
fpoint_t dir = entity_facing(player);
|
||||
fpoint_t dir = fdir(player->movement.facing);
|
||||
|
||||
for(int i = 0; i < m->entity_count; i++) {
|
||||
entity_t *e = m->entities[i];
|
||||
if(e->type != ENTITY_AGENT) continue;
|
||||
for(int i = 0; i < game.entity_count; i++) {
|
||||
entity_t *e = game.entities[i];
|
||||
if(e == player) continue;
|
||||
|
||||
/* Distance to player */
|
||||
fixed_t dx = e->x - player->x;
|
||||
fixed_t dy = e->y - player->y;
|
||||
fixed_t dx = e->movement.x - player->movement.x;
|
||||
fixed_t dy = e->movement.y - player->movement.y;
|
||||
fixed_t dist2 = fmul(dx, dx) + fmul(dy, dy);
|
||||
if(dist2 > reach2) continue;
|
||||
|
||||
|
@ -231,7 +218,7 @@ int main(void)
|
|||
continue;
|
||||
|
||||
/* Inflict damage */
|
||||
e->agent.hitbox.color = C_RED;
|
||||
e->hitbox.color = C_RED;
|
||||
}
|
||||
//~
|
||||
}
|
||||
|
@ -239,7 +226,6 @@ int main(void)
|
|||
}
|
||||
|
||||
timer_stop(timer_id);
|
||||
map_destroy(m);
|
||||
prof_quit();
|
||||
usb_close();
|
||||
return 1;
|
||||
|
|
47
src/map.c
47
src/map.c
|
@ -12,7 +12,7 @@ void tile_shape(struct tile const *tile, shape_t *s)
|
|||
s->rect.b = fix(1)/2;
|
||||
}
|
||||
|
||||
struct tile *map_tile(struct map *m, int x, int y)
|
||||
struct tile *map_tile(map_t const *m, int x, int y)
|
||||
{
|
||||
if(x < 0 || x >= m->width || y < 0 || y >= m->height)
|
||||
return NULL;
|
||||
|
@ -20,28 +20,10 @@ struct tile *map_tile(struct map *m, int x, int y)
|
|||
return &m->tiles[y * m->width + x];
|
||||
}
|
||||
|
||||
void map_add_entity(struct map *m, entity_t *e)
|
||||
bool map_entity_collides(map_t *m, shape_t const *hitbox)
|
||||
{
|
||||
size_t new_size = (m->entity_count + 1) * sizeof *m->entities;
|
||||
entity_t **new_entities = realloc(m->entities, new_size);
|
||||
if(!new_entities) return;
|
||||
|
||||
m->entities = new_entities;
|
||||
m->entities[m->entity_count] = e;
|
||||
m->entity_count++;
|
||||
}
|
||||
|
||||
bool map_entity_collides(struct map *m, entity_t *e)
|
||||
{
|
||||
shape_t entity_hitbox;
|
||||
shape_t tile_hitbox;
|
||||
|
||||
if(e->type == ENTITY_PARTICLE) return false;
|
||||
|
||||
/* Determine the entity hitbox */
|
||||
entity_hitbox = e->agent.hitbox;
|
||||
shape_translate(&entity_hitbox, (fpoint_t){ e->x, e->y });
|
||||
|
||||
/* Collisions against walls and static objects */
|
||||
for(int y = 0; y < m->height; y++)
|
||||
for(int x = 0; x < m->width; x++) {
|
||||
|
@ -52,32 +34,9 @@ bool map_entity_collides(struct map *m, entity_t *e)
|
|||
fpoint_t center = { fix(x) + fix(1)/2, fix(y) + fix(1)/2 };
|
||||
shape_translate(&tile_hitbox, center);
|
||||
|
||||
if(shape_collide(&entity_hitbox, &tile_hitbox))
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Collision against entities */
|
||||
for(int i = 0; i < m->entity_count; i++) {
|
||||
entity_t *other = m->entities[i];
|
||||
if(other == e || other->type == ENTITY_PARTICLE) continue;
|
||||
|
||||
shape_t other_hitbox = other->agent.hitbox;
|
||||
shape_translate(&other_hitbox, (fpoint_t){ other->x, other->y });
|
||||
|
||||
if(shape_collide(&entity_hitbox, &other_hitbox))
|
||||
if(shape_collide(hitbox, &tile_hitbox))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void map_destroy(struct map *m)
|
||||
{
|
||||
free(m->tiles);
|
||||
|
||||
for(int i = 0; i < m->entity_count; i++)
|
||||
free(m->entities[i]);
|
||||
free(m->entities);
|
||||
|
||||
free(m);
|
||||
}
|
||||
|
|
20
src/map.h
20
src/map.h
|
@ -40,24 +40,16 @@ void tile_shape(struct tile const *tile, shape_t *s);
|
|||
// Map grid, tiles location in space, and entities
|
||||
//---
|
||||
|
||||
struct map {
|
||||
typedef struct {
|
||||
/* Dimensions, columns are 0 to width-1, rows are 0 to height-1 */
|
||||
int width, height;
|
||||
/* All tiles */
|
||||
struct tile *tiles;
|
||||
/* Entities on the map */
|
||||
entity_t **entities;
|
||||
int entity_count;
|
||||
};
|
||||
|
||||
} map_t;
|
||||
|
||||
/* Get a pointer to the tile at (x,y) in map; NULL if out of bounds. */
|
||||
struct tile *map_tile(struct map *m, int x, int y);
|
||||
struct tile *map_tile(map_t const *m, int x, int y);
|
||||
|
||||
/* Add an entity to the map. */
|
||||
void map_add_entity(struct map *m, entity_t *e);
|
||||
|
||||
/* Check whether an entity collides with the map. */
|
||||
bool map_entity_collides(struct map *m, entity_t *e);
|
||||
|
||||
/* Free resources associated with the map. */
|
||||
void map_destroy(struct map *m);
|
||||
/* Check whether a hitbox collides with the map. */
|
||||
bool map_entity_collides(map_t *m, shape_t const *hitbox);
|
||||
|
|
43
src/render.c
43
src/render.c
|
@ -1,11 +1,13 @@
|
|||
#include "render.h"
|
||||
#include "game.h"
|
||||
|
||||
#include <gint/display.h>
|
||||
|
||||
//---
|
||||
// Camera management
|
||||
//---
|
||||
|
||||
void camera_init(struct camera *c, struct map *m)
|
||||
void camera_init(camera_t *c, map_t const *m)
|
||||
{
|
||||
c->zoom = 1;
|
||||
|
||||
|
@ -27,7 +29,7 @@ void camera_init(struct camera *c, struct map *m)
|
|||
c->y = (c->limits.y_min + c->limits.y_max) / 2;
|
||||
}
|
||||
|
||||
ipoint_t camera_map2screen(struct camera *c, fpoint_t p)
|
||||
ipoint_t camera_map2screen(camera_t const *c, fpoint_t p)
|
||||
{
|
||||
return (ipoint_t){
|
||||
.x = DWIDTH / 2 + fround((p.x - c->x) * TILE_WIDTH * c->zoom),
|
||||
|
@ -35,7 +37,7 @@ ipoint_t camera_map2screen(struct camera *c, fpoint_t p)
|
|||
};
|
||||
}
|
||||
/* Translate screen coordinates to map coordinates */
|
||||
fpoint_t camera_screen2map(struct camera *c, ipoint_t p)
|
||||
fpoint_t camera_screen2map(camera_t const *c, ipoint_t p)
|
||||
{
|
||||
return (fpoint_t){
|
||||
.x = c->x + fix(p.x - DWIDTH / 2) / TILE_WIDTH / c->zoom,
|
||||
|
@ -44,7 +46,7 @@ fpoint_t camera_screen2map(struct camera *c, ipoint_t p)
|
|||
}
|
||||
|
||||
/* Lock the camera at the center if set by settings. */
|
||||
static bool camera_lock(struct camera *c)
|
||||
static bool camera_lock(camera_t *c)
|
||||
{
|
||||
if(c->zoom == 1 && CAMERA_LOCK_AT_x1)
|
||||
{
|
||||
|
@ -56,7 +58,7 @@ static bool camera_lock(struct camera *c)
|
|||
}
|
||||
|
||||
/* Bound the camera to the map limits */
|
||||
static void camera_bound(struct camera *c)
|
||||
static void camera_bound(camera_t *c)
|
||||
{
|
||||
/* Project top left and bottom-right corners of viewport onto the map */
|
||||
ipoint_t v_tl = { c->viewport.x_min, c->viewport.y_min };
|
||||
|
@ -76,7 +78,7 @@ static void camera_bound(struct camera *c)
|
|||
c->y += (c->limits.y_max - m_br.y);
|
||||
}
|
||||
|
||||
void camera_move(struct camera *c, map_coord_t dx, map_coord_t dy)
|
||||
void camera_move(camera_t *c, map_coord_t dx, map_coord_t dy)
|
||||
{
|
||||
if(camera_lock(c)) return;
|
||||
c->x += dx;
|
||||
|
@ -84,7 +86,7 @@ void camera_move(struct camera *c, map_coord_t dx, map_coord_t dy)
|
|||
camera_bound(c);
|
||||
}
|
||||
|
||||
void camera_zoom(struct camera *c, int zoom)
|
||||
void camera_zoom(camera_t *c, int zoom)
|
||||
{
|
||||
if(zoom < ZOOM_MIN) zoom = ZOOM_MIN;
|
||||
if(zoom > ZOOM_MAX) zoom = ZOOM_MAX;
|
||||
|
@ -94,7 +96,7 @@ void camera_zoom(struct camera *c, int zoom)
|
|||
camera_bound(c);
|
||||
}
|
||||
|
||||
fixed_t camera_ppu(struct camera *c)
|
||||
fixed_t camera_ppu(camera_t const *c)
|
||||
{
|
||||
/* Since this assumes isotropic space we can use TILE_WIDTH or TILE_HEIGHT
|
||||
indifferently (they're assumed equal) */
|
||||
|
@ -107,11 +109,10 @@ fixed_t camera_ppu(struct camera *c)
|
|||
|
||||
/* TODO: render_map(): Split into render_game() which takes the game
|
||||
structure as input (including the player) */
|
||||
void render_map(struct map *m, struct camera *c, entity_t *player)
|
||||
void render_map(map_t const *m, camera_t const *c)
|
||||
{
|
||||
extern bopti_image_t img_tileset_base;
|
||||
extern bopti_image_t img_tileset_decor;
|
||||
extern bopti_image_t img_spritesheet;
|
||||
|
||||
/* Render floor and walls */
|
||||
for(int row = 0; row < m->height; row++)
|
||||
|
@ -131,16 +132,24 @@ void render_map(struct map *m, struct camera *c, entity_t *player)
|
|||
TILE_WIDTH * (t->decor % 16), TILE_HEIGHT * (t->decor / 16),
|
||||
TILE_WIDTH, TILE_HEIGHT, DIMAGE_NONE);
|
||||
}
|
||||
}
|
||||
|
||||
void render_game(game_t const *g)
|
||||
{
|
||||
extern bopti_image_t img_spritesheet;
|
||||
camera_t const *c = &g->camera;
|
||||
entity_t const *player = g->player;
|
||||
|
||||
render_map(&g->map, &g->camera);
|
||||
|
||||
/* Render entities */
|
||||
for(int i = 0; i < m->entity_count; i++) {
|
||||
entity_t *e = m->entities[i];
|
||||
if(e->type == ENTITY_PARTICLE) continue;
|
||||
for(int i = 0; i < g->entity_count; i++) {
|
||||
entity_t *e = g->entities[i];
|
||||
|
||||
ipoint_t p = camera_map2screen(c, (fpoint_t){ e->x, e->y });
|
||||
ipoint_t p = camera_map2screen(c, entity_pos(e));
|
||||
|
||||
/* Translate entity hitbox to the map coordinate system */
|
||||
shape_t s = e->agent.hitbox;
|
||||
shape_t s = e->hitbox;
|
||||
shape_scale(&s, camera_ppu(c));
|
||||
shape_translate(&s, point_i2f(p));
|
||||
shape_draw(&s);
|
||||
|
@ -151,7 +160,7 @@ void render_map(struct map *m, struct camera *c, entity_t *player)
|
|||
}
|
||||
|
||||
/* Render player */
|
||||
ipoint_t pp = camera_map2screen(c, (fpoint_t){ player->x, player->y });
|
||||
ipoint_t pp = camera_map2screen(c, entity_pos(player));
|
||||
dsubimage(pp.x-8, pp.y-8, &img_spritesheet,
|
||||
16 * player->agent.facing, 0, 16, 16, DIMAGE_NONE);
|
||||
16 * player->movement.facing, 0, 16, 16, DIMAGE_NONE);
|
||||
}
|
||||
|
|
23
src/render.h
23
src/render.h
|
@ -13,7 +13,7 @@
|
|||
// Camera management
|
||||
//---
|
||||
|
||||
struct camera {
|
||||
typedef struct {
|
||||
/* Current zoom level */
|
||||
int zoom;
|
||||
/* Boundaries of the viewable space, in map coordinates */
|
||||
|
@ -30,25 +30,26 @@ struct camera {
|
|||
map_coord_t width, height;
|
||||
/* Current center point (position of center of screen in the map) */
|
||||
map_coord_t x, y;
|
||||
};
|
||||
|
||||
} camera_t;
|
||||
|
||||
/* Initialize camera to look at the middle of the map, and set boundaries. */
|
||||
void camera_init(struct camera *c, struct map *m);
|
||||
void camera_init(camera_t *c, map_t const *m);
|
||||
|
||||
/* Translate map coordinates to pixel coordinates on screen. */
|
||||
ipoint_t camera_map2screen(struct camera *c, fpoint_t map_point);
|
||||
ipoint_t camera_map2screen(camera_t const *c, fpoint_t map_point);
|
||||
|
||||
/* Translate screen coordinates to map coordinates. */
|
||||
fpoint_t camera_screen2map(struct camera *c, ipoint_t screen_point);
|
||||
fpoint_t camera_screen2map(camera_t const *c, ipoint_t screen_point);
|
||||
|
||||
/* Move camera by a distance in map coordinates. */
|
||||
void camera_move(struct camera *c, map_coord_t dx, map_coord_t dy);
|
||||
void camera_move(camera_t *c, map_coord_t dx, map_coord_t dy);
|
||||
|
||||
/* Set the zoom level of the camera. */
|
||||
void camera_zoom(struct camera *c, int zoom);
|
||||
void camera_zoom(camera_t *c, int zoom);
|
||||
|
||||
/* Number of pixels per unit of map. Assumes isotropic space. */
|
||||
fixed_t camera_ppu(struct camera *c);
|
||||
fixed_t camera_ppu(camera_t const *c);
|
||||
|
||||
//---
|
||||
// Rendering
|
||||
|
@ -57,4 +58,8 @@ fixed_t camera_ppu(struct camera *c);
|
|||
/* Render map for the specified camera.
|
||||
TODO: render_map: Honor zoom
|
||||
TODO: render_map: Clip around viewport */
|
||||
void render_map(struct map *m, struct camera *c, entity_t *player);
|
||||
void render_map(map_t const *m, camera_t const *c);
|
||||
|
||||
/* Render game full-screen. */
|
||||
struct game;
|
||||
void render_game(struct game const *g);
|
||||
|
|
|
@ -26,8 +26,7 @@
|
|||
/* Ideal frame rate (FPS) */
|
||||
#define FRAME_RATE 30
|
||||
/* Player speed in tiles per second */
|
||||
#define PLAYER_SPEED_X fix(3)
|
||||
#define PLAYER_SPEED_Y fix(3)
|
||||
#define PLAYER_SPEED fix(4)
|
||||
/* Player reach for attacks, in tiles */
|
||||
#define ATTACK_REACH fix(1)
|
||||
/* Width of the cone where attacks reach, in cosine */
|
||||
|
|
Loading…
Reference in New Issue