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.
|
||||
12. Runtime task glyphs: active NPCs now keep compact task-intent markers in
|
||||
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:
|
||||
|
||||
1. Improve camera staging plus readable path and landmark composition without
|
||||
adding new simulation mechanics.
|
||||
1. Strengthen landmark and work-site silhouettes without adding new simulation
|
||||
mechanics.
|
||||
|
||||
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
|
||||
|
||||
@@ -685,6 +685,8 @@ Recently completed:
|
||||
village.
|
||||
- Runtime task glyphs preserve active NPC task intent in cinematic mode while
|
||||
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
|
||||
progress suitable for public development updates.
|
||||
|
||||
@@ -218,8 +218,9 @@ plugin content, not game architecture.
|
||||
- The player is a `CharacterBody3D` represented by placeholder primitive
|
||||
geometry.
|
||||
- WASD movement is camera-relative.
|
||||
- The elevated third-person camera rotates with the mouse and uses smoothed
|
||||
follow/focus behavior.
|
||||
- The elevated third-person camera rotates with the mouse, uses smoothed
|
||||
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
|
||||
village through the same `ResourceNode` contract used by NPCs.
|
||||
- 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;
|
||||
- deterministic shaped terrain data and six game-owned surface layers;
|
||||
- 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;
|
||||
- eight ResourceNodes preserving stable food/wood discovery IDs;
|
||||
- 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
|
||||
cinematic/debug toggle produces a shareable view without changing simulation
|
||||
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 camera still favors verification over composition;
|
||||
- placeholder characters, houses, and work props remain useful but visibly
|
||||
prototype-grade;
|
||||
- terrain texture variation reads better than the old flat map, but paths,
|
||||
landmarks, water, and foreground silhouettes need stronger first-read staging;
|
||||
- terrain texture variation and path strips read better than the old flat map,
|
||||
but landmarks, water, and foreground silhouettes need stronger first-read
|
||||
staging;
|
||||
- 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
|
||||
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
|
||||
deadzone_right = 2.0
|
||||
deadzone_forward = 2.0
|
||||
max_zoom_offset = Vector3(0, 20, 15)
|
||||
|
||||
[node name="Camera3D" type="Camera3D" parent="CameraRig" unique_id=1992528776]
|
||||
current = true
|
||||
|
||||
+19
-2
@@ -6,6 +6,7 @@ extends Node3D
|
||||
@export var follow_speed := 12.0
|
||||
@export var rotation_smooth_speed := 18.0
|
||||
@export var focus_smooth_speed := 14.0
|
||||
@export var pitch_degrees := -55.0
|
||||
|
||||
@export var mouse_sensitivity := 0.002
|
||||
|
||||
@@ -32,7 +33,7 @@ func _ready() -> void:
|
||||
if target:
|
||||
focus_position = target.global_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:
|
||||
@@ -59,7 +60,7 @@ func _physics_process(delta: float) -> void:
|
||||
|
||||
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 diff := target_pos - desired_focus_position
|
||||
@@ -89,3 +90,19 @@ func _physics_process(delta: float) -> void:
|
||||
var desired_position := focus_position + rotated_offset
|
||||
|
||||
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"),
|
||||
"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(
|
||||
not main_scene.has_node("JajceWorld/NavigationRegion3D/GreyboxGround"),
|
||||
@@ -47,6 +58,12 @@ func _run() -> void:
|
||||
simulation_manager.resource_states.size() == 12,
|
||||
"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_region := main_scene.get_node("JajceWorld/NavigationRegion3D") as NavigationRegion3D
|
||||
|
||||
@@ -25,8 +25,10 @@ func _run() -> void:
|
||||
|
||||
root.add_child(main_scene)
|
||||
await process_frame
|
||||
_apply_capture_camera(main_scene)
|
||||
for _frame in WARMUP_FRAMES:
|
||||
await process_frame
|
||||
_apply_capture_camera(main_scene)
|
||||
|
||||
var active_npcs := main_scene.get_node("ActiveNPCs")
|
||||
var demo_controller := main_scene.get_node("DemoController")
|
||||
@@ -104,3 +106,14 @@ func _analyze_image(image: Image) -> Dictionary:
|
||||
"average_luma": total_luma / samples,
|
||||
"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="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"]
|
||||
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"]
|
||||
sky_top_color = Color(0.23, 0.43, 0.62, 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")]
|
||||
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="Tree_West_01" parent="FoliageRoot" instance=ExtResource("4_tree")]
|
||||
|
||||
Reference in New Issue
Block a user