Example rendering
This commit is contained in:
@@ -10,4 +10,5 @@ target_sources(${DUSK_TARGET_NAME}
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Subdirs
|
# Subdirs
|
||||||
add_subdirectory(entity)
|
add_subdirectory(entity)
|
||||||
|
add_subdirectory(world)
|
@@ -45,4 +45,19 @@ void entityUpdate(entity_t *entity) {
|
|||||||
assertNotNull(entity, "Entity pointer cannot be NULL");
|
assertNotNull(entity, "Entity pointer cannot be NULL");
|
||||||
assertTrue(entity->type < ENTITY_TYPE_COUNT, "Invalid entity type");
|
assertTrue(entity->type < ENTITY_TYPE_COUNT, "Invalid entity type");
|
||||||
assertTrue(entity->type != ENTITY_TYPE_NULL, "Cannot have NULL entity type");
|
assertTrue(entity->type != ENTITY_TYPE_NULL, "Cannot have NULL entity type");
|
||||||
|
|
||||||
|
// Velocity
|
||||||
|
for(uint8_t i = 0; i < WORLD_DIMENSIONS; i++) {
|
||||||
|
if(entity->velocity[i] == 0) continue;
|
||||||
|
|
||||||
|
worldChunkPosAdd(&entity->position[i], entity->velocity[i]);
|
||||||
|
|
||||||
|
if(entity->velocity[i] < 0) {
|
||||||
|
entity->velocity[i] += ENTITY_FRICTION;
|
||||||
|
if(entity->velocity[i] >= -ENTITY_MIN_VELOCITY) entity->velocity[i] = 0;
|
||||||
|
} else {
|
||||||
|
entity->velocity[i] -= ENTITY_FRICTION;
|
||||||
|
if(entity->velocity[i] <= ENTITY_MIN_VELOCITY) entity->velocity[i] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
@@ -29,8 +29,8 @@ typedef enum {
|
|||||||
typedef struct entity_s {
|
typedef struct entity_s {
|
||||||
entitytype_t type;
|
entitytype_t type;
|
||||||
direction_t direction;
|
direction_t direction;
|
||||||
worldchunkpos_t position[3];
|
worldchunkpos_t position[WORLD_DIMENSIONS];
|
||||||
worldsubtile_t velocity[3];
|
worldsubtile_t velocity[WORLD_DIMENSIONS];
|
||||||
|
|
||||||
union {
|
union {
|
||||||
player_t player;
|
player_t player;
|
||||||
|
@@ -11,6 +11,10 @@
|
|||||||
errorret_t rpgInit(void) {
|
errorret_t rpgInit(void) {
|
||||||
// TEST
|
// TEST
|
||||||
entityInit(&ENTITIES[0], ENTITY_TYPE_PLAYER);
|
entityInit(&ENTITIES[0], ENTITY_TYPE_PLAYER);
|
||||||
|
// ENTITIES[0].position[0].tile = 2;
|
||||||
|
// ENTITIES[0].position[0].subtile = 8;
|
||||||
|
// ENTITIES[0].position[1].tile = 3;
|
||||||
|
// ENTITIES[0].position[1].subtile = 12;
|
||||||
|
|
||||||
errorOk();
|
errorOk();
|
||||||
}
|
}
|
||||||
|
10
src/rpg/world/CMakeLists.txt
Normal file
10
src/rpg/world/CMakeLists.txt
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
# Copyright (c) 2025 Dominic Masters
|
||||||
|
#
|
||||||
|
# This software is released under the MIT License.
|
||||||
|
# https://opensource.org/licenses/MIT
|
||||||
|
|
||||||
|
# Sources
|
||||||
|
target_sources(${DUSK_TARGET_NAME}
|
||||||
|
PRIVATE
|
||||||
|
worldunit.c
|
||||||
|
)
|
39
src/rpg/world/worldunit.c
Normal file
39
src/rpg/world/worldunit.c
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2025 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "worldunit.h"
|
||||||
|
#include "assert/assert.h"
|
||||||
|
|
||||||
|
void worldChunkPosAdd(
|
||||||
|
worldchunkpos_t *pos,
|
||||||
|
const worldsubtile_t amt
|
||||||
|
) {
|
||||||
|
int8_t a = pos->subtile; // current signed subtile
|
||||||
|
int8_t b = amt; // signed delta
|
||||||
|
uint8_t unsignedAdded = (uint8_t)a + (uint8_t)b; // well-defined wrap add
|
||||||
|
int8_t r = (int8_t)unsignedAdded; // result in signed domain
|
||||||
|
|
||||||
|
pos->subtile = r;
|
||||||
|
|
||||||
|
// Signed-overflow detection for a + b -> r:
|
||||||
|
// overflow if sign(a) == sign(b) and sign(r) != sign(a)
|
||||||
|
uint8_t ov = (uint8_t)((a ^ r) & (b ^ r)) >> 7; // 1 if overflow, else 0
|
||||||
|
|
||||||
|
// Direction of carry: +1 for b >= 0, -1 for b < 0 (computed branchlessly)
|
||||||
|
uint8_t neg = ((uint8_t)b) >> 7; // 0 if b>=0, 1 if b<0
|
||||||
|
int8_t dir = (int8_t)(1 - (neg << 1)); // +1 or -1
|
||||||
|
|
||||||
|
// Apply tile adjustment (mod-256 via uint8_t arithmetic)
|
||||||
|
pos->tile = (uint8_t)(pos->tile + (uint8_t)(ov * (uint8_t)dir));
|
||||||
|
}
|
||||||
|
|
||||||
|
float_t worldChunkPosToF32(
|
||||||
|
const worldchunkpos_t pos,
|
||||||
|
const uint8_t tileSize
|
||||||
|
) {
|
||||||
|
return (float_t)(pos.tile * tileSize) + ((float_t)pos.subtile / 256.0f);
|
||||||
|
}
|
@@ -8,13 +8,20 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "dusk.h"
|
#include "dusk.h"
|
||||||
|
|
||||||
|
#define WORLD_DIMENSIONS 3
|
||||||
|
#define WORLD_SUBTITLE_MIN -128
|
||||||
|
#define WORLD_SUBTITLE_MAX 127
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Position in SUBTILE space in a world, each unit represents a single subtile.
|
* Position in SUBTILE space in a world, each unit represents a single subtile.
|
||||||
* This is divided by the size of the tile, e.g. if a tile is 16x16 then there
|
* This is divided by the size of the tile, e.g. if a tile is 16x16 then there
|
||||||
* are 256 / tile size = units per pixel of a tile. This means there are always
|
* are 128 / tile size = units per pixel of a tile. This means there are always
|
||||||
* uint8_t max subtiles in a tile.
|
* between WORLD_SUBTITLE_MIN and WORLD_SUBTITLE_MAX subtiles in a tile.
|
||||||
|
*
|
||||||
|
* The extra benefit of this is that different tile sizes don't require any game
|
||||||
|
* logic updates!
|
||||||
*/
|
*/
|
||||||
typedef uint8_t worldsubtile_t;
|
typedef int8_t worldsubtile_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Position in TILE space in a world, each unit represents a single tile. This
|
* Position in TILE space in a world, each unit represents a single tile. This
|
||||||
@@ -51,4 +58,29 @@ typedef struct worldpos_s {
|
|||||||
worldsubtile_t subtile;
|
worldsubtile_t subtile;
|
||||||
worldtile_t tile;
|
worldtile_t tile;
|
||||||
worldchunk_t chunk;
|
worldchunk_t chunk;
|
||||||
} worldpos_t;
|
} worldpos_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a number of subtiles to a world chunk position, rolling over into tiles
|
||||||
|
* and chunks as necessary.
|
||||||
|
*
|
||||||
|
* @param pos Pointer to the world chunk position to modify.
|
||||||
|
* @param amt The amount of subtiles to add (can be negative).
|
||||||
|
*/
|
||||||
|
void worldChunkPosAdd(
|
||||||
|
worldchunkpos_t *pos,
|
||||||
|
const worldsubtile_t amt
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a world chunk position to a floating point number, given the tile
|
||||||
|
* size in pixels.
|
||||||
|
*
|
||||||
|
* @param pos Pointer to the world chunk position to convert.
|
||||||
|
* @param tileSize The size of a tile in pixels.
|
||||||
|
* @return The position as a floating point number.
|
||||||
|
*/
|
||||||
|
float_t worldChunkPosToF32(
|
||||||
|
const worldchunkpos_t pos,
|
||||||
|
const uint8_t tileSize
|
||||||
|
);
|
@@ -6,9 +6,21 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "scenemap.h"
|
#include "scenemap.h"
|
||||||
|
#include "scene/scenedata.h"
|
||||||
#include "display/spritebatch.h"
|
#include "display/spritebatch.h"
|
||||||
|
#include "assert/assert.h"
|
||||||
|
#include "rpg/entity/entity.h"
|
||||||
|
|
||||||
|
#define TILE_WIDTH 1
|
||||||
|
#define TILE_HEIGHT TILE_WIDTH
|
||||||
|
|
||||||
errorret_t sceneMapInit(scenedata_t *data) {
|
errorret_t sceneMapInit(scenedata_t *data) {
|
||||||
|
cameraInitPerspective(&data->sceneMap.camera);
|
||||||
|
|
||||||
|
data->sceneMap.camera.lookat.position[0] = 3;
|
||||||
|
data->sceneMap.camera.lookat.position[1] = 3;
|
||||||
|
data->sceneMap.camera.lookat.position[2] = 3;
|
||||||
|
|
||||||
errorOk();
|
errorOk();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -16,21 +28,29 @@ void sceneMapUpdate(scenedata_t *data) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void sceneMapRender(scenedata_t *data) {
|
void sceneMapRender(scenedata_t *data) {
|
||||||
|
cameraPushMatrix(&data->sceneMap.camera);
|
||||||
|
|
||||||
entity_t *ent = ENTITIES;
|
entity_t *ent = ENTITIES;
|
||||||
do {
|
do {
|
||||||
sceneMapRenderEntity(ent);
|
sceneMapRenderEntity(ent);
|
||||||
} while(++ent, ent < &ENTITIES[ENTITY_COUNT]);
|
} while(++ent, ent < &ENTITIES[ENTITY_COUNT]);
|
||||||
|
spriteBatchFlush();
|
||||||
|
|
||||||
|
cameraPopMatrix();
|
||||||
}
|
}
|
||||||
|
|
||||||
void sceneMapRenderEntity(const entity_t *entity) {
|
void sceneMapRenderEntity(const entity_t *entity) {
|
||||||
assertNotNull(entity);
|
assertNotNull(entity, "Entity cannot be NULL");
|
||||||
|
|
||||||
if(entity->type == ENTITY_TYPE_NULL) return;
|
if(entity->type == ENTITY_TYPE_NULL) return;
|
||||||
|
|
||||||
|
float_t x = worldChunkPosToF32(entity->position[0], TILE_WIDTH);
|
||||||
|
float_t y = worldChunkPosToF32(entity->position[1], TILE_HEIGHT);
|
||||||
|
|
||||||
spriteBatchPush(
|
spriteBatchPush(
|
||||||
NULL,
|
NULL,
|
||||||
0.0f, 0.0f,
|
x, y,
|
||||||
32.0f, 32.0f,
|
x + TILE_WIDTH, y + TILE_HEIGHT,
|
||||||
COLOR_RED,
|
COLOR_RED,
|
||||||
0.0f, 0.0f,
|
0.0f, 0.0f,
|
||||||
1.0f, 1.0f
|
1.0f, 1.0f
|
||||||
|
@@ -8,9 +8,11 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "scene/scene.h"
|
#include "scene/scene.h"
|
||||||
#include "rpg/entity/entity.h"
|
#include "rpg/entity/entity.h"
|
||||||
|
#include "display/camera.h"
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
int32_t nothing;
|
int32_t nothing;
|
||||||
|
camera_t camera;
|
||||||
} scenemap_t;
|
} scenemap_t;
|
||||||
|
|
||||||
errorret_t sceneMapInit(scenedata_t *data);
|
errorret_t sceneMapInit(scenedata_t *data);
|
||||||
|
Reference in New Issue
Block a user