Extend the render queue to tilemap

This is only applied to the main game
main
En Yi 2024-12-21 16:53:47 +08:00
parent e24bb382f9
commit ee65e3c974
6 changed files with 178 additions and 138 deletions

View File

@ -1,6 +1,7 @@
#ifndef __COLLISION_FUNCS_H
#define __COLLISION_FUNCS_H
#include "EC.h"
#include "render_queue.h"
typedef enum SolidType
{
@ -42,6 +43,7 @@ typedef struct TileGrid
unsigned int max_tiles;
unsigned int tile_size;
Tile_t* tiles;
RenderInfoNode* render_nodes;
}TileGrid_t;
typedef struct TileArea {

View File

@ -1,6 +1,8 @@
// Constants to be used in game
#ifndef __CONSTANTS_H
#define __CONSTANTS_H
#include <stdint.h>
#ifndef TILE16_SIZE
#define TILE_SIZE 32
#define DEFAULT_MAP_WIDTH 48
@ -47,4 +49,11 @@
#define WATER_BBOX_STEP (TILE_SIZE / MAX_WATER_LEVEL)
#define MAX_LEVEL_NUM 50
static const uint8_t CONNECTIVITY_TILE_MAPPING[16] = {
0,3,15,14,
1,2,12,13,
7,6,11,10,
4,5,8 ,9 ,
};
#endif // __CONSTANTS_H

View File

@ -58,13 +58,6 @@ static Vector2 urchin_click_pos = {0,0};
#define SELECTION_LAYER 0
#define CONTROL_LAYER 1
static const uint8_t CONNECTIVITY_TILE_MAPPING[16] = {
0,3,15,14,
1,2,12,13,
7,6,11,10,
4,5,8 ,9 ,
};
#define N_SOLID_TILESETS 3
static const char* SOLID_TILE_SELECTIONS[N_SOLID_TILESETS] = {
"stile0", "stile1", "stile2"
@ -342,6 +335,12 @@ static void render_editor_game_scene(Scene_t* scene)
continue;
}
if (p_ent->m_tag == LEVEL_END_TAG)
{
p_cspr->current_frame = 2 * data->selected_solid_tilemap + (
(data->coins.current < data->coins.total) ? 0 : 1);
}
p_cspr->node.spr = spr.sprite;
p_cspr->node.frame_num = p_cspr->current_frame;
p_cspr->node.pos = pos;

View File

@ -9,12 +9,7 @@
#include <stdio.h>
static Tile_t all_tiles[MAX_N_TILES] = {0};
static const uint8_t CONNECTIVITY_TILE_MAPPING[16] = {
0,3,15,14,
1,2,12,13,
7,6,11,10,
4,5,8 ,9 ,
};
static RenderInfoNode all_tile_rendernodes[MAX_N_TILES] = {0};
#define GAME_LAYER 0
#define CONTROL_LAYER 1
@ -161,99 +156,41 @@ static void render_regular_game_scene(Scene_t* scene)
max.x = (int)fmin(tilemap.width, max.x + 1);
max.y = (int)fmin(tilemap.height, max.y + 1);
Texture2D* bg = get_texture(&scene->engine->assets, "bg_tex");
BeginTextureMode(scene->layers.render_layers[GAME_LAYER].layer_tex);
ClearBackground(WHITE);
DrawTexturePro(*bg,
//(Rectangle){0,0,64,64},
(Rectangle){min.x,0, data->game_rec.width, data->game_rec.height},
(Rectangle){0,0, data->game_rec.width, data->game_rec.height},
//(Rectangle){0,0,game_rec.width, game_rec.height},
(Vector2){0,0}, 0.0f, WHITE
);
BeginMode2D(data->camera.cam);
// Queue TileMap rendering
for (int tile_y = min.y; tile_y < max.y; tile_y++)
{
for (int tile_x = min.x; tile_x < max.x; tile_x++)
{
int i = tile_x + tile_y * tilemap.width;
int x = tile_x * TILE_SIZE;
int y = tile_y * TILE_SIZE;
int i = tile_x + tile_y * tilemap.width;
// Tile render nodes is static on load and is not dynamically updated.
// We exploit the fact that all tiles can only change into an empty
// tile.
// This won't work if a tile can change into something else
if (tilemap.tiles[i].tile_type == EMPTY_TILE) continue;
uint8_t depth = 2;
if (tilemap.tiles[i].tile_type == LADDER)
{
if (data->tile_sprites[tilemap.tiles[i].tile_type] != NULL)
{
draw_sprite(data->tile_sprites[tilemap.tiles[i].tile_type], 0, (Vector2){x,y}, 0.0f, false);
}
else
{
DrawRectangle(x, y, TILE_SIZE, TILE_SIZE, ORANGE);
}
depth = 0;
}
add_render_node(&data->render_manager, all_tile_rendernodes + i, depth);
}
}
sc_map_foreach_value(&scene->ent_manager.entities_map[LEVEL_END_TAG], p_ent)
{
// There is only a few exits, can may be get away without culling
CSprite_t* p_cspr = get_component(p_ent, CSPRITE_T);
if (p_cspr != NULL)
{
const SpriteRenderInfo_t spr = p_cspr->sprites[p_cspr->current_idx];
if (spr.sprite != NULL)
{
Vector2 pos = p_ent->position;
pos = Vector2Subtract(
pos,
get_anchor_offset(spr.sprite->frame_size, spr.src_anchor, p_cspr->node.flip & 1)
);
Vector2 offset = spr.offset;
if (p_cspr->node.flip & 1) offset.x *= -1;
pos = Vector2Add(pos, offset);
draw_sprite(
spr.sprite,
2 * data->selected_solid_tilemap + (
(data->coins.current < data->coins.total) ? 0 : 1
),
pos, 0.0f, p_cspr->node.flip & 1
);
}
}
else
{
DrawCircleV(p_ent->position, tilemap.tile_size >> 1, (data->coins.current < data->coins.total)? RED : GREEN);
}
}
// Queue Sprite rendering
sc_map_foreach_value(&scene->ent_manager.entities, p_ent)
{
if (p_ent->m_tag == LEVEL_END_TAG) continue;
if (!p_ent->m_alive) continue;
CBBox_t* p_bbox = get_component(p_ent, CBBOX_COMP_T);
// Entity culling
Vector2 box_size = {0};
if (p_bbox != NULL) box_size = p_bbox->size;
if (
p_ent->position.x + box_size.x < min.x * tilemap.tile_size
|| p_ent->position.x > max.x * tilemap.tile_size
|| p_ent->position.y + box_size.y < min.y * tilemap.tile_size
|| p_ent->position.y > max.y * tilemap.tile_size
) continue;
// Render Sprite only
CSprite_t* p_cspr = get_component(p_ent, CSPRITE_T);
if (p_cspr != NULL)
{
if (p_cspr == NULL) continue;
const SpriteRenderInfo_t spr = p_cspr->sprites[p_cspr->current_idx];
if (spr.sprite != NULL)
{
if (spr.sprite == NULL) continue;
Vector2 pos = p_ent->position;
CBBox_t* p_bbox = get_component(p_ent, CBBOX_COMP_T);
if (p_bbox != NULL)
{
pos = Vector2Add(
@ -270,11 +207,77 @@ static void render_regular_game_scene(Scene_t* scene)
if (p_cspr->node.flip & 1) offset.x *= -1;
pos = Vector2Add(pos, offset);
draw_sprite(spr.sprite, p_cspr->current_frame, pos, 0.0f, p_cspr->node.flip & 1);
}
// Entity culling
if (
pos.x + spr.sprite->frame_size.x < min.x * tilemap.tile_size
|| pos.x > max.x * tilemap.tile_size
|| pos.y + spr.sprite->frame_size.y < min.y * tilemap.tile_size
|| pos.y > max.y * tilemap.tile_size
)
{
continue;
}
if (p_ent->m_tag == LEVEL_END_TAG)
{
p_cspr->current_frame = 2 * data->selected_solid_tilemap + (
(data->coins.current < data->coins.total) ? 0 : 1);
}
p_cspr->node.spr = spr.sprite;
p_cspr->node.frame_num = p_cspr->current_frame;
p_cspr->node.pos = pos;
add_render_node(&data->render_manager, &p_cspr->node, p_cspr->depth);
}
Texture2D* bg = get_texture(&scene->engine->assets, "bg_tex");
BeginTextureMode(scene->layers.render_layers[GAME_LAYER].layer_tex);
ClearBackground(WHITE);
DrawTexturePro(*bg,
//(Rectangle){0,0,64,64},
(Rectangle){min.x,0, data->game_rec.width, data->game_rec.height},
(Rectangle){0,0, data->game_rec.width, data->game_rec.height},
//(Rectangle){0,0,game_rec.width, game_rec.height},
(Vector2){0,0}, 0.0f, WHITE
);
BeginMode2D(data->camera.cam);
#ifdef ENABLE_RENDER_FALLBACK
for (int tile_y = min.y; tile_y < max.y; tile_y++)
{
for (int tile_x = min.x; tile_x < max.x; tile_x++)
{
int i = tile_x + tile_y * tilemap.width;
int x = tile_x * TILE_SIZE;
int y = tile_y * TILE_SIZE;
if (tilemap.tiles[i].tile_type == LADDER)
{
if (data->tile_sprites[tilemap.tiles[i].tile_type] == NULL)
{
DrawRectangle(x, y, TILE_SIZE, TILE_SIZE, ORANGE);
}
}
}
}
sc_map_foreach_value(&scene->ent_manager.entities, p_ent)
{
CBBox_t* p_bbox = get_component(p_ent, CBBOX_COMP_T);
// Entity culling
Vector2 box_size = {0};
if (p_bbox != NULL) box_size = p_bbox->size;
if (
p_ent->position.x + box_size.x < min.x * tilemap.tile_size
|| p_ent->position.x > max.x * tilemap.tile_size
|| p_ent->position.y + box_size.y < min.y * tilemap.tile_size
|| p_ent->position.y > max.y * tilemap.tile_size
) continue;
CSprite_t* p_cspr = get_component(p_ent, CSPRITE_T);
if (p_cspr != NULL) continue;
// Continue here only if no sprite
Color colour;
switch(p_ent->m_tag)
@ -293,6 +296,12 @@ static void render_regular_game_scene(Scene_t* scene)
break;
}
if (p_ent->m_tag == LEVEL_END_TAG)
{
DrawCircleV(p_ent->position, tilemap.tile_size >> 1, (data->coins.current < data->coins.total)? RED : GREEN);
continue;
}
if (p_bbox != NULL)
{
if (p_ent->m_tag == BOULDER_ENT_TAG)
@ -357,7 +366,6 @@ static void render_regular_game_scene(Scene_t* scene)
}
}
}
for (int tile_y = min.y; tile_y < max.y; tile_y++)
{
for (int tile_x = min.x; tile_x < max.x; tile_x++)
@ -369,20 +377,15 @@ static void render_regular_game_scene(Scene_t* scene)
if (tilemap.tiles[i].tile_type != LADDER)
{
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], 0, (Vector2){x,y}, 0.0f, false);
}
else if (tilemap.tiles[i].tile_type == SOLID_TILE)
if (tilemap.tiles[i].tile_type == SOLID_TILE)
{
if (data->solid_tile_sprites == NULL) {
DrawRectangle(x, y, TILE_SIZE, TILE_SIZE, BLACK);
draw_sprite(
data->solid_tile_sprites,
CONNECTIVITY_TILE_MAPPING[tilemap.tiles[i].connectivity],
(Vector2){x,y}, 0.0f, false
);
}
else if (tilemap.tiles[i].tile_type == ONEWAY_TILE)
}
else if (data->tile_sprites[tile_sprite_idx] == NULL)
{
if (tilemap.tiles[i].tile_type == ONEWAY_TILE)
{
DrawRectangle(x, y, TILE_SIZE, TILE_SIZE, MAROON);
}
@ -394,6 +397,26 @@ static void render_regular_game_scene(Scene_t* scene)
);
}
}
}
}
}
#endif //ENABLE_RENDER_FALLBACK
execute_render(&data->render_manager);
// Particle effect will always be in front of everything except water
draw_particle_system(&scene->part_sys);
// Render water
for (int tile_y = min.y; tile_y < max.y; tile_y++)
{
for (int tile_x = min.x; tile_x < max.x; tile_x++)
{
int i = tile_x + tile_y * tilemap.width;
int x = tile_x * TILE_SIZE;
int y = tile_y * TILE_SIZE;
// Draw water flow
if (tilemap.tiles[i].wet)
{
#define SURFACE_THICKNESS 4
@ -407,7 +430,6 @@ static void render_regular_game_scene(Scene_t* scene)
DrawLineEx((Vector2){x + TILE_SIZE / 2, y}, (Vector2){x + TILE_SIZE / 2, y + TILE_SIZE - tilemap.tiles[i].water_level * WATER_BBOX_STEP}, SURFACE_THICKNESS, ColorAlpha(BLUE, 0.7));
}
if (
bot <= tilemap.n_tiles
&& tilemap.tiles[i].water_level == 0
@ -428,28 +450,6 @@ static void render_regular_game_scene(Scene_t* scene)
DrawRectangleLinesEx((Rectangle){x, y, TILE_SIZE, TILE_SIZE}, 2.0, ColorAlpha(BLUE, 0.5));
}
}
}
}
//sc_map_foreach_value(&scene->ent_manager.entities_map[DYNMEM_ENT_TAG], p_ent)
//{
// CWaterRunner_t* p_runner = get_component(p_ent, CWATERRUNNER_T);
// unsigned int x = ((p_runner->current_tile) % tilemap.width) * tilemap.tile_size;
// unsigned int y = ((p_runner->current_tile) / tilemap.width) * tilemap.tile_size;
// DrawCircle(x+16, y+16, 8, ColorAlpha(BLUE, 0.6));
//}
draw_particle_system(&scene->part_sys);
// Draw water tile
for (int tile_y = min.y; tile_y < max.y; tile_y++)
{
for (int tile_x = min.x; tile_x < max.x; tile_x++)
{
int i = tile_x + tile_y * tilemap.width;
int x = tile_x * TILE_SIZE;
int y = tile_y * TILE_SIZE;
uint32_t water_height = tilemap.tiles[i].water_level * WATER_BBOX_STEP;
Color water_colour = ColorAlpha(BLUE, 0.5);
@ -462,14 +462,6 @@ static void render_regular_game_scene(Scene_t* scene)
);
}
}
//// Draw Border (remove later)
//DrawLine(0, 0, 0, tilemap.height * tilemap.tile_size, BLACK);
//DrawLine(0, 0, tilemap.width * TILE_SIZE, 0, BLACK);
//int val = (tilemap.width) * tilemap.tile_size;
//DrawLine(val, 0, val, tilemap.height * tilemap.tile_size, BLACK);
//val = (tilemap.height) * tilemap.tile_size;
//DrawLine(0, val, tilemap.width * TILE_SIZE, val, BLACK);
EndMode2D();
EndTextureMode();
}
@ -505,6 +497,7 @@ void init_game_scene(LevelScene_t* scene)
init_entity_tag_map(&scene->scene.ent_manager, DYNMEM_ENT_TAG, 16);
scene->data.tilemap.tiles = all_tiles;
scene->data.tilemap.render_nodes = all_tile_rendernodes;
init_level_scene_data(
&scene->data, MAX_N_TILES, all_tiles,
(Rectangle){
@ -512,6 +505,16 @@ void init_game_scene(LevelScene_t* scene)
VIEWABLE_MAP_WIDTH*TILE_SIZE, VIEWABLE_MAP_HEIGHT*TILE_SIZE
}
);
for (size_t i = 0; i < MAX_N_TILES; i++)
{
memset(all_tile_rendernodes + i, 0, sizeof(RenderInfoNode));
all_tile_rendernodes[i].pos = (Vector2){
(i % scene->data.tilemap.width) * TILE_SIZE,
(i / scene->data.tilemap.width) * TILE_SIZE,
};
all_tile_rendernodes[i].scale = (Vector2){1,1};
all_tile_rendernodes[i].colour = WHITE;
}
scene->data.sm.state_functions[LEVEL_STATE_STARTING] = at_level_start;
scene->data.sm.state_functions[LEVEL_STATE_RUNNING] = NULL;
scene->data.sm.state_functions[LEVEL_STATE_DEAD] = NULL;

