From 1255c94f4d0693676a92136833db3f8904c5eecf Mon Sep 17 00:00:00 2001 From: Alexei Dmitrievtsev Date: Sun, 7 Dec 2025 20:45:34 +0300 Subject: [PATCH] gameobjects scripts acting with uids --- src/bin/main.rs | 134 ++++++++++-------- src/engine/mod.rs | 19 ++- .../scene/game_object/components/script.rs | 2 +- src/engine/scene/mod.rs | 13 +- src/engine/scene/object_manager.rs | 9 +- src/engine/scene_manager.rs | 16 ++- src/interface/mod.rs | 10 +- 7 files changed, 131 insertions(+), 72 deletions(-) diff --git a/src/bin/main.rs b/src/bin/main.rs index 394a6af..045e569 100644 --- a/src/bin/main.rs +++ b/src/bin/main.rs @@ -1,9 +1,64 @@ +use image::ImageReader; + use rusty_ache::engine::Engine; use rusty_ache::engine::scene::game_object::GameObject; +use rusty_ache::engine::scene::game_object::components::ComponentType::Action; use rusty_ache::engine::scene::game_object::components::script::Script; +use rusty_ache::engine::scene::game_object::components::sprite::Sprite; +use rusty_ache::engine::scene::game_object::components::{Component, ComponentType}; use rusty_ache::engine::scene::game_object::position::Position; use rusty_ache::interface::{create_obj_with_img, init_end_scene, init_engine, init_scene}; + use rusty_ache::screen::{HEIGHT, WIDTH}; +use std::any::Any; + +use std::thread; +use std::time::Duration; + +#[derive(Clone)] +pub struct MyScript { + is_downed: bool, + movement: u64, +} + +impl Script for MyScript { + fn action(&mut self, game_object: &mut GameObject) { + self.movement += 1; + println!("movement: {}", self.movement); + if self.movement < 30 { + game_object.position.y -= 1; + } else if self.movement < 60 { + game_object.position.y += 1; + } else { + self.movement = 0; + } + } + + fn new(is_downed: bool) -> MyScript { + MyScript { + is_downed, + movement: 1, + } + } + + fn clone_box(&self) -> Box { + Box::new(self.clone()) + } +} + +impl Component for MyScript { + fn as_any(&self) -> &dyn Any { + self + } + + fn get_component_type(&self) -> ComponentType { + Action + } + + fn clone_box(&self) -> Box { + Box::new(self.clone()) + } +} fn main() { let tower_obj = create_obj_with_img("src/bin/resources/tower.png", 82, 37, true); @@ -15,8 +70,8 @@ fn main() { let main_ship_obj = create_obj_with_img("src/bin/resources/white_ship.png", 0, 0, true); let hermit_house_obj = create_obj_with_img("src/bin/resources/junk_house.png", 400, 240, true); - - let scene = init_scene( + let mut uids: Vec = vec![]; + let mut scene = init_scene( &[ cabin_obj, skyscraper_obj, @@ -27,60 +82,43 @@ fn main() { pool_house_obj, ], main_ship_obj, + &mut uids, ); + uids.push(scene.manager.add_game_object( + vec![Box::new(Sprite::new( + Some(ImageReader::open("src/bin/resources/white_ship.png").unwrap().decode().unwrap()), + true, + (0, 0), + ))], + Position { + x: 60, + y: -10, + z: 40, + is_relative: false, + }, + )); + + let script = MyScript::new(false); let end_scene = init_end_scene("src/bin/resources/game_over.jpg", None); let mut engine = init_engine(scene, end_scene, WIDTH, HEIGHT); let main_pos_arc = engine.main_pos.clone(); let end_scene_flag = engine.is_end_scene_active.clone(); - std::thread::spawn(move || { + let is_removed = false; + + thread::spawn(move || { loop { let (x, y) = *main_pos_arc.read().unwrap(); - println!("position of main object is ({}, {})", x, y); if x > 150 { end_scene_flag.store(true, std::sync::atomic::Ordering::SeqCst); } + thread::sleep(Duration::from_millis(1000)); } }); engine.render().unwrap(); - engine.run().unwrap() -} - -#[derive(Clone)] -pub struct MyScript { - is_downed: bool, -} - -impl Script for MyScript { - fn new(is_downed: bool) -> MyScript { - MyScript { is_downed } - } - - fn action(&mut self, game_object: &mut GameObject) { - if !self.is_downed { - game_object.position = Position { - x: game_object.position.x, - y: game_object.position.y - 1, - z: game_object.position.z, - is_relative: game_object.position.is_relative, - }; - self.is_downed = true; - } else { - game_object.position = Position { - x: game_object.position.x, - y: game_object.position.y + 1, - z: game_object.position.z, - is_relative: game_object.position.is_relative, - }; - self.is_downed = false; - } - } - - fn clone_box(&self) -> Box { - Box::new(self.clone()) - } + engine.run(vec![(8, Box::new(script))]).unwrap() } #[cfg(test)] @@ -110,20 +148,4 @@ mod tests { assert_eq!(game_object.position.y, 24); assert_eq!(game_object.position.z, 35); } - - #[test] - fn test_actions_is_downed_true() { - let mut script = MyScript::new(true); - let position = Position { - x: 15, - y: 25, - z: 35, - is_relative: false, - }; - let game_object = &mut GameObject::new(vec![], None, position); - script.action(game_object); - assert_eq!(game_object.position.x, 15); - assert_eq!(game_object.position.y, 26); - assert_eq!(game_object.position.z, 35); - } } diff --git a/src/engine/mod.rs b/src/engine/mod.rs index 1b8f82e..99e916d 100644 --- a/src/engine/mod.rs +++ b/src/engine/mod.rs @@ -24,6 +24,7 @@ use crate::render::renderer::{DEFAULT_BACKGROUND_COLOR, Renderer}; use crate::screen::{App, HEIGHT, WIDTH}; // use crate::end_scene::EndScene; //use image::ImageReader; +use crate::engine::scene::game_object::components::script::Script; use std::io::Error; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, RwLock}; @@ -46,7 +47,7 @@ pub trait Engine { fn render(&mut self) -> Result<(), Error>; /// Starts and runs the engine main loop. - fn run(&mut self) -> Result<(), Error>; + fn run(&mut self, actionable: Vec<(usize, Box)>) -> Result<(), Error>; /// Creates a new engine instance from configuration and initial scene. fn new(config: Box, scene: Scene, end_scene: EndScene) -> Self @@ -111,7 +112,7 @@ impl Engine for GameEngine { /// Spawns a producer thread that updates the main object's position based on key input /// and triggers rendering updates. /// Runs the `winit` event loop with the associated GUI application. - fn run(&mut self) -> Result<(), Error> { + fn run(&mut self, mut actionable: Vec<(usize, Box)>) -> Result<(), Error> { let initial_resolution = Resolution { width: WIDTH, height: HEIGHT, @@ -148,6 +149,7 @@ impl Engine for GameEngine { .end_scene .background .clone(); + const SCREEN_SIZE: usize = (WIDTH * HEIGHT) as usize; thread::spawn(move || { let window_arc: Arc = loop { @@ -159,7 +161,6 @@ impl Engine for GameEngine { //dbg!("Producer has started"); - let screen_size = (WIDTH * HEIGHT) as usize; loop { if is_end_scene_active.load(Ordering::SeqCst) { let prev_background = renderer @@ -167,7 +168,8 @@ impl Engine for GameEngine { .unwrap() .set_background(new_background.clone()); let empty_object = create_obj_with_img(EMPTY, 0, 0, false); - let scene = init_scene(&[], empty_object); + let mut uids: Vec = vec![]; + let scene = init_scene(&[], empty_object, &mut uids); let timeout_ms = renderer.read().unwrap().scene_manager.end_scene.timeout_ms; renderer.write().unwrap().scene_manager = SceneManager::new(scene, EndScene::new(new_background.clone(), timeout_ms)); @@ -185,7 +187,7 @@ impl Engine for GameEngine { .write() .expect("Producer couldn't lock pixel data"); - for (idx, p) in pixels.iter_mut().take(screen_size).enumerate() { + for (idx, p) in pixels.iter_mut().take(SCREEN_SIZE).enumerate() { *p = colors[idx]; } @@ -233,6 +235,10 @@ impl Engine for GameEngine { .main_object .add_position((vector_move.0, vector_move.1)); + for (uid, script) in actionable.iter_mut() { + script.action(renderer.write().unwrap().scene_manager.ref_mut_by_uid(*uid)) + } + { let pos = renderer .read() @@ -253,7 +259,7 @@ impl Engine for GameEngine { .write() .expect("Producer couldn't lock pixel data"); - for (idx, p) in pixels.iter_mut().take(screen_size).enumerate() { + for (idx, p) in pixels.iter_mut().take(SCREEN_SIZE).enumerate() { *p = colors[idx]; } @@ -300,6 +306,7 @@ mod tests { z: 0, is_relative: false, }, + &mut vec![], ) } } diff --git a/src/engine/scene/game_object/components/script.rs b/src/engine/scene/game_object/components/script.rs index b5050a7..5864aab 100644 --- a/src/engine/scene/game_object/components/script.rs +++ b/src/engine/scene/game_object/components/script.rs @@ -14,7 +14,7 @@ use crate::engine::scene::game_object::GameObject; /// - a `new` constructor with an initialization parameter `is_downed` /// /// Implementors can define custom behavior for initialization and per-frame updates. -pub trait Script { +pub trait Script: Send { /// Apply the script's action on the given mutable game object reference. /// /// Allows modifying object state, trigger events, or update components. diff --git a/src/engine/scene/mod.rs b/src/engine/scene/mod.rs index 2d2b9a8..79522c4 100644 --- a/src/engine/scene/mod.rs +++ b/src/engine/scene/mod.rs @@ -21,7 +21,7 @@ mod object_manager; #[derive(Clone)] pub struct Scene { /// Manager responsible for storing and controlling multiple game objects. - manager: GameObjectManager, + pub manager: GameObjectManager, /// The main game object within this scene. pub main_object: GameObject, } @@ -40,10 +40,11 @@ impl Scene { objects: Vec, main_components: Vec>, main_position: Position, + uids: &mut Vec, ) -> Self { let mut obj_manager = GameObjectManager::new(256); for obj in objects { - obj_manager.add_game_object(obj.components, obj.position) + uids.push(obj_manager.add_game_object(obj.components, obj.position)); } Scene { manager: obj_manager, @@ -111,6 +112,10 @@ impl Scene { self.manager.remove_game_object(uid); self.init() } + + pub fn ref_mut_by_uid(&mut self, uid: usize) -> &mut GameObject { + self.manager.game_objects.get_mut(&uid).unwrap() + } } #[cfg(test)] @@ -128,6 +133,7 @@ mod tests { z: 3, is_relative: false, }, + &mut vec![], ); assert_eq!(scene.manager.game_objects.len(), 0); assert_eq!(scene.main_object.components.len(), 0); @@ -168,6 +174,7 @@ mod tests { z: 0, is_relative: false, }, + &mut vec![], ); assert_eq!(scene.manager.game_objects.len(), 2); } @@ -183,6 +190,7 @@ mod tests { z: 2, is_relative: false, }, + &mut vec![], ); assert_eq!(scene.main_object.components.len(), 0); } @@ -208,6 +216,7 @@ mod tests { z: 0, is_relative: false, }, + &mut vec![], ); let result = scene.init(); assert_eq!(result.len(), 0); diff --git a/src/engine/scene/object_manager.rs b/src/engine/scene/object_manager.rs index c712fa6..1e1f6f4 100644 --- a/src/engine/scene/object_manager.rs +++ b/src/engine/scene/object_manager.rs @@ -9,7 +9,7 @@ use crate::engine::scene::game_object::Position; use crate::engine::scene::game_object::components::Component; use crate::engine::scene::game_object::{GameObject, Object}; -use std::collections::{HashMap, HashSet}; +use std::collections::{BTreeMap, HashSet}; /// Factory struct for creating game objects with unique IDs. /// @@ -281,14 +281,14 @@ mod factory_tests { #[derive(Clone)] pub struct GameObjectManager { - pub game_objects: HashMap, + pub game_objects: BTreeMap, factory: GameObjectFactory, } impl GameObjectManager { pub fn new(max_objects: usize) -> Self { GameObjectManager { - game_objects: HashMap::new(), + game_objects: BTreeMap::new(), factory: GameObjectFactory::new(max_objects), } } @@ -297,9 +297,10 @@ impl GameObjectManager { &mut self, components: Vec>, position: Position, - ) { + ) -> usize { let (uid, object) = self.factory.create_object(components, position); self.game_objects.insert(uid, object); + uid } pub fn remove_game_object(&mut self, uid: usize) { diff --git a/src/engine/scene_manager.rs b/src/engine/scene_manager.rs index 7f3459f..bb7c49c 100644 --- a/src/engine/scene_manager.rs +++ b/src/engine/scene_manager.rs @@ -37,6 +37,14 @@ impl SceneManager { &self.active_scene } + /// Returns a mut reference to the currently active scene. + /// + /// # Returns + /// A mut reference to the active `Scene`. + pub fn ref_mut_by_uid(&mut self, uid: usize) -> &mut GameObject { + self.active_scene.ref_mut_by_uid(uid) + } + /// Initializes and retrieves all renderable objects from the active scene. /// /// This method calls the `init()` method of the current scene, which prepares @@ -92,6 +100,7 @@ mod tests { vec![], _create_test_components(), _create_test_position(0, 0, 0, false), + &mut vec![], ) } @@ -107,7 +116,12 @@ mod tests { objects.push(obj); } - Scene::new(objects, vec![], _create_test_position(0, 0, 0, false)) + Scene::new( + objects, + vec![], + _create_test_position(0, 0, 0, false), + &mut vec![], + ) } } diff --git a/src/interface/mod.rs b/src/interface/mod.rs index 8914b04..bd352d6 100644 --- a/src/interface/mod.rs +++ b/src/interface/mod.rs @@ -103,8 +103,13 @@ pub fn create_obj_with_img(image_path: &str, x: i32, y: i32, has_shadow: bool) - /// /// # Returns /// A full `Scene` instance initialized and ready for rendering. -pub fn init_scene(objs: &[ObjectWithImage], main_obj: ObjectWithImage) -> Scene { +pub fn init_scene( + objs: &[ObjectWithImage], + main_obj: ObjectWithImage, + uids: &mut Vec, +) -> Scene { let game_objs = create_gameobj_vec(objs); + Scene::new( game_objs, vec![Box::new(Sprite::new( @@ -123,6 +128,7 @@ pub fn init_scene(objs: &[ObjectWithImage], main_obj: ObjectWithImage) -> Scene z: 0, is_relative: false, }, + uids, ) } @@ -188,7 +194,7 @@ mod tests { let main_obj = create_obj_with_img("./resources/perf_diag.png", 300, 300, true); let main_obj_x = main_obj.x; let main_obj_y = main_obj.y; - let scene = init_scene(&objs, main_obj); + let scene = init_scene(&objs, main_obj, &mut vec![]); assert_eq!(scene.main_object.position.x, main_obj_x); assert_eq!(scene.main_object.position.y, main_obj_y); }