Time is better.

This commit is contained in:
2025-11-09 18:32:33 -06:00
parent b9ec6523d6
commit 943e775364
17 changed files with 293 additions and 146 deletions

View File

@@ -80,12 +80,17 @@ void inputUpdate(void) {
// Reset all actions
inputactiondata_t *action = &INPUT.actions[0];
do {
action->lastValue = action->currentValue;
action->currentValue = 0.0f;
if(TIME.fixedUpdate) {
action->lastFixedValue = action->currentFixedValue;
action->currentFixedValue = 0.0f;
}
#if TIME_FIXED == 0
action->lastDynamicValue = action->currentDynamicValue;
action->currentDynamicValue = 0.0f;
if(!TIME.dynamicUpdate) {
action->lastValue = action->currentValue;
action->currentValue = 0.0f;
}
#else
action->lastValue = action->currentValue;
action->currentValue = 0.0f;
#endif
action++;
} while(action < &INPUT.actions[INPUT_ACTION_COUNT]);
@@ -102,50 +107,55 @@ void inputUpdate(void) {
}
// Update current val.
INPUT.actions[cur->action].currentValue = mathMax(
cur->curVal, INPUT.actions[cur->action].currentValue
);
if(TIME.fixedUpdate) {
INPUT.actions[cur->action].currentFixedValue = mathMax(
cur->curVal, INPUT.actions[cur->action].currentFixedValue
#if TIME_FIXED == 0
INPUT.actions[cur->action].currentDynamicValue = mathMax(
cur->curVal, INPUT.actions[cur->action].currentDynamicValue
);
}
if(!TIME.dynamicUpdate) {
INPUT.actions[cur->action].currentValue = mathMax(
cur->curVal, INPUT.actions[cur->action].currentValue
);
}
#else
INPUT.actions[cur->action].currentValue = mathMax(
cur->curVal, INPUT.actions[cur->action].currentValue
);
#endif
cur++;
} while(cur->name);
}
float_t inputGetCurrentValue(const inputaction_t action) {
// This may be cursed, not sure yet!
if(TIME.fixedUpdate) return inputGetCurrentValueFixed(action);
return inputGetCurrentValueNonFixed(action);
}
#if TIME_FIXED == 0
if(TIME.dynamicUpdate) return inputGetCurrentValueDynamic(action);
#endif
float_t inputGetCurrentValueFixed(const inputaction_t action) {
assertTrue(action < INPUT_ACTION_COUNT, "Input action out of bounds");
return INPUT.actions[action].currentFixedValue;
}
float_t inputGetCurrentValueNonFixed(const inputaction_t action) {
assertTrue(action < INPUT_ACTION_COUNT, "Input action out of bounds");
return INPUT.actions[action].currentValue;
}
float_t inputGetLastValue(const inputaction_t action) {
if(TIME.fixedUpdate) return inputGetLastValueFixed(action);
return inputGetLastValueNonFixed(action);
}
#if TIME_FIXED == 0
if(TIME.dynamicUpdate) return inputGetLastValueDynamic(action);
#endif
float_t inputGetLastValueFixed(const inputaction_t action) {
assertTrue(action < INPUT_ACTION_COUNT, "Input action out of bounds");
return INPUT.actions[action].lastFixedValue;
}
float_t inputGetLastValueNonFixed(const inputaction_t action) {
assertTrue(action < INPUT_ACTION_COUNT, "Input action out of bounds");
return INPUT.actions[action].lastValue;
}
#if TIME_FIXED == 0
float_t inputGetCurrentValueDynamic(const inputaction_t action) {
assertTrue(action < INPUT_ACTION_COUNT, "Input action out of bounds");
return INPUT.actions[action].currentDynamicValue;
}
float_t inputGetLastValueDynamic(const inputaction_t action) {
assertTrue(action < INPUT_ACTION_COUNT, "Input action out of bounds");
return INPUT.actions[action].lastDynamicValue;
}
#endif
bool_t inputIsDown(const inputaction_t action) {
return inputGetCurrentValue(action) > 0.0f;
}

View File

