This commit is contained in:
2026-07-19 14:05:46 -05:00
parent 725338cd5a
commit 13a435e9c1
12 changed files with 389 additions and 0 deletions
+3
View File
@@ -22,3 +22,6 @@ X(CAMERA, entitycamera_t, camera, entityCameraInit, NULL, NULL)
X(RENDERABLE, entityrenderable_t, renderable,
entityRenderableInit, entityRenderableDispose, NULL)
X(PHYSICS, entityphysics_t, physics, entityPhysicsInit, NULL, NULL)
// Game-specific components
#include "entity/gamecomponentlist.h"
+2
View File
@@ -9,6 +9,8 @@ target_include_directories(${DUSK_LIBRARY_TARGET_NAME}
${CMAKE_CURRENT_LIST_DIR}
)
add_subdirectory(entity)
add_subdirectory(game)
add_subdirectory(input)
add_subdirectory(item)
add_subdirectory(scene)
+8
View File
@@ -0,0 +1,8 @@
# Copyright (c) 2026 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
# Header-only: gamecomponentlist.h is an X-macro list, no implementation.
add_subdirectory(component)
@@ -0,0 +1,6 @@
# Copyright (c) 2026 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
add_subdirectory(overworld)
@@ -0,0 +1,9 @@
# Copyright (c) 2026 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
target_sources(${DUSK_LIBRARY_TARGET_NAME}
PUBLIC
entityplayer.c
)
@@ -0,0 +1,88 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "entityplayer.h"
#include "entity/entitymanager.h"
#include "entity/component/physics/entityphysics.h"
#include "entity/component/display/entitycamera.h"
#include "input/input.h"
#include "util/memory.h"
#define ENTITY_PLAYER_MOVE_SPEED_DEFAULT 4.0f
#define ENTITY_PLAYER_JUMP_IMPULSE_DEFAULT 6.0f
void entityPlayerInit(
entitymanager_t *mgr,
const entityid_t entityId,
const componentid_t componentId
) {
entityplayer_t *player = entityPlayerGet(mgr, entityId, componentId);
memoryZero(player, sizeof(entityplayer_t));
player->moveSpeed = ENTITY_PLAYER_MOVE_SPEED_DEFAULT;
player->jumpImpulse = ENTITY_PLAYER_JUMP_IMPULSE_DEFAULT;
entityUpdateAdd(mgr, entityId, entityPlayerUpdate, componentId, NULL);
}
entityplayer_t *entityPlayerGet(
entitymanager_t *mgr,
const entityid_t entityId,
const componentid_t componentId
) {
return componentGetData(mgr, entityId, componentId, COMPONENT_TYPE_PLAYER);
}
void entityPlayerUpdate(
entitymanager_t *mgr,
const entityid_t entityId,
const componentid_t componentId,
void *user
) {
componentid_t physComp = entityGetComponent(
mgr, entityId, COMPONENT_TYPE_PHYSICS
);
if(physComp == COMPONENT_ID_INVALID) return;
entityplayer_t *player = entityPlayerGet(mgr, entityId, componentId);
vec2 moveInput;
inputAxis2D(
INPUT_BIND_LEFT, INPUT_BIND_RIGHT,
INPUT_BIND_DOWN, INPUT_BIND_UP,
moveInput
);
// Move relative to the current camera's horizontal facing, not fixed
// world axes, so "up" always means "away from the camera".
vec2 forward = { 0.0f, -1.0f };
vec2 right = { 1.0f, 0.0f };
entityid_t camEntity = entityCameraGetCurrent(mgr);
if(camEntity != ENTITY_ID_INVALID) {
entityCameraGetForward(mgr, camEntity, forward);
entityCameraGetRight(mgr, camEntity, right);
}
vec2 moveDir = {
right[0] * moveInput[0] + forward[0] * moveInput[1],
right[1] * moveInput[0] + forward[1] * moveInput[1]
};
// Clamp to unit length so diagonal input isn't faster than cardinal.
float_t mag = sqrtf(moveDir[0] * moveDir[0] + moveDir[1] * moveDir[1]);
if(mag > 1.0f) {
moveDir[0] /= mag;
moveDir[1] /= mag;
}
vec3 velocity;
entityPhysicsGetVelocity(mgr, entityId, physComp, velocity);
velocity[0] = moveDir[0] * player->moveSpeed;
velocity[2] = moveDir[1] * player->moveSpeed;
entityPhysicsSetVelocity(mgr, entityId, physComp, velocity);
}
@@ -0,0 +1,64 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "entity/entitybase.h"
typedef struct {
float_t moveSpeed;
float_t jumpImpulse;
} entityplayer_t;
/**
* Initializes the player component: sets default move speed and jump
* impulse.
*
* @param mgr The entity manager that owns the entity.
* @param entityId The entity ID.
* @param componentId The component ID.
*/
void entityPlayerInit(
entitymanager_t *mgr,
const entityid_t entityId,
const componentid_t componentId
);
/**
* Gets the underlying player structure (temporarily) for the given entity.
* This is really just intended for doing operations faster than using the
* getters and setters, but it is preferred that you use those.
*
* @param mgr The entity manager that owns the entity.
* @param entityId The entity ID.
* @param componentId The component ID.
* @return The player component data for the given entity and component ID.
*/
entityplayer_t *entityPlayerGet(
entitymanager_t *mgr,
const entityid_t entityId,
const componentid_t componentId
);
/**
* Per-tick update for the player component: reads the movement input axes
* and drives the entity's physics velocity, relative to the current
* camera's horizontal facing (so "up" always moves away from the camera
* and "left"/"right" strafe relative to its view), rather than fixed world
* axes. No-op if the entity has no physics component. Registered
* automatically as an update callback by entityPlayerInit.
*
* @param mgr The entity manager that owns the entity.
* @param entityId The entity ID.
* @param componentId The component ID.
* @param user Unused.
*/
void entityPlayerUpdate(
entitymanager_t *mgr,
const entityid_t entityId,
const componentid_t componentId,
void *user
);
+19
View File
@@ -0,0 +1,19 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
// Game-specific component types, appended after the engine's inbuilt
// components in entity/componentlist.h.
#include "entity/component/overworld/entityplayer.h"
// Name (Uppercase)
// Structure
// Field name (lowercase)
// Init function (optional)
// Dispose function (optional)
// Render function (optional)
X(PLAYER, entityplayer_t, player, entityPlayerInit, NULL, NULL)
+3
View File
@@ -6,8 +6,11 @@
*/
#include "game/game.h"
#include "scene/scene.h"
#include "scene/overworldscene.h"
errorret_t gameInit(void) {
sceneSetActive(overworldSceneCreate());
errorOk();
}
+9
View File
@@ -0,0 +1,9 @@
# Copyright (c) 2026 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
target_sources(${DUSK_LIBRARY_TARGET_NAME}
PUBLIC
overworldscene.c
)
+133
View File
@@ -0,0 +1,133 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "overworldscene.h"
#include "scene/scene.h"
#include "entity/entitymanager.h"
#include "entity/component/display/entityposition.h"
#include "entity/component/display/entitycamera.h"
#include "entity/component/display/entityrenderable.h"
#include "entity/component/physics/entityphysics.h"
#include "display/mesh/plane.h"
#include "display/mesh/capsule.h"
#include "display/color.h"
#include "time/time.h"
#define OVERWORLD_SCENE_CAMERA_ORBIT_RADIUS 8.0f
#define OVERWORLD_SCENE_CAMERA_ORBIT_HEIGHT 5.0f
#define OVERWORLD_SCENE_CAMERA_ORBIT_SPEED 0.5f
static overworldcameraorbit_t OVERWORLD_SCENE_CAMERA_ORBIT;
sceneid_t overworldSceneCreate(void) {
sceneid_t sceneId = sceneCreate();
entitymanager_t *mgr = sceneGetEntities(sceneId);
// Camera, orbiting the origin (see overworldSceneCameraOrbitUpdate).
entityid_t camEntity = entityManagerAdd(mgr);
componentid_t camPosition = entityAddComponent(
mgr, camEntity, COMPONENT_TYPE_POSITION
);
entityAddComponent(mgr, camEntity, COMPONENT_TYPE_CAMERA);
OVERWORLD_SCENE_CAMERA_ORBIT = (overworldcameraorbit_t){
.angle = 0.0f,
.radius = OVERWORLD_SCENE_CAMERA_ORBIT_RADIUS,
.height = OVERWORLD_SCENE_CAMERA_ORBIT_HEIGHT,
.speed = OVERWORLD_SCENE_CAMERA_ORBIT_SPEED
};
entityUpdateAdd(
mgr, camEntity, overworldSceneCameraOrbitUpdate, camPosition,
&OVERWORLD_SCENE_CAMERA_ORBIT
);
// Position it correctly for the very first rendered frame, rather than
// waiting for the first fixed tick to run.
overworldSceneCameraOrbitUpdate(
mgr, camEntity, camPosition, &OVERWORLD_SCENE_CAMERA_ORBIT
);
// Static ground plane. Physics ignores the entity's position component --
// the shape's own normal/distance fully define the plane in world space.
entityid_t planeEntity = entityManagerAdd(mgr);
componentid_t planePosition = entityAddComponent(
mgr, planeEntity, COMPONENT_TYPE_POSITION
);
entityPositionSetLocalPosition(
mgr, planeEntity, planePosition, (vec3){ -10.0f, 0.0f, -10.0f }
);
entityPositionSetLocalScale(
mgr, planeEntity, planePosition, (vec3){ 20.0f, 1.0f, 20.0f }
);
componentid_t planePhysics = entityAddComponent(
mgr, planeEntity, COMPONENT_TYPE_PHYSICS
);
entityPhysicsSetBodyType(
mgr, planeEntity, planePhysics, PHYSICS_BODY_STATIC
);
entityPhysicsSetShape(mgr, planeEntity, planePhysics, (physicsshape_t){
.type = PHYSICS_SHAPE_PLANE,
.data.plane = { .normal = { 0.0f, 1.0f, 0.0f }, .distance = 0.0f }
});
componentid_t planeRenderable = entityAddComponent(
mgr, planeEntity, COMPONENT_TYPE_RENDERABLE
);
entityrenderable_t *planeR = componentGetData(
mgr, planeEntity, planeRenderable, COMPONENT_TYPE_RENDERABLE
);
planeR->data.material.meshes[0] = &PLANE_MESH_SIMPLE;
entityRenderableSetColor(mgr, planeEntity, planeRenderable, COLOR_GRAY);
// Player: dynamic capsule body, moved relative to the camera by
// COMPONENT_TYPE_PLAYER's own update callback (see entityplayer.c).
entityid_t playerEntity = entityManagerAdd(mgr);
componentid_t playerPosition = entityAddComponent(
mgr, playerEntity, COMPONENT_TYPE_POSITION
);
entityPositionSetLocalPosition(
mgr, playerEntity, playerPosition, (vec3){ 0.0f, 2.0f, 0.0f }
);
componentid_t playerPhysics = entityAddComponent(
mgr, playerEntity, COMPONENT_TYPE_PHYSICS
);
entityPhysicsSetShape(mgr, playerEntity, playerPhysics, (physicsshape_t){
.type = PHYSICS_SHAPE_CAPSULE,
.data.capsule = { .radius = 0.5f, .halfHeight = 0.5f }
});
componentid_t playerRenderable = entityAddComponent(
mgr, playerEntity, COMPONENT_TYPE_RENDERABLE
);
entityrenderable_t *playerR = componentGetData(
mgr, playerEntity, playerRenderable, COMPONENT_TYPE_RENDERABLE
);
playerR->data.material.meshes[0] = &CAPSULE_MESH_SIMPLE;
entityRenderableSetColor(mgr, playerEntity, playerRenderable, COLOR_BLUE);
entityAddComponent(mgr, playerEntity, COMPONENT_TYPE_PLAYER);
return sceneId;
}
void overworldSceneCameraOrbitUpdate(
entitymanager_t *mgr,
const entityid_t entityId,
const componentid_t componentId,
void *user
) {
overworldcameraorbit_t *orbit = (overworldcameraorbit_t *)user;
orbit->angle += TIME.delta * orbit->speed;
vec3 eye = {
cosf(orbit->angle) * orbit->radius,
orbit->height,
sinf(orbit->angle) * orbit->radius
};
entityPositionLookAt(
mgr, entityId, componentId,
eye,
(vec3){ 0.0f, 0.0f, 0.0f },
(vec3){ 0.0f, 1.0f, 0.0f }
);
}
+45
View File
@@ -0,0 +1,45 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "scene/scenebase.h"
#include "entity/entitybase.h"
typedef struct {
float_t angle;
float_t radius;
float_t height;
float_t speed;
} overworldcameraorbit_t;
/**
* Creates the overworld scene: a static ground plane, a player entity
* (dynamic capsule body, moved relative to the camera by
* COMPONENT_TYPE_PLAYER's own update callback), and a camera orbiting the
* origin (see overworldSceneCameraOrbitUpdate). Does not make the scene
* active -- call sceneSetActive with the returned ID.
*
* @return The ID of the newly created scene.
*/
sceneid_t overworldSceneCreate(void);
/**
* Per-tick update for the test camera: orbits it around the world origin
* at a fixed radius/height/speed, always looking back at the origin.
* Registered automatically by overworldSceneCreate.
*
* @param mgr The entity manager that owns the entity.
* @param entityId The camera entity ID.
* @param componentId The camera's position component ID.
* @param user Pointer to this camera's overworldcameraorbit_t state.
*/
void overworldSceneCameraOrbitUpdate(
entitymanager_t *mgr,
const entityid_t entityId,
const componentid_t componentId,
void *user
);