feat: stage simulation garden first read
This commit is contained in:
@@ -637,11 +637,14 @@ Completed:
|
|||||||
and verifies live NPC visuals in the terrain-backed village.
|
and verifies live NPC visuals in the terrain-backed village.
|
||||||
12. Runtime task glyphs: active NPCs now keep compact task-intent markers in
|
12. Runtime task glyphs: active NPCs now keep compact task-intent markers in
|
||||||
cinematic mode while debug names, labels, and inspector UI remain optional.
|
cinematic mode while debug names, labels, and inspector UI remain optional.
|
||||||
|
13. Runtime first-read staging: the capture tool uses an explicit presentation
|
||||||
|
camera preset, the gameplay camera supports the same staging API, and the
|
||||||
|
village has authored dirt path strips for the current task loop.
|
||||||
|
|
||||||
Next:
|
Next:
|
||||||
|
|
||||||
1. Improve camera staging plus readable path and landmark composition without
|
1. Strengthen landmark and work-site silhouettes without adding new simulation
|
||||||
adding new simulation mechanics.
|
mechanics.
|
||||||
|
|
||||||
Do not start with GIS data, a full city, a large asset pack, or more NPC
|
Do not start with GIS data, a full city, a large asset pack, or more NPC
|
||||||
mechanics. The next proof is a beautiful stage for the systems that already
|
mechanics. The next proof is a beautiful stage for the systems that already
|
||||||
|
|||||||
@@ -685,6 +685,8 @@ Recently completed:
|
|||||||
village.
|
village.
|
||||||
- Runtime task glyphs preserve active NPC task intent in cinematic mode while
|
- Runtime task glyphs preserve active NPC task intent in cinematic mode while
|
||||||
leaving the simulation task state authoritative.
|
leaving the simulation task state authoritative.
|
||||||
|
- Runtime first-read staging now includes a presentation camera preset and
|
||||||
|
authored path strips that reveal the active village task loop.
|
||||||
|
|
||||||
This order strengthens the simulation while regularly producing visible
|
This order strengthens the simulation while regularly producing visible
|
||||||
progress suitable for public development updates.
|
progress suitable for public development updates.
|
||||||
|
|||||||
@@ -218,8 +218,9 @@ plugin content, not game architecture.
|
|||||||
- The player is a `CharacterBody3D` represented by placeholder primitive
|
- The player is a `CharacterBody3D` represented by placeholder primitive
|
||||||
geometry.
|
geometry.
|
||||||
- WASD movement is camera-relative.
|
- WASD movement is camera-relative.
|
||||||
- The elevated third-person camera rotates with the mouse and uses smoothed
|
- The elevated third-person camera rotates with the mouse, uses smoothed
|
||||||
follow/focus behavior.
|
follow/focus behavior, and exposes an opt-in presentation preset for
|
||||||
|
reproducible runtime captures.
|
||||||
- Pressing `E` near a berry bush or tree extracts its configured yield into the
|
- Pressing `E` near a berry bush or tree extracts its configured yield into the
|
||||||
village through the same `ResourceNode` contract used by NPCs.
|
village through the same `ResourceNode` contract used by NPCs.
|
||||||
- Guard, study, rest, and food interactions now use typed world sites.
|
- Guard, study, rest, and food interactions now use typed world sites.
|
||||||
@@ -331,6 +332,7 @@ distance with resource `safety_risk`, `comfort_distance`, and
|
|||||||
- a centered 512 m Terrain3D landscape split across four regions;
|
- a centered 512 m Terrain3D landscape split across four regions;
|
||||||
- deterministic shaped terrain data and six game-owned surface layers;
|
- deterministic shaped terrain data and six game-owned surface layers;
|
||||||
- fortress, houses, mill, bridge, shader river/waterfall, mist, and smoke;
|
- fortress, houses, mill, bridge, shader river/waterfall, mist, and smoke;
|
||||||
|
- authored dirt path strips that make the current village task loop readable;
|
||||||
- warm sky, fog, shadows, and wind-reactive foliage;
|
- warm sky, fog, shadows, and wind-reactive foliage;
|
||||||
- eight ResourceNodes preserving stable food/wood discovery IDs;
|
- eight ResourceNodes preserving stable food/wood discovery IDs;
|
||||||
- typed village pantry storage and typed guard, study, and rest activity sites;
|
- typed village pantry storage and typed guard, study, and rest activity sites;
|
||||||
|
|||||||
@@ -29,15 +29,18 @@ Terrain3D world is running in `main.tscn`, NPCs are alive in the terrain-backed
|
|||||||
village, the selected-NPC reason inspector is fed by real task scoring, and the
|
village, the selected-NPC reason inspector is fed by real task scoring, and the
|
||||||
cinematic/debug toggle produces a shareable view without changing simulation
|
cinematic/debug toggle produces a shareable view without changing simulation
|
||||||
state. A follow-up pass adds small task glyphs above active NPCs, so the
|
state. A follow-up pass adds small task glyphs above active NPCs, so the
|
||||||
cinematic view now preserves task intent after the debug labels are hidden.
|
cinematic view now preserves task intent after the debug labels are hidden. A
|
||||||
|
camera-staging pass then gives the runtime capture a wider village view and adds
|
||||||
|
authored dirt path strips that make the task loop readable from the first frame.
|
||||||
|
|
||||||
The frame also makes the next presentation risks plain:
|
The frame also makes the next presentation risks plain:
|
||||||
|
|
||||||
- the camera still favors verification over composition;
|
- the camera still favors verification over composition;
|
||||||
- placeholder characters, houses, and work props remain useful but visibly
|
- placeholder characters, houses, and work props remain useful but visibly
|
||||||
prototype-grade;
|
prototype-grade;
|
||||||
- terrain texture variation reads better than the old flat map, but paths,
|
- terrain texture variation and path strips read better than the old flat map,
|
||||||
landmarks, water, and foreground silhouettes need stronger first-read staging;
|
but landmarks, water, and foreground silhouettes need stronger first-read
|
||||||
|
staging;
|
||||||
- task glyphs help, but the cinematic view still needs clearer work-site props
|
- task glyphs help, but the cinematic view still needs clearer work-site props
|
||||||
and route composition so intent is readable before the viewer learns the icon
|
and route composition so intent is readable before the viewer learns the icon
|
||||||
language.
|
language.
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 506 KiB After Width: | Height: | Size: 707 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 530 KiB After Width: | Height: | Size: 694 KiB |
@@ -53,6 +53,7 @@ target = NodePath("../Player")
|
|||||||
follow_speed = 8.0
|
follow_speed = 8.0
|
||||||
deadzone_right = 2.0
|
deadzone_right = 2.0
|
||||||
deadzone_forward = 2.0
|
deadzone_forward = 2.0
|
||||||
|
max_zoom_offset = Vector3(0, 20, 15)
|
||||||
|
|
||||||
[node name="Camera3D" type="Camera3D" parent="CameraRig" unique_id=1992528776]
|
[node name="Camera3D" type="Camera3D" parent="CameraRig" unique_id=1992528776]
|
||||||
current = true
|
current = true
|
||||||
|
|||||||
+19
-2
@@ -6,6 +6,7 @@ extends Node3D
|
|||||||
@export var follow_speed := 12.0
|
@export var follow_speed := 12.0
|
||||||
@export var rotation_smooth_speed := 18.0
|
@export var rotation_smooth_speed := 18.0
|
||||||
@export var focus_smooth_speed := 14.0
|
@export var focus_smooth_speed := 14.0
|
||||||
|
@export var pitch_degrees := -55.0
|
||||||
|
|
||||||
@export var mouse_sensitivity := 0.002
|
@export var mouse_sensitivity := 0.002
|
||||||
|
|
||||||
@@ -32,7 +33,7 @@ func _ready() -> void:
|
|||||||
if target:
|
if target:
|
||||||
focus_position = target.global_position
|
focus_position = target.global_position
|
||||||
desired_focus_position = focus_position
|
desired_focus_position = focus_position
|
||||||
global_position = focus_position + min_zoom_offset.lerp(max_zoom_offset, zoom_smooth)
|
_apply_camera_transform()
|
||||||
|
|
||||||
|
|
||||||
func _unhandled_input(event: InputEvent) -> void:
|
func _unhandled_input(event: InputEvent) -> void:
|
||||||
@@ -59,7 +60,7 @@ func _physics_process(delta: float) -> void:
|
|||||||
|
|
||||||
smoothed_yaw = lerp_angle(smoothed_yaw, yaw, rot_t)
|
smoothed_yaw = lerp_angle(smoothed_yaw, yaw, rot_t)
|
||||||
|
|
||||||
rotation = Vector3(deg_to_rad(-55), smoothed_yaw, 0)
|
rotation = Vector3(deg_to_rad(pitch_degrees), smoothed_yaw, 0)
|
||||||
|
|
||||||
var target_pos := target.global_position
|
var target_pos := target.global_position
|
||||||
var diff := target_pos - desired_focus_position
|
var diff := target_pos - desired_focus_position
|
||||||
@@ -89,3 +90,19 @@ func _physics_process(delta: float) -> void:
|
|||||||
var desired_position := focus_position + rotated_offset
|
var desired_position := focus_position + rotated_offset
|
||||||
|
|
||||||
global_position = global_position.lerp(desired_position, follow_t)
|
global_position = global_position.lerp(desired_position, follow_t)
|
||||||
|
|
||||||
|
|
||||||
|
func apply_presentation_preset(focus: Vector3, yaw_degrees: float, zoom_value: float) -> void:
|
||||||
|
yaw = deg_to_rad(yaw_degrees)
|
||||||
|
smoothed_yaw = yaw
|
||||||
|
zoom = clampf(zoom_value, 0.0, 1.0)
|
||||||
|
zoom_smooth = zoom
|
||||||
|
focus_position = focus
|
||||||
|
desired_focus_position = focus
|
||||||
|
_apply_camera_transform()
|
||||||
|
|
||||||
|
|
||||||
|
func _apply_camera_transform() -> void:
|
||||||
|
rotation = Vector3(deg_to_rad(pitch_degrees), smoothed_yaw, 0)
|
||||||
|
var active_offset := min_zoom_offset.lerp(max_zoom_offset, zoom_smooth)
|
||||||
|
global_position = focus_position + active_offset.rotated(Vector3.UP, smoothed_yaw)
|
||||||
|
|||||||
@@ -27,6 +27,17 @@ func _run() -> void:
|
|||||||
terrain.get_camera() == main_scene.get_node("CameraRig/Camera3D"),
|
terrain.get_camera() == main_scene.get_node("CameraRig/Camera3D"),
|
||||||
"Runtime Terrain3D should bind to the gameplay camera"
|
"Runtime Terrain3D should bind to the gameplay camera"
|
||||||
)
|
)
|
||||||
|
var camera_rig := main_scene.get_node("CameraRig")
|
||||||
|
_check(
|
||||||
|
camera_rig.has_method("apply_presentation_preset"),
|
||||||
|
"Runtime camera should expose a presentation staging preset"
|
||||||
|
)
|
||||||
|
var presentation_focus := Vector3(-2.0, 1.2, -0.5)
|
||||||
|
camera_rig.apply_presentation_preset(presentation_focus, 128.0, 0.95)
|
||||||
|
_check(
|
||||||
|
camera_rig.global_position.distance_to(presentation_focus) > 10.0,
|
||||||
|
"Presentation preset should pull the camera back from the village focus"
|
||||||
|
)
|
||||||
_check(terrain.collision_mode != 0, "Runtime Terrain3D collision should be enabled")
|
_check(terrain.collision_mode != 0, "Runtime Terrain3D collision should be enabled")
|
||||||
_check(
|
_check(
|
||||||
not main_scene.has_node("JajceWorld/NavigationRegion3D/GreyboxGround"),
|
not main_scene.has_node("JajceWorld/NavigationRegion3D/GreyboxGround"),
|
||||||
@@ -47,6 +58,12 @@ func _run() -> void:
|
|||||||
simulation_manager.resource_states.size() == 12,
|
simulation_manager.resource_states.size() == 12,
|
||||||
"Simulation authority should bind all twelve Jajce resources"
|
"Simulation authority should bind all twelve Jajce resources"
|
||||||
)
|
)
|
||||||
|
var village_root := main_scene.get_node("JajceWorld/VillageRoot")
|
||||||
|
var path_strips := 0
|
||||||
|
for child in village_root.get_children():
|
||||||
|
if child.name.begins_with("Path_"):
|
||||||
|
path_strips += 1
|
||||||
|
_check(path_strips >= 4, "Runtime should include authored path strips for first-read composition")
|
||||||
|
|
||||||
var navigation_map: RID = main_scene.get_world_3d().navigation_map
|
var navigation_map: RID = main_scene.get_world_3d().navigation_map
|
||||||
var navigation_region := main_scene.get_node("JajceWorld/NavigationRegion3D") as NavigationRegion3D
|
var navigation_region := main_scene.get_node("JajceWorld/NavigationRegion3D") as NavigationRegion3D
|
||||||
|
|||||||
@@ -25,8 +25,10 @@ func _run() -> void:
|
|||||||
|
|
||||||
root.add_child(main_scene)
|
root.add_child(main_scene)
|
||||||
await process_frame
|
await process_frame
|
||||||
|
_apply_capture_camera(main_scene)
|
||||||
for _frame in WARMUP_FRAMES:
|
for _frame in WARMUP_FRAMES:
|
||||||
await process_frame
|
await process_frame
|
||||||
|
_apply_capture_camera(main_scene)
|
||||||
|
|
||||||
var active_npcs := main_scene.get_node("ActiveNPCs")
|
var active_npcs := main_scene.get_node("ActiveNPCs")
|
||||||
var demo_controller := main_scene.get_node("DemoController")
|
var demo_controller := main_scene.get_node("DemoController")
|
||||||
@@ -104,3 +106,14 @@ func _analyze_image(image: Image) -> Dictionary:
|
|||||||
"average_luma": total_luma / samples,
|
"average_luma": total_luma / samples,
|
||||||
"range": max_luma - min_luma,
|
"range": max_luma - min_luma,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func _apply_capture_camera(main_scene: Node) -> void:
|
||||||
|
var camera_rig := main_scene.get_node("CameraRig")
|
||||||
|
if camera_rig.has_method("apply_presentation_preset"):
|
||||||
|
camera_rig.pitch_degrees = -42.0
|
||||||
|
camera_rig.apply_presentation_preset(Vector3(-2.0, 1.2, -0.5), 128.0, 0.95)
|
||||||
|
camera_rig.set_physics_process(false)
|
||||||
|
|
||||||
|
var camera := main_scene.get_node("CameraRig/Camera3D") as Camera3D
|
||||||
|
camera.fov = 62.0
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
[gd_scene load_steps=27 format=3]
|
[gd_scene load_steps=29 format=3]
|
||||||
|
|
||||||
[ext_resource type="Terrain3DAssets" path="res://terrain/jajce/assets.tres" id="1_assets"]
|
[ext_resource type="Terrain3DAssets" path="res://terrain/jajce/assets.tres" id="1_assets"]
|
||||||
[ext_resource type="PackedScene" path="res://world/resource_nodes/ResourceNode.tscn" id="2_resource"]
|
[ext_resource type="PackedScene" path="res://world/resource_nodes/ResourceNode.tscn" id="2_resource"]
|
||||||
@@ -45,6 +45,14 @@ size = Vector3(64, 0.2, 64)
|
|||||||
[sub_resource type="BoxShape3D" id="Shape_ground"]
|
[sub_resource type="BoxShape3D" id="Shape_ground"]
|
||||||
size = Vector3(64, 0.2, 64)
|
size = Vector3(64, 0.2, 64)
|
||||||
|
|
||||||
|
[sub_resource type="StandardMaterial3D" id="Material_path_readability"]
|
||||||
|
albedo_color = Color(0.52, 0.39, 0.24, 1)
|
||||||
|
roughness = 0.95
|
||||||
|
|
||||||
|
[sub_resource type="BoxMesh" id="Mesh_path_readability"]
|
||||||
|
material = SubResource("Material_path_readability")
|
||||||
|
size = Vector3(1, 0.06, 1)
|
||||||
|
|
||||||
[sub_resource type="ProceduralSkyMaterial" id="SkyMaterial_jajce"]
|
[sub_resource type="ProceduralSkyMaterial" id="SkyMaterial_jajce"]
|
||||||
sky_top_color = Color(0.23, 0.43, 0.62, 1)
|
sky_top_color = Color(0.23, 0.43, 0.62, 1)
|
||||||
sky_horizon_color = Color(0.82, 0.68, 0.5, 1)
|
sky_horizon_color = Color(0.82, 0.68, 0.5, 1)
|
||||||
@@ -170,6 +178,34 @@ position = Vector3(19, 2, 7)
|
|||||||
[node name="Bridge" parent="VillageRoot" instance=ExtResource("8_bridge")]
|
[node name="Bridge" parent="VillageRoot" instance=ExtResource("8_bridge")]
|
||||||
position = Vector3(25, 0.5, 0)
|
position = Vector3(25, 0.5, 0)
|
||||||
|
|
||||||
|
[node name="Path_Village_Spine" type="MeshInstance3D" parent="VillageRoot"]
|
||||||
|
position = Vector3(-3, 0.04, -1)
|
||||||
|
rotation_degrees = Vector3(0, 20, 0)
|
||||||
|
scale = Vector3(1.25, 1, 13)
|
||||||
|
cast_shadow = 0
|
||||||
|
mesh = SubResource("Mesh_path_readability")
|
||||||
|
|
||||||
|
[node name="Path_Pantry_Worksites" type="MeshInstance3D" parent="VillageRoot"]
|
||||||
|
position = Vector3(-2, 0.04, -7.5)
|
||||||
|
rotation_degrees = Vector3(0, 82, 0)
|
||||||
|
scale = Vector3(1.15, 1, 8)
|
||||||
|
cast_shadow = 0
|
||||||
|
mesh = SubResource("Mesh_path_readability")
|
||||||
|
|
||||||
|
[node name="Path_River_Bridge" type="MeshInstance3D" parent="VillageRoot"]
|
||||||
|
position = Vector3(12, 0.04, 3.5)
|
||||||
|
rotation_degrees = Vector3(0, 72, 0)
|
||||||
|
scale = Vector3(1.35, 1, 18)
|
||||||
|
cast_shadow = 0
|
||||||
|
mesh = SubResource("Mesh_path_readability")
|
||||||
|
|
||||||
|
[node name="Path_Forest_Edge" type="MeshInstance3D" parent="VillageRoot"]
|
||||||
|
position = Vector3(4, 0.04, -9)
|
||||||
|
rotation_degrees = Vector3(0, -42, 0)
|
||||||
|
scale = Vector3(1.1, 1, 9)
|
||||||
|
cast_shadow = 0
|
||||||
|
mesh = SubResource("Mesh_path_readability")
|
||||||
|
|
||||||
[node name="FoliageRoot" type="Node3D" parent="."]
|
[node name="FoliageRoot" type="Node3D" parent="."]
|
||||||
|
|
||||||
[node name="Tree_West_01" parent="FoliageRoot" instance=ExtResource("4_tree")]
|
[node name="Tree_West_01" parent="FoliageRoot" instance=ExtResource("4_tree")]
|
||||||
|
|||||||
Reference in New Issue
Block a user