View File

@ -143,7 +143,7 @@ Entity_t* create_dead_player(EntityManager_t* ent_manager)
p_cspr->current_idx = SPR_PLAYER_DEAD;
p_cspr->node.colour = WHITE;
p_cspr->node.scale = (Vector2){1, 1};
p_cspr->depth = 2;
p_cspr->depth = 3;
add_component(p_ent, CMOVEMENTSTATE_T);

View File

@ -132,7 +132,15 @@ bool load_level_tilemap(LevelScene_t* scene, unsigned int level_num)
scene->data.tilemap.tiles[i].size = (Vector2){TILE_SIZE, TILE_SIZE};
scene->data.tilemap.tiles[i].def = 0;
if (lvl_map.tiles[i].tile_type == 1)
memset(scene->data.tilemap.render_nodes + i, 0, sizeof(RenderInfoNode));
scene->data.tilemap.render_nodes[i].pos = (Vector2){
(i % scene->data.tilemap.width) * TILE_SIZE,
(i / scene->data.tilemap.width) * TILE_SIZE,
};
scene->data.tilemap.render_nodes[i].scale = (Vector2){1,1};
scene->data.tilemap.render_nodes[i].colour = WHITE;
if (lvl_map.tiles[i].tile_type == SOLID_TILE)
{
change_a_tile(&scene->data.tilemap, i, SOLID_TILE);
}
@ -147,7 +155,8 @@ bool load_level_tilemap(LevelScene_t* scene, unsigned int level_num)
scene->data.tilemap.tiles[i].wet = scene->data.tilemap.tiles[i].water_level > 0;
}
}
// Two pass
// Two pass, because some tile depends on the solidity of the tiles
for (size_t i = 0; i < scene->data.tilemap.n_tiles;i++)
{
if (lvl_map.tiles[i].tile_type >= 8 && lvl_map.tiles[i].tile_type < 20)
@ -250,6 +259,24 @@ bool load_level_tilemap(LevelScene_t* scene, unsigned int level_num)
}
}
}
// This only works for static loading.
// If a tilemap change change to some other arbitrary tile.
// Then this should be done while changing a tile.
if (lvl_map.tiles[i].tile_type == SOLID_TILE)
{
scene->data.tilemap.render_nodes[i].spr = scene->data.solid_tile_sprites;
scene->data.tilemap.render_nodes[i].frame_num = CONNECTIVITY_TILE_MAPPING[
scene->data.tilemap.tiles[i].connectivity
];
}
else
{
uint8_t tile_sprite_idx = scene->data.tilemap.tiles[i].tile_type + scene->data.tilemap.tiles[i].rotation;
scene->data.tilemap.render_nodes[i].spr = scene->data.tile_sprites[
tile_sprite_idx
];
}
}
return true;