working demo part 1

This commit is contained in:
2026-01-02 15:11:14 +00:00
parent 17ea08caac
commit 7e3d338373
6 changed files with 703 additions and 6 deletions

View File

@@ -0,0 +1,194 @@
//! Demo: Find all PlaySFX components and their locations in VR_Horror_YouCantRun
//!
//! This example demonstrates:
//! 1. Parsing a real Unity project
//! 2. Finding custom MonoBehaviour components (PlaySFX)
//! 3. Querying the ECS world for components
//! 4. Accessing Transform data for component locations
use cursebreaker_parser::{UnityComponent, UnityFile};
use std::path::Path;
/// PlaySFX component from VR_Horror_YouCantRun
///
/// C# definition:
/// ```csharp
/// public class PlaySFX : MonoBehaviour
/// {
/// [SerializeField] float volume;
/// [SerializeField] float startTime;
/// [SerializeField] float endTime;
/// [SerializeField] bool isLoop;
/// }
/// ```
#[derive(Debug, Clone, UnityComponent)]
#[unity_class("PlaySFX")]
pub struct PlaySFX {
#[unity_field("volume")]
pub volume: f64,
#[unity_field("startTime")]
pub start_time: f64,
#[unity_field("endTime")]
pub end_time: f64,
#[unity_field("isLoop")]
pub is_loop: bool,
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("🎮 VR Horror - PlaySFX Component Finder");
println!("{}", "=".repeat(70));
println!();
let project_path = Path::new("test_data/VR_Horror_YouCantRun");
// Check if project exists
if !project_path.exists() {
eprintln!("❌ Error: VR_Horror_YouCantRun project not found at {}", project_path.display());
eprintln!(" Run the integration tests first to download it:");
eprintln!(" cargo test test_vr_horror_project");
return Ok(());
}
println!("📁 Scanning project: {}", project_path.display());
println!();
// Find all Unity scene files
let scene_files = find_unity_files(project_path, "unity");
println!("📄 Found {} scene file(s)", scene_files.len());
println!();
let mut total_playsfx = 0;
// Parse each scene
for scene_path in scene_files {
println!("🔍 Parsing: {}", scene_path.file_name().unwrap().to_string_lossy());
match UnityFile::from_path(&scene_path) {
Ok(UnityFile::Scene(scene)) => {
// Get views for all component types we need
let playsfx_view = scene.world.borrow::<PlaySFX>();
let transform_view = scene.world.borrow::<cursebreaker_parser::Transform>();
let rect_transform_view = scene.world.borrow::<cursebreaker_parser::RectTransform>();
let gameobject_view = scene.world.borrow::<cursebreaker_parser::GameObject>();
// Find all entities that have PlaySFX
let mut found_count = 0;
let mut found_entities = Vec::new();
// Iterate through all entities in the entity_map
for entity in scene.entity_map.values() {
if let Some(playsfx) = playsfx_view.get(*entity) {
found_entities.push((*entity, playsfx.clone()));
found_count += 1;
}
}
if found_count > 0 {
println!(" ✅ Found {} PlaySFX component(s)", found_count);
total_playsfx += found_count;
// Process each found PlaySFX component
for (entity, playsfx) in found_entities {
let transform = transform_view.get(entity);
let rect_transform = rect_transform_view.get(entity);
let game_object = gameobject_view.get(entity);
let name = game_object
.and_then(|go| go.name())
.unwrap_or("(unnamed)");
println!();
println!(" 🔊 PlaySFX on GameObject: \"{}\"", name);
println!(" Entity: {:?}", entity);
println!(" Properties:");
println!(" • volume: {}", playsfx.volume);
println!(" • startTime: {}", playsfx.start_time);
println!(" • endTime: {}", playsfx.end_time);
println!(" • isLoop: {}", playsfx.is_loop);
// Print position if available
if let Some(transform) = transform {
if let Some(pos) = transform.local_position() {
println!(" Transform:");
println!(" • Position: ({:.2}, {:.2}, {:.2})",
pos.x, pos.y, pos.z);
}
if let Some(rot) = transform.local_rotation() {
println!(" • Rotation: ({:.2}, {:.2}, {:.2}, {:.2})",
rot.x, rot.y, rot.z, rot.w);
}
if let Some(scale) = transform.local_scale() {
println!(" • Scale: ({:.2}, {:.2}, {:.2})",
scale.x, scale.y, scale.z);
}
} else if let Some(rect_transform) = rect_transform {
let transform = rect_transform.transform();
if let Some(pos) = transform.local_position() {
println!(" RectTransform (UI):");
println!(" • Position: ({:.2}, {:.2}, {:.2})",
pos.x, pos.y, pos.z);
}
} else {
println!(" ⚠️ No Transform found");
}
}
} else {
println!(" ⊘ No PlaySFX components found");
}
println!();
}
Ok(_) => {
println!(" ⊘ Skipped (not a scene file)");
println!();
}
Err(e) => {
println!(" ❌ Parse error: {}", e);
println!();
}
}
}
println!("{}", "=".repeat(70));
println!("📊 Summary:");
println!(" Total PlaySFX components found: {}", total_playsfx);
println!("{}", "=".repeat(70));
Ok(())
}
/// Find all Unity files with a specific extension in a directory
fn find_unity_files(dir: &Path, extension: &str) -> Vec<std::path::PathBuf> {
let mut files = Vec::new();
fn visit_dir(dir: &Path, extension: &str, files: &mut Vec<std::path::PathBuf>) {
if let Ok(entries) = std::fs::read_dir(dir) {
for entry in entries.flatten() {
let path = entry.path();
// Skip Library, Temp, Builds, and .git directories
if let Some(name) = path.file_name().and_then(|n| n.to_str()) {
if name == "Library" || name == "Temp" || name == "Builds" || name == ".git" {
continue;
}
}
if path.is_dir() {
visit_dir(&path, extension, files);
} else if let Some(ext) = path.extension().and_then(|e| e.to_str()) {
if ext == extension {
files.push(path);
}
}
}
}
}
visit_dir(dir, extension, &mut files);
files.sort();
files
}

View File

@@ -25,13 +25,20 @@ use std::collections::HashMap;
pub fn build_world_from_documents(
documents: Vec<RawDocument>,
) -> Result<(World, HashMap<FileID, Entity>)> {
// Create World with registered component types
let mut world = World::builder()
// Create World builder with registered component types
let mut builder = World::builder();
builder
.register::<GameObject>()
.register::<Transform>()
.register::<RectTransform>()
.register::<PrefabInstanceComponent>()
.build();
.register::<PrefabInstanceComponent>();
// Register all custom components from inventory
for reg in inventory::iter::<crate::types::ComponentRegistration> {
(reg.register)(&mut builder);
}
let mut world = builder.build();
let linking_ctx = RefCell::new(LinkingContext::new());

View File

@@ -2,6 +2,7 @@
use crate::types::*;
use serde_yaml::{Mapping, Value};
use sparsey::world::WorldBuilder;
use sparsey::Entity;
use std::cell::RefCell;
use std::collections::HashMap;
@@ -118,6 +119,8 @@ pub struct ComponentRegistration {
pub class_name: &'static str,
/// Parser function that parses and inserts the component into the ECS world
pub parse_and_insert: fn(&Mapping, &ComponentContext, &mut sparsey::World, Entity) -> bool,
/// Function to register this component type with a WorldBuilder
pub register: for<'a> fn(&'a mut WorldBuilder) -> &'a mut WorldBuilder,
}
// Collect all component registrations submitted via the macro