intent refactor
This commit is contained in:
@@ -1,4 +1,13 @@
|
|||||||
use glam::Mat4;
|
use glam::{Mat4, Vec3};
|
||||||
|
|
||||||
|
pub struct CameraTransition
|
||||||
|
{
|
||||||
|
pub source_position: Vec3,
|
||||||
|
pub source_yaw: f32,
|
||||||
|
pub source_pitch: f32,
|
||||||
|
pub elapsed: f32,
|
||||||
|
pub duration: f32,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
pub struct CameraComponent
|
pub struct CameraComponent
|
||||||
|
|||||||
8
src/components/intent.rs
Normal file
8
src/components/intent.rs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
pub struct FollowPlayerIntent;
|
||||||
|
|
||||||
|
pub struct StopFollowingIntent;
|
||||||
|
|
||||||
|
pub struct CameraTransitionIntent
|
||||||
|
{
|
||||||
|
pub duration: f32,
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@ pub mod dialog;
|
|||||||
pub mod dissolve;
|
pub mod dissolve;
|
||||||
pub mod follow;
|
pub mod follow;
|
||||||
pub mod input;
|
pub mod input;
|
||||||
|
pub mod intent;
|
||||||
pub mod jump;
|
pub mod jump;
|
||||||
pub mod lights;
|
pub mod lights;
|
||||||
pub mod mesh;
|
pub mod mesh;
|
||||||
@@ -14,7 +15,7 @@ pub mod rotate;
|
|||||||
pub mod tree_instances;
|
pub mod tree_instances;
|
||||||
pub mod trigger;
|
pub mod trigger;
|
||||||
|
|
||||||
pub use camera::CameraComponent;
|
pub use camera::{CameraComponent, CameraTransition};
|
||||||
pub use dialog::{
|
pub use dialog::{
|
||||||
DialogBubbleComponent, DialogOutcome, DialogOutcomeEvent, DialogPhase,
|
DialogBubbleComponent, DialogOutcome, DialogOutcomeEvent, DialogPhase,
|
||||||
DialogProjectileComponent, DialogSourceComponent, ParryButton,
|
DialogProjectileComponent, DialogSourceComponent, ParryButton,
|
||||||
|
|||||||
@@ -3,8 +3,6 @@ mod inspector;
|
|||||||
use sdl3_sys::events::SDL_Event;
|
use sdl3_sys::events::SDL_Event;
|
||||||
|
|
||||||
use crate::entity::EntityHandle;
|
use crate::entity::EntityHandle;
|
||||||
use crate::systems::camera_noclip_system;
|
|
||||||
use crate::utility::input::InputState;
|
|
||||||
use crate::world::World;
|
use crate::world::World;
|
||||||
|
|
||||||
pub use inspector::FrameStats;
|
pub use inspector::FrameStats;
|
||||||
@@ -73,18 +71,8 @@ impl EditorState
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn editor_loop(
|
pub fn editor_loop(editor: &mut EditorState, world: &mut World, stats: &FrameStats)
|
||||||
editor: &mut EditorState,
|
|
||||||
world: &mut World,
|
|
||||||
input_state: &InputState,
|
|
||||||
stats: &FrameStats,
|
|
||||||
delta: f32,
|
|
||||||
)
|
|
||||||
{
|
{
|
||||||
if editor.right_mouse_held
|
|
||||||
{
|
|
||||||
camera_noclip_system(world, input_state, delta);
|
|
||||||
}
|
|
||||||
let selected = editor.selected_entity;
|
let selected = editor.selected_entity;
|
||||||
let show_player_state = editor.show_player_state;
|
let show_player_state = editor.show_player_state;
|
||||||
editor
|
editor
|
||||||
|
|||||||
71
src/main.rs
71
src/main.rs
@@ -21,20 +21,21 @@ use crate::bundles::spotlight::spawn_spotlights;
|
|||||||
use crate::bundles::terrain::{TerrainBundle, TerrainConfig};
|
use crate::bundles::terrain::{TerrainBundle, TerrainConfig};
|
||||||
use crate::bundles::test_char::TestCharBundle;
|
use crate::bundles::test_char::TestCharBundle;
|
||||||
use crate::bundles::Bundle;
|
use crate::bundles::Bundle;
|
||||||
|
use crate::components::intent::{FollowPlayerIntent, StopFollowingIntent};
|
||||||
use crate::debug::{collider_debug, DebugMode};
|
use crate::debug::{collider_debug, DebugMode};
|
||||||
use crate::editor::{editor_loop, EditorState, FrameStats};
|
use crate::editor::{editor_loop, EditorState, FrameStats};
|
||||||
use crate::entity::EntityHandle;
|
use crate::entity::EntityHandle;
|
||||||
use crate::loaders::scene::Space;
|
use crate::loaders::scene::Space;
|
||||||
use crate::physics::PhysicsManager;
|
use crate::physics::PhysicsManager;
|
||||||
use crate::render::snow::{SnowConfig, SnowLayer};
|
use crate::render::snow::{SnowConfig, SnowLayer};
|
||||||
use crate::systems::camera::stop_camera_following;
|
|
||||||
use crate::systems::{
|
use crate::systems::{
|
||||||
camera_follow_system, camera_input_system, camera_view_matrix, dialog_bubble_render_system,
|
camera_follow_system, camera_ground_clamp_system, camera_input_system, camera_intent_system,
|
||||||
dialog_camera_system, dialog_projectile_system, dialog_system, physics_sync_system,
|
camera_noclip_system, camera_transition_system, camera_view_matrix,
|
||||||
player_input_system, render_system, rotate_system, snow_system, spotlight_sync_system,
|
dialog_bubble_render_system, dialog_camera_system, dialog_camera_transition_system,
|
||||||
start_camera_following, state_machine_physics_system, state_machine_system,
|
dialog_projectile_system, dialog_system, physics_sync_system, player_input_system,
|
||||||
tree_dissolve_update_system, tree_instance_buffer_update_system, tree_occlusion_system,
|
render_system, rotate_system, snow_system, spotlight_sync_system, state_machine_physics_system,
|
||||||
trigger_system,
|
state_machine_system, tree_dissolve_update_system, tree_instance_buffer_update_system,
|
||||||
|
tree_occlusion_system, trigger_system,
|
||||||
};
|
};
|
||||||
use crate::utility::input::InputState;
|
use crate::utility::input::InputState;
|
||||||
use crate::utility::time::Time;
|
use crate::utility::time::Time;
|
||||||
@@ -72,6 +73,7 @@ fn init() -> Result<Game, Box<dyn std::error::Error>>
|
|||||||
.window("snow_trail", 1200, 900)
|
.window("snow_trail", 1200, 900)
|
||||||
.position_centered()
|
.position_centered()
|
||||||
.resizable()
|
.resizable()
|
||||||
|
.high_pixel_density()
|
||||||
.vulkan()
|
.vulkan()
|
||||||
.build()?;
|
.build()?;
|
||||||
let renderer = pollster::block_on(Renderer::new(&window, 2))?;
|
let renderer = pollster::block_on(Renderer::new(&window, 2))?;
|
||||||
@@ -85,7 +87,9 @@ fn init() -> Result<Game, Box<dyn std::error::Error>>
|
|||||||
editor.init_platform(&window);
|
editor.init_platform(&window);
|
||||||
|
|
||||||
let (mut world, camera_entity) = init_world()?;
|
let (mut world, camera_entity) = init_world()?;
|
||||||
start_camera_following(&mut world, camera_entity);
|
world
|
||||||
|
.follow_player_intents
|
||||||
|
.insert(camera_entity, FollowPlayerIntent);
|
||||||
|
|
||||||
let _event_pump = sdl_context.event_pump()?;
|
let _event_pump = sdl_context.event_pump()?;
|
||||||
let input_state = InputState::new();
|
let input_state = InputState::new();
|
||||||
@@ -208,7 +212,9 @@ fn process_events(game: &mut Game) -> bool
|
|||||||
{
|
{
|
||||||
game.editor.right_mouse_held = true;
|
game.editor.right_mouse_held = true;
|
||||||
game.input_state.mouse_captured = true;
|
game.input_state.mouse_captured = true;
|
||||||
stop_camera_following(&mut game.world, game.camera_entity);
|
game.world
|
||||||
|
.stop_following_intents
|
||||||
|
.insert(game.camera_entity, StopFollowingIntent);
|
||||||
game.sdl_context
|
game.sdl_context
|
||||||
.mouse()
|
.mouse()
|
||||||
.set_relative_mouse_mode(&game.window, true);
|
.set_relative_mouse_mode(&game.window, true);
|
||||||
@@ -265,7 +271,9 @@ fn toggle_editor(game: &mut Game)
|
|||||||
game.editor.active = !game.editor.active;
|
game.editor.active = !game.editor.active;
|
||||||
if game.editor.active
|
if game.editor.active
|
||||||
{
|
{
|
||||||
stop_camera_following(&mut game.world, game.camera_entity);
|
game.world
|
||||||
|
.stop_following_intents
|
||||||
|
.insert(game.camera_entity, StopFollowingIntent);
|
||||||
game.sdl_context
|
game.sdl_context
|
||||||
.mouse()
|
.mouse()
|
||||||
.set_relative_mouse_mode(&game.window, false);
|
.set_relative_mouse_mode(&game.window, false);
|
||||||
@@ -274,7 +282,9 @@ fn toggle_editor(game: &mut Game)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
start_camera_following(&mut game.world, game.camera_entity);
|
game.world
|
||||||
|
.follow_player_intents
|
||||||
|
.insert(game.camera_entity, FollowPlayerIntent);
|
||||||
game.input_state.mouse_captured = true;
|
game.input_state.mouse_captured = true;
|
||||||
game.sdl_context
|
game.sdl_context
|
||||||
.mouse()
|
.mouse()
|
||||||
@@ -323,7 +333,7 @@ fn submit_frame(game: &mut Game, draw_calls: &[render::DrawCall], time: f32, del
|
|||||||
let view_proj = projection * view;
|
let view_proj = projection * view;
|
||||||
let player_pos = game.world.player_position();
|
let player_pos = game.world.player_position();
|
||||||
|
|
||||||
let billboard_calls =
|
let (billboard_calls, text_vertices) =
|
||||||
dialog_bubble_render_system(&game.world, camera_transform.position, view_proj);
|
dialog_bubble_render_system(&game.world, camera_transform.position, view_proj);
|
||||||
|
|
||||||
let frame = render::render(
|
let frame = render::render(
|
||||||
@@ -333,6 +343,7 @@ fn submit_frame(game: &mut Game, draw_calls: &[render::DrawCall], time: f32, del
|
|||||||
player_pos,
|
player_pos,
|
||||||
draw_calls,
|
draw_calls,
|
||||||
&billboard_calls,
|
&billboard_calls,
|
||||||
|
&text_vertices,
|
||||||
time,
|
time,
|
||||||
delta,
|
delta,
|
||||||
game.world.debug_mode,
|
game.world.debug_mode,
|
||||||
@@ -384,36 +395,28 @@ fn main() -> Result<(), Box<dyn std::error::Error>>
|
|||||||
game.editor.show_player_state = !game.editor.show_player_state;
|
game.editor.show_player_state = !game.editor.show_player_state;
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- camera + input ---
|
// --- intent generation ---
|
||||||
camera_input_system(&mut game.world, &game.input_state);
|
camera_input_system(&mut game.world, &game.input_state);
|
||||||
|
player_input_system(&mut game.world, &game.input_state);
|
||||||
|
dialog_camera_transition_system(&mut game.world, game.camera_entity);
|
||||||
|
|
||||||
|
// --- intent processing + camera ---
|
||||||
|
camera_intent_system(&mut game.world);
|
||||||
|
camera_noclip_system(&mut game.world, &game.input_state, delta);
|
||||||
|
dialog_camera_system(&mut game.world, delta);
|
||||||
|
camera_follow_system(&mut game.world);
|
||||||
|
camera_transition_system(&mut game.world, delta);
|
||||||
|
camera_ground_clamp_system(&mut game.world);
|
||||||
|
|
||||||
|
// --- editor overlay ---
|
||||||
if game.editor.active
|
if game.editor.active
|
||||||
{
|
{
|
||||||
editor_loop(
|
editor_loop(&mut game.editor, &mut game.world, &game.stats);
|
||||||
&mut game.editor,
|
|
||||||
&mut game.world,
|
|
||||||
&game.input_state,
|
|
||||||
&game.stats,
|
|
||||||
delta,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
let dialog_active = !game.world.bubble_tags.all().is_empty();
|
|
||||||
if dialog_active
|
|
||||||
{
|
|
||||||
dialog_camera_system(&mut game.world, delta);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
camera_follow_system(&mut game.world);
|
|
||||||
}
|
|
||||||
player_input_system(&mut game.world, &game.input_state);
|
|
||||||
if game.editor.show_player_state
|
if game.editor.show_player_state
|
||||||
{
|
{
|
||||||
game.editor.build_hud(&game.world);
|
game.editor.build_hud(&game.world);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// --- fixed-step physics ---
|
// --- fixed-step physics ---
|
||||||
let physics_start = Instant::now();
|
let physics_start = Instant::now();
|
||||||
@@ -443,7 +446,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>>
|
|||||||
let spotlights = spotlight_sync_system(&game.world);
|
let spotlights = spotlight_sync_system(&game.world);
|
||||||
render::update_spotlights(spotlights);
|
render::update_spotlights(spotlights);
|
||||||
|
|
||||||
snow_system(&mut game.world, game.editor.active);
|
snow_system(&mut game.world);
|
||||||
|
|
||||||
// --- draw call collection ---
|
// --- draw call collection ---
|
||||||
let mut draw_calls = render_system(&game.world);
|
let mut draw_calls = render_system(&game.world);
|
||||||
|
|||||||
15
src/paths.rs
15
src/paths.rs
@@ -63,6 +63,16 @@ pub mod dialogs
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub mod fonts
|
||||||
|
{
|
||||||
|
use crate::paths::ASSETS_DIR;
|
||||||
|
|
||||||
|
pub fn departure_mono() -> String
|
||||||
|
{
|
||||||
|
format!("{}/fonts/DepartureMono-Regular.otf", ASSETS_DIR)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub mod shaders
|
pub mod shaders
|
||||||
{
|
{
|
||||||
use crate::paths::SHADERS_DIR;
|
use crate::paths::SHADERS_DIR;
|
||||||
@@ -87,6 +97,11 @@ pub mod shaders
|
|||||||
format!("{}/snow_deform.wgsl", SHADERS_DIR)
|
format!("{}/snow_deform.wgsl", SHADERS_DIR)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn text() -> String
|
||||||
|
{
|
||||||
|
format!("{}/text.wgsl", SHADERS_DIR)
|
||||||
|
}
|
||||||
|
|
||||||
pub const SHADOW_PACKAGE: &str = "package::shadow";
|
pub const SHADOW_PACKAGE: &str = "package::shadow";
|
||||||
pub const MAIN_PACKAGE: &str = "package::main";
|
pub const MAIN_PACKAGE: &str = "package::main";
|
||||||
pub const SNOW_LIGHT_ACCUMULATION_PACKAGE: &str = "package::snow_light_accumulation";
|
pub const SNOW_LIGHT_ACCUMULATION_PACKAGE: &str = "package::snow_light_accumulation";
|
||||||
|
|||||||
@@ -1,9 +1,14 @@
|
|||||||
use glam::Vec3;
|
use glam::Vec3;
|
||||||
|
|
||||||
|
use crate::components::camera::CameraTransition;
|
||||||
use crate::components::FollowComponent;
|
use crate::components::FollowComponent;
|
||||||
|
use crate::entity::EntityHandle;
|
||||||
|
use crate::physics::PhysicsManager;
|
||||||
use crate::utility::input::InputState;
|
use crate::utility::input::InputState;
|
||||||
use crate::world::{Transform, World};
|
use crate::world::{Transform, World};
|
||||||
|
|
||||||
|
const CAMERA_GROUND_OFFSET: f32 = 2.0;
|
||||||
|
|
||||||
pub fn camera_view_matrix(world: &World) -> Option<glam::Mat4>
|
pub fn camera_view_matrix(world: &World) -> Option<glam::Mat4>
|
||||||
{
|
{
|
||||||
let (camera_entity, camera_component) = world.active_camera()?;
|
let (camera_entity, camera_component) = world.active_camera()?;
|
||||||
@@ -66,6 +71,35 @@ pub fn camera_input_system(world: &mut World, input_state: &InputState)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn camera_intent_system(world: &mut World)
|
||||||
|
{
|
||||||
|
let follow_entities: Vec<EntityHandle> = world.follow_player_intents.all();
|
||||||
|
for entity in follow_entities
|
||||||
|
{
|
||||||
|
start_camera_following(world, entity);
|
||||||
|
world.follow_player_intents.remove(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
let stop_entities: Vec<EntityHandle> = world.stop_following_intents.all();
|
||||||
|
for entity in stop_entities
|
||||||
|
{
|
||||||
|
stop_camera_following(world, entity);
|
||||||
|
world.stop_following_intents.remove(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
let transition_entities: Vec<EntityHandle> = world.camera_transition_intents.all();
|
||||||
|
for entity in transition_entities
|
||||||
|
{
|
||||||
|
let duration = world
|
||||||
|
.camera_transition_intents
|
||||||
|
.get(entity)
|
||||||
|
.map(|i| i.duration)
|
||||||
|
.unwrap_or(0.5);
|
||||||
|
start_camera_transition(world, entity, duration);
|
||||||
|
world.camera_transition_intents.remove(entity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn camera_follow_system(world: &mut World)
|
pub fn camera_follow_system(world: &mut World)
|
||||||
{
|
{
|
||||||
let camera_entities: Vec<_> = world.follows.all();
|
let camera_entities: Vec<_> = world.follows.all();
|
||||||
@@ -109,10 +143,20 @@ pub fn camera_follow_system(world: &mut World)
|
|||||||
|
|
||||||
pub fn camera_noclip_system(world: &mut World, input_state: &InputState, delta: f32)
|
pub fn camera_noclip_system(world: &mut World, input_state: &InputState, delta: f32)
|
||||||
{
|
{
|
||||||
|
if !input_state.mouse_captured
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let cameras: Vec<_> = world.cameras.all();
|
let cameras: Vec<_> = world.cameras.all();
|
||||||
|
|
||||||
for camera_entity in cameras
|
for camera_entity in cameras
|
||||||
{
|
{
|
||||||
|
if world.follows.get(camera_entity).is_some()
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(camera) = world.cameras.get(camera_entity)
|
if let Some(camera) = world.cameras.get(camera_entity)
|
||||||
{
|
{
|
||||||
if !camera.is_active
|
if !camera.is_active
|
||||||
@@ -167,7 +211,7 @@ pub fn camera_noclip_system(world: &mut World, input_state: &InputState, delta:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn start_camera_following(world: &mut World, camera_entity: crate::entity::EntityHandle)
|
fn start_camera_following(world: &mut World, camera_entity: crate::entity::EntityHandle)
|
||||||
{
|
{
|
||||||
if let Some(camera_transform) = world.transforms.get(camera_entity)
|
if let Some(camera_transform) = world.transforms.get(camera_entity)
|
||||||
{
|
{
|
||||||
@@ -202,7 +246,7 @@ pub fn start_camera_following(world: &mut World, camera_entity: crate::entity::E
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn stop_camera_following(world: &mut World, camera_entity: crate::entity::EntityHandle)
|
fn stop_camera_following(world: &mut World, camera_entity: crate::entity::EntityHandle)
|
||||||
{
|
{
|
||||||
if let Some(follow) = world.follows.get(camera_entity)
|
if let Some(follow) = world.follows.get(camera_entity)
|
||||||
{
|
{
|
||||||
@@ -226,3 +270,134 @@ pub fn stop_camera_following(world: &mut World, camera_entity: crate::entity::En
|
|||||||
world.follows.remove(camera_entity);
|
world.follows.remove(camera_entity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn camera_ground_clamp_system(world: &mut World)
|
||||||
|
{
|
||||||
|
let Some((camera_entity, _)) = world.active_camera()
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
if world.follows.get(camera_entity).is_none()
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
world.transforms.with_mut(camera_entity, |t| {
|
||||||
|
let ground_y =
|
||||||
|
PhysicsManager::get_terrain_height_at(t.position.x, t.position.z).unwrap_or(0.0);
|
||||||
|
let min_y = ground_y + CAMERA_GROUND_OFFSET;
|
||||||
|
if t.position.y < min_y
|
||||||
|
{
|
||||||
|
t.position.y = min_y;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn start_camera_transition(world: &mut World, camera_entity: EntityHandle, duration: f32)
|
||||||
|
{
|
||||||
|
let Some(camera) = world.cameras.get(camera_entity)
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let source_yaw = camera.yaw;
|
||||||
|
let source_pitch = camera.pitch;
|
||||||
|
|
||||||
|
let source_position = world
|
||||||
|
.transforms
|
||||||
|
.with(camera_entity, |t| t.position)
|
||||||
|
.unwrap_or(Vec3::ZERO);
|
||||||
|
|
||||||
|
world.camera_transitions.insert(
|
||||||
|
camera_entity,
|
||||||
|
CameraTransition {
|
||||||
|
source_position,
|
||||||
|
source_yaw,
|
||||||
|
source_pitch,
|
||||||
|
elapsed: 0.0,
|
||||||
|
duration,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn camera_transition_system(world: &mut World, delta: f32)
|
||||||
|
{
|
||||||
|
let entities: Vec<EntityHandle> = world.camera_transitions.all();
|
||||||
|
|
||||||
|
for entity in entities
|
||||||
|
{
|
||||||
|
let finished = {
|
||||||
|
let Some(transition) = world.camera_transitions.get_mut(entity)
|
||||||
|
else
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
transition.elapsed += delta;
|
||||||
|
let t = (transition.elapsed / transition.duration).min(1.0);
|
||||||
|
let t = smoothstep(t);
|
||||||
|
|
||||||
|
let source_position = transition.source_position;
|
||||||
|
let source_yaw = transition.source_yaw;
|
||||||
|
let source_pitch = transition.source_pitch;
|
||||||
|
let finished = t >= 1.0;
|
||||||
|
|
||||||
|
world.transforms.with_mut(entity, |transform| {
|
||||||
|
transform.position = source_position.lerp(transform.position, t);
|
||||||
|
});
|
||||||
|
|
||||||
|
if let Some(camera) = world.cameras.get_mut(entity)
|
||||||
|
{
|
||||||
|
camera.yaw = lerp_angle(source_yaw, camera.yaw, t);
|
||||||
|
camera.pitch = source_pitch + (camera.pitch - source_pitch) * t;
|
||||||
|
}
|
||||||
|
|
||||||
|
if !finished
|
||||||
|
{
|
||||||
|
if let Some(transition) = world.camera_transitions.get_mut(entity)
|
||||||
|
{
|
||||||
|
let pos = world
|
||||||
|
.transforms
|
||||||
|
.with(entity, |tr| tr.position)
|
||||||
|
.unwrap_or(Vec3::ZERO);
|
||||||
|
let cam = world.cameras.get(entity);
|
||||||
|
transition.source_position = pos;
|
||||||
|
if let Some(cam) = cam
|
||||||
|
{
|
||||||
|
transition.source_yaw = cam.yaw;
|
||||||
|
transition.source_pitch = cam.pitch;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
finished
|
||||||
|
};
|
||||||
|
|
||||||
|
if finished
|
||||||
|
{
|
||||||
|
world.camera_transitions.remove(entity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn smoothstep(t: f32) -> f32
|
||||||
|
{
|
||||||
|
t * t * (3.0 - 2.0 * t)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn lerp_angle(from: f32, to: f32, t: f32) -> f32
|
||||||
|
{
|
||||||
|
let mut diff = to - from;
|
||||||
|
while diff > std::f32::consts::PI
|
||||||
|
{
|
||||||
|
diff -= std::f32::consts::TAU;
|
||||||
|
}
|
||||||
|
while diff < -std::f32::consts::PI
|
||||||
|
{
|
||||||
|
diff += std::f32::consts::TAU;
|
||||||
|
}
|
||||||
|
from + diff * t
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,7 +1,21 @@
|
|||||||
use glam::Vec3;
|
use glam::Vec3;
|
||||||
|
|
||||||
|
use crate::components::intent::CameraTransitionIntent;
|
||||||
|
use crate::entity::EntityHandle;
|
||||||
use crate::world::World;
|
use crate::world::World;
|
||||||
|
|
||||||
|
pub fn dialog_camera_transition_system(world: &mut World, camera_entity: EntityHandle)
|
||||||
|
{
|
||||||
|
let dialog_active = !world.bubble_tags.all().is_empty();
|
||||||
|
if dialog_active != world.was_dialog_active
|
||||||
|
{
|
||||||
|
world
|
||||||
|
.camera_transition_intents
|
||||||
|
.insert(camera_entity, CameraTransitionIntent { duration: 0.8 });
|
||||||
|
world.was_dialog_active = dialog_active;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const CAMERA_LAG: f32 = 4.0;
|
const CAMERA_LAG: f32 = 4.0;
|
||||||
const VERTICAL_BIAS: f32 = 0.4;
|
const VERTICAL_BIAS: f32 = 0.4;
|
||||||
const MIN_DISTANCE: f32 = 8.0;
|
const MIN_DISTANCE: f32 = 8.0;
|
||||||
@@ -17,23 +31,20 @@ pub fn dialog_camera_system(world: &mut World, delta: f32)
|
|||||||
|
|
||||||
let player_pos = world.player_position();
|
let player_pos = world.player_position();
|
||||||
|
|
||||||
let character_positions: Vec<Vec3> = world
|
let bubble_positions: Vec<Vec3> = world
|
||||||
.bubble_tags
|
.bubble_tags
|
||||||
.all()
|
.all()
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|&bubble| {
|
.filter_map(|&bubble| world.transforms.with(bubble, |t| t.position))
|
||||||
let char_entity = world.dialog_bubbles.with(bubble, |b| b.character_entity)?;
|
|
||||||
world.transforms.with(char_entity, |t| t.position)
|
|
||||||
})
|
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
if character_positions.is_empty()
|
if bubble_positions.is_empty()
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let all_positions: Vec<Vec3> = std::iter::once(player_pos)
|
let all_positions: Vec<Vec3> = std::iter::once(player_pos)
|
||||||
.chain(character_positions.iter().copied())
|
.chain(bubble_positions.iter().copied())
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let centroid =
|
let centroid =
|
||||||
@@ -63,7 +74,7 @@ pub fn dialog_camera_system(world: &mut World, delta: f32)
|
|||||||
t.position = smoothed;
|
t.position = smoothed;
|
||||||
});
|
});
|
||||||
|
|
||||||
let look_target = centroid + Vec3::Y * 1.0;
|
let look_target = centroid;
|
||||||
|
|
||||||
if let Some(camera) = world.cameras.get_mut(camera_entity)
|
if let Some(camera) = world.cameras.get_mut(camera_entity)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -5,6 +5,11 @@ use crate::world::World;
|
|||||||
|
|
||||||
pub fn player_input_system(world: &mut World, input_state: &InputState)
|
pub fn player_input_system(world: &mut World, input_state: &InputState)
|
||||||
{
|
{
|
||||||
|
if !world.camera_is_following()
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let Some((_, camera)) = world.active_camera()
|
let Some((_, camera)) = world.active_camera()
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
pub mod camera;
|
pub mod camera;
|
||||||
pub mod dialog_system;
|
|
||||||
pub mod dialog_camera;
|
pub mod dialog_camera;
|
||||||
pub mod dialog_projectile;
|
pub mod dialog_projectile;
|
||||||
pub mod dialog_render;
|
pub mod dialog_render;
|
||||||
|
pub mod dialog_system;
|
||||||
pub mod follow;
|
pub mod follow;
|
||||||
pub mod input;
|
pub mod input;
|
||||||
pub mod physics_sync;
|
pub mod physics_sync;
|
||||||
@@ -15,13 +15,13 @@ pub mod tree_dissolve;
|
|||||||
pub mod trigger;
|
pub mod trigger;
|
||||||
|
|
||||||
pub use camera::{
|
pub use camera::{
|
||||||
camera_follow_system, camera_input_system, camera_noclip_system, camera_view_matrix,
|
camera_follow_system, camera_ground_clamp_system, camera_input_system, camera_intent_system,
|
||||||
start_camera_following,
|
camera_noclip_system, camera_transition_system, camera_view_matrix,
|
||||||
};
|
};
|
||||||
pub use dialog_system::dialog_system;
|
pub use dialog_camera::{dialog_camera_system, dialog_camera_transition_system};
|
||||||
pub use dialog_camera::dialog_camera_system;
|
|
||||||
pub use dialog_projectile::dialog_projectile_system;
|
pub use dialog_projectile::dialog_projectile_system;
|
||||||
pub use dialog_render::dialog_bubble_render_system;
|
pub use dialog_render::dialog_bubble_render_system;
|
||||||
|
pub use dialog_system::dialog_system;
|
||||||
pub use input::player_input_system;
|
pub use input::player_input_system;
|
||||||
pub use physics_sync::physics_sync_system;
|
pub use physics_sync::physics_sync_system;
|
||||||
pub use render::render_system;
|
pub use render::render_system;
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
use crate::world::World;
|
use crate::world::World;
|
||||||
|
|
||||||
pub fn snow_system(world: &mut World, noclip: bool)
|
pub fn snow_system(world: &mut World)
|
||||||
{
|
{
|
||||||
let camera_pos = world.active_camera_position();
|
let camera_pos = world.active_camera_position();
|
||||||
let player_pos = world.player_position();
|
let player_pos = world.player_position();
|
||||||
|
let is_following = world.camera_is_following();
|
||||||
if let Some(ref mut snow_layer) = world.snow_layer
|
if let Some(ref mut snow_layer) = world.snow_layer
|
||||||
{
|
{
|
||||||
if !noclip
|
if is_following
|
||||||
{
|
{
|
||||||
snow_layer.deform_at_position(player_pos, 1.5, 10.0);
|
snow_layer.deform_at_position(player_pos, 1.5, 10.0);
|
||||||
}
|
}
|
||||||
|
|||||||
28
src/world.rs
28
src/world.rs
@@ -5,6 +5,7 @@ use crate::components::dialog::{
|
|||||||
};
|
};
|
||||||
use crate::components::dissolve::DissolveComponent;
|
use crate::components::dissolve::DissolveComponent;
|
||||||
use crate::components::follow::FollowComponent;
|
use crate::components::follow::FollowComponent;
|
||||||
|
use crate::components::intent::{CameraTransitionIntent, FollowPlayerIntent, StopFollowingIntent};
|
||||||
use crate::components::lights::spot::SpotlightComponent;
|
use crate::components::lights::spot::SpotlightComponent;
|
||||||
use crate::components::player_states::{
|
use crate::components::player_states::{
|
||||||
FallingState, IdleState, JumpingState, LeapingState, RollingState, WalkingState,
|
FallingState, IdleState, JumpingState, LeapingState, RollingState, WalkingState,
|
||||||
@@ -12,8 +13,8 @@ use crate::components::player_states::{
|
|||||||
use crate::components::tree_instances::TreeInstancesComponent;
|
use crate::components::tree_instances::TreeInstancesComponent;
|
||||||
use crate::components::trigger::{TriggerComponent, TriggerEvent};
|
use crate::components::trigger::{TriggerComponent, TriggerEvent};
|
||||||
use crate::components::{
|
use crate::components::{
|
||||||
CameraComponent, InputComponent, JumpComponent, MeshComponent, MovementComponent,
|
CameraComponent, CameraTransition, InputComponent, JumpComponent, MeshComponent,
|
||||||
PhysicsComponent, RotateComponent,
|
MovementComponent, PhysicsComponent, RotateComponent,
|
||||||
};
|
};
|
||||||
use crate::debug::DebugMode;
|
use crate::debug::DebugMode;
|
||||||
use crate::entity::{EntityHandle, EntityManager};
|
use crate::entity::{EntityHandle, EntityManager};
|
||||||
@@ -94,6 +95,7 @@ pub struct World
|
|||||||
pub leaping_states: Storage<LeapingState>,
|
pub leaping_states: Storage<LeapingState>,
|
||||||
pub rolling_states: Storage<RollingState>,
|
pub rolling_states: Storage<RollingState>,
|
||||||
pub cameras: Storage<CameraComponent>,
|
pub cameras: Storage<CameraComponent>,
|
||||||
|
pub camera_transitions: Storage<CameraTransition>,
|
||||||
pub spotlights: Storage<SpotlightComponent>,
|
pub spotlights: Storage<SpotlightComponent>,
|
||||||
pub tree_tags: Storage<()>,
|
pub tree_tags: Storage<()>,
|
||||||
pub dissolves: Storage<DissolveComponent>,
|
pub dissolves: Storage<DissolveComponent>,
|
||||||
@@ -110,9 +112,15 @@ pub struct World
|
|||||||
pub projectile_tags: Storage<()>,
|
pub projectile_tags: Storage<()>,
|
||||||
pub dialog_outcomes: Vec<DialogOutcomeEvent>,
|
pub dialog_outcomes: Vec<DialogOutcomeEvent>,
|
||||||
|
|
||||||
|
// --- intents (one-frame, consumed after processing) ---
|
||||||
|
pub follow_player_intents: Storage<FollowPlayerIntent>,
|
||||||
|
pub stop_following_intents: Storage<StopFollowingIntent>,
|
||||||
|
pub camera_transition_intents: Storage<CameraTransitionIntent>,
|
||||||
|
|
||||||
// --- singleton state (not per-entity) ---
|
// --- singleton state (not per-entity) ---
|
||||||
pub snow_layer: Option<SnowLayer>,
|
pub snow_layer: Option<SnowLayer>,
|
||||||
pub debug_mode: DebugMode,
|
pub debug_mode: DebugMode,
|
||||||
|
pub was_dialog_active: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl World
|
impl World
|
||||||
@@ -136,6 +144,7 @@ impl World
|
|||||||
leaping_states: Storage::new(),
|
leaping_states: Storage::new(),
|
||||||
rolling_states: Storage::new(),
|
rolling_states: Storage::new(),
|
||||||
cameras: Storage::new(),
|
cameras: Storage::new(),
|
||||||
|
camera_transitions: Storage::new(),
|
||||||
spotlights: Storage::new(),
|
spotlights: Storage::new(),
|
||||||
tree_tags: Storage::new(),
|
tree_tags: Storage::new(),
|
||||||
dissolves: Storage::new(),
|
dissolves: Storage::new(),
|
||||||
@@ -151,8 +160,12 @@ impl World
|
|||||||
bubble_tags: Storage::new(),
|
bubble_tags: Storage::new(),
|
||||||
projectile_tags: Storage::new(),
|
projectile_tags: Storage::new(),
|
||||||
dialog_outcomes: Vec::new(),
|
dialog_outcomes: Vec::new(),
|
||||||
|
follow_player_intents: Storage::new(),
|
||||||
|
stop_following_intents: Storage::new(),
|
||||||
|
camera_transition_intents: Storage::new(),
|
||||||
snow_layer: None,
|
snow_layer: None,
|
||||||
debug_mode: DebugMode::default(),
|
debug_mode: DebugMode::default(),
|
||||||
|
was_dialog_active: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -178,6 +191,7 @@ impl World
|
|||||||
self.leaping_states.remove(entity);
|
self.leaping_states.remove(entity);
|
||||||
self.rolling_states.remove(entity);
|
self.rolling_states.remove(entity);
|
||||||
self.cameras.remove(entity);
|
self.cameras.remove(entity);
|
||||||
|
self.camera_transitions.remove(entity);
|
||||||
self.spotlights.remove(entity);
|
self.spotlights.remove(entity);
|
||||||
self.tree_tags.remove(entity);
|
self.tree_tags.remove(entity);
|
||||||
self.dissolves.remove(entity);
|
self.dissolves.remove(entity);
|
||||||
@@ -191,6 +205,9 @@ impl World
|
|||||||
self.dialog_projectiles.remove(entity);
|
self.dialog_projectiles.remove(entity);
|
||||||
self.bubble_tags.remove(entity);
|
self.bubble_tags.remove(entity);
|
||||||
self.projectile_tags.remove(entity);
|
self.projectile_tags.remove(entity);
|
||||||
|
self.follow_player_intents.remove(entity);
|
||||||
|
self.stop_following_intents.remove(entity);
|
||||||
|
self.camera_transition_intents.remove(entity);
|
||||||
self.entities.despawn(entity);
|
self.entities.despawn(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -211,6 +228,13 @@ impl World
|
|||||||
.unwrap_or(glam::Vec3::ZERO)
|
.unwrap_or(glam::Vec3::ZERO)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn camera_is_following(&self) -> bool
|
||||||
|
{
|
||||||
|
self.active_camera()
|
||||||
|
.map(|(e, _)| self.follows.get(e).is_some())
|
||||||
|
.unwrap_or(false)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn player_position(&self) -> glam::Vec3
|
pub fn player_position(&self) -> glam::Vec3
|
||||||
{
|
{
|
||||||
self.player_tags
|
self.player_tags
|
||||||
|
|||||||
Reference in New Issue
Block a user