24 цагийн дотор Rust тоглох: хувийн хөгжлийн туршлага

24 цагийн дотор Rust тоглох: хувийн хөгжлийн туршлага

Энэ нийтлэлд би Rust дахь жижиг тоглоомыг хөгжүүлэх хувийн туршлагаа ярих болно. Ажлын хувилбарыг бүтээхэд 24 цаг зарцуулсан (би ихэвчлэн орой эсвэл амралтын өдрүүдэд ажилладаг байсан). Тоглоом дуусаагүй байгаа ч туршлага нь ашигтай байх болно гэж би бодож байна. Би тоглоомыг эхнээс нь бүтээх явцад олж мэдсэн зүйлээ болон зарим ажиглалтаа хуваалцах болно.

Skillbox зөвлөж байна: Хоёр жилийн практик сургалт "Би PRO вэб хөгжүүлэгч".

Бид танд сануулж байна: "Хабр" -ын бүх уншигчдад - "Habr" сурталчилгааны кодыг ашиглан Skillbox-ын аль ч курст бүртгүүлэхдээ 10 рублийн хөнгөлөлт.

Яагаад Rust гэж?

Би энэ хэлийг сонгосон учир нь би энэ хэлийг олон сайхан зүйл сонсож, тоглоом хөгжүүлэхэд улам бүр түгээмэл болж байгааг харж байна. Тоглоом бичихээс өмнө би Rust дээр энгийн програмуудыг хөгжүүлэх туршлага багатай байсан. Энэ нь тоглоом бичих явцад надад эрх чөлөөг мэдрэхэд хангалттай байсан.

Яагаад тоглоом, ямар тоглоом вэ?

Тоглоом хийх нь хөгжилтэй! Илүү олон шалтгаан байгаасай гэж би хүсч байна, гэхдээ "гэрийн" төслүүдэд би байнгын ажилтай тийм ч нягт холбоогүй сэдвүүдийг сонгодог. Энэ ямар тоглоом вэ? Би Cities Skylines, Zoo Tycoon, Prison Architect болон теннисийг хослуулсан теннисний симулятор шиг зүйлийг хийхийг хүссэн. Ер нь бол хүмүүс тоглохоор ирдэг теннисний академийн тухай тоглоом болж таарсан.

Техникийн сургалт

Би Rust-ийг ашиглахыг хүссэн ч эхлүүлэхийн тулд яг ямар хэмжээний суурь ажил шаардагдахыг мэдэхгүй байсан. Би pixel shader бичиж, drag-n-drop ашиглахыг хүсээгүй тул хамгийн уян хатан шийдлүүдийг хайж байсан.

Би тантай хуваалцах хэрэгтэй эх сурвалжуудыг олсон:

Би хэд хэдэн Rust тоглоомын хөдөлгүүрийг судалж, эцэст нь Piston болон ggez-ийг сонгосон. Би өмнөх төсөл дээр ажиллаж байхдаа тэдэнтэй таарсан. Эцэст нь жижиг 2D тоглоомыг хэрэгжүүлэхэд илүү тохиромжтой санагдсан тул ggez-ийг сонгосон. Поршений модульчлагдсан бүтэц нь шинэхэн хөгжүүлэгч (эсвэл Rust-тэй анх удаа ажиллаж байгаа) хувьд хэтэрхий төвөгтэй байдаг.

Тоглоомын бүтэц

Би төслийн архитектурын талаар хэсэг хугацаанд бодож байсан. Эхний алхам бол "газар", хүмүүс, теннисний талбайг хийх явдал юм. Хүмүүс шүүхийг тойрон нүүж, хүлээх хэрэгтэй. Тоглогчид цаг хугацааны явцад сайжирдаг ур чадвартай байх ёстой. Дээрээс нь танд шинэ хүмүүс, шүүх нэмэх боломжийг олгодог редактор байх ёстой, гэхдээ энэ нь үнэ төлбөргүй байхаа больсон.

Бүх зүйлийг сайтар бодсоны эцэст би ажилдаа орлоо.

Тоглоом бүтээх

Эхлэл: Тойрог ба хийсвэрлэл

Би ggez-ээс жишээ аваад дэлгэцэн дээр дугуй зурсан. Гайхалтай! Одоо зарим хийсвэрлэл. Тоглоомын объектын санаанаас хийсвэрлэх нь сайхан байх болно гэж би бодсон. Объект бүрийг энд заасны дагуу буулгаж, шинэчлэх ёстой:

// the game object trait
trait GameObject {
    fn update(&mut self, _ctx: &mut Context) -> GameResult<()>;
    fn draw(&mut self, ctx: &mut Context) -> GameResult<()>;
}
 
// a specific game object - Circle
struct Circle {
    position: Point2,
}
 
 impl Circle {
    fn new(position: Point2) -> Circle {
        Circle { position }
    }
}
impl GameObject for Circle {
    fn update(&mut self, _ctx: &mut Context) -> GameResult<()> {
        Ok(())
    }
    fn draw(&mut self, ctx: &mut Context) -> GameResult<()> {
        let circle =
            graphics::Mesh::new_circle(ctx, graphics::DrawMode::Fill, self.position, 100.0, 2.0)?;
 
         graphics::draw(ctx, &circle, na::Point2::new(0.0, 0.0), 0.0)?;
        Ok(())
    }
}

Энэ кодын хэсэг нь надад шинэчлэгдэж, ижил төстэй гогцоонд үзүүлэх боломжтой объектуудын сайхан жагсаалтыг өгсөн.

mpl event::EventHandler for MainState {
    fn update(&mut self, context: &mut Context) -> GameResult<()> {
        // Update all objects
        for object in self.objects.iter_mut() {
            object.update(context)?;
        }
 
        Ok(())
    }
 
    fn draw(&mut self, context: &mut Context) -> GameResult<()> {
        graphics::clear(context);
 
        // Draw all objects
        for object in self.objects.iter_mut() {
            object.draw(context)?;
        }
 
        graphics::present(context);
 
        Ok(())
    }
}

main.rs нь кодын бүх мөрийг агуулсан учраас зайлшгүй шаардлагатай. Би файлуудыг салгаж, лавлах бүтцийг оновчтой болгоход бага зэрэг цаг зарцуулсан. Үүний дараа иймэрхүү харагдсан:
нөөц -> энэ бол бүх хөрөнгө (зураг)
SRC
- аж ахуйн нэгжүүд
— game_object.rs
- тойрог. rs
— main.rs -> үндсэн давталт

Хүмүүс, шал, зураг

Дараагийн алхам бол Person тоглоомын объект үүсгэж, зураг ачаалах явдал юм. Бүх зүйл 32*32 хавтангийн үндсэн дээр баригдсан байх ёстой.

24 цагийн дотор Rust тоглох: хувийн хөгжлийн туршлага

Теннисний талбай

Теннисний талбай ямар байдгийг судалж үзээд 4*2 хавтангаар хийхээр шийдсэн. Эхэндээ ийм хэмжээтэй зураг хийх, эсвэл 8 тусдаа хавтанг нэгтгэх боломжтой байсан. Гэхдээ дараа нь би зөвхөн хоёр өвөрмөц хавтан хэрэгтэй гэдгийг ойлгосон бөгөөд яагаад ийм байна.

Нийтдээ бидэнд ийм хоёр хавтан байна: 1 ба 2.

Шүүхийн хэсэг бүр нь 1-р хавтан эсвэл 2-р хавтанцараас бүрдэнэ. Тэдгээрийг ердийн байдлаар байрлуулж эсвэл 180 градус эргүүлж болно.

24 цагийн дотор Rust тоглох: хувийн хөгжлийн туршлага

Барилга угсралтын (угсрах) үндсэн горим

Сайт, хүмүүс, газрын зургийг үзүүлж чадсаны дараа би угсралтын үндсэн горим хэрэгтэй гэдгийг ойлгосон. Би үүнийг ингэж хэрэгжүүлсэн: товчлуурыг дарахад объект сонгогдож, товшилтоор хүссэн газар нь байрлуулна. Тиймээс 1-р товчлуур нь талбайг сонгох боломжийг олгодог бөгөөд 2-р товчлуур нь тоглогчийг сонгох боломжийг олгоно.

Гэхдээ бид 1 ба 2 гэдэг нь юу гэсэн үг болохыг санах хэрэгтэй хэвээр байгаа тул аль объект сонгогдсоныг тодорхой болгохын тулд би утасны хүрээ нэмсэн. Энэ нь иймэрхүү харагдаж байна.

24 цагийн дотор Rust тоглох: хувийн хөгжлийн туршлага

Архитектур ба рефакторын асуултууд

Одоо надад хэд хэдэн тоглоомын объект байна: хүмүүс, талбай, шал. Гэхдээ утас фрэймүүд ажиллахын тулд объектууд өөрсдөө үзүүлэх горимд байгаа эсэх, эсвэл зүгээр л хүрээ зурсан эсэхийг объект бүрд хэлэх шаардлагатай. Энэ нь тийм ч тохиромжтой биш юм.

Архитектурыг зарим хязгаарлалтыг илчлэх байдлаар дахин бодож үзэх шаардлагатай юм шиг надад санагдсан:

  • Өөрийгөө дүрсэлж, шинэчилдэг аж ахуйн нэгжтэй байх нь асуудал үүсгэдэг, учир нь тухайн аж ахуйн нэгж нь юу үзүүлэх ёстойгоо "мэдэхгүй" байх болно - зураг болон утас;
  • бие даасан аж ахуйн нэгжүүдийн хооронд шинж чанар, зан төлөвийг солилцох хэрэгсэл дутмаг (жишээлбэл, is_build_mode шинж чанар эсвэл зан төлөвийг үзүүлэх). Rust-д үүнийг хэрэгжүүлэх зохистой арга байхгүй ч өв залгамжлалыг ашиглах боломжтой болно. Надад үнэхээр хэрэгтэй зүйл бол зохион байгуулалт байсан;
  • хүмүүсийг шүүхэд шилжүүлэхийн тулд аж ахуйн нэгжүүдийн харилцан үйлчлэлийн хэрэгсэл шаардлагатай байсан;
  • аж ахуйн нэгжүүд өөрсдөө өгөгдөл, логикийн холимог байсан тул хяналтаас хурдан гарсан.

Би дахин судалгаа хийж, архитектурыг олж мэдсэн ECS - Аж ахуйн нэгжийн бүрэлдэхүүн хэсгүүдийн систем, энэ нь ихэвчлэн тоглоомд ашиглагддаг. ECS-ийн давуу талууд энд байна.

  • өгөгдөл нь логикоос тусгаарлагдсан;
  • өв залгамжлалын оронд найрлага;
  • өгөгдөл төвтэй архитектур.

ECS нь гурван үндсэн ойлголтоор тодорхойлогддог.

  • аж ахуйн нэгжүүд - танигч нь хамаарах объектын төрөл (энэ нь тоглогч, бөмбөг эсвэл өөр зүйл байж болно);
  • бүрэлдэхүүн хэсгүүд - аж ахуйн нэгжүүд нь тэдгээрээс бүрддэг. Жишээ нь - дүрслэх бүрэлдэхүүн хэсэг, байршил болон бусад. Эдгээр нь мэдээллийн агуулах юм;
  • системүүд - тэдгээр нь объект болон бүрэлдэхүүн хэсгүүдийг хоёуланг нь ашигладаг бөгөөд энэ өгөгдөл дээр суурилсан зан төлөв, логикийг агуулдаг. Жишээ нь рэндэрлэх бүрэлдэхүүн хэсгүүдтэй бүх байгууллагуудыг давтаж, дүрслэлийг хийдэг дүрслэх систем юм.

Үүнийг судалсны дараа ECS нь дараахь асуудлуудыг шийддэг нь тодорхой болсон.

  • аж ахуйн нэгжүүдийг системтэйгээр зохион байгуулахын тулд өв залгамжлалын оронд байршлыг ашиглах;
  • хяналтын системээр дамжуулан кодын будлианаас ангижрах;
  • is_build_mode гэх мэт аргуудыг ашиглан wireframe логикийг нэг газар - дүрслэх системд хадгална.

ECS-ийг хэрэгжүүлсний дараа ийм зүйл болсон.

нөөц -> энэ бол бүх хөрөнгө (зураг)
SRC
- бүрэлдэхүүн хэсгүүд
— албан тушаал.rs
- хүн.rs
— tennis_court.rs
- давхар
- wireframe.rs
— mouse_tracked.rs
- нөөц
— хулгана.rs
- системүүд
— rendering.rs
— тогтмол.rs
— utils.rs
— world_factory.rs -> дэлхийн үйлдвэрийн үйл ажиллагаа
— main.rs -> үндсэн давталт

Бид шүүх рүү хүмүүсийг томилдог

ECS нь амьдралыг илүү хялбар болгосон. Одоо би аж ахуйн нэгжүүдэд өгөгдөл нэмэх, тэр өгөгдөл дээр үндэслэн логик нэмэх системтэй аргатай болсон. Энэ нь эргээд шүүхүүдэд хүмүүсийг хуваарилах ажлыг зохион байгуулах боломжтой болсон.

Би юу хийсэн:

  • Тухайн хүнд томилогдсон шүүхийн талаарх мэдээллийг нэмсэн;
  • TennisCourt-д хуваарилагдсан хүмүүсийн талаарх мэдээллийг нэмсэн;
  • Хүмүүс болон шүүхүүдэд дүн шинжилгээ хийх, боломжтой шүүхүүдийг илрүүлэх, тоглогчдыг тэдэнд хуваарилах боломжийг олгодог CourtChoosingSystem нэмсэн;
  • Шүүхэд томилогдсон хүмүүсийг хайж, хэрэв тэнд байхгүй бол хүмүүсийг шаардлагатай газар нь илгээдэг PersonMovementSystem нэмсэн.

24 цагийн дотор Rust тоглох: хувийн хөгжлийн туршлага

Дуусгах

Энэ энгийн тоглоом дээр ажиллах нь надад үнэхээр таалагдсан. Түүгээр ч зогсохгүй би Зэвийг бичсэндээ баяртай байна, учир нь:

  • Зэв танд хэрэгтэй зүйлээ өгдөг;
  • энэ нь маш сайн баримт бичигтэй, Rust нь нэлээд дэгжин;
  • тууштай байдал хүйтэн байна;
  • та C++ дээр миний хийдэг клончлох, хуулбарлах болон бусад ижил төстэй үйлдлүүдийг хийх шаардлагагүй;
  • Сонголтуудыг ашиглахад маш хялбар бөгөөд алдааг маш сайн зохицуулдаг;
  • хэрэв төслийг эмхэтгэх боломжтой байсан бол 99% нь ажиллах ёстой бөгөөд яг байх ёстой. Хөрвүүлэгчийн алдааны мэдэгдлүүд бол миний үзсэн хамгийн сайн зүйл гэж би бодож байна.

Rust дахь тоглоомын хөгжүүлэлт дөнгөж эхэлж байна. Гэхдээ Зэвийг хүн бүрт нээлттэй болгохын тулд тогтвортой, нэлээд том нийгэмлэг аль хэдийн ажиллаж байна. Тиймээс би хэлнийхээ ирээдүйг өөдрөгөөр харж, хамтын ажлынхаа үр дүнг тэсэн ядан хүлээж байна.

Skillbox зөвлөж байна:

Эх сурвалж: www.habr.com

сэтгэгдэл нэмэх