Compare commits

...

3 Commits

Author SHA1 Message Date
En Yi 0c540d5053 Move frame counter out of sprite component
This allows individual animation
2023-11-04 20:32:29 +08:00
En Yi 97f7afc401 Add more particle effects
Changelog:
- Add a dedicated tile destroy function
    Similar reason to destroy entity
- Add more sprites for particles
2023-11-04 17:05:54 +08:00
En Yi ebecc68941 Add better support for particle system
Changelog:
- Move sprite to emitter itself to allow better reusing emitter config
- Dedicate a function for destroying entity. This is helpful for dealing
  with events that only occurs during entity destruction
2023-11-04 14:36:59 +08:00
12 changed files with 138 additions and 56 deletions

View File

@ -31,6 +31,7 @@ int main(void)
add_font(&assets, "testfont", "res/test_font.ttf");
Font* fnt = get_font(&assets, "testfont");
int current_frame = 0;
while(!WindowShouldClose())
{
if (IsKeyReleased(KEY_C))
@ -43,16 +44,16 @@ int main(void)
DrawTextEx(*fnt, "Press C to play a sound", (Vector2){64, 64}, 24, 1, RED);
// Draw the static Sprite and animated Sprite
draw_sprite(spr, (Vector2){64,128}, 0.0f, false);
draw_sprite(spr2, (Vector2){64,180}, 0.0f, true);
draw_sprite(spr, 0, (Vector2){64,128}, 0.0f, false);
draw_sprite(spr2, current_frame, (Vector2){64,180}, 0.0f, true);
EndDrawing();
// Update the animated Sprite
spr2->elapsed++;
if (spr2->elapsed == spr2->speed)
{
spr2->current_frame++;
spr2->current_frame %= spr2->frame_count;
current_frame++;
current_frame %= spr2->frame_count;
spr2->elapsed = 0;
}
}

View File