@@ -47,22 +47,6 @@ void inputUpdate(void);
*/
float_t inputGetCurrentValue(const inputaction_t action);
/**
* Gets the current value of a specific input action (fixed timestep).
*
* @param action The input action to get the value for.
* @return The current value of the action (0.0f to 1.0f).
*/
float_t inputGetCurrentValueFixed(const inputaction_t action);
/**
* Gets the current value of a specific input action (non-fixed timestep).
*
* @param action The input action to get the value for.
* @return The current value of the action (0.0f to 1.0f).
*/
float_t inputGetCurrentValueNonFixed(const inputaction_t action);
/**
* Gets the last value of a specific input action.
*
@@ -71,21 +55,23 @@ float_t inputGetCurrentValueNonFixed(const inputaction_t action);
*/
float_t inputGetLastValue(const inputaction_t action);
/**
* Gets the last value of a specific input action (fixed timestep).
*
* @param action The input action to get the value for.
* @return The last value of the action (0.0f to 1.0f).
*/
float_t inputGetLastValueFixed(const inputaction_t action);
#if TIME_FIXED == 0
/**
* Gets the current value of a specific input action (dynamic timestep).
*
* @param action The input action to get the value for.
* @return The current value of the action (0.0f to 1.0f).
*/
float_t inputGetCurrentValueDynamic(const inputaction_t action);
/**
* Gets the last value of a specific input action (non-fixed timestep).
*
* @param action The input action to get the value for.
* @return The last value of the action (0.0f to 1.0f).
*/
float_t inputGetLastValueNonFixed(const inputaction_t action);
/**
* Gets the last value of a specific input action (dynamic timestep).
*
* @param action The input action to get the value for.
* @return The last value of the action (0.0f to 1.0f).
*/
float_t inputGetLastValueDynamic(const inputaction_t action);
#endif
/**
* Checks if a specific input action is currently pressed.

View File

@@ -9,22 +9,13 @@
#include "assert/assert.h"
#include "util/string.h"
const char_t* INPUT_ACTION_NAMES[INPUT_ACTION_COUNT] = {
[INPUT_ACTION_UP] = "UP",
[INPUT_ACTION_DOWN] = "DOWN",
[INPUT_ACTION_LEFT] = "LEFT",
[INPUT_ACTION_RIGHT] = "RIGHT",
[INPUT_ACTION_ACCEPT] = "ACCEPT",
[INPUT_ACTION_CANCEL] = "CANCEL",
};
// inputaction_t inputActionGetByName(const char_t *name) {
// assertNotNull(name, "name must not be NULL");
inputaction_t inputActionGetByName(const char_t *name) {
assertNotNull(name, "name must not be NULL");
// for(inputaction_t i = 0; i < INPUT_ACTION_COUNT; i++) {
// if(stringCompareInsensitive(INPUT_ACTION_NAMES[i], name) != 0) continue;
// return i;
// }
for(inputaction_t i = 0; i < INPUT_ACTION_COUNT; i++) {
if(stringCompareInsensitive(INPUT_ACTION_NAMES[i], name) != 0) continue;
return i;
}
return INPUT_ACTION_COUNT;
}
// return INPUT_ACTION_COUNT;
// }

View File

@@ -6,7 +6,7 @@
*/
#pragma once
#include "dusk.h"
#include "time/time.h"
typedef enum {
INPUT_ACTION_NULL,
@@ -25,11 +25,22 @@ typedef struct {
inputaction_t action;
float_t lastValue;
float_t currentValue;
float_t lastFixedValue;
float_t currentFixedValue;
#if TIME_FIXED == 0
float_t lastDynamicValue;
float_t currentDynamicValue;
#endif
} inputactiondata_t;
extern const char_t* INPUT_ACTION_NAMES[INPUT_ACTION_COUNT];
// static const char_t* INPUT_ACTION_NAMES[INPUT_ACTION_COUNT] = {
// [INPUT_ACTION_UP] = "UP",
// [INPUT_ACTION_DOWN] = "DOWN",
// [INPUT_ACTION_LEFT] = "LEFT",
// [INPUT_ACTION_RIGHT] = "RIGHT",
// [INPUT_ACTION_ACCEPT] = "ACCEPT",
// [INPUT_ACTION_CANCEL] = "CANCEL",
// [INPUT_ACTION_RAGEQUIT] = "RAGEQUIT",
// };
/**
* Gets an input action by its name.
@@ -37,4 +48,4 @@ extern const char_t* INPUT_ACTION_NAMES[INPUT_ACTION_COUNT];
* @param name The name of the input action.
* @return The input action, or INPUT_ACTION_COUNT if not found.
*/
inputaction_t inputActionGetByName(const char_t *name);
// inputaction_t inputActionGetByName(const char_t *name);

View File

@@ -44,7 +44,7 @@ void cutsceneItemUpdate(const cutsceneitem_t *item, cutsceneitemdata_t *data) {
break;
case CUTSCENE_ITEM_TYPE_WAIT:
data->wait -= TIME.fixedDelta;
data->wait -= TIME.delta;
if(data->wait <= 0) cutsceneSystemNext();
break;

View File

@@ -42,7 +42,9 @@ errorret_t rpgInit(void) {
}
void rpgUpdate(void) {
if(!TIME.fixedUpdate) return;
#if TIME_FIXED == 0
if(TIME.dynamicUpdate) return;
#endif
// TODO: Do not update if the scene is not the map scene?
mapUpdate();

View File

@@ -19,7 +19,11 @@ void rpgCameraInit(void) {
void rpgCameraUpdate(void) {
switch(RPG_CAMERA.mode) {
case RPG_CAMERA_MODE_FREE:
// Free camera mode; nothing to do.
mapPositionSet(
(int16_t)(RPG_CAMERA.free.x / CHUNK_WIDTH) - (MAP_CHUNK_WIDTH / 2),
(int16_t)(RPG_CAMERA.free.y / CHUNK_HEIGHT) - (MAP_CHUNK_HEIGHT / 2),
(int16_t)(RPG_CAMERA.free.z / CHUNK_DEPTH) - (MAP_CHUNK_DEPTH / 2)
);
break;
case RPG_CAMERA_MODE_FOLLOW_ENTITY: {

View File

@@ -6,5 +6,6 @@
# Sources
target_sources(${DUSK_TARGET_NAME}
PRIVATE
chunk.c
map.c
)

20
src/rpg/world/chunk.c Normal file
View File

@@ -0,0 +1,20 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "chunk.h"
uint32_t chunkGetTileIndex(
const uint8_t relativeTileX,
const uint8_t relativeTileY,
const uint8_t relativeTileZ
) {
return (
(relativeTileZ * CHUNK_WIDTH * CHUNK_HEIGHT) +
(relativeTileY * CHUNK_WIDTH) +
relativeTileX
);
}

View File

@@ -17,3 +17,17 @@ typedef struct chunk_s {
int16_t x, y, z;
tile_t tiles[CHUNK_TILE_COUNT];
} chunk_t;
/**
* Gets the tile index for a tile position within a chunk.
*
* @param relativeTileX The X coordinate of the tile within the chunk.
* @param relativeTileY The Y coordinate of the tile within the chunk.
* @param relativeTileZ The Z coordinate of the tile within the chunk.
* @return The tile index within the chunk.
*/
uint32_t chunkGetTileIndex(
const uint8_t relativeTileX,
const uint8_t relativeTileY,
const uint8_t relativeTileZ
);

View File

@@ -7,24 +7,14 @@
#include "map.h"
#include "util/memory.h"
#include <stdio.h> // For printf
map_t MAP;
// Dummy functions for chunk loading/unloading
void mapChunkUnload(chunk_t* chunk) {
// Placeholder for unloading logic
printf("Unloading chunk at (%d, %d, %d)\n", chunk->x, chunk->y, chunk->z);
}
void mapChunkLoad(chunk_t* chunk) {
// Placeholder for loading logic
printf("Loading chunk at (%d, %d, %d)\n", chunk->x, chunk->y, chunk->z);
}
void mapInit() {
memoryZero(&MAP, sizeof(map_t));
// Init the default chunks. In future I'll probably make this based on where
// the player spawns in to save an initial mapSet.
uint32_t index = 0;
for(uint32_t z = 0; z < MAP_CHUNK_DEPTH; z++) {
for(uint32_t y = 0; y < MAP_CHUNK_HEIGHT; y++) {
@@ -123,3 +113,49 @@ void mapPositionSet(const int16_t x, const int16_t y, const int16_t z) {
void mapUpdate() {
}
void mapChunkUnload(chunk_t* chunk) {
}
void mapChunkLoad(chunk_t* chunk) {
memoryZero(chunk->tiles, sizeof(tile_t) * CHUNK_TILE_COUNT);
uint8_t x, y, z;
x = 1;
y = 2;
z = 0;
chunk->tiles[
(z * CHUNK_WIDTH * CHUNK_HEIGHT) +
(y * CHUNK_WIDTH) +
x
] = (tile_t){ .id = 1 };
}
uint8_t mapGetChunkIndexAt(
const int16_t chunkX,
const int16_t chunkY,
const int16_t chunkZ
) {
int16_t relX = chunkX - MAP.x;
int16_t relY = chunkY - MAP.y;
int16_t relZ = chunkZ - MAP.z;
if(
relX < 0 || relX >= MAP_CHUNK_WIDTH ||
relY < 0 || relY >= MAP_CHUNK_HEIGHT ||
relZ < 0 || relZ >= MAP_CHUNK_DEPTH
) {
return UINT8_MAX;
}
return (
(relZ * MAP_CHUNK_WIDTH * MAP_CHUNK_HEIGHT) +
(relY * MAP_CHUNK_WIDTH) +
relX
);
}
chunk_t* mapGetChunkByIndex(const uint8_t index) {
if(index >= MAP_CHUNK_COUNT) return NULL;
return &MAP.chunks[index];
}

View File

@@ -33,15 +33,45 @@ void mapUpdate();
/**
* Sets the map position and updates chunks accordingly.
*
* @param x The new X position.
* @param y The new Y position.
* @param z The new Z position.
*/
void mapPositionSet(const int16_t x, const int16_t y, const int16_t z);
/**
* Dummy: Unloads a chunk.
* Unloads a chunk.
*
* @param chunk The chunk to unload.
*/
void mapChunkUnload(chunk_t* chunk);
/**
* Dummy: Loads a chunk.
* Loads a chunk.
*
* @param chunk The chunk to load.
*/
void mapChunkLoad(chunk_t* chunk);
/**
* Gets the index of a chunk at the specified CHUNK coordinates.
*
* @param chunkX The X coordinate of the chunk (in CHUNK units).
* @param chunkY The Y coordinate of the chunk (in CHUNK units).
* @param chunkZ The Z coordinate of the chunk (in CHUNK units).
* @return The index of the chunk, or UINT8_MAX if out of bounds.
*/
uint8_t mapGetChunkIndexAt(
const int16_t chunkX,
const int16_t chunkY,
const int16_t chunkZ
);
/**
* Gets a chunk by its index.
*
* @param chunkIndex The index of the chunk.
* @return A pointer to the chunk.
*/
chunk_t * mapGetChunk(const uint8_t chunkIndex);

View File

@@ -9,6 +9,7 @@
#include "dusk.h"
typedef uint8_t worldunit_t;
typedef int16_t chunkunit_t;
typedef int8_t worldunits_t;
typedef struct worldpos_s {

View File

@@ -19,6 +19,5 @@ elseif(DUSK_TARGET_SYSTEM STREQUAL "psp")
target_compile_definitions(${DUSK_TARGET_NAME}
PRIVATE
TIME_FIXED=1
TIME_PLATFORM_STEP=0.016
)
endif()

View File

@@ -22,31 +22,33 @@ void timeInit(void) {
TIME.time = TIME_STEP;
TIME.delta = TIME_STEP;
TIME.fixedDelta = TIME_STEP;
TIME.fixedTime = TIME_STEP;
#if TIME_FIXED == 0
TIME.dynamicTime = TIME_STEP;
TIME.dynamicDelta = TIME_STEP;
TIME.dynamicUpdate = false;
#endif
}
void timeUpdate(void) {
float_t delta;
#if TIME_SDL2
delta = (float_t)SDL_GetTicks() / 1000.0f - TIME.time;
#elif TIME_FIXED
delta = TIME_PLATFORM_STEP;
#if TIME_FIXED == 0
#if TIME_SDL2
float_t newTime = (float_t)SDL_GetTicks() / 1000.0f;
TIME.dynamicDelta = newTime - TIME.dynamicTime;
TIME.dynamicTime = newTime;
TIME.dynamicUpdate = true;
#else
#error "No time platform defined"
#endif
assertTrue(TIME.dynamicDelta >= 0.0f, "Time delta is negative");
if(TIME.dynamicTime - TIME.time >= TIME_STEP) {
TIME.dynamicUpdate = false;
TIME.delta = TIME_STEP;
TIME.time += TIME_STEP;
}
#else
#error "No time platform defined"
TIME.delta = TIME_STEP;
TIME.time += TIME_STEP;
#endif
TIME.delta = delta;
assertTrue(TIME.delta >= 0.0f, "Time delta is negative");
TIME.time += TIME.delta;
// Perform a fixed time step.
if(TIME.time - TIME.fixedTime >= (TIME_STEP * 0.9f)) {
TIME.fixedUpdate = true;
TIME.fixedDelta = TIME_STEP;
TIME.fixedTime += TIME_STEP;
} else {
TIME.fixedDelta = 0.0f;
TIME.fixedUpdate = false;
}
}

View File

@@ -8,18 +8,37 @@
#pragma once
#include "dusk.h"
#define TIME_STEP (1.0f / 60.0f) // 60 Ticks per second.
#ifndef TIME_FIXED
#define TIME_FIXED 0
#else
#ifndef TIME_PLATFORM_STEP
#error "TIME_PLATFORM_STEP must be defined when TIME_FIXED is enabled"
#endif
#if TIME_PLATFORM_STEP <= 0.0f
#error "TIME_PLATFORM_STEP must be greater than zero"
#endif
#if TIME_PLATFORM_STEP != TIME_STEP
#define TIME_FIXED 0
#endif
#endif
typedef struct {
float_t delta;
float_t time;
bool_t fixedUpdate;
float_t fixedDelta;
float_t fixedTime;
#if TIME_FIXED == 0
bool_t dynamicUpdate;
float_t dynamicDelta;
float_t dynamicTime;
#endif
} dusktime_t;
extern dusktime_t TIME;
#define TIME_STEP (1.0f / 60.0f) // 60 Ticks per second.
/**
* Initializes the time system.

View File

@@ -17,21 +17,42 @@ bool_t UI_FPS_DRAW = true;
void uiFPSRender(const tileset_t *tileset, texture_t *texture) {
if(!UI_FPS_DRAW) return;
float_t fps = TIME.delta > 0.0f ? (1.0f / TIME.delta) : 0.0f;
char_t buffer[64];
snprintf(
buffer,
sizeof(buffer),
"%.2f/%d",
TIME.delta * 1000.0f,
(int32_t)fps
);
char_t buffer[96];
color_t color;
color_t color = (
fps >= 50.0f ? COLOR_GREEN :
fps >= 30.0f ? COLOR_YELLOW :
COLOR_RED
);
#if TIME_FIXED == 0
float_t fpsDynamic = TIME.dynamicDelta > 0.0f ? (1.0f / TIME.dynamicDelta) : 0.0f;
float_t fpsFixed = TIME.delta > 0.0f ? (1.0f / TIME.delta) : 0.0f;
snprintf(
buffer,
sizeof(buffer),
"%.2f/%.2f/%d",
TIME.dynamicDelta * 1000.0f,
TIME.delta * 1000.0f,
(int32_t)fpsDynamic
);
color = (
fpsDynamic >= 50.0f ? COLOR_GREEN :
fpsDynamic >= 30.0f ? COLOR_YELLOW :
COLOR_RED
);
#else
float_t fps = TIME.delta > 0.0f ? (1.0f / TIME.delta) : 0.0f;
snprintf(
buffer,
sizeof(buffer),
"%.2f/%d",
TIME.delta * 1000.0f,
(int32_t)fps
);
color = (
fps >= 50.0f ? COLOR_GREEN :
fps >= 30.0f ? COLOR_YELLOW :
COLOR_RED
);
#endif
int32_t w, h;
uiTextMeasure(buffer, tileset, &w, &h);