Scene prefabs

This commit is contained in:
2026-07-20 15:11:38 -05:00
parent 38c2f6b6f9
commit 7ceb9e571d
18 changed files with 349 additions and 52 deletions
@@ -11,10 +11,13 @@
#include "entity/component/display/entitycamera.h"
#include "entity/component/display/entityposition.h"
#include "input/input.h"
#include "time/time.h"
#include "util/memory.h"
#include "util/math.h"
#define ENTITY_PLAYER_MOVE_SPEED_DEFAULT 4.0f
#define ENTITY_PLAYER_JUMP_IMPULSE_DEFAULT 6.0f
#define ENTITY_PLAYER_TURN_SPEED_DEFAULT 10.0f
// Below this squared magnitude, movement input is treated as "not moving"
// and the player keeps facing whichever way it last faced, rather than
@@ -32,6 +35,7 @@ void entityPlayerInit(
player->moveSpeed = ENTITY_PLAYER_MOVE_SPEED_DEFAULT;
player->jumpImpulse = ENTITY_PLAYER_JUMP_IMPULSE_DEFAULT;
player->turnSpeed = ENTITY_PLAYER_TURN_SPEED_DEFAULT;
entityUpdateAdd(mgr, entityId, entityPlayerUpdate, componentId, NULL);
}
@@ -109,7 +113,16 @@ void entityPlayerUpdate(
// faces the opposite way), which for a pure yaw rotation works out to
// (sin(yaw), 0, cos(yaw)) -- invert that to solve for the yaw facing
// moveDir.
rotation[1] = atan2f(moveDir[0], moveDir[1]);
float_t targetYaw = atan2f(moveDir[0], moveDir[1]);
// Turn towards targetYaw at turnSpeed rad/s instead of snapping to it:
// wrap the shortest signed delta into [-PI, PI), then clamp the step.
float_t yawDelta = mathModFloat(
targetYaw - rotation[1] + (float_t)MATH_PI, 2.0f * (float_t)MATH_PI
) - (float_t)MATH_PI;
float_t maxStep = player->turnSpeed * TIME.delta;
rotation[1] += mathClamp(yawDelta, -maxStep, maxStep);
entityPositionSetLocalRotation(mgr, entityId, posComp, rotation);
}
@@ -124,6 +137,7 @@ errorret_t entityPlayerSerialize(
yyjson_mut_obj_add_real(doc, json, "moveSpeed", player->moveSpeed);
yyjson_mut_obj_add_real(doc, json, "jumpImpulse", player->jumpImpulse);
yyjson_mut_obj_add_real(doc, json, "turnSpeed", player->turnSpeed);
errorOk();
}
@@ -143,6 +157,9 @@ errorret_t entityPlayerDeserialize(
if((v = yyjson_obj_get(json, "jumpImpulse"))) {
player->jumpImpulse = (float_t)yyjson_get_num(v);
}
if((v = yyjson_obj_get(json, "turnSpeed"))) {
player->turnSpeed = (float_t)yyjson_get_num(v);
}
errorOk();
}
@@ -13,11 +13,14 @@
typedef struct {
float_t moveSpeed;
float_t jumpImpulse;
/** Yaw turn rate, in radians/second, when facing the movement direction. */
float_t turnSpeed;
} entityplayer_t;
/**
* Initializes the player component: sets default move speed and jump
* impulse.
* Initializes the player component: sets default move speed, jump
* impulse, and turn speed.
*
* @param mgr The entity manager that owns the entity.
* @param entityId The entity ID.
@@ -53,7 +56,8 @@ entityplayer_t *entityPlayerGet(
* axes. No-op if the entity has no physics component.
*
* Also yaws the entity's position component (if any) to face the
* direction of movement. Facing is only updated while there's actual
* direction of movement, turning at most turnSpeed radians/second rather
* than snapping instantly. Facing is only updated while there's actual
* movement input -- releasing input leaves the entity facing whichever
* way it last moved, rather than snapping to a default orientation.
*
@@ -72,8 +76,8 @@ void entityPlayerUpdate(
);
/**
* Serializes the player's "moveSpeed" and "jumpImpulse" into the given
* JSON object.
* Serializes the player's "moveSpeed", "jumpImpulse", and "turnSpeed"
* into the given JSON object.
*
* @param mgr The entity manager that owns the entity.
* @param entityId The entity ID.
@@ -91,9 +95,9 @@ errorret_t entityPlayerSerialize(
);
/**
* Reads "moveSpeed" and "jumpImpulse" (see entityPlayerSerialize()) from
* the given JSON object and applies whichever are present, leaving the
* rest at their current values.
* Reads "moveSpeed", "jumpImpulse", and "turnSpeed" (see
* entityPlayerSerialize()) from the given JSON object and applies
* whichever are present, leaving the rest at their current values.
*
* @param mgr The entity manager that owns the entity.
* @param entityId The entity ID.
+2
View File
@@ -8,9 +8,11 @@
// Game-specific prefabs, appended after the engine's inbuilt prefabs in
// entity/entityprefablist.h.
#include "entity/prefab/entityprefabplayer.h"
#include "entity/prefab/entityprefaboverworldcamera.h"
// Name (Uppercase)
// Extends (name of another prefab to apply first, or "" for none)
// Apply function
X(PLAYER, "", entityPrefabPlayerApply)
X(OVERWORLD_CAMERA, "", entityPrefabOverworldCameraApply)
+1
View File
@@ -6,4 +6,5 @@
target_sources(${DUSK_LIBRARY_TARGET_NAME}
PUBLIC
entityprefabplayer.c
entityprefaboverworldcamera.c
)
@@ -0,0 +1,37 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "entityprefaboverworldcamera.h"
#include "entity/entitymanager.h"
#include "entity/component/display/entityposition.h"
#include "entity/component/display/entitycamera.h"
errorret_t entityPrefabOverworldCameraApply(
entitymanager_t *mgr,
const entityid_t entityId
) {
componentid_t posComp = entityAddComponent(
mgr, entityId, COMPONENT_TYPE_POSITION
);
vec3 eye = { 10.0f, 10.0f, 10.0f };
vec3 target = { 0.0f, 0.0f, 0.0f };
vec3 up = { 0.0f, 1.0f, 0.0f };
entityPositionLookAt(mgr, entityId, posComp, eye, target, up);
componentid_t camComp = entityAddComponent(
mgr, entityId, COMPONENT_TYPE_CAMERA
);
entitycamera_t *cam = componentGetData(
mgr, entityId, camComp, COMPONENT_TYPE_CAMERA
);
cam->projType = ENTITY_CAMERA_PROJECTION_TYPE_PERSPECTIVE;
cam->nearClip = 0.1f;
cam->farClip = 1000.0f;
cam->perspective.fov = 0.9f;
errorOk();
}
@@ -0,0 +1,25 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "entity/entitybase.h"
#include "error/error.h"
/**
* Applies the OVERWORLD_CAMERA prefab: a POSITION at (10, 10, 10) looking
* at the world origin, and a perspective CAMERA (0.1/1000 near/far clip,
* 0.9 fov). C-coded equivalent of the hand-authored "camera" entity
* previously in assets/scenes/test.json.
*
* @param mgr The entity manager that owns the entity.
* @param entityId The ID of the entity to apply the prefab to.
* @return Error state.
*/
errorret_t entityPrefabOverworldCameraApply(
entitymanager_t *mgr,
const entityid_t entityId
);
+2
View File
@@ -7,3 +7,5 @@ target_sources(${DUSK_LIBRARY_TARGET_NAME}
PUBLIC
overworldscene.c
)
add_subdirectory(prefab)
+16
View File
@@ -0,0 +1,16 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
// Game-specific scene prefabs, appended after the engine's inbuilt scene
// prefabs in scene/sceneprefablist.h.
#include "scene/prefab/sceneprefaboverworldscene.h"
// Name (Uppercase)
// Extends (name of another prefab to apply first, or "" for none)
// Apply function
X(OVERWORLDSCENE, "", scenePrefabOverworldSceneApply)
+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
sceneprefaboverworldscene.c
)
@@ -0,0 +1,26 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "sceneprefaboverworldscene.h"
#include "scene/scene.h"
#include "entity/entityprefab.h"
errorret_t scenePrefabOverworldSceneApply(const sceneid_t sceneId) {
entitymanager_t *mgr = sceneGetEntities(sceneId);
entityid_t cameraEntity = entityManagerAdd(mgr);
entitySetName(mgr, cameraEntity, "camera");
errorChain(
entityPrefabResolveAndApply(mgr, cameraEntity, "OVERWORLD_CAMERA")
);
entityid_t playerEntity = entityManagerAdd(mgr);
entitySetName(mgr, playerEntity, "player");
errorChain(entityPrefabResolveAndApply(mgr, playerEntity, "PLAYER"));
errorOk();
}
@@ -0,0 +1,20 @@
/**
* 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 "error/error.h"
/**
* Applies the OVERWORLDSCENE prefab: spawns a "camera" entity (see
* entityPrefabOverworldCameraApply()) and a "player" entity (see
* entityPrefabPlayerApply()) into the scene.
*
* @param sceneId The ID of the scene to apply the prefab to.
* @return Error state.
*/
errorret_t scenePrefabOverworldSceneApply(const sceneid_t sceneId);