@ -185,7 +185,6 @@ typedef struct Sprite {
Vector2 origin;
Vector2 anchor;
int frame_count;
int current_frame;
int elapsed;
int speed;
char* name;
@ -205,6 +204,7 @@ typedef struct _CSprite_t {
bool flip_x;
bool flip_y;
bool pause;
int current_frame;
} CSprite_t;
typedef struct _CMoveable_t {
@ -229,6 +229,7 @@ struct Entity {
unsigned long components[N_COMPONENTS];
EntityManager_t* manager;
bool m_alive;
bool m_active;
};
enum EntityUpdateEvent

View File

@ -147,7 +147,6 @@ EmitterConfig_t* add_emitter_conf(Assets_t* assets, const char* name, Sprite_t*
uint8_t emitter_idx = n_loaded[5];
assert(emitter_idx < MAX_EMITTER_CONF);
memset(emitter_confs + emitter_idx, 0, sizeof(EmitterConfData_t));
emitter_confs[emitter_idx].conf.spr = sprite;
strncpy(emitter_confs[emitter_idx].name, name, MAX_NAME_LEN);
sc_map_put_s64(&assets->m_emitter_confs, emitter_confs[emitter_idx].name, emitter_idx);
n_loaded[5]++;
@ -462,10 +461,10 @@ LevelPack_t* get_level_pack(Assets_t* assets, const char* name)
return NULL;
}
void draw_sprite(Sprite_t* spr, Vector2 pos, float rotation, bool flip_x)
void draw_sprite(Sprite_t* spr, int frame_num, Vector2 pos, float rotation, bool flip_x)
{
Rectangle rec = {
spr->origin.x + spr->frame_size.x * spr->current_frame,
spr->origin.x + spr->frame_size.x * frame_num,
spr->origin.y,
spr->frame_size.x * (flip_x ? -1:1),
spr->frame_size.y

View File

@ -68,7 +68,7 @@ Sound* get_sound(Assets_t* assets, const char* name);
Font* get_font(Assets_t* assets, const char* name);
LevelPack_t* get_level_pack(Assets_t* assets, const char* name);
void draw_sprite(Sprite_t* spr, Vector2 pos, float rotation, bool flip_x);
void draw_sprite(Sprite_t* spr, int frame_num, Vector2 pos, float rotation, bool flip_x);
typedef struct SFX
{

View File

@ -121,7 +121,7 @@ void draw_particle_system(ParticleSystem_t* system)
{
if (part->alive)
{
if (emitter->config->spr == NULL)
if (emitter->spr == NULL)
{
Rectangle rect = {
.x = part->position.x,
@ -137,7 +137,7 @@ void draw_particle_system(ParticleSystem_t* system)
}
else
{
draw_sprite(emitter->config->spr, part->position, part->rotation, false);
draw_sprite(emitter->spr, 0, part->position, part->rotation, false);
}
}
}

View File

@ -32,13 +32,13 @@ typedef struct EmitterConfig
float speed_range[2];
uint32_t particle_lifetime[2];
PartEmitterType_t type;
Sprite_t* spr;
bool one_shot;
}EmitterConfig_t;
typedef struct ParticleEmitter
{
const EmitterConfig_t* config;
Sprite_t* spr;
Vector2 position;
Particle_t particles[MAX_PARTICLES];
uint32_t n_particles;

View File

@ -58,7 +58,6 @@ int main(void)
.origin = (Vector2){0, 0},
.anchor = (Vector2){tex.width / 2, tex.height / 2},
.frame_count = 0,
.current_frame = 0,
.elapsed = 0,
.speed = 0,
.name = "test_spr"
@ -69,13 +68,13 @@ int main(void)
.launch_range = {0, 360},
.speed_range = {400, 2000},
.particle_lifetime = {30, 110},
.spr = (tex.width == 0) ? NULL : &spr,
};
ParticleEmitter_t emitter = {
.config = &conf,
.n_particles = MAX_PARTICLES,
.update_func = &simple_particle_system_update
.update_func = &simple_particle_system_update,
.spr = (tex.width == 0) ? NULL : &spr,
};
bool key_press = false;

View File

@ -68,7 +68,7 @@ int main(void)
scenes[0] = &scene.scene;
change_scene(&engine, 0);
EmitterConfig_t* conf = add_emitter_conf(&engine.assets, "pe_wood", get_sprite(&engine.assets, "bomb"));
EmitterConfig_t* conf = add_emitter_conf(&engine.assets, "pe_burst", get_sprite(&engine.assets, "bomb"));
conf->launch_range[0] = 240;
conf->launch_range[1] = 300;
conf->one_shot = true;

View File

@ -225,7 +225,7 @@ static void render_editor_game_scene(Scene_t* scene)
uint8_t tile_sprite_idx = tilemap.tiles[i].tile_type + tilemap.tiles[i].rotation;
if (data->tile_sprites[tile_sprite_idx] != NULL)
{
draw_sprite(data->tile_sprites[tile_sprite_idx], (Vector2){x,y}, 0.0f, false);
draw_sprite(data->tile_sprites[tile_sprite_idx], 0, (Vector2){x,y}, 0.0f, false);
}
else if (tilemap.tiles[i].tile_type == SOLID_TILE)
{
@ -429,7 +429,7 @@ static void render_editor_game_scene(Scene_t* scene)
if (spr.sprite != NULL)
{
Vector2 pos = Vector2Add(p_ct->position, spr.offset);
draw_sprite(spr.sprite, pos, 0.0f, p_cspr->flip_x);
draw_sprite(spr.sprite, p_cspr->current_frame, pos, 0.0f, p_cspr->flip_x);
}
}
}

View File

@ -131,7 +131,7 @@ static void render_regular_game_scene(Scene_t* scene)
if (data->tile_sprites[tilemap.tiles[i].tile_type] != NULL)
{
draw_sprite(data->tile_sprites[tilemap.tiles[i].tile_type], (Vector2){x,y}, 0.0f, false);
draw_sprite(data->tile_sprites[tilemap.tiles[i].tile_type], 0, (Vector2){x,y}, 0.0f, false);
}
else if (tilemap.tiles[i].tile_type == SOLID_TILE)
{
@ -223,7 +223,7 @@ static void render_regular_game_scene(Scene_t* scene)
if (spr.sprite != NULL)
{
Vector2 pos = Vector2Add(p_ct->position, spr.offset);
draw_sprite(spr.sprite, pos, 0.0f, p_cspr->flip_x);
draw_sprite(spr.sprite, p_cspr->current_frame, pos, 0.0f, p_cspr->flip_x);
}
continue;
}

View File

@ -29,6 +29,50 @@ static inline unsigned int get_tile_idx(int x, int y, unsigned int tilemap_width
return tile_y * tilemap_width + tile_x;
}
static inline void destroy_tile(LevelSceneData_t* lvl_data, unsigned int tile_idx)
{
Scene_t* scene = &CONTAINER_OF(lvl_data, LevelScene_t, data)->scene;
TileGrid_t tilemap = lvl_data->tilemap;
Sprite_t* spr = NULL;
switch (tilemap.tiles[tile_idx].tile_type)
{
case LADDER:
spr = get_sprite(&scene->engine->assets, "p_ladder");
break;
case ONEWAY_TILE:
spr = get_sprite(&scene->engine->assets, "p_wood");
break;
case SPIKES:
spr = get_sprite(&scene->engine->assets, "p_spike");
break;
default:
break;
}
if (spr != NULL)
{
ParticleEmitter_t emitter = {
.spr = spr,
.config = get_emitter_conf(&scene->engine->assets, "pe_burst"),
.position = {
.x = tile_idx % tilemap.width * tilemap.tile_size + tilemap.tile_size / 2,
.y = tile_idx / tilemap.width * tilemap.tile_size + tilemap.tile_size / 2,
},
.n_particles = 5,
.user_data = lvl_data,
.update_func = &simple_particle_system_update,
};
play_particle_emitter(&scene->part_sys, &emitter);
}
tilemap.tiles[tile_idx].tile_type = EMPTY_TILE;
tilemap.tiles[tile_idx].solid = NOT_SOLID;
tilemap.tiles[tile_idx].moveable = true;
}
// ------------------------- Collision functions ------------------------------------
// Do not subtract one for the size for any collision check, just pass normally. The extra one is important for AABB test
@ -194,6 +238,53 @@ static Vector2 shift_bbox(Vector2 bbox, Vector2 new_bbox, AnchorPoint_t anchor)
return offset;
}
void destroy_entity(Scene_t* scene, TileGrid_t* tilemap, Entity_t* p_ent)
{
if (p_ent->m_tag == BOULDER_ENT_TAG)
{
const CTransform_t* p_ctransform = get_component(p_ent, CTRANSFORM_COMP_T);
//const CBBox_t* p_bbox = get_component(p_ent, CBBOX_COMP_T);
ParticleEmitter_t emitter = {
.spr = get_sprite(&scene->engine->assets, "p_rock"),
.config = get_emitter_conf(&scene->engine->assets, "pe_burst"),
.position = p_ctransform->position,
.n_particles = 5,
.user_data = &(CONTAINER_OF(scene, LevelScene_t, scene)->data),
.update_func = &simple_particle_system_update,
};
play_particle_emitter(&scene->part_sys, &emitter);
}
else if (p_ent->m_tag == CRATES_ENT_TAG)
{
const CContainer_t* p_container = get_component(p_ent, CCONTAINER_T);
const CTransform_t* p_ctransform = get_component(p_ent, CTRANSFORM_COMP_T);
//const CBBox_t* p_bbox = get_component(p_ent, CBBOX_COMP_T);
ParticleEmitter_t emitter = {
.spr = get_sprite(&scene->engine->assets, (p_container->material == WOODEN_CONTAINER) ? "p_wood" : "p_metal"),
.config = get_emitter_conf(&scene->engine->assets, "pe_burst"),
.position = p_ctransform->position,
.n_particles = 5,
.user_data = &(CONTAINER_OF(scene, LevelScene_t, scene)->data),
.update_func = &simple_particle_system_update,
};
play_particle_emitter(&scene->part_sys, &emitter);
}
else if (p_ent->m_tag == CHEST_ENT_TAG)
{
const CTransform_t* p_ctransform = get_component(p_ent, CTRANSFORM_COMP_T);
ParticleEmitter_t emitter = {
.spr = get_sprite(&scene->engine->assets, "p_wood"),
.config = get_emitter_conf(&scene->engine->assets, "pe_burst"),
.position = p_ctransform->position,
.n_particles = 5,
.user_data = &(CONTAINER_OF(scene, LevelScene_t, scene)->data),
.update_func = &simple_particle_system_update,
};
play_particle_emitter(&scene->part_sys, &emitter);
}
remove_entity_from_tilemap(&scene->ent_manager, tilemap, p_ent);
}
void player_respawn_system(Scene_t* scene)
{
Entity_t* p_player;
@ -605,7 +696,7 @@ void spike_collision_system(Scene_t* scene)
}
else
{
tilemap.tiles[tile_idx].tile_type = EMPTY_TILE;
destroy_tile(data, tile_idx);
}
}
}
@ -1219,7 +1310,7 @@ void movement_update_system(Scene_t* scene)
|| p_ctransform->position.y < 0 || p_ctransform->position.y > level_height
)
{
remove_entity_from_tilemap(&scene->ent_manager, &tilemap, p_ent);
destroy_entity(scene, &tilemap, p_ent);
}
}
@ -1366,6 +1457,15 @@ void state_transition_update_system(Scene_t* scene)
if (p_mstate->water_state == 0b01)
{
play_sfx(scene->engine, WATER_IN_SFX);
ParticleEmitter_t emitter = {
.spr = get_sprite(&scene->engine->assets, "p_water"),
.config = get_emitter_conf(&scene->engine->assets, "pe_burst"),
.position = p_ctransform->position,
.n_particles = 5,
.user_data = &(CONTAINER_OF(scene, LevelScene_t, scene)->data),
.update_func = &simple_particle_system_update,
};
play_particle_emitter(&scene->part_sys, &emitter);
}
}
}
@ -1475,7 +1575,7 @@ void hitbox_update_system(Scene_t* scene)
hit = true;
if (p_hitbox->atk > tilemap.tiles[tile_idx].def)
{
change_a_tile(&tilemap, tile_idx, EMPTY_TILE);
destroy_tile(data, tile_idx);
continue;
}
}
@ -1555,13 +1655,13 @@ void hitbox_update_system(Scene_t* scene)
{
remove_component(p_other_ent, CHURTBOX_T);
CLifeTimer_t* p_clifetimer = add_component(p_other_ent, CLIFETIMER_T);
p_clifetimer->life_time = 6;
p_clifetimer->life_time = 3;
}
}
else
{
// Need to remove immediately, otherwise will interfere with bomb spawning
remove_entity_from_tilemap(&scene->ent_manager, &tilemap, p_other_ent);
destroy_entity(scene, &tilemap, p_other_ent);
if (p_other_ent->m_tag == CHEST_ENT_TAG)
{
data->coins.current++;
@ -1576,11 +1676,12 @@ void hitbox_update_system(Scene_t* scene)
}
if (p_hitbox->one_hit && hit)
{
remove_entity_from_tilemap(&scene->ent_manager, &tilemap, p_ent);
destroy_entity(scene, &tilemap, p_ent);
}
}
}
void boulder_destroy_wooden_tile_system(Scene_t* scene)
{
LevelSceneData_t* data = &(CONTAINER_OF(scene, LevelScene_t, scene)->data);
@ -1598,24 +1699,19 @@ void boulder_destroy_wooden_tile_system(Scene_t* scene)
p_ctransform->position.y + p_bbox->size.y,
tilemap.width
);
unsigned int tile_x = (p_ctransform->position.x + p_bbox->half_size.x) / TILE_SIZE;
unsigned int tile_x = (p_ctransform->position.x + p_bbox->half_size.x) / tilemap.tile_size;
if (tilemap.tiles[tile_idx].tile_type == ONEWAY_TILE)
{
tilemap.tiles[tile_idx].tile_type = EMPTY_TILE;
tilemap.tiles[tile_idx].solid = NOT_SOLID;
tilemap.tiles[tile_idx].moveable = true;
destroy_tile(data, tile_idx);
if (tile_x > 0 && tilemap.tiles[tile_idx - 1].tile_type == ONEWAY_TILE)
{
tilemap.tiles[tile_idx - 1].tile_type = EMPTY_TILE;
tilemap.tiles[tile_idx - 1].solid = NOT_SOLID;
tilemap.tiles[tile_idx - 1].moveable = true;
destroy_tile(data, tile_idx - 1);
}
if (tile_x < tilemap.width && tilemap.tiles[tile_idx + 1].tile_type == ONEWAY_TILE)
{
tilemap.tiles[tile_idx + 1].tile_type = EMPTY_TILE;
tilemap.tiles[tile_idx + 1].solid = NOT_SOLID;
tilemap.tiles[tile_idx + 1].moveable = true;
destroy_tile(data, tile_idx + 1);
}
}
}
@ -1630,20 +1726,6 @@ void container_destroy_system(Scene_t* scene)
Entity_t* p_ent = get_entity(&scene->ent_manager, ent_idx);
if (!p_ent->m_alive)
{
if (p_ent->m_tag == CRATES_ENT_TAG)
{
const CTransform_t* p_ctransform = get_component(p_ent, CTRANSFORM_COMP_T);
//const CBBox_t* p_bbox = get_component(p_ent, CBBOX_COMP_T);
ParticleEmitter_t emitter = {
.config = get_emitter_conf(&scene->engine->assets, "pe_wood"),
.position = p_ctransform->position,
.n_particles = 3,
.user_data = &(CONTAINER_OF(scene, LevelScene_t, scene)->data),
.update_func = &simple_particle_system_update,
};
play_particle_emitter(&scene->part_sys, &emitter);
}
Entity_t* dmg_src = NULL;
CHurtbox_t* p_hurtbox = get_component(p_ent, CHURTBOX_T);
if(p_hurtbox != NULL)
@ -1717,7 +1799,7 @@ void lifetimer_update_system(Scene_t* scene)
p_lifetimer->life_time--;
if (p_lifetimer->life_time == 0)
{
remove_entity_from_tilemap(&scene->ent_manager, &tilemap, get_entity(&scene->ent_manager, ent_idx));
destroy_entity(scene, &tilemap, get_entity(&scene->ent_manager, ent_idx));
}
}
}
@ -1786,7 +1868,7 @@ void airtimer_update_system(Scene_t* scene)
}
else
{
remove_entity_from_tilemap(&scene->ent_manager, &tilemap, get_entity(&scene->ent_manager, ent_idx));
destroy_entity(scene, &tilemap, get_entity(&scene->ent_manager, ent_idx));
}
}
@ -1816,14 +1898,14 @@ void sprite_animation_system(Scene_t* scene)
SpriteRenderInfo_t spr = p_cspr->sprites[p_cspr->current_idx];
if (spr.sprite == NULL) continue;
if (reset) spr.sprite->current_frame = 0;
if (reset) p_cspr->current_frame = 0;
// Animate it (handle frame count)
spr.sprite->elapsed++;
if (spr.sprite->elapsed == spr.sprite->speed)
{
spr.sprite->current_frame++;
spr.sprite->current_frame %= spr.sprite->frame_count;
p_cspr->current_frame++;
p_cspr->current_frame %= spr.sprite->frame_count;
spr.sprite->elapsed = 0;
}
}

View File

@ -109,7 +109,7 @@ static void level_scene_render_func(Scene_t* scene)
if (spr.sprite != NULL)
{
Vector2 pos = Vector2Add(p_ct->position, spr.offset);
draw_sprite(spr.sprite, pos, 0.0f, p_cspr->flip_x);
draw_sprite(spr.sprite, p_cspr->current_frame, pos, 0.0f, p_cspr->flip_x);
}
}
}