دا زما د Rust-Bevy لارښوونې لړۍ "د Impatient Programmer's Guide to Bevy and Rust: Build a Video Game from Scratch" 1 فصل دی. زه به په دې لړۍ کې د Bevy او د لوبې پراختیا په عمده توګه راځي، او همدارنګه د NPCs له خوا د AI اډیټونو د بریښنا د جوړولو. زموږ ټولنیز ته ګډون وکړئ ترڅو د دې لړۍ نوي نسخهونو په اړه تازه وي. د دې فصل لپاره د سرچینې کوډ دلته شتون لري. زموږ په ټولنې کې راځي شته دلته At the end of this chapter, you’ll be able to achieve the results in the GIF below: لارښوونې Setup د جوړولو که تاسو نه نصب کړئ په هرصورت، مهرباني وکړئ د د جوړولو لپاره. رخصتۍ د رسمي لارښود Create a fresh project: cargo new bevy_game cd bevy_game Open and add Bevy: Cargo.toml [dependencies] bevy = "0.16.1" زه به فرض وکړم چې تاسو په هر یو زبان کې د پروګرام کولو تجربه لري لکه جاواسکرپټ یا پیتون. په سیستمونو کې فکر موږ د یو ساده لوبه جوړولو لپاره څه ته اړتیا لري چې بازیکن ته اجازه ورکوي چې د ټیټارډ انټرنټ څخه حرکت وکړي؟ د نړۍ سیسټم: د لوبې نړۍ جوړ کړئ چې د لوبغاړي کولی شي حرکت وکړي. د انټرنېټ سیسټم: د ټایټ بورډ د انټرنېټ څارنه او دا په لوبغاړي حرکت کې بدل کړئ. موږ په جوړولو لوبې کې د دې نمونې، د جوړولو / نصب کولو نمونې، او د لوبې حالت بدلونونو څارنې او د لوبې نړۍ انټرنټونو تازه کولو نمونې کې خورا شتون لري. سیسټم: د مختلفو رفتارونو سره د دشمن د سپریټونو او انیمیشنونو لوډ کول، د دشمن روغتیا او خسارت ارزښتونه تنظیم کړئ. Update: د دشمنانو ته د لوبغاړي په غاړه حرکت وکړئ، چمتو کړئ که د دشمنانو د توپونو له خوا ځي، د مقتولو دشمنانو له لاسه ورکړئ او په دوامداره وختونو کې نوي دشمنان وده ورکړئ. د Bevy کورس کې، موږ د دې ساده دا بنسټیز نمونې دا دی چې د Bevy په دوامداره توګه قوي او آسانه درکوي - کله چې تاسو د دې مفهوم پوه شئ، د bevy سره جوړولو آسانه کیږي. setup and update system تصور وکړئ چې یو فابریکه د سیټ اپ په نوم دی، کوم چې تاسو ته د "پیمانونو" په توګه د بحث ورکوي، او دا تاسو ته د هر څه چې تاسو غواړئ جوړولو یا جوړولو وړتیا ورکوي. ښه، bevy تاسو ته دقیقا دا ورکوي. // Pseudo code, doesn't compile fn setup(mut commands: Commands) { // Create anything you want commands.spawn() } What’s mut? په Rust کې موږ اړتیا لرئ چې د دې ارزښت بدلولو لپاره په ځانګړې توګه اشاره وکړئ. په معياري توګه، Rust د اعلان شوي ارزښتونو په توګه یوازې لوستل په توګه درملنه کوي. Rust وايي چې موږ د ارزښت بدلولو لپاره طرح کوو. Rust د دې معلوماتو کاروي چې د بیګونو ټول ټولګي مخنیوی کړي. mut mut د ? And what’s this mut commands: Commands دا له bevy کتابتون څخه دی، کوم چې تاسو ته اجازه ورکوي چې ستاسو د لوبې نړۍ ته شیان اضافه کړئ. Rust د واضح ډولونو څخه خوښوي، نو اجازه ورکړئ چې کمپیلر پوه شي چې دا پارامتر د Bevy د امر انټرنیټ دی. د لارښوونې له لاسه ورکړئ او Rust کولای شي نه ډاډه کړي چې څه ښکاري، نو دا د جوړولو بلاکوي. :Commands What’s a type? یو ډول وايي Rust چې څه ډول ارزښت تاسو په کارولو کې دي - شمیره، متن، ټیمرونه، Bevy امرونه، او داسې نور. کله چې کمپیلر د ډول پوه شي، دا کولی شي چمتو کړي چې هر عمل چې تاسو په دې کې ترسره کوي په حقیقت کې معنوي ده. Isn’t this too much work? دا غلطاتو مخنیوی کوي او تاسو سره د فعالیت سره مرسته کوي، د بیلګې په توګه: که تاسو د یو شمیره سره یو string اضافه کړئ، د کمپیوټر تاسو ته د لوبې چلولو دمخه بندوي. تر ټولو، د دقیق ډول پوهیدنه اجازه ورکوي چې د Rust ډاټا راټول کړي. د دا یوازې دوه شمیره ده، نو د Rust دقیقا دوه شمیره ذخیره کوي، هیڅ حیرانتیا اضافي فضا نه، کوم چې کارونه چټک کوي کله چې تاسو د ډیری ډیری لوبې entities لري. دا ستاسو د لوبې سره مرسته کوي چې د حافظه اغیزمن وي. Vec2 کله چې تاسو د جاواسکرپټ یا پیټون کې د Vec2 اجزا جوړ کړئ، تاسو یوازې دوه شمیره ذخیره کړئ. د چلولو وخت د ډول میټاډاټاټاټ، د مالیتونو معلوماتو، او پروتوټ پیژندلونه اضافه کوي - ستاسو ساده 8 بټی ډاټا جوړښت ته ~48 بټی یادښت بدل کړئ. د Rust ډول سیسټم د کمپیل وخت کې کار کوي. کله چې تاسو د دوو f32 فیلډونو سره د Vec2 struct اعلان کړئ، دا دقیقا هغه څه دی چې ذخیره کیږي - یوازې 8 بټونه، د اضافي میټاډاټونه ندي. د کمپیلر د ډولونو په پام کې لري، نو د Runtime ډول معلوماتو ته اړتیا ندي. د 1000 لوبې انټرنټونو سره، دا فرق د نندارتونونو ته وده ورکړي: Language Memory Usage Overhead Rust 8KB None Dynamic language ~48KB+ 6x overhead رخصتۍ د 8kB نه د متحرک زبان ~48KB + 6x په سر کې دا یوازې د حافظه کارولو په اړه نه ده - دا د کړنو په اړه دی. کوچني، مخکښ حافظه ترتیبونه د CPU کیچ کارولو ښه معنی لري، کوم چې په مستقیم ډول په لوبو کې د فریم نرخونو په چټکۍ سره بدل کیږي چې تاسو هر فریم کې د هزاران entities پروسس کړئ. د کمپیوټر د خپل لوبې چلولو دمخه د ډول غلطیونه کښته کوي، او د حافظه بسته بندۍ په چټکۍ سره کار کوي. د کیمرې جوړولو What should we setup first? موږ د کیمرې ته اړتيا لري ځکه چې هیڅکله په ډیزاین کې نه ښيي چې په ډاټا کې شتون نلري. د نړۍ کولی شي په ډاټا کې شتون لري، مګر د کیمرې ډاټا کوي چې څه په حقیقت کې ښيي. //Pseudo code, don't use this yet fn setup(mut commands: Commands) { commands.spawn(Camera2d) } What’s Camera2d? د Bevy د جوړ شوی 2D کیمرې بکس دی. دا راټول کړئ او تاسو د خپل 2D منظر لپاره چمتو کړئ. Camera2d What’s a bundle? د بیلګه یوازې د برخو ټولګه دی چې تاسو په اغیزمنه توګه یوځای کړئ. د مثال په توګه، Bevy د چې پوښښونه، ټیکتور، رنګ ټینټ، او ښیښه کوي؛ د دې بڼه له خوا په یوه تماس کې د سپریټ entity ورکوي. SpriteBundle زموږ د نصب دنده ثبت کول موږ باید زموږ د نصب دنده سره bevy ثبت کړي چې په Startup کې فعال شي. اټکل کړئ د لاندې کوډ سره. src/main.rs // Replace your main.rs with the following code. use bevy::prelude::*; fn main() { App::new() .add_plugins(DefaultPlugins) .add_systems(Startup, setup) .run(); } fn setup(mut commands: Commands) { commands.spawn(Camera2d); } What’s bevy::prelude? لکه څنګه چې د سپارلو کیټ، هغه څه چې تاسو په دوامداره توګه کله چې یو لوبه جوړ کړئ - د ، برخو، د ریاضی مسلکي، او داسې نور. bevy::prelude App Commands What’s App::new()…? د لوبې غوښتنلیک جوړوي، Loads rendering، input، آډیو، او نورو سیستمونو، زموږ د پیل کولو دنده ثبت کړئ، او د دستو کنټرول لپاره د Bevy اصلي چڼاسکه. App::new() add_plugins(DefaultPlugins) add_systems(Startup, setup) run() Why register a startup function? What does it do? د پیل کولو سیسټمونه د لومړي فریم څخه مخکې یو ځل کارول کیږي. موږ دوی کاروي ترڅو کیمرې، لوبغاړي او نورو شیانو جوړ کړي چې باید د پنجرو په چټکۍ سره ونیسئ. What’s bevy main loop? د پیل کولو وروسته، Bevy خپل اصلي چڼاسکه ته ورسیږي: دا د انټرنېټ سروې کوي، ستاسو سیسټمونه چلوي، د نړۍ تازه کوي، او د فریم ورکوي. دا چڼاسکه تکرار کیږي تر څو چې تاسو د لوبې څخه ترک کړئ. چیرته دا چلند cargo run یو خالی پرده؟ بله، موږ یوازې د کیمرې تنظیم کړی، اوس موږ زموږ لوبغاړی اضافه کوو. د لوبغاړي جوړولو اوس موږ زموږ د لوبغاړي جوړوي! زموږ په یاد او په Bevy کې، هر څه یو سره د دې سیستمونو سره کار وکړي. Setup System Update System Entity Components د انټرنټ په توګه یو ځانګړي ID (چې د ټولنیز امنیت شمیره) او برخو په توګه د معلوماتو په توګه فکر وکړئ. زموږ لوبغاړی، موږ اړتیا لري برخو لکه د حرکت سرعت، روغتیا، یا ځانګړي مهارتونه. په Rust کې، د دې برخو د جوړولو په بشپړ ډول سره . struct دا د رڼا د اصلي جوړولو بکسونو څخه یو دی. دا د ورته معلوماتو سره یوځای کوي. دلته موږ یو خالی اعلان کوو struct نو د ډول ځان په توګه د ټګ په توګه کار کوي چې موږ کولی شو د لوبغاړي entity سره تړاو کړي. وروسته موږ کولی شو د لوبغاړي روغتیا په څیر څه اضافه کړئ. struct Player // Place this before main function in main.rs #[derive(Component)] struct Player; Why tag? Tag د راتلونکي څیړنې لپاره یو entity mark. Because یوازې زموږ د ګروپ سره تړاو لري، سیسټمونه کولی شي Bevy ته پوښتنه، "ما ته د Player ټګ سره سم" او یوازې د هغه سره کار وکړي. Player What’s this #[derive(component)]? Rust ته اجازه ورکوي چې د اجزاء ماکرو کوډ د دې struct سره تړاو کړئ. د ماکرو د Rust لپاره د مخکښ نمونې کوډ تولید کولو لاره ده. په اتوماتيک ډول د Bevy اړتیاوو د کیبل پلیټ انجکشن کوي، نو دا کولی شي ذخیره کړي او پیدا کړي. د انټرنټونو، موږ ته په هر ځای کې د ورته لنډ کوډ کاپی څخه خوندي دي. موږ به په سیریز کې په اوږدو کې د میکروونو په اړه د څېړنې لرو. دا هغه وخت دی چې زموږ لوبغاړی ډول یو اجزاء وي. derive #[derive(Component)] Player What’s a component, and why should the player be a component? یو عنصر د معلوماتو ټوټه دی چې د یو entity سره تړاو لري. موقعیت، سرعت، روغتیا، او حتی د "په دې چې دا د لوبغاړي" مفکوره ټول په برخو کې ژوند کوي. له خوا د جوړولو موږ کولی شو په راتلونکي کې د دې انټرنټ لپاره یو اجزای ته پوښتنه ورکړو، نور اجزایونه اضافه کړئ (چې د روغتیا یا انټرنټ) او د بیوی سیسټمونو ته اجازه ورکړئ چې دقیقا هغه انټرنټونه غوره کړئ چې دوی باید تازه کړي. Player د دې لپاره، موږ به د "@" نمونوي په کارولو سره په پرده کې زموږ د ځانګړتیاو رامینځته کړي. د سیسټم فعالیت بدل کړئ. // Replace existing setup function in main.rs with the following code fn setup(mut commands: Commands) { commands.spawn(Camera2d); // Code Update Alert // Append the following lines to your setup function. commands.spawn(( Text2d::new("@"), TextFont { font_size: 12.0, font: default(), ..default() }, TextColor(Color::WHITE), Transform::from_translation(Vec3::ZERO), Player, )); } دا ټاپل راځي او دا په توګه د اجزاءونو بڼه په توګه کاروي. دا واحد تماس د متن چې موږ غواړئ ښودل کړي، د دې فورټ ترتیبات، رنګ، موقعیت، او ټیټ چې د entity راټولوي. commands.spawn(( ... )) Player What’s a tuple? د Tuple ده د ارزښتونو په ترتیب کې د لیست. Rust د هر پوزیشن څارنه کوي، نو د دوو ارزښتونو سره تړاو لري د جوړولو لپاره اړتیا نلري. (Text2d::new("@"), TextColor(Color::WHITE)) Whats an entity? یو انټرنټ د واحد ID دی چې Bevy د اجزاءو سره تړاو کولو لپاره کاروي. په ځان کې دا د معلوماتو نه لري، مګر کله چې تاسو د اجزاءو سره تړاو کړئ، دا ID ستاسو د لوبې نړۍ کې څه دي. هر بڼه د یو ځانګړي ID او لیست شوي اجزاء سره یو نوی انټرنټ تولید کوي، کوم چې تاسو کولی شئ په دې ډول انځور کړئ: Entity Components it carries #42 Camera2d #43 , , , , Text2d("@") TextFont TextColor Transform Player # د 42 Camera2d # د 43 د د د , Text2d("@") TextFont TextColor Transform Player کله چې د رخصتۍ د رخصتۍ له لاسه ورکړئ، دا انټرنټونه په نړۍ کې ژوند کوي، د سیسټمونو لپاره چمتو کیږي چې دوی د تګونو (د اجزایو) له لارې کشف کړي. موږ به د دې څخه وروسته کار واخلئ کله چې موږ غواړو چې هغه کارونه وکړي چې د دوی حرکت کولو یا انیمیشن کولو په څیر، یا دوی ته د دشمنانو ته حمله کولو لپاره، او داسې نور. د Player Movement کارولو اوس موږ زموږ د د لوبغاړي حرکت لپاره! فکر وکړئ چې موږ د لوبغاړي حرکت کولو لپاره څه اړتیا لري: Update System د ټایټ بورډ انټرنټ - د معلوماتو لپاره چې کوم کلیدونه فشار شوي دي وخت - د حرکت په چټکۍ سره پرته له خوا د فریم سرعت - to actually move the player Player position په نورو لوبې انجنونو کې، تاسو کولی شئ د دې سیسټمونو سره manually اړیکه ونیسئ. خو دلته د Bevy جادو ده - تاسو یوازې په خپل دنده پارامترونو کې هغه څه ته اړتیا لرئ، او Bevy په اتوماتيک ډول دا وړاندې کوي! موږ زموږ د حرکت لوبغاړي دنده لیکلو. // Append this code to main.rs fn move_player( // "Bevy, give me keyboard input" input: Res<ButtonInput<KeyCode>>, // "Bevy, give me the game timer" time: Res<Time>, // "Bevy, give me the player's position" mut player_transform: Single<&mut Transform, With<Player>>, ) { let mut direction = Vec2::ZERO; if input.pressed(KeyCode::ArrowLeft) { direction.x -= 1.0; } if input.pressed(KeyCode::ArrowRight) { direction.x += 1.0; } if input.pressed(KeyCode::ArrowUp) { direction.y += 1.0; } if input.pressed(KeyCode::ArrowDown) { direction.y -= 1.0; } if direction != Vec2::ZERO { let speed = 300.0; // pixels per second let delta = direction.normalize() * speed * time.delta_secs(); player_transform.translation.x += delta.x; player_transform.translation.y += delta.y; } } What’s Res? Res یا سرچینو د لوبې په پراخه کچه د معلوماتو ټوټې دي چې د هر واحد انټرنټ سره تړاو نه لري. د مثال په توګه، تاسو د لوبې ماسټر ساعت ورکوي، نو هر سیسټم د ورته "د مخکښ فریم څخه د وخت" ارزښت وګورئ. Res<Time> Explain Single<&mut Transform, With>? د BeVy لپاره د دقیقا یو entity ته پوښتنه چې د Komponent او همدارنګه د Tag. د برخه معنی چې موږ غواړم چې بدلون یا د لوبغاړي موقعیت بدلون (د یادونه، موږ د نصب فعالیت کې بدلون اجزاء اضافه کړ). که د یو څخه زيات لوبغاړي شتون لري، دا اخستونکي به شکایت وکړي، کوم چې د یو واحد ګروپ لوبغاړي لپاره مناسب دی. Single<&mut Transform, With<Player>> Transform Player &mut Transform What’s Vec2::ZERO? د دویمیزه وکتور دی چې د دویمیزه ارزښتونو سره د 0 نصب شوی دی: موږ دا د ټیټارډ انټرنټ څخه مخکې د پیل لارښوونې په توګه کاروي. Vec2::ZERO Vec2 { x: 0.0, y: 0.0 } What’s this KeyCode::… pattern? د ... دي enums (د پوښلو enums وروسته) چې په ټایټ بورډ کې د ځانګړي کلیدونو ندي. چیک یوازې د Bevy ته پوښتنه چې دا کلید د اوسني فریم په وخت کې بند دی. KeyCode::ArrowLeft KeyCode::ArrowRight input.pressed(KeyCode::ArrowLeft) موږ د زېړ لارښوونې په لټه کې دي، نو لوبغاړی کله چې د کلیدونو پرته فشار نلري. کله چې موږ د انټرنېټ لري، د ویکتور په اوږدوالي 1 کې بدل کیږي نو د diagonal حرکت د مستقیم حرکت څخه سرعت نه وي. ښيي چې په هر ثانیو کې کتنه pixels ته حرکت وکړي، او د فریم وخت راټول کوي - د مخکښ فریم څخه د ثانیو شمیره - نو دوی راټولولو به موږ د دې تازه کولو ته سفر وکړي. په پایله کې موږ د سپریټ په پرده کې حرکت کولو لپاره د سپریټ بدلولو ته دا ډالټا اضافه کوو. normalize() speed time.delta_secs() د حرکت سیستم ثبت کول // Update main function fn main() { App::new() .add_plugins(DefaultPlugins) .add_systems(Startup, setup) .add_systems(Update, move_player) // Line update alert .run(); } Why is move_player added as Update? What’s Update? هر فریم کاروي، نو موږ دا په د جدول د Bevy د جوړ شوي مرحله دی چې د پیل کولو وروسته د لوبې په لور کې یو ځله راټول کیږي. move_player Update Update So does systems mean functions we want bevy to execute at a particular time, like initial setup or on every game loop update? د سیسټم یوازې یو Rust دنده دی چې تاسو د Bevy سره یو ښکلي نوټ ورکړئ چې وايي "د Startup په وخت کې زما چلند" یا "د هر Update په وخت کې زما چلند"، او د انجن به داوطلبانه ولري. چلو دا چلولو. د Sprite Graphics اضافه کول موږ به د جامع LPC SpriteSheet جنراتور کاروي ترڅو زموږ د شخصیت یو څه شخصیت ورکړي. تاسو کولی شئ د بدن برخو، لباس، او رنګونه په او د بشپړ spritesheet صادر کړي. دا لنډول د دې پروژې لپاره د spritesheet اوس په کې شامل دی . د وړاندیز شوي انځور فایلونه ته ډک کړئ نو Bevy کولی شي دوی راټول کړي کله چې د لوبې چلول. تاسو به د جوړولو ته اړتیا لري د src فورمه په کور کې. د Repo src/assets assets Refactoring Code موږ باید نور کوډ اضافه کړئ او د main.rs اضافه کولو به د لوستلو او درکولو سخت کړي. Update your main.rs file to the following, also create another file player.rs د Pulls په ماډل، زموږ اصلي فایل ضخامت کوي. د کوډ په څیر د دې کولو په اسانۍ سره د پروژې پراختیا آسانوي، د یو لوی فایل د ټولو کارولو لپاره. mod player; player.rs //main.rs use bevy::prelude::*; mod player; fn main() { App::new() .insert_resource(ClearColor(Color::WHITE)) // We have updated the bg color to white .add_plugins( DefaultPlugins.set(AssetPlugin { // Our assets live in `src/assets` for this project file_path: "src/assets".into(), ..default() }), ) .add_systems(Startup, setup_camera) .run(); } fn setup_camera(mut commands: Commands) { commands.spawn(Camera2d); } په اپلیکیشن کې د ټولې ترتیبات راټول کیږي نو هر فریم سره د سپین پیژندل پیل کیږي. .insert_resource(ClearColor(Color::WHITE)) د textures، آډیو، او نورو اټکلونو لپاره د Bevy لوډر دی. موږ د دې لکه څنګه چې په داخلي ، کوم چې زموږ sprites ژوند کوي. AssetPlugin file_path src/assets د Player Module جوړولو اوس چې موږ زموږ د بنسټیز اپلیکیشن جوړښت لري، دا وخت دی چې زموږ د شخصیت په ژوند کې وده ورکړي! موږ به یو اختصاصي جوړ کړي ماډل د خپلو کوچني ماجراجورونو سره د هر څه سره تړاو لري. دا زموږ کوډ سازماني کوي او دا په آسانه توګه نور ځانګړتیاوې اضافه کوي. player.rs دا ماډل به د ټولو پایلو، برخو، او سیسټمونه لري چې زموږ لوبغاړی ته حرکت کوي، animate، او د نړۍ سره تعامل کوي. په player.rs کې لاندې کوډ اضافه کړئ // player.rs use bevy::prelude::*; // Atlas constants const TILE_SIZE: u32 = 64; // 64x64 tiles const WALK_FRAMES: usize = 9; // 9 columns per walking row const MOVE_SPEED: f32 = 140.0; // pixels per second const ANIM_DT: f32 = 0.1; // seconds per frame (~10 FPS) #[derive(Component)] struct Player; // Moved from main.rs to here دا اټکلونه د اټکلونو نومونه ورکوي چې موږ د spritesheet او حرکت ریاضی لپاره تکرار کوو: د ټیل اندازه، په رنګونو کې د رنګونو، د چلولو سرعت، او د انیمیشن په چټکۍ سره حرکت کوي. marker دلته ټول لوبغاړی ځانګړي ډولونه په یوه ماډل کې ذخیره کوي. Player د لوبغاړي لارښوونه // Append these lines of code to player.rs #[derive(Component, Debug, Clone, Copy, PartialEq, Eq)] enum Facing { Up, Left, Down, Right, } What’s an enum? یو enum د وړاندیز شوي ارزښتونو ټیټ لیست کوي. زموږ د شخصیت یوازې هر وخت د چارو لارو ته ونیسئ، نو د enum د دغو انتخابونو په یوه ځای کې راټول کوي. Facing Why can’t I use a struct here? د Struct به د Booleans ټوټه ته اړتيا لري لکه د ، او تاسو باید دوی په sync کې ونیسئ. دا دا پیچلي کوي. Enum تضمین کوي چې تاسو یوازې یو لاره غوره کړئ. facing_up:true facing_left:false When to decide between using enum and struct? د Struct کاروي کله چې تاسو د څو ځمکې ته اړتیا لرئ، لکه د او ، یا د لوبغاړي اټکل سره روغتیا او مقاومت. کله چې تاسو د لیست څخه یو اختیاري انتخاب کړئ، لکه هغه وسیله چې لوبغاړي کار کوي (د شمشیر، بڼلي، بڼلي) یا کوم مینو پرده فعال دی. x y Why the purpose of adding Debug, Clone, Copy, PartialEq, Eq macros? موږ ته د لوستلو لپاره د چټک چاپ کولو لپاره، او دا د ارزښت په دوامداره توګه بدیل کړي، او / د برابرولو چیکونه اجازه ورکوي کله چې موږ د لارښوونې مقایسه کوو. Debug Clone Copy PartialEq Eq Why can’t I copy through simple assignment, why should I add these macros? په معياري توګه Rust د ارزښتونو په ځای کې د دوی کاپی کولو لپاره حرکت کوي، نو د کمپیلر تاسو ته اجازه ورکوي چې opt-in. (د لکه څنګه چې د مسلکي) وايي "نه ارزانه ده، په چټکۍ سره ونیسئ او دا په دوامداره توګه." او چیرته موږ په مستقیم ډول د دوو چیرې مقایسه کوو، کوم چې موږ څنګه څومره څومره څومره څومره څومره څومره څومره. Copy Clone PartialEq Eq What do you mean by rust moves values, instead of copying? کله چې تاسو په Rust کې د اکثریت ارزښتونه تادیه کړئ، د قدیم متغیر به د هغې د مالکیت څخه مخه ونیسئ یا په بل کلمه کې مړ. Adding په هر دوو متغیرونو کې اعتبار لري. Copy Why does the old variable stop owning or die when assigned? Rust تضمین کوي چې هر ارزښت یو واحد مالک لري نو د یادښت په خوندي ډول آزاد شي. موږ به د مالکیت قوانین او په راتلونکي فصلونو کې قرض واخلئ. This is a bit going over my head! بله، فکر نه وکړئ، موږ ډیری نور فصلونه لري او لکه څنګه چې تاسو د دوی له لارې چمتو کړئ، موږ به د دې په حل کې وي. دا ښه ده چې اوس د دغو مفاهیمو نه درکوي. انیمیشن سیسټم اجزاء کله چې موږ د څو فریمونو سره spritesheet لري (چې زموږ د 9 فریم چلند انیمیشن) موږ ته اړتيا لري چې د دې فریمونو چټک څومره کنټرول کړي. د وخت کنټرول له لارې، زموږ د شخصیت به په یوه فریم کې یا په فریمونو کې د چټک ډول چټک څومره یخ شي! د Flipbook په څیر فکر وکړئ - که تاسو د صفحاتو په اوږدو کې ډک کړئ، د انیمیشن ښکاري. که تاسو په اوږدو کې ډک کړئ، تاسو کولی شئ نه وګورئ چې څه دی. موږ ته د دې وخت په اړه دقیق کنترول ورکوي، زموږ د شخصیت د چلولو انیمیشن په مناسب سرعت کې چټک او طبيعي ښکاري. AnimationTimer د پروپیلن په دې توګه هر لوبغاړی entity پوه شي چې کله به د راتلونکي انیمیشن فریم ته وده ورکړي. هر ټک د وخت د یو ټیټ رامینځته کوي؛ کله چې د ټیمر د انټرنېټ ته ځي، موږ په پاڼه کې د راتلونکي سپریټ ته حرکت کوو. AnimationTimer Timer // Append these lines of code to player.rs #[derive(Component, Deref, DerefMut)] struct AnimationTimer(Timer); #[derive(Component)] struct AnimationState { facing: Facing, moving: bool, was_moving: bool, } What’s Deref, DerefMut macros doing? اجازه ورکړي چې زموږ د پوښونو په داخلي کې وي کله چې موږ له دې څخه زده کوو، او د کتابونو لپاره ورته کار کوي. دا معنی لري چې موږ کولی شو یوازې د پرته له لومړی د داخلي ارزښت په لارښوونې پرته. Deref Timer DerefMut timer.tick(time.delta()) AnimationTimer So we are renaming the Timer to AnimationTimer? موږ د ټیمر پوښښ، نه د نوم بدلون. فکر وکړئ لکه څنګه چې یو کوچني بکس چې د ، او همدارنګه یو لیبل چې وايي "په دې کې د لوبغاړی انیمیشن لري." کله چې موږ يو لوبغاړی رامینځته کوو موږ د تازه او دا په دې بکس کې ورسئ، نو هر بازیکن کولی شي د خپل ټیمر لري که موږ د ډیری قهرمانونو ته اړتيا لري. AnimationTimer Timer Timer So it’s an instance of AnimationTime? نه، is a tuple struct that contains a . موږ یو جوړوي کله چې موږ د لوبغاړي پیاوړئ نو هر entity کولی شي د خپل خپل ټیمر ډاټا ولري. دا نمونې ښکاري کله چې تاسو غواړئ د یو موجوده ډول ته اضافي معنی ورکړئ پرته د یو نوي API لیکنه. AnimationTimer Timer په یاد ولرئ چې لوبغاړی څرنګه کوي، که چیرې دوی حرکت کوي، او که چیرې دوی یوازې پیل شوي یا بند شوي. سیسټمونه دا وګورئ ترڅو انیمیشن رنګونه وټاکئ او کله چې حرکت بدلونونه رامینځته کړي. AnimationState د لوبغاړي د سپارلو موږ د spritesheet له لارې ، د تخنیکي اټالس ترتیب جوړ کړئ ترڅو Bevy د ریټ پوه شي، او د ګرځنده ګرځنده لپاره د پیل فریم انتخاب کړئ. بيا موږ د سپریټ سره یو انټیټ ولري، په اصل کې بدلون، زموږ د markers برخو، او د ټیمر چې د انیمیشن چلولو وکړي. AssetServer // Append these lines of code to player.rs fn spawn_player( mut commands: Commands, asset_server: Res<AssetServer>, mut atlas_layouts: ResMut<Assets<TextureAtlasLayout>>, ) { // Load the spritesheet and build a grid layout: 64x64 tiles, 9 columns, 12 rows let texture = asset_server.load("male_spritesheet.png"); let layout = atlas_layouts.add(TextureAtlasLayout::from_grid( UVec2::splat(TILE_SIZE), WALK_FRAMES as u32, // columns used for walking frames 12, // at least 12 rows available None, None, )); // Start facing down (towards user), idle on first frame of that row let facing = Facing::Down; let start_index = atlas_index_for(facing, 0); commands.spawn(( Sprite::from_atlas_image( texture, TextureAtlas { layout, index: start_index, }, ), Transform::from_translation(Vec3::ZERO), Player, AnimationState { facing, moving: false, was_moving: false }, AnimationTimer(Timer::from_seconds(ANIM_DT, TimerMode::Repeating)), )); } د پیل د لارښوونې او پرچون تنظیم کوي چې د شخصیت په دې وخت کې نه دی او نه دی وروستۍ فریم. AnimationState { facing, moving: false, was_moving: false } د تکرار د ساعت جوړوي چې د هر د spritesheet پرمختګ لپاره ثانیو. AnimationTimer(Timer::from_seconds(ANIM_DT, TimerMode::Repeating)) ANIM_DT ? What’s an AssetServer د د Bevy د فایل لوډر او مدیر دی چې د لوبې اسنادو لکه انځورونه، غږونه او 3D ماډلونو د لوډ کولو او کیش کولو سره کار کوي. AssetServer کله چې تاسو د ، دا فورا د فایل په حافظه کې لوډ نه کوي. په عوض، دا د ګرځنده راټول کوي چې تاسو کولی شئ وروسته کار واخلئ. دا د "لوري لوډ" په نامه ده - د واقعي فایل لوډ په پایله کې ترسره کیږي، او Bevy به تاسو ته خبر ورکوي کله چې دا چمتو ده. asset_server.load("path/to/sprite.png") دا روښانه ده ځکه چې: Multiple entities can share the same sprites without loading it multiple times. د ډیرو entities کولی شي په ورته sprites کې شریک شي پرته له دې چې دا د ډیرو وختونو کې لوستل شي. محصوالت یوازې کله چې په حقیقت کې اړتيا لري لوستل کیږي Bevy کولی شي د ښه کړنو لپاره د بیلګې د رسولو اټکل او اټکلونه غوره کړي که یو محصوالت ناکام وي، دا به ستاسو ټولې لوبه ناڅاپي نه وي په اسانۍ کې، د spritesheet ته پوښتنه کوي او د لوډ کولو حالت د څارنې لپاره د دستکشو ته ورسیږي. asset_server.load("sprites/player.png") د حرکت سیستم اوس چې موږ زموږ د لوبغاړي سره د ټولو اړین اجزاءونه ولډنګ لري، موږ باید زموږ د موجوده حرکت سیسټم تازه کړي ترڅو د لوبغاړي په څیر څرنګول کړي. دا د انیمیشن سیسټم لپاره مهم دی چې پوه شي چې د spritesheet چې باید کار واخلئ - هر سمته (غور، لاندې، چپ، چپ) زموږ په spritesatlas کې د مختلفو لړونو سره مطابقت لري. دا تازه سیستم به د حرکت لارښوونې تشخیص کړي او د په مطابقت کې، نو زموږ انیمیشن سیستم کولی شي په هر سمت کې د چلولو انیمیشنونو لپاره د Sprite لړ درست انتخاب کړي. AnimationState // Append these lines of code to player.rs fn move_player( input: Res<ButtonInput<KeyCode>>, time: Res<Time>, mut player: Query<(&mut Transform, &mut AnimationState), With<Player>>, ) { let Ok((mut transform, mut anim)) = player.single_mut() else { return; }; let mut direction = Vec2::ZERO; if input.pressed(KeyCode::ArrowLeft) { direction.x -= 1.0; } if input.pressed(KeyCode::ArrowRight) { direction.x += 1.0; } if input.pressed(KeyCode::ArrowUp) { direction.y += 1.0; } if input.pressed(KeyCode::ArrowDown) { direction.y -= 1.0; } if direction != Vec2::ZERO { let delta = direction.normalize() * MOVE_SPEED * time.delta_secs(); transform.translation.x += delta.x; transform.translation.y += delta.y; anim.moving = true; // Update facing based on dominant direction if direction.x.abs() > direction.y.abs() { anim.facing = if direction.x > 0.0 { Facing::Right } else { Facing::Left }; } else { anim.facing = if direction.y > 0.0 { Facing::Up } else { Facing::Down }; } } else { anim.moving = false; } } د پوښتنې د ټګار شوي واحد انټرنټ لپاره د Bevy پوښتنه کوي موږ ته د دې لپاره د بدلون وړ لاس رسی او . موږ د فشار شوي کلیدونو څخه د لارښوونې ویکټور جوړوي، دا ته عادي کړي نو د پړاو انټرنټ ډیر سرعت نلري، او د سپیکر د سرعت × فریم وخت له مخې حرکت کوي. د چمتوولو منطق په پرتله د افقی vs فوريک قوت برابر کوي ترڅو فیصلہ کړي چې څنګه د سپیکټ باید چمتو شي. موږ ریکارډ کوو که آیا د سپیکر اوس حرکت کوي نو وروسته سیسټمونه کولی شي څومره کله چې حرکت پیل کیږي یا بند کیږي. Player Transform AnimationState د Animation کارول اوس موږ د ټولو ټوټې په ځای کې دي - زموږ لوبغاړی کولی شي په مختلفو لارو کې حرکت وکړي، او موږ په څیر چمتو کوو چې دوی په څیر چمتو شوي دي. د پایلې ټوټې د انیمیشن سیسټم دی چې په حقیقت کې د سپریټ فریمونه تازه کوي ترڅو د چلولو انیمیشن جوړ کړي. دا سیستم زموږ د حرکت سیسټم څخه د لارښوونې معلوماتو لیږدوي او د انیمیشن ټیمر کاروي ترڅو په مناسب سرعت کې د سپریټ فریمونو له لارې چټک کړي. دا د مختلف انیمیشن رنګونو تر منځ بدلولو پیچلي منطق سره کار کوي کله چې لوبغاړی د لارښوونې په لارښوونې کې بدل کیږي، او د پیښو او غیر معمولي حالتونو تر منځ د تبادلې تبادلې تضمین کوي. // Append these lines of code to player.rs fn animate_player( time: Res<Time>, mut query: Query<(&mut AnimationState, &mut AnimationTimer, &mut Sprite), With<Player>>, ) { let Ok((mut anim, mut timer, mut sprite)) = query.single_mut() else { return; }; let atlas = match sprite.texture_atlas.as_mut() { Some(a) => a, None => return, }; // Compute the target row and current position in the atlas (column/row within the 9-column row) let target_row = row_zero_based(anim.facing); let mut current_col = atlas.index % WALK_FRAMES; let mut current_row = atlas.index / WALK_FRAMES; // If the facing changed (or we weren't on a walking row), snap to the first frame of the target row if current_row != target_row { atlas.index = row_start_index(anim.facing); current_col = 0; current_row = target_row; timer.reset(); } let just_started = anim.moving && !anim.was_moving; let just_stopped = !anim.moving && anim.was_moving; if anim.moving { if just_started { // On tap or movement start, immediately advance one frame for visible feedback let row_start = row_start_index(anim.facing); let next_col = (current_col + 1) % WALK_FRAMES; atlas.index = row_start + next_col; // Restart the timer so the next advance uses a full interval timer.reset(); } else { // Continuous movement: advance based on timer cadence timer.tick(time.delta()); if timer.just_finished() { let row_start = row_start_index(anim.facing); let next_col = (current_col + 1) % WALK_FRAMES; atlas.index = row_start + next_col; } } } else if just_stopped { // Not moving: keep current frame to avoid snap. Reset timer on transition to idle. timer.reset(); } // Update previous movement state anim.was_moving = anim.moving; } // Returns the starting atlas index for the given facing row fn row_start_index(facing: Facing) -> usize { row_zero_based(facing) * WALK_FRAMES } fn atlas_index_for(facing: Facing, frame_in_row: usize) -> usize { row_start_index(facing) + frame_in_row.min(WALK_FRAMES - 1) } fn row_zero_based(facing: Facing) -> usize { match facing { Facing::Up => 8, Facing::Left => 9, Facing::Down => 10, Facing::Right => 11, } } په دوامداره توګه د پایلو چک کوي او موږ ته اړتیا لرونکي ټوټې نومونه ورکوي. که پوښتنې بریالیتوب کوي، د کوډ تړل کیږي د او نو موږ کولی شو دوی وروسته وکاروي. که دا ناکام وي (نه یو لوبغاړی، یا د یو څخه زيات) موږ د د زيرمه او خوندي کړئ. د Rust کاروي د دې لپاره ډول: معنی: "کړتیا په بشپړه توګه یو پایلې ته ورسیږي"، معنی "د دې پوښتنې په اړه څه ندي". let Ok((mut anim, mut timer, mut sprite)) = query.single_mut() else { return; }; anim timer sprite else Result Ok Err د دې وروسته موږ د ، کوم چې د Rust "شاید یو ارزښت" ډول دی. معنی کوي چې د ټیکسټور اټال موجود دی او موږ کولی شو دا تنظیموي؛ له دې امله، موږ له دې ته ورسیږي او به د راتلونکي فریم دوبار هڅه وکړي. دا ورته نمونې دی چې تاسو د نقشه یا کیش چک کولو کې کاروي: یوازې د ارزښت کاروئ کله چې د څیړنې څه ته ورسیږي. match Option Some(atlas) None د ګرځنده لپاره د انیمیشن حالت، ټیمر، او سپریټ دستکشو ټکول کوي. دا معلوموي کوي چې د اټالس کیدای شي د اوسني چمتو کولو سره مطابقت کوي، کله چې د لارښوونې بدلون کې د هغه کرښې ته راټول کیږي، او د ټیمر کاروي ترڅو په مستطیل سرعت کې ټیلرونو کې راټول شي. کله چې حرکت بند کیږي موږ ټیمر ته راټول کړو نو د انیمیشن په پایله ریمو کې آرام کوي. د مسلکي فعالیتونه د صحیح ریمو او ریمو انډیز ته د چمتو کولو لپاره د چمتو کولو لپاره کاروي. animate_player د Player Plugin جوړولو // Append these lines of code to player.rs pub struct PlayerPlugin; impl Plugin for PlayerPlugin { fn build(&self, app: &mut App) { app.add_systems(Startup, spawn_player) .add_systems(Update, (move_player, animate_player)); } } په Bevy کې، یو پلگ ان یوازې یو struct دی چې پوهې کوي چې څنګه سیستمونه، سرچینې، او اسناد ثبت کړي. د ، موږ Bevy یو چیک لست ورکړي: کله چې دا پلگ ان د اپلیکیشن ته اضافه کیږي، د کوډ په داخله کې چلولو ته هر څه چې د لوبغاړي ځانګړتیا ته اړتيا لري جوړ کړي. د ګرځنده ځانګړي تماسونو د تبادلې څخه. PlayerPlugin Plugin PlayerPlugin main.rs د Method is the checklist. Bevy له موږ ته د بدلونونو ، او موږ د سیسټمونو په اړه چې موږ اړتيا لري. د پروګرام په لکه څنګه چې sprites ښکاري کله چې د لوبې پیل. او راځي د له دې امله چې دوی هر فریم کاروي - په lockstep کې د انټرنېټ او انیمیشن په کارولو سره. د دلته اعلان شوي ټولو سره، د په په اتوماتيک ډول د ټولو لوبغاړي جریان ته ورسیږي. build App spawn_player Startup move_player animate_player Update PlayerPlugin App::new() So this build function is an in-built structure, which I have to write? بله. د ټریټ بیان کوي چې "هر پلگ ان باید د د فعالیت." موږ د دې ځانګړتیا پیژندل، نو Rust انتظار کوي چې موږ ته د بدن د رسولو. Bevy دا روش راځي کله چې دا د پلگ ان لوډوي، کوم چې د دې لپاره چې موږ په دې کې زموږ ټول سیستمونه اضافه کوو. Plugin build(&self, app: &mut App) What’s a trait? د ځانګړتیاو یو قرارداد دی چې بیان کوي چې یو ډول باید څومره وړاندې کړي. ټریټ وايي: "د ما د A له دې امله چې زه ستاسو سیستمونه ثبت کولی شي. " ، موږ په Bevy د پیل کولو پروسه کې تړل شوي او زموږ د خپل نصب کوډ ونیسئ. ځانګړتیاوې اجازه ورکوي چې د مختلفو ډولونو د چلند شریک کړي، د هر نورو Bevy پلگ ان په څیر چلند کوي، مګر دا زموږ د لوبغاړي ځانګړي سيستمونه نصب کوي. Plugin build PlayerPlugin PlayerPlugin د بشپړولو // Update the main function in main.rs use crate::player::PlayerPlugin; fn main() { App::new() .insert_resource(ClearColor(Color::WHITE)) .add_plugins( DefaultPlugins.set(AssetPlugin { file_path: "src/assets".into(), ..default() }), ) .add_systems(Startup, setup_camera) .add_plugins(PlayerPlugin) // Update this line .run(); } چلو دا چلولو. cargo run دا مقاله په اصل کې په زما بلاګ کې نشر شوي. دا مقاله په اصل کې په . زموږ د بلاگ نور برخو ته راځي، د تڼۍ سره اړیکه ونیسئ. په لینډینډ کې زما سره اړیکه ونیسئ، د وروستیو تازه معلومات لپاره د X په اړه زما پیژندل کړئ. د لنډول X