add player animations

This commit is contained in:
Lephenixnoir 2020-08-21 11:15:33 +02:00
parent 516d9cab00
commit 30ab7bae0a
Signed by: Lephenixnoir
GPG Key ID: 1BBA026E13FC0495
7 changed files with 242 additions and 19 deletions

View File

@ -10,6 +10,7 @@ include(Fxconv)
find_package(Gint 2.1 REQUIRED)
set(SOURCES
src/animation.c
src/main.c
src/engine.c
# ...

Binary file not shown.

103
src/animation.c Normal file
View File

@ -0,0 +1,103 @@
#include <gint/display.h>
#include <gint/defs/util.h>
#include "animation.h"
#include "engine.h"
//---
// Generation of animation frames from sheets
//---
/* struct sheet: Structure of an image with animation frames */
struct sheet
{
/* Source image */
bopti_image_t *img;
/* Width and height of single entry */
int frame_w;
int frame_h;
};
extern bopti_image_t img_spritesheet;
struct sheet const anim_player = {
.img = &img_spritesheet,
.frame_w = 12,
.frame_h = 16,
};
/* anim_frame(): Get a frame from a sheet */
static struct anim_frame anim_frame(struct sheet const *sheet, int col,int row)
{
struct anim_frame f = {
.source = sheet->img,
.left = sheet->frame_w * col,
.top = sheet->frame_h * row,
.w = sheet->frame_w,
.h = sheet->frame_h,
};
return f;
}
void dframe(int x, int y, struct anim_frame const frame)
{
dsubimage(x, y, frame.source, frame.left, frame.top, frame.w, frame.h,
DIMAGE_NONE);
}
//---
// Animation functions
//---
int anim_player_idle(struct anim_data *data, int init)
{
/* Animation initialization; takes [data->dir] as input */
if(init)
{
data->function = anim_player_idle;
data->frame = 0;
data->duration = 500;
}
else
{
data->frame = (data->frame + 1) % 2;
data->duration += 500;
}
data->img = anim_frame(&anim_player, data->dir, data->frame);
data->dx = 0;
data->dy = 0;
return 0;
}
int anim_player_walking(struct anim_data *data, int init)
{
/* Animation initialization; takes [data->dir] as input */
if(init)
{
data->function = anim_player_walking;
data->frame = 0;
data->duration = 50;
int dx = (data->dir == DIR_LEFT) - (data->dir == DIR_RIGHT);
int dy = (data->dir == DIR_UP) - (data->dir == DIR_DOWN);
data->dx = 10 * dx;
data->dy = 10 * dy;
}
else
{
data->dx -= sgn(data->dx);
data->dy -= sgn(data->dy);
/* Animation finishes if both displacements are zero */
if(!data->dx && !data->dy)
{
return anim_player_idle(data, 1);
}
data->frame = (data->frame + 1) % 8;
data->duration += 50;
}
data->img = anim_frame(&anim_player, data->dir + 4, data->frame / 2);
return 1;
}

42
src/animation.h Normal file
View File

@ -0,0 +1,42 @@
#ifndef _MYSTNB_ANIMATION_H
#define _MYSTNB_ANIMATION_H
#include <gint/display.h>
struct anim_data;
/* anim_function_t: Update function for each animation */
typedef int (anim_function_t)(struct anim_data *data, int init);
anim_function_t anim_player_idle;
anim_function_t anim_player_walking;
/* struct anim_frame: Subrectangle of an animation sheet */
struct anim_frame
{
bopti_image_t *source;
int left, top;
int w, h;
};
/* struct anim_data: Data for currently-running animations */
struct anim_data
{
/* ANimation update function */
anim_function_t *function;
/* Frame to draw */
struct anim_frame img;
/* On-screen entity displacement */
int dx, dy;
/* Animation direction */
int dir;
/* Current frame */
int frame;
/* Duration left until next frame; updated by the engine. Animation
function is called when it becomes negative or null */
int duration;
};
/* Draw an animation frame */
void dframe(int x, int y, struct anim_frame const frame);
#endif /* _MYSTNB_ANIMATION_H */

View File

@ -5,16 +5,36 @@
#define CELL_X(x) (-2 + 10 * (x))
#define CELL_Y(y) (-3 + 10 * (y))
//---
// Animations
//---
void engine_tick(struct game *game, int dt)
{
game->time += dt;
/* Update the animations for every player */
for(int p = 0; game->players[p]; p++)
{
struct player *player = game->players[p];
player->anim.duration -= dt;
if(player->anim.duration > 0) continue;
/* Call the animation function to generate the next frame */
player->idle = !player->anim.function(&player->anim, 0);
}
}
//---
// Rendering
//---
static void engine_draw_player(struct player const *player)
{
extern bopti_image_t img_spritesheet;
dsubimage(CELL_X(player->x) - 1, CELL_Y(player->y) - 5,
&img_spritesheet, player->dir * 12, 0, 12, 16, DIMAGE_NONE);
dframe(CELL_X(player->x) - 1 + player->anim.dx,
CELL_Y(player->y) - 5 + player->anim.dy,
player->anim.img);
}
void engine_draw(struct game const *game)
@ -42,15 +62,31 @@ int engine_move(struct game *game, struct player *player, int dir)
{
int dx = (dir == DIR_RIGHT) - (dir == DIR_LEFT);
int dy = (dir == DIR_DOWN) - (dir == DIR_UP);
int olddir = player->dir;
/* Always update the direction */
/* Don't move players that are already moving */
if(!player->idle) return 0;
/* Update the direction */
player->dir = dir;
player->anim.dir = dir;
/* Only move the player if the destination is walkable */
if(!map_walkable(game->map, player->x + dx, player->y + dy)) return 0;
if(!map_walkable(game->map, player->x + dx, player->y + dy))
{
/* If not, set the new idle animation */
if(dir != olddir)
{
player->idle = !anim_player_idle(&player->anim,1);
}
return 0;
}
player->x += dx;
player->y += dy;
/* Set the walking animation */
player->idle = !anim_player_walking(&player->anim, 1);
return 1;
}

View File

@ -2,9 +2,15 @@
#define _MYSTNB_ENGINE_H
#include <stdint.h>
#include <gint/display.h>
#include "animation.h"
/* Maximum number of players */
#define PLAYER_COUNT 1
/* Time per engine tick (ms) */
#define ENGINE_TICK 25
/* Directions */
#define DIR_DOWN 0
#define DIR_RIGHT 1
@ -18,9 +24,10 @@ struct player
int x, y;
/* Direction currently facing */
int dir;
/* Animation and frame */
struct animation const *anim;
int frame;
/* Whether playing is currently idle (ie. can move) */
int idle;
/* Current animation function and data */
struct anim_data anim;
};
/* struct map: A map with moving doors, collectibles, and fog */
@ -45,6 +52,9 @@ struct game
int time;
};
/* Update animations */
void engine_tick(struct game *game, int dt);
/* Draw the current game frame from scratch; does not dupdate() */
void engine_draw(struct game const *game);

View File

@ -1,5 +1,7 @@
#include <gint/display.h>
#include <gint/keyboard.h>
#include <gint/timer.h>
#include <gint/clock.h>
#include "engine.h"
@ -61,11 +63,14 @@ static int main_menu(void)
static int get_inputs(void)
{
int opt = GETKEY_DEFAULT & ~GETKEY_REP_ARROWS;
int timeout = 1;
while(1)
{
int key = getkey_opt(opt, NULL).key;
key_event_t ev = getkey_opt(opt, &timeout);
if(ev.type == KEYEV_NONE) return -1;
int key = ev.key;
if(key == KEY_DOWN) return DIR_DOWN;
if(key == KEY_RIGHT) return DIR_RIGHT;
if(key == KEY_UP) return DIR_UP;
@ -73,14 +78,25 @@ static int get_inputs(void)
}
}
static int callback_tick(volatile int *tick)
{
*tick = 1;
return TIMER_CONTINUE;
}
int main(void)
{
GUNUSED int level = main_menu();
struct player singleplayer = {
.x = 2,
.y = 3
.y = 3,
.dir = DIR_DOWN,
.anim.function = anim_player_idle,
.anim.dir = DIR_DOWN,
};
singleplayer.idle = !anim_player_idle(&singleplayer.anim, 1);
struct map map = {
.w = 13,
.h = 7
@ -91,22 +107,37 @@ int main(void)
.time = 0,
};
int level_finished = 0;
/* Global tick clock */
static volatile int tick = 1;
int t = timer_configure(TIMER_ANY, ENGINE_TICK*1000,
GINT_CALL(callback_tick, &tick));
if(t >= 0) timer_start(t);
int level_finished = 0;
while(!level_finished)
{
int turn_finished = 0;
while(!turn_finished)
{
engine_draw(&game);
dupdate();
while(!tick) sleep();
tick = 0;
int dir = get_inputs();
engine_draw(&game);
dupdate();
int dir = get_inputs();
int turn_finished = 0;
if(dir >= 0)
{
turn_finished = engine_move(&game, &singleplayer, dir);
}
if(turn_finished)
{
/* Update doors, etc */
}
/* Update doors, etc */
engine_tick(&game, ENGINE_TICK);
}
if(t >= 0) timer_stop(t);
return 1;
}