This is chapter 1 of my Rust-Bevy tutorial series “The Impatient Programmer’s Guide to Bevy and Rust: Build a Video Game from Scratch”. ຂໍຂອບໃຈຫຼາຍກ່ຽວກັບ Bevy ແລະການພັດທະນາເກມໃນຊຸດນີ້, ແລະຍັງຈະກວມເອົາ NPCs Powered by AI Agents. ສະ ຫນັບ ສະ ຫນູນ ສະ ຫນູນ ສະ ຫນູນ ສະ ຫນູນ ສະ ຫນູນ ສະ ຫນູນ ສະ ຫນູນ ສະ ຫນູນ ສະ ຫນູນ ສະ ຫນູນ ສະ ຫນູນ ສະ ຫນູນ ສະ ຫນູນ ສະ ຫນູນ ສະ ຫນູນ ສະ ຫນູນ ສະ ຫນູນ ສະ ຫນູນ ສະ ຫນູນ ຊື່ຫຍໍ້ຂອງ : ການນໍາໃຊ້ At the end of this chapter, you’ll be able to achieve the results in the GIF below: ລະຫັດ QR Setup ລະຫັດ QR ຖ້າຫາກວ່າທ່ານບໍ່ໄດ້ສ້າງຕັ້ງ ຂໍຂອບໃຈວ່າທ່ານກໍາລັງຊອກຫາ ການຕິດຕັ້ງ ພາສາລາວ ຄູ່ມື Official Create a fresh project: cargo new bevy_game cd bevy_game Open and add Bevy: Cargo.toml [dependencies] bevy = "0.16.1" ຂໍຂອບໃຈວ່າທ່ານມີປະສົບການໂຄງການໃນແຕ່ລະດູແລນຫນຶ່ງເຊັ່ນ javascript ຫຼື python. ຊື່ຫຍໍ້ຂອງ : Thinking in Systems ພວກເຮົາມີສິ່ງທີ່ພວກເຮົາມີຄວາມຕ້ອງການທີ່ຈະສ້າງເກມງ່າຍທີ່ໃຫ້ຜູ້ຊ່ຽວຊານໃຫ້ການປ່ຽນແປງຈາກ input keyboard? ລະບົບໂລກ: Create a game world where the player can move. ລະບົບ Input: ການຄວບຄຸມ input keyboard ແລະການປ່ຽນແປງມັນ into player movements. ພວກເຮົາຮູ້ວ່າຮູບແບບທີ່ຜ່ານມາແມ່ນປົກກະຕິໃນການສ້າງເກມ, ຮູບແບບຂອງການສ້າງ / ການຕິດຕັ້ງແລະຮູບແບບຂອງການຄວບຄຸມການປ່ຽນແປງຂອງສະຖານທີ່ເກມແລະການປັບປຸງອົງປະກອບຂອງໂລກເກມ. ການຕິດຕັ້ງ: Spawn ຜູ້ສະຫມັກທີ່ມີຄຸນນະພາບທີ່ແຕກຕ່າງກັນ, ດາວໂຫລດ sprites ຜູ້ສະຫມັກແລະ animations, ການສ້າງຄຸນນະພາບຂອງຜູ້ສະຫມັກແລະຄຸນນະພາບການປິ່ນປົວ. ການປັບປຸງ: ການເດີນທາງອຸປະກອນການຝຶກອົບຮົມ, ການເດີນທາງອຸປະກອນການຝຶກອົບຮົມແລະການຝຶກອົບຮົມ ໃນພື້ນຖານຂອງ Bevy, ພວກເຮົາມີຄວາມງ່າຍດາຍນີ້ ຮູບແບບພື້ນຖານນີ້ແມ່ນສິ່ງທີ່ເຮັດໃຫ້ Bevy ມີຄວາມເຂັ້ມແຂງແລະໄດ້ຢ່າງງ່າຍດາຍທີ່ຈະຮູ້ - ໃນຂະນະທີ່ທ່ານກວດສອບຄວາມຄິດສ້າງສັນນີ້, ການກໍ່ສ້າງທີ່ມີ bevy ຈະໄດ້ຮັບການງ່າຍດາຍ. setup and update system ສະ ຫນັບ ສະ ຫນູນ ສະ ຫນູນ ສະ ຫນູນ ສະ ຫນູນ ສະ ຫນູນ ສະ ຫນູນ ສະ ຫນູນ ສະ ຫນູນ ສະ ຫນູນ ສະ ຫນູນ ສະ ຫນູນ ສະ ຫນູນ ສະ ຫນູນ ສະ ຫນູນ ສະ ຫນູນ ສະ ຫນູນ ສະ ຫນູນ ສະ ຫນູນ ສະ ຫນູນ ສະ ຫນູນ ສະ ຫນູນ ສະ ຫນູນ ສະ ຫນູນ // Pseudo code, doesn't compile fn setup(mut commands: Commands) { // Create anything you want commands.spawn() } What’s mut? ໃນ Rust ພວກເຮົາມີຄວາມຮູ້ສຶກວ່າພວກເຮົາມີຄວາມຕ້ອງການທີ່ຈະປ່ຽນແປງຄຸນນະພາບນີ້. ໂດຍປົກກະຕິ, Rust ມີການປິ່ນປົວຄຸນນະພາບ declared ເປັນພຽງແຕ່ອ່ານ. Rust ເວົ້າວ່າພວກເຮົາມີການຄາດວ່າຈະປ່ຽນແປງຄຸນນະພາບ. Rust ໃຊ້ຄວາມຮູ້ນີ້ເພື່ອປ້ອງກັນປະເພດທັງຫມົດຂອງ bugs. mut mut ພາສາລາວ ? And what’s this mut commands: Commands ມັນຈາກ library bevy, ເຊິ່ງໃຫ້ທ່ານສາມາດເພີ່ມສິ່ງທຸກຢ່າງກັບໂລກຂອງເກມຂອງທ່ານ. Rust ຕ້ອງປະເພດພິເສດ, ດັ່ງນັ້ນ ເຮັດໃຫ້ຜູ້ຊ່ຽວຊານທີ່ຮູ້ວ່າ parameter ນີ້ແມ່ນ interface command ຂອງ Bevy. Skip the hint and Rust cannot be sure what appears, so it blocks the build. :Commands What’s a type? ປະເພດຂຽນ Rust ປະເພດຄຸນນະພາບທີ່ທ່ານກໍາລັງເຮັດວຽກ - ຈໍານວນ, ວັດສະດຸ, timers, comands Bevy, ແລະອື່ນໆ. ຫຼັງຈາກທີ່ compiler ໄດ້ຮູ້ປະເພດ, ມັນສາມາດກວດສອບວ່າທຸກການປະຕິບັດທີ່ທ່ານປະຕິບັດກ່ຽວກັບມັນໄດ້ເຮັດໄດ້ຢ່າງວ່ອງໄວ. Isn’t this too much work? ມັນປ້ອງກັນຄວາມຜິດພາດແລະຊ່ວຍໃຫ້ທ່ານມີຜົນປະໂຫຍດ, ເຊັ່ນດຽວກັນ: ຖ້າຫາກວ່າທ່ານກໍາລັງຊອກຫາການເພີ່ມ string ກັບຈໍານວນ, compiler ຈະປິ່ນປົວທ່ານກ່ອນທີ່ຈະດໍາເນີນການເກມ. ປະເພດ ນີ້ແມ່ນພຽງແຕ່ສອງຈໍານວນ, ດັ່ງນັ້ນ Rust ສະຫນັບສະຫນູນທັງຫມົດສອງຈໍານວນ, ບໍ່ມີສະຖານທີ່ເພີ່ມເຕີມ, ເຊິ່ງເຮັດໃຫ້ການເຮັດວຽກໄດ້ຢ່າງໄວ້ວາງໃຈໃນເວລາທີ່ທ່ານມີອຸປະກອນຂອງເກມ. These help your game to be memory efficient. Vec2 ໃນເວລາທີ່ທ່ານສ້າງ Object Vec2 ໃນ JavaScript ຫຼື Python, ທ່ານບໍ່ພຽງແຕ່ເກັບຮັກສາສອງຈໍານວນ. Runtime ສະຫນັບສະຫນູນການເພີ່ມ metadata ປະເພດ, ຂໍ້ມູນຄຸນນະພາບ, ແລະການຢັ້ງຢືນ prototype - ການປ່ຽນແປງອຸປະກອນຂໍ້ມູນ 8-bit ຂອງທ່ານຢ່າງງ່າຍດາຍໃນ ~48 bytes ຂອງຄວາມຮູ້ສຶກ. ລະບົບປະເພດຂອງ Rust ແມ່ນເຮັດວຽກໃນເວລາ compile. When you declare a Vec2 struct with two f32 fields, that’s exactly what gets stored – just 8 bytes, no additional metadata. Compiler already knows the types, so no runtime type information is needed. ມີ 1000 entities ເກມ, ການຄາດຄະເນດິນທີ່ແຕກຕ່າງກັນແມ່ນ dramatic: Language Memory Usage Overhead Rust 8KB None Dynamic language ~48KB+ 6x overhead ພາສາລາວ ລະຫັດ QR ບໍ່ມີ ປະເພດ Dynamic ຊື່ຫຍໍ້ຂອງ : 48KB ລະຫັດ QR ມັນບໍ່ແມ່ນພຽງແຕ່ກ່ຽວກັບການນໍາໃຊ້ຄວາມຮູ້ສຶກ - ມັນແມ່ນກ່ຽວກັບຜົນປະໂຫຍດ. ການອອກແບບຄວາມຮູ້ສຶກທີ່ມີຂະຫນາດນ້ອຍແມ່ນການນໍາໃຊ້ cache CPU ທີ່ດີກວ່າ, ເຊິ່ງໄດ້ຖືກນໍາໃຊ້ຢ່າງວ່ອງໄວໃນເກມທີ່ທ່ານກໍາລັງປິ່ນປົວຄວາມຮູ້ສຶກຫຼາຍກ່ວາຫຼາຍກ່ວາຄວາມຮູ້ສຶກທີ່ມີຄວາມຮູ້ສຶກທີ່ມີຄວາມຮູ້ສຶກທີ່ມີຄວາມຮູ້ສຶກທີ່ມີຄວາມຮູ້ສຶກທີ່ມີຄວາມຮູ້ສຶກທີ່ມີຄວາມຮູ້ສຶກທີ່ມີຄວາມຮູ້ສຶກທີ່ມີຄວາມຮູ້ສຶກທີ່ມີຄວາມຮູ້ສຶກທີ່ມີຄວາມຮູ້ສຶກ. ດາວໂຫລດ Camera What should we setup first? ພວກເຮົາມີຄວາມຕ້ອງການຂອງຄອມພິວເຕີ, ໃນຂະນະທີ່ພວກເຮົາມີຄວາມຮູ້ສຶກວ່າບໍ່ມີຄອມພິວເຕີທີ່ແຕກຕ່າງກັນ. World can exist in data, but the camera decides what actually gets drawn. //Pseudo code, don't use this yet fn setup(mut commands: Commands) { commands.spawn(Camera2d) } What’s Camera2d? BeVy ເປັນ 2D camera bundle ທີ່ສ້າງຕັ້ງຂຶ້ນ. Spawn ມັນແລະທ່ານໄດ້ຮັບການຊອກຫາທີ່ຫນ້າສົນໃຈຂອງຮູບພາບ 2D ຂອງທ່ານ. Camera2d What’s a bundle? ການຫຸ້ມຫໍ່ແມ່ນພຽງແຕ່ກຸ່ມຂອງອຸປະກອນທີ່ທ່ານຄ້າຍຄືກັນໂດຍທົ່ວໄປ. ຜະລິດຕະພັນຂອງ Bevy ການບັນທຶກທີ່ບັນທຶກສະຖານທີ່, texture, ສີຂາວ, ແລະ visibility; spawning that bundle gives a sprite entity in one call. SpriteBundle ດາວນ໌ໂຫລດ Function Setup ພວກເຮົາມີຄວາມຕ້ອງການທີ່ຈະລົງທະບຽນ Function Setup ຂອງພວກເຮົາທີ່ມີ 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? ເປັນຫຍັງເປັນ starter kit, ສິ່ງທີ່ທ່ານຊອກຫາໂດຍທົ່ວໄປໃນເວລາທີ່ສ້າງເກມ - ຂໍຂອບໃຈ , ອຸປະກອນ, ຜູ້ສະຫນອງ Math, ແລະອື່ນໆ bevy::prelude App Commands What’s App::new()…? ດາວນ໌ໂຫລດ The Game App ດາວໂຫລດ rendering, input, audio, ແລະລະບົບອື່ນໆ, ດາວນ໌ໂຫລດ Startup Function ສາຍການຄວບຄຸມກັບ loop Main ຂອງ Bevy. App::new() add_plugins(DefaultPlugins) add_systems(Startup, setup) run() Why register a startup function? What does it do? ລະບົບການເລີ່ມຕົ້ນຈະເຮັດວຽກຫນຶ່ງໃນເວລາທີ່ກໍານົດຮູບເງົາຄັ້ງທໍາອິດ. ພວກເຮົານໍາໃຊ້ພວກເຂົາເພື່ອຕິດຕັ້ງຄອມພິວເຕີ, ຜູ້ດາວໂຫລດແລະສິ່ງອື່ນໆທີ່ຈໍາເປັນຕ້ອງມີໃນຂະນະທີ່ window ໄດ້ເປີດ. What’s bevy main loop? ຫຼັງຈາກການເລີ່ມຕົ້ນ, Bevy ໄດ້ເຂົ້າໄປໃນ loop ທີ່ສໍາຄັນຂອງຕົນ: ມັນກວດສອບ input, ດໍາເນີນການລະບົບຂອງທ່ານ, ການປັບປຸງໂລກແລະ render a frame. That loop repeats until you quit the game. ຂໍຂອບໃຈຫຼາຍ cargo run ຮູບພາບ ສໍາ ລັບ Yup, we have just set up the camera, now let's add our player. ດາວນ໌ໂຫລດ The Player ໃນປັດຈຸບັນ, ກະລຸນາສ້າງຜູ້ຊ່ຽວຊານຂອງພວກເຮົາ! ກະລຸນາຮູ້ພວກເຮົາ ແລະ ທີ່ຜ່ານມາ: ໃນ Bevy, ທັງຫມົດແມ່ນ ປະເພດ ລະບົບທີ່ສາມາດເຮັດວຽກກັບ. Setup System Update System Entity Components ການຄາດຄະເນດິນຟ້າອິນເຕີເນັດແລະການຄາດຄະເນດິນຟ້າອິນເຕີເນັດ - ການຄາດຄະເນດິນຟ້າອິນເຕີເນັດ . struct ມັນເປັນຫນຶ່ງໃນໂຄງສ້າງພື້ນຖານຂອງ rust. ມັນກຸ່ມຂໍ້ມູນທີ່ແຕກຕ່າງກັນກັບຄືນໄປບ່ອນນີ້. Here we declare an empty ປະເພດຂອງຕົນເອງເຮັດວຽກເປັນ tag ພວກເຮົາສາມາດຕິດຕໍ່ກັບຜູ້ຊ່ຽວຊານຂອງຜູ້ຊ່ຽວຊານ. ຫຼັງຈາກນັ້ນພວກເຮົາມີຄວາມສາມາດເພີ່ມສິ່ງທີ່ເປັນການຄຸ້ມຄອງຂອງຜູ້ຊ່ຽວຊານ. struct Player // Place this before main function in main.rs #[derive(Component)] struct Player; Why tag? Tag marks entity ສໍາລັບການຊອກຫາຫຼັງຈາກນັ້ນ. because ລະບົບສາມາດໃຫ້ Bevy, "ໃຫ້ຂ້ອຍຜູ້ທີ່ມີ Tag Player" ແລະເຮັດວຽກທີ່ມີພຽງແຕ່ຫນຶ່ງ. Player What’s this #[derive(component)]? ວິທີການຂອງ Rust ເພື່ອສ້າງຕົວຢ່າງຂອງຕົວຢ່າງຂອງຕົວຢ່າງສໍາລັບທ່ານ ອັດຕະໂນມັດ injects boilerplate Bevy needs, ດັ່ງນັ້ນມັນສາມາດເກັບຮັກສາແລະຊອກຫາ ພວກເຮົາຈະຊອກຫາ macros ຫຼາຍກ່ວາຂ້າງລຸ່ມນີ້ໃນຊຸດ. ນີ້ແມ່ນເວລາທີ່ປະເພດຂອງຜູ້ດາວໂຫລດຂອງພວກເຮົາມີຄວາມເປັນສ່ວນຕົວ. derive #[derive(Component)] Player What’s a component, and why should the player be a component? Component ເປັນສ່ວນຂອງຂໍ້ມູນທີ່ເຊື່ອມຕໍ່ກັບອຸປະກອນ. ສະຖານທີ່, ຄວາມໄວ, ຄວາມປອດໄພ, ແລະເຖິງແມ່ນວ່າຄວາມຄິດຂອງ "This is the player" ທັງຫມົດແມ່ນຢູ່ໃນອຸປະກອນ. ໂດຍການສ້າງ ການຄົ້ນຄວ້າແລະການປິ່ນປົວຂອງການປິ່ນປົວຂອງການປິ່ນປົວຂອງການປິ່ນປົວຂອງການປິ່ນປົວຂອງການປິ່ນປົວຂອງການປິ່ນປົວຂອງການປິ່ນປົວຂອງການປິ່ນປົວຂອງການປິ່ນປົວຂອງການປິ່ນປົວຂອງການປິ່ນປົວຂອງການປິ່ນປົວຂອງການປິ່ນປົວຂອງການປິ່ນປົວຂອງການປິ່ນປົວຂອງການປິ່ນປົວຂອງການປັບປຸງ 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, )); } ການສື່ຫຍໍ້ຂອງຂອງພວກເຮົາແມ່ນ Tuple, Tuple ແລະ Tuple. This single call adds the text we want to show, its font settings, the color, the position, and the ຊື່ຫຍໍ້ຂອງ : identify the entity commands.spawn(( ... )) Player What’s a tuple? ຊື່ຫຍໍ້ຂອງ : A tuple is an ordered list of values written in parentheses. Rust keeps track of each position, so ມີສອງຄຸນນະພາບຂ້າງຂວາງໂດຍບໍ່ຈໍາເປັນຕ້ອງສ້າງ struktur. (Text2d::new("@"), TextColor(Color::WHITE)) Whats an entity? ຊື່ຫຍໍ້ຂອງ : A entity is the unique ID that Bevy uses to tie components together. By itself it holds no data, but once you attach components to it, that ID represents something in your game world. ຄົ້ນຫາທີ່ດີທີ່ສຸດ ສໍາ ລັບຜູ້ຊ່ຽວຊານໃນການຄົ້ນຄວ້າຄົ້ນຄວ້າ Entity Components it carries #42 Camera2d #43 , , , , Text2d("@") TextFont TextColor Transform Player #42 ຊື່ Camera2d #43 ຊື່ ຂໍຂອບໃຈ ຂໍຂອບໃຈ ຂໍຂອບໃຈ ຂໍຂອບໃຈ Text2d("@") TextFont TextColor Transform Player ຫຼັງຈາກນັ້ນ, ພວກເຮົາຈະໃຊ້ການນີ້ຫຼັງຈາກນັ້ນ, ໃນເວລາທີ່ພວກເຮົາມີຄວາມຕ້ອງການທີ່ຈະເຮັດສິ່ງທຸກຢ່າງເຊັ່ນດຽວກັນກັບການປ່ຽນແປງຫຼື animating ຂອງພວກເຂົາ, ຫຼືເຮັດໃຫ້ພວກເຂົາໄປຢ້ຽມຢາມອາຫານ, ແລະອື່ນໆ. Implementing Player Movement ໃນປັດຈຸບັນພວກເຮົາຈະສ້າງ ສໍາລັບການເດີນທາງຂອງຜູ້ຊ່ຽວຊານ! ຂໍຂອບໃຈກ່ຽວກັບສິ່ງທີ່ພວກເຮົາມີຄວາມຕ້ອງການສໍາລັບການເດີນທາງຂອງຜູ້ຊ່ຽວຊານ: Update System ລະຫັດ QR ທີ່ໃຊ້ເວລາ - ເພື່ອເຮັດໃຫ້ການເດີນທາງຢ່າງງ່າຍດາຍໂດຍບໍ່ມີການເຊື່ອມຕໍ່ຄວາມໄວ frame ສະຖານທີ່ຜູ້ຊ່ຽວຊານ - ເພື່ອຈົດທະບຽນການປ່ຽນແປງຜູ້ຊ່ຽວຊານ ໃນເຄື່ອງມືເກມອື່ນໆ, ທ່ານຈະໃຊ້ເວລາການເຊື່ອມຕໍ່ລະບົບເຫຼົ່ານີ້ກັບຄືນໄປບ່ອນ. ແຕ່ here’s the magic of Bevy – ທ່ານພຽງແຕ່ຊອກຫາສິ່ງທີ່ທ່ານຕ້ອງການໃນຄໍາຮ້ອງສະຫມັກຂອງທ່ານ, ແລະ Bevy ອັດຕະໂນມັດໃຫ້ມັນ! ດາວນ໌ໂຫລດ Fly Player Function // 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 ຫຼື Resources ແມ່ນສ່ວນຂອງຂໍ້ມູນທັງຫມົດຂອງເກມທີ່ບໍ່ເຊື່ອມຕໍ່ກັບອຸປະກອນຫນຶ່ງ. ຫຼັງຈາກນັ້ນທ່ານຈະຊອກຫາຄູ່ມືຂອງເກມ, ດັ່ງນັ້ນແຕ່ລະລະບົບຈະຊອກຫາຄູ່ມືທີ່ແຕກຕ່າງກັນຂອງ "Time Since Last Frame". Res<Time> Explain Single<&mut Transform, With>? ສະ ຫນັບ ສະ ຫນູນ ສະ ຫນູນ ສະ ຫນູນ ສະ ຫນູນ ສະ ຫນູນ ສະ ຫນູນ ສະ ຫນູນ ສະ ຫນູນ ສະ ຫນູນ ສະ ຫນູນ ສະ ຫນູນ component and also carries the ປະເພດ: The ໃນຖານະເປັນບໍລິສັດທີ່ໃຫ້ບໍລິການໃຫ້ບໍລິການໃຫ້ບໍລິການໃຫ້ບໍລິການໃຫ້ບໍລິການໃຫ້ບໍລິການໃຫ້ບໍລິການໃຫ້ບໍລິການໃຫ້ບໍລິການໃຫ້ບໍລິການ. Single<&mut Transform, With<Player>> Transform Player &mut Transform What’s Vec2::ZERO? is a two-dimensional vector with both values set to zero: . ພວກເຮົານໍາໃຊ້ມັນເປັນທາງການເລີ່ມຕົ້ນກ່ອນທີ່ຈະອ່ານ input keyboard. Vec2::ZERO Vec2 { x: 0.0, y: 0.0 } What’s this KeyCode::… pattern? ຂໍຂອບໃຈ ... ແມ່ນ enums (ຈະ cover enums ຫຼັງຈາກນັ້ນ) ທີ່ສະຫນັບສະຫນູນຄຸນສົມບັດພິເສດໃນ keyboard. Checking simply asks Bevy whether that key is held down during the current frame. KeyCode::ArrowLeft KeyCode::ArrowRight input.pressed(KeyCode::ArrowLeft) ພວກເຮົາມີຄວາມຮູ້ສຶກວ່າຄູ່ຮ່ວມງານຂອງພວກເຮົາມີຄວາມຮູ້ສຶກວ່າຄູ່ຮ່ວມງານຂອງພວກເຮົາມີຄວາມຮູ້ສຶກວ່າຄູ່ຮ່ວມງານຂອງພວກເຮົາມີຄວາມຮູ້ສຶກວ່າຄູ່ຮ່ວມງານຂອງພວກເຮົາມີຄວາມຮູ້ສຶກວ່າຄູ່ຮ່ວມງານຂອງພວກເຂົາ. ການປ່ຽນແປງ vector ກັບການຂະຫນາດ 1 ດັ່ງນັ້ນການເດີນທາງ diagonal ແມ່ນບໍ່ໄວຫຼາຍກ່ວາການເດີນທາງຈິງ. ສະແດງໃຫ້ເຫັນວ່າມີຫຼາຍ pixels ທົ່ວໂມງສໍາລັບການເດີນທາງແລະ ສະຫນັບສະຫນູນການປັບປຸງແລະການປັບປຸງຂອງການປັບປຸງຂອງການປັບປຸງຂອງການປັບປຸງຂອງການປັບປຸງຂອງການປັບປຸງຂອງການປັບປຸງຂອງການປັບປຸງຂອງການປັບປຸງ normalize() speed time.delta_secs() ດາວນ໌ໂຫລດ The Movement System // 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 ກັບຄໍາແນະນໍາເພີ່ມເຕີມທີ່ກ່ຽວຂ້ອງກັບ "Run me during Startup" ຫຼື "Run me on every Update" ແລະເຄື່ອງປິ່ນປົວຢ່າງວ່ອງໄວ. ຂໍຂອບໃຈວ່າພວກເຮົາຈະເຮັດວຽກ Adding Sprite Graphics We will use the Universal LPC SpriteSheet Generator to give our character some personality. You can remix body parts, clothes, and colors at ແລະ export a full spritesheet. this link For this project the spritesheet is already included in the . ດາວນ໌ໂຫລດເອກະສານຮູບພາບທີ່ຢູ່ so Bevy can find them when the game runs. You will need to create the ລະຫັດ QR ລະຫັດ QR src/assets assets ລະຫັດການປ່ຽນແປງ ພວກເຮົາມີຄວາມຕ້ອງການທີ່ຈະເພີ່ມລະຫັດເພີ່ມເຕີມແລະເພີ່ມ main.rs ຈະເຮັດໃຫ້ມັນງ່າຍທີ່ຈະອ່ານແລະຮູ້. ດາວນ໌ໂຫລດເອກະສານ main.rs ຂອງທ່ານເພື່ອສ້າງເອກະສານ player.rs ອື່ນໆ Pulls ໃນ the ຊື່ຫຍໍ້ຂອງ : Dividing code like this makes it easier to grow the project without one big file doing everything. 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)) ເປັນ loader ຂອງ Bevy ສໍາລັບ Textures, Audio, ແລະອຸປະກອນອື່ນໆ. We tweak ມັນ ໃນຖານະເປັນເຈົ້າຂອງພວກເຮົາ , ນີ້ແມ່ນທີ່ sprites ຂອງພວກເຮົາມີ. AssetPlugin file_path src/assets ການສ້າງ Module Player ໃນປັດຈຸບັນພວກເຮົາມີໂຄງສ້າງ app ທີ່ສໍາຄັນຂອງພວກເຮົາມີ, ມັນເປັນເວລາທີ່ຈະເຮັດໃຫ້ character ຂອງພວກເຮົາມີຊີວິດ! ພວກເຮົາຈະສ້າງໂຄງສ້າງ dedicated module ເພື່ອປິ່ນປົວສິ່ງທຸກຢ່າງທີ່ກ່ຽວຂ້ອງກັບ adventurer ຂອງພວກເຮົາຫນ້ອຍ. ນີ້ເຮັດໃຫ້ລະຫັດຂອງພວກເຮົາມີການຄຸ້ມຄອງແລະເຮັດໃຫ້ມັນງ່າຍທີ່ຈະເພີ່ມເຕີມ features ຕໍ່ໄປ. 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 ພວກເຮົາ ກໍາ ລັງເຮັດທຸລະກິດໃນ 2012. ພວກເຮົາແມ່ນບໍລິສັດທີ່ໃຫຍ່ທີ່ສຸດ ສໍາ ລັບຜູ້ໃຫ້ບໍລິການລູກຄ້າຂອງພວກເຮົາ. ພວກເຮົາແມ່ນບໍລິສັດທີ່ໃຫຍ່ທີ່ສຸດ ສໍາ ລັບຜູ້ໃຫ້ບໍລິການລູກຄ້າຂອງພວກເຮົາ. markers here keeps ທັງຫມົດຜູ້ຊົມຊ່ຽວຊານປະເພດໃນຫນຶ່ງ module. Player ຊື່ຫຍໍ້ຂອງ : Defining Player Directions // 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 ດາວນ໌ໂຫລດຂະຫນາດນ້ອຍຂອງຄຸນນະສົມບັດທີ່ສາມາດໄດ້ຮັບການອະນຸຍາດ. ຄຸນນະສົມບັດຂອງພວກເຮົາມີພຽງແຕ່ 4 ປະເພດ, ດັ່ງນັ້ນ enum captures ຄໍາຮ້ອງສະຫມັກນີ້ໃນຫນຶ່ງສະຖານທີ່. Facing Why can’t I use a struct here? Struct ຂອງທ່ານຈະຈໍາເປັນຕ້ອງກຸ່ມຂອງ booleans ເປັນ ຂໍຂອບໃຈ , ແລະທ່ານຈະຕ້ອງໃຫ້ເຂົາເຈົ້າໃນ sync. That makes it complicated. Enum guarantees you only choose one direction. facing_up:true facing_left:false When to decide between using enum and struct? Use a struct when you need several fields, like a position with ແລະ ການນໍາໃຊ້ enum ໃນເວລາທີ່ທ່ານເລືອກເອົາຫນຶ່ງຂອງອຸປະກອນຈາກຄອມພິວເຕີ, ເຊັ່ນດຽວກັນກັບອຸປະກອນທີ່ຜູ້ຊ່ຽວຊານໃຊ້ (sword, bow, wand) ຫຼືອຸປະກອນທີ່ໃຊ້ເວລາ. x y Why the purpose of adding Debug, Clone, Copy, PartialEq, Eq macros? ສະ ຫນັບ ສະ ຫນູນ ສະ ຫນູນ ສະ ຫນູນ ສະ ຫນູນ ສະ ຫນູນ ສະ ຫນູນ ສະ ຫນູນ ສະ ຫນູນ ສະ ຫນູນ ສະ ຫນູນ ສະ ຫນູນ ແລະ ເຮັດໃຫ້ມັນ trivial ເພື່ອ duplicate ລາຄາ, ແລະ / allow equality checks when we compare directions. Debug Clone Copy PartialEq Eq Why can’t I copy through simple assignment, why should I add these macros? ໃນຖານະເປັນຕົວແທນ, Rust ສະຫນັບສະຫນູນຄຸນນະພາບໃນຂະນະທີ່ບໍ່ໄດ້ຮັບການຕັດສິນໃຈ, ດັ່ງນັ້ນ compiler ເຮັດໃຫ້ທ່ານ opt-in. (ແລະ ໃນຖານະເປັນການຊ່ວຍເຫຼືອ) ເວົ້າວ່າ "ມັນເປັນຄ່າໃຊ້ຈ່າຍຕ່ໍາ, ຂໍຂອບໃຈແລະ duplicate ມັນ." ແລະ ພວກເຮົາມີຄວາມຮູ້ສຶກວ່າພວກເຮົາມີຄວາມຮູ້ສຶກວ່າພວກເຮົາມີຄວາມຮູ້ສຶກວ່າພວກເຮົາມີຄວາມຮູ້ສຶກວ່າພວກເຮົາມີຄວາມຮູ້ສຶກວ່າພວກເຮົາມີຄວາມຮູ້ສຶກວ່າພວກເຮົາມີຄວາມຮູ້ສຶກ. Copy Clone PartialEq Eq What do you mean by rust moves values, instead of copying? ໃນເວລາທີ່ທ່ານອະນຸຍາດຈໍານວນຫຼາຍຂອງຄຸນນະພາບໃນ Rust, ອຸນຫະພູມທີ່ຍິ່ງໃຫຍ່ຈະກາຍເປັນເຈົ້າຂອງມັນຫຼັງຈາກນັ້ນຫຼັງຈາກນັ້ນຫຼັງຈາກນັ້ນ. ຂໍຂອບໃຈວ່າພວກເຮົາກໍາລັງເຮັດວຽກຮ່ວມກັບທ່ານ Copy Why does the old variable stop owning or die when assigned? Rust ສະຫນັບສະຫນູນໃຫ້ເຫັນວ່າແຕ່ລະຄຸນນະສົມບັດມີຄຸນນະສົມບັດຫນຶ່ງເພື່ອໃຫ້ມາດຕະຖານສາມາດຖືກຟຣີຢ່າງວ່ອງໄວ. We will unpack the ownership rules and borrowing in later chapters. This is a bit going over my head! ຂ້າພະເຈົ້າຫວັງວ່າຈະໄດ້ເຮັດວຽກຮ່ວມກັບທ່ານອີກເທື່ອຫນຶ່ງ. ຂ້າພະເຈົ້າຫວັງວ່າຈະເຮັດວຽກຮ່ວມກັບທ່ານ. Animation System Components ໃນເວລາທີ່ພວກເຮົາມີ spritesheet ມີຮູບເງົາຫຼາຍ (ເຊັ່ນດຽວກັນກັບ animation walking ຂອງພວກເຮົາ 9 frame), ພວກເຮົາມີຄວາມຕ້ອງການທີ່ຈະຄວບຄຸມຄວາມໄວຂອງຮູບເງົານີ້. ບໍ່ມີການຄວບຄຸມເວລາ, ຜູ້ຊາຍຂອງພວກເຮົາມີຄວາມຮ້ອນຂ້າງຂວາງໃນຮູບເງົາຫນຶ່ງຫຼື cycling ຜ່ານຮູບເງົາທີ່ດີທີ່ສຸດ! ຂໍຂອບໃຈວ່າມັນເປັນ flipbook - ຖ້າຫາກວ່າທ່ານ flip ເຫຼົ່ານີ້ຢ່າງງ່າຍດາຍຫຼາຍ, ການ animation ຈະເບິ່ງ choppy. ຖ້າຫາກວ່າທ່ານ flip ຫຼາຍໄວ, ທ່ານບໍ່ສາມາດເບິ່ງສິ່ງທີ່ເກີດຂຶ້ນ. ພວກເຮົາມີການຄວບຄຸມຢ່າງກວ້າງຂວາງກ່ຽວກັບເວລານີ້, ໃຫ້ແນ່ໃຈວ່າ animation walking ຂອງ character ຂອງພວກເຮົາມີຜົນກະທົບຢ່າງງ່າຍດາຍແລະເຫມາະສົມກັບຄວາມໄວທີ່ຖືກຕ້ອງ. AnimationTimer ຊື່ຫຍໍ້ຂອງ : Wrap A Bevy ໃນຖານະເປັນຜູ້ຊ່ຽວຊານຂອງຜູ້ຊ່ຽວຊານ, ຜູ້ຊ່ຽວຊານຂອງພວກເຮົາແມ່ນຜູ້ຊ່ຽວຊານໃນການຊ່ຽວຊານໃນການຊ່ຽວຊານໃນການຊ່ຽວຊານໃນການຊ່ຽວຊານໃນການຊ່ຽວຊານໃນການຊ່ຽວຊານໃນການຊ່ຽວຊານໃນການຊ່ຽວຊານໃນການຊ່ຽວຊານໃນການຊ່ຽວຊານ. 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? ສະ ຫນັບ ສະ ຫນູນ ສະ ຫນູນ ສະ ຫນູນ ສະ ຫນູນ ສະ ຫນູນ ສະ ຫນູນ ສະ ຫນູນ ສະ ຫນູນ ສະ ຫນູນ ສະ ຫນູນ ໃນຂະນະທີ່ພວກເຮົາໄດ້ອ່ານຈາກມັນ, ແລະ ຊອກ ຫາ ຄູ່ ຮ່ວມ ງານ ຂອງ ຊີ ວິດ ປະເພດ ດາວນ໌ໂຫລດ The Internal Value Deref Timer DerefMut timer.tick(time.delta()) AnimationTimer So we are renaming the Timer to AnimationTimer? ພວກເຮົາມີຄວາມປອດໄພຂອງ timer, ບໍ່ແມ່ນ re-naming ມັນ. Think of ໃນຖານະເປັນປ່ອງຂະຫນາດນ້ອຍທີ່ມີ A , plus a label that says “This one belongs to the player animation.” ແລະລວມເອົາມັນໃນປັດຈຸບັນ, ດັ່ງນັ້ນຜູ້ຊ່ຽວຊານສາມາດມີເວລາຂອງຕົນເອງ, ຖ້າຫາກວ່າພວກເຮົາມີຄວາມຕ້ອງການຫຼາຍກ່ວາ heroes. AnimationTimer Timer Timer So it’s an instance of AnimationTime? ຂ້າພະເຈົ້າ, ເປັນ Tuple Structure ທີ່ ມີ A . ພວກເຮົາກໍ່ສ້າງຫນຶ່ງໃນເວລາທີ່ພວກເຮົາມີຜູ້ດາວໂຫລດໃຫ້ຜູ້ດາວໂຫລດເພື່ອໃຫ້ຜູ້ດາວໂຫລດຜູ້ດາວໂຫລດຜູ້ດາວໂຫລດຜູ້ດາວໂຫລດຜູ້ດາວໂຫລດຜູ້ດາວໂຫລດຜູ້ດາວໂຫລດຜູ້ດາວໂຫລດຜູ້ດາວໂຫລດຜູ້ດາວໂຫລດຜູ້ດາວໂຫລດຜູ້ດາວໂຫລດຜູ້ດາວໂຫລດຜູ້ດາວໂຫລດຜູ້ດາວໂຫລດຜູ້ດາວໂຫລດຜູ້ດາວໂຫລດຜູ້ດາວໂຫລດຜູ້ດາວໂຫລດຜູ້ດາວໂຫລດຜູ້ດາວໂຫລດຜູ້ດາວໂຫລດຜູ້ດາວໂຫລດຜູ້ດາວໂຫລດຜູ້ດາວໂຫລດຜູ້ດາວໂຫລດຜູ້ດາວໂຫລດຜູ້ດາວໂຫລດຜູ້ AnimationTimer Timer ການຄົ້ນຄວ້າແລະການປິ່ນປົວຂອງການປິ່ນປົວຂອງການປິ່ນປົວຂອງການປິ່ນປົວຂອງການດໍາເນີນການ AnimationState ຊື່ຫຍໍ້ຂອງ : Spawning the Player ພວກເຮົາມີ spritesheet ໂດຍຜ່ານ , create a texture atlas layout so Bevy knows the grid, and pick the starting frame for a hero facing down. Then we spawn an entity with the sprite, transform at the origin, our marker components, and the timer that will drive animation. 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)), )); } ສະຫນັບສະຫນູນການເລີ່ມຕົ້ນແລະ flags ທີ່ character ແມ່ນ vacillate ໃນປັດຈຸບັນແລະໄດ້ vacillate ໃນປັດຈຸບັນ. AnimationState { facing, moving: false, was_moving: false } ດາວນ໌ໂຫລດ Hot Tags ຊົ່ວໂມງສໍາລັບການ advance spritesheet. AnimationTimer(Timer::from_seconds(ANIM_DT, TimerMode::Repeating)) ANIM_DT ? What’s an AssetServer ການ ແມ່ນການດາວໂຫລດແລະການຄຸ້ມຄອງເອກະສານຂອງ Bevy ທີ່ປິ່ນປົວການດາວໂຫລດແລະ caching ຂອງອຸປະກອນເກມເຊັ່ນດຽວກັນກັບຮູບພາບ, ສະພາບແວດລ້ອມແລະຮູບແບບ 3D. AssetServer ໃນເວລາທີ່ທ່ານໂທ , ມັນບໍ່ໄດ້ດາວໂຫລດເອກະສານໃນສະພາບແວດລ້ອມຫຼັງຈາກນັ້ນ. ໃນປັດຈຸບັນ, ມັນໄດ້ດາວໂຫລດການດາວໂຫລດທີ່ທ່ານສາມາດນໍາໃຊ້ຫຼັງຈາກນັ້ນ. ນີ້ແມ່ນການດາວໂຫລດ "lazy loading" - ການດາວໂຫລດເອກະສານທີ່ແທ້ຈິງເກີດຂຶ້ນໃນປັດຈຸບັນ, ແລະ Bevy ຈະທົດສອບທ່ານໃນເວລາທີ່ການດາວໂຫລດໄດ້ປັດຈຸບັນ. asset_server.load("path/to/sprite.png") ວິທີການນີ້ເປັນປະສິດທິພາບໂດຍ: ຊື່ຫຍໍ້ຂອງ : Multiple entities can share the same sprite without loading it multiple times ສະຫນັບສະຫນູນໄດ້ຖືກອັບໂຫລດພຽງແຕ່ໃນເວລາທີ່ບໍ່ຈໍາເປັນຕ້ອງ Bevy ສາມາດ optimize ແລະ batch-load assets ສໍາລັບຜົນປະໂຫຍດທີ່ດີກວ່າ If an asset fails to load, it will not crash your entire game ໃນກໍລະນີຂອງພວກເຮົາ, ຄໍາຖາມ spritesheet ແລະສະຫນັບສະຫນູນການຂັບຂັບຂີ້ຝຸ່ນເພື່ອຊອກຫາສະພາບອາກາດຂອງຕົນ. asset_server.load("sprites/player.png") ລະຫັດ QR ໃນປັດຈຸບັນພວກເຮົາມີຜູ້ດາວໂຫລດຂອງພວກເຮົາມີຄຸນນະສົມບັດທີ່ຕ້ອງການທັງຫມົດ, ພວກເຮົາມີຄວາມຕ້ອງການທີ່ຈະປັບປຸງລະບົບການຂົນສົ່ງຂອງພວກເຮົາທີ່ຢູ່ເພື່ອທົດສອບວິທີການທີ່ຜູ້ດາວໂຫລດກໍາລັງຊອກຫາ. ນີ້ເປັນສິ່ງທີ່ສໍາຄັນສໍາລັບລະບົບ animation ເພື່ອຮູ້ສຶກທີ່ຊັ້ນຂອງ spritesheet ເພື່ອນໍາໃຊ້ - ການຄຸນນະສົມບັດຂອງພວກເຮົາມີການຄຸນນະສົມບັດທີ່ແຕກຕ່າງກັນໃນ sprites Atlas ຂອງພວກເຮົາ. ລະບົບການປັບປຸງນີ້ຈະກວດສອບເສັງການຂົນສົ່ງແລະປັບປຸງ ໂດຍສະເພາະ, ດັ່ງນັ້ນລະບົບ animation ຂອງພວກເຮົາສາມາດເລືອກຊັ້ນ Sprite ທີ່ຖືກຕ້ອງສໍາລັບການ animations walking ໃນແຕ່ລະວິທີ. 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; } } ຄໍາຮ້ອງສະຫມັກຢືນຢັນ Proof for the single entity tagged ພວກເຮົາສະຫນອງການເຂົ້າເຖິງທີ່ແຕກຕ່າງກັນກັບ ແລະ . ພວກເຮົາມີວິທີການກໍ່ສ້າງ vector ການອອກແບບຈາກຄອມພິວເຕີທີ່ພິມ, normalizes ມັນເພື່ອເຂົ້າລະຫັດ diagonal ແມ່ນບໍ່ໄວ, ແລະ moves ພືດໂດຍ speed × frame time. The facing logic compares horizontal vs vertical strength to decide which way the sprite should look. We record whether the player is moving now so later systems can detect when motion starts or stops. Player Transform AnimationState ການນໍາໃຊ້ animation ໃນປັດຈຸບັນ, ພວກເຮົາມີທຸກສິ່ງທຸກຢ່າງໃນສະຖານທີ່ - ເກມຂອງພວກເຮົາມີຄວາມສາມາດໃນການປ່ຽນແປງໃນວິທີທີ່ແຕກຕ່າງກັນ, ແລະພວກເຮົາມີຄວາມຮູ້ສຶກກ່ຽວກັບວິທີທີ່ພວກເຮົາມີຄວາມເປັນໄປໄດ້. ຊຸດຫຼ້າສຸດແມ່ນລະບົບ animation ທີ່ຖືກປັບປຸງຢ່າງງ່າຍດາຍຂອງ sprites ເພື່ອສ້າງ animation walking. ລະບົບເຫຼົ່ານີ້ນໍາໃຊ້ຂໍ້ມູນຈຸດທະບຽນຈາກລະບົບການเคลื่อนไหวຂອງພວກເຮົາແລະນໍາໃຊ້ timer animation ເພື່ອຈຸດທະບຽນໂດຍຜ່ານ sprites ໃນຄວາມໄວທີ່ຖືກຕ້ອງ. ມັນປິ່ນປົວລັກສະນະທີ່ສົມບູນແບບຂອງການປ່ຽນແປງລະຫວ່າງຊຸດ 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, } } ການຄວບຄຸມຜົນປະໂຫຍດແລະຊື່ຊຸດທີ່ພວກເຮົາມີຄວາມຕ້ອງການ. ຖ້າຫາກວ່າການຄົ້ນຄວ້າໄດ້ຮັບຜົນປະໂຫຍດ, ລະຫັດເຊື່ອມຕໍ່ ຂໍຂອບໃຈ , ແລະ ດັ່ງນັ້ນພວກເຮົາສາມາດນໍາໃຊ້ມັນຫຼັງຈາກນັ້ນ. ຖ້າຫາກວ່າມັນບໍ່ໄດ້ (ບໍ່ມີຜູ້ຊ່ຽວຊານ, ຫຼືຫຼາຍກວ່າຫນຶ່ງ), ພວກເຮົາມີຄວາມປອດໄພ Branch ແລະ Exit Immediately. Rust ໃຊ້ ປະເພດສໍາລັບການນີ້: ຄໍາຮ້ອງສະຫມັກທີ່ກ່ຽວຂ້ອງກັບ "query returned exactly one result" ຂໍຂອບໃຈວ່າທ່ານກໍາລັງຊອກຫາຂໍ້ມູນເພີ່ມເຕີມກ່ຽວກັບການຊອກຫານີ້. let Ok((mut anim, mut timer, mut sprite)) = query.single_mut() else { return; }; anim timer sprite else Result Ok Err ຫຼັງຈາກນັ້ນພວກເຮົາ ຂ້າພະເຈົ້າ , ທີ່ເປັນປະເພດຂອງ Rust "Maybe there is a value" ປະເພດ Atlas Texture ແມ່ນມີແລະພວກເຮົາສາມາດປັບປຸງມັນ; ມັນເປັນຮູບແບບດຽວກັນທີ່ທ່ານຈະນໍາໃຊ້ໃນເວລາທີ່ການກວດສອບການບິນຫຼື cache: ນໍາໃຊ້ຄ່າໃຊ້ຈ່າຍພຽງແຕ່ໃນເວລາທີ່ການຊອກຫາ returns something. match Option Some(atlas) None ສະພາບອຸປະກອນການ animation, timer, ແລະ Sprite Handle ສໍາລັບຜູ້ດາວໂຫລດ. ມັນຊອກຫາຊຸດທີ່ຂອງ atlas matches ການທົດສອບທົດສອບທົດສອບທົດສອບທົດສອບທົດສອບທົດສອບທົດສອບທົດສອບທົດສອບທົດສອບທົດສອບທົດສອບທົດສອບທົດສອບທົດສອບທົດສອບທົດສອບທົດສອບທົດສອບທົດສອບທົດສອບທົດສອບທົດສອບທົດສອບທົດສອບທົດສອບທົດສອບທົດສອບທົດສອບທົດສອບທົດສອບທົດສອບທົດສອບທົດສອບທົດສອບທົດສອບທົດສອບທົດສອບທົດສອບ animate_player ການສ້າງ Plugin Player // 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, plugin ແມ່ນພຽງແຕ່ struct ທີ່ຮູ້ວິທີການລົງທະບຽນລະບົບ, ຄຸນນະພາບ, ແລະ assets. ໂດຍການປະຕິບັດ ປະເພດ , ພວກເຮົາໃຫ້ Bevy ລະບົບການຄວບຄຸມ: ໃນເວລາທີ່ plugin ນີ້ແມ່ນເພີ່ມຂຶ້ນກັບ app, ດໍາເນີນການລະຫັດພາຍໃນເພື່ອຕິດຕັ້ງສິ່ງທຸກຢ່າງທີ່ຜູ້ຊ່ຽວຊານຕ້ອງການ ການປ່ຽນແປງຂອງການສົນທະນາກັບຜູ້ຊ່ຽວຊານ PlayerPlugin Plugin PlayerPlugin main.rs ການ ວິທີການແມ່ນ checklist. Bevy ຂ້າງລຸ່ມພວກເຮົາເປັນ mutable , ແລະພວກເຮົາ bolt ກ່ຽວກັບລະບົບທີ່ພວກເຮົາມີຄວາມຮູ້. ມັນຖືກອອກແບບມາໃນ ຫຼັງຈາກນັ້ນ Sprite ຈະສະແດງໃຫ້ເຫັນໃນເວລາທີ່ເກມໄດ້ເລີ່ມຕົ້ນ. ແລະ ດາວໂຫລດ The ດາວນ໌ໂຫລດຮູບພາບ ສໍາ ລັບ ດາວນ໌ໂຫລດຮູບພາບ ສໍາ ລັບ ດາວນ໌ໂຫລດຮູບພາບ ສໍາ ລັບ ດາວນ໌ໂຫລດຮູບພາບ ສໍາ ລັບ ດາວນ໌ໂຫລດຮູບພາບ ສໍາ ລັບ ດາວນ໌ໂຫລດຮູບພາບ ສໍາ ລັບ ດາວນ໌ໂຫລດຮູບພາບ ສໍາ ລັບ ດາວນ໌ໂຫລດຮູບພາບ ສໍາ ລັບ ດາວນ໌ໂຫລດຮູບພາບ ສໍາ ລັບ ດາວນ໌ໂຫລດຮູບພາບ ສໍາ ລັບ ດາວນ໌ໂຫລດຮູບພາບ ສໍາ ລັບ ດາວນ໌ໂຫລດຮູບພາບ ສໍາ ລັບ ດາວນ໌ໂຫລດຮູບພາບ ສໍາ ລັບ ດາວນ໌ໂຫລດ ປະເພດ ອັດຕະໂນມັດອຸປະກອນການຂົນສົ່ງທັງຫມົດຂອງ player flow. 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? ດາວໂຫລດ The trait ເວົ້າວ່າ "ແຕ່ລະ plugin must provide a ພວກເຮົາມີການນໍາໃຊ້ຄຸນນະສົມບັດທີ່, ດັ່ງນັ້ນ Rust ສະຫນັບສະຫນູນພວກເຮົາເພື່ອສະຫນອງຮ່າງກາຍ. Bevy ຄິດຈະກໍານີ້ໃນເວລາທີ່ມັນດາວໂຫລດ plug-in, ເປັນຫຍັງພວກເຮົາມີການເພີ່ມທັງຫມົດຂອງລະບົບຂອງພວກເຮົາໃນມັນ. Plugin build(&self, app: &mut App) What’s a trait? ຄຸນນະສົມບັດແມ່ນຄຸນນະສົມບັດທີ່ຄຸນນະສົມບັດທີ່ຄຸນນະສົມບັດຄຸນນະສົມບັດຄຸນສົມບັດຄຸນສົມບັດຄຸນສົມບັດຄຸນສົມບັດຄຸນສົມບັດ trait ເວົ້າວ່າ "ໃຫ້ຂ້ອຍ A ລະບົບການເຮັດວຽກຂອງຂ້າພະເຈົ້າສາມາດຕິດຕັ້ງລະບົບຂອງທ່ານ.” ໂດຍ implementing that trait for , ພວກເຮົາເຊື່ອມຕໍ່ໃນໂຄງການ Startup ຂອງ Bevy ແລະ inject ລະຫັດການຕິດຕັ້ງຂອງພວກເຮົາເອງ. ຄຸນນະສົມບັດໃຫ້ປະເພດທີ່ແຕກຕ່າງກັນຂອງການຮ່ວມເພດ, ການເຮັດວຽກເຊັ່ນດຽວກັນກັບ plugin ອື່ນໆຂອງ Bevy, ແຕ່ມັນຕິດຕັ້ງລະບົບຜູ້ຊ່ຽວຊານຂອງພວກເຮົາ. Plugin build PlayerPlugin PlayerPlugin ການເຊື່ອມຕໍ່ Final // 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 ຫນ້າທໍາອິດຂອງບົດຄວາມນີ້ໄດ້ຖືກຂຽນໃນ blog ຂອງຂ້າພະເຈົ້າ. ຫນ້າທໍາອິດ / This post was originally published on . ຫນ້າທໍາອິດ Blog ຂ້າພະເຈົ້າສືບຕໍ່ໄດ້ຮັບການປະທັບໃຈກໍໂດຍການບໍລິການລູກຄ້າຂອງພວກເຮົາ ປະເພດ LinkedIn X