add player animations
This commit is contained in:
parent
74d6ab74e3
commit
b8ae9615b8
|
@ -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;
|
||||
}
|
|
@ -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 */
|
48
src/engine.c
48
src/engine.c
|
@ -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;
|
||||
}
|
||||
|
|
16
src/engine.h
16
src/engine.h
|
@ -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);
|
||||
|
||||
|
|
51
src/main.c
51
src/main.c
|
@ -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,23 +107,36 @@ int main(void)
|
|||
.time = 0,
|
||||
};
|
||||
|
||||
int level_finished = 0;
|
||||
/* Global tick clock */
|
||||
static volatile int tick = 1;
|
||||
|
||||
int t = timer_setup(TIMER_ANY, ENGINE_TICK*1000, 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();
|
||||
stream_mono();
|
||||
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;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue