Гульня на Rust за 24 гадзіны: асабісты досвед распрацоўкі

Гульня на Rust за 24 гадзіны: асабісты досвед распрацоўкі

У гэтым артыкуле я распавяду пра асабісты досвед распрацоўкі невялікай гульні на Rust. На стварэнне працоўнай версіі спатрэбілася каля 24 гадзін (пераважна я працавала па вечарах ці на выходных). Гульня яшчэ далёкая ад завяршэння, але я думаю, што досвед будзе карысным. Я раскажу, чаму навучылася, і пра некаторыя назіранні, зробленыя пры пабудове гульні з нуля.

Skillbox рэкамендуе: Двухгадовы практычны курс "Я - вэб-распрацоўшчык PRO".

Нагадваем: для ўсіх чытачоў "Хабра" - зніжка 10 000 рублёў пры запісе на любы курс Skillbox па промакодзе "Хабр".

Чаму Rust?

Я выбрала гэтую мову, паколькі чула пра яе шмат добрага і бачу, што яна становіцца ўсё больш папулярнай у сферы распрацоўкі гульняў. Да напісання гульні ў мяне быў невялікі досвед распрацоўкі простых прыкладанняў на Rust. Гэтага было якраз дастаткова, каб адчуць пэўную свабоду падчас напісання гульні.

Чаму менавіта гульня і што за гульня?

Стварэнне гульняў - гэта весела! Я б хацела, каб прычын было больш, але для "хатніх" праектаў я выбіраю тэмы, якія не занадта цесна звязаныя з маёй звычайнай працай. Што за гульня? Мне хацелася зрабіць нешта накшталт тэніснага сімулятара, дзе спалучаюцца Cities Skylines, Zoo Tycoon, Prison Architect і ўласна тэніс. Увогуле атрымалася гульня аб акадэміі тэніса, куды людзі прыходзяць для гульні.

Тэхнічная падрыхтоўка

Мне хацелася выкарыстоўваць Rust, але я не ведала дакладна, наколькі "з нуля" спатрэбіцца пачаць працу. Я не хацела пісаць піксельныя шэйдары і выкарыстоўваць drag-n-drop, таму шукала самыя гнуткія рашэнні.

Знайшла карысныя рэсурсы, якімі дзялюся з вамі:

Я вывучыла некалькі гульнявых рухавічкоў Rust, абраўшы ў канчатковым выніку Piston і ggez. З імі я сутыкалася пры працы над папярэднім праектам. У выніку выбрала ggez, паколькі ён здаўся больш прыдатным для рэалізацыі невялікай 2D-гульні. Модульная структура Piston залішне складаная для пачаткоўца распрацоўніка (ці таго, хто ўпершыню працуе з 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 неабходны таму, што ў ім - усе радкі кода. Я патраціла крыху часу, каб падзяліць файлы і аптымізаваць структуру дырэкторый. Вось як усё стала выглядаць пасля гэтага:
resources -> гэта where all the assets are (images)
SRC
- entities
- game_object.rs
- circle.rs
- main.rs -> main loop

Людзі, паверхі і выявы

Наступны этап - стварэнне гульнявога аб'екта Person і загрузка малюнкаў. Усё павінна будавацца на аснове плітак памерам 32*32.

Гульня на Rust за 24 гадзіны: асабісты досвед распрацоўкі

тэнісныя корты

Вывучыўшы, як выглядаюць тэнісныя корты, я вырашыла зрабіць іх з плітак 4*2. Першапачаткова можна было зрабіць выяву такога памеру або скласці разам 8 асобных плітак. Але потым я зразумела, што неабходны толькі дзве ўнікальныя пліткі, і вось чаму.

Усяго ў нас дзве такія пліткі: 1 і 2.

Кожная секцыя корта складаецца з пліткі 1 ці пліткі 2. Яны могуць размяшчацца як звычайна ці быць перавернутымі на 180 градусаў.

Гульня на Rust за 24 гадзіны: асабісты досвед распрацоўкі

Асноўны рэжым будаўніцтва (зборкі)

Пасля таго як атрымалася дамагчыся рэндэрынгу пляцовак, людзей і карт, я зразумела, што неабходны яшчэ і базавы рэжым зборкі. Яго рэалізавала так: калі націснутая кнопка - аб'ект абраны, а клік размяшчае яго на патрэбным месцы. Так, кнопка 1 дае магчымасць абраць корт, а кнопка 2 дазваляе абраць гульца.

Але ж трэба яшчэ запомніць, што ў нас азначае 1 і 2, таму я дадала вайрфрэйм ​​для таго, каб было зразумела, які аб'ект абраны. Вось як гэта выглядае.

Гульня на Rust за 24 гадзіны: асабісты досвед распрацоўкі

Пытанні па архітэктуры і рэфактарынгу

Цяпер у мяне ёсць некалькі гульнявых аб'ектаў: ​​людзі, корты і паверхі. Але для таго, каб працавалі вайрфрэймы, трэба кожнай сутнасці аб'екта паведаміць, ці знаходзяцца самі аб'екты ў рэжыме дэманстрацыі, ці проста намаляваная рамка. Гэта ня вельмі зручна.

Мне падалося, што трэба пераасэнсаваць архітэктуру так, каб выявіліся некаторыя абмежаванні:

  • наяўнасць сутнасці, якая адлюстроўвае і абнаўляе сябе саму, - гэта праблема, паколькі гэтая сутнасць не зможа "даведацца", што яна павінна рэндэрыць - малюнак і вайрфрэйм;
  • адсутнасць прылады абмену ўласцівасцямі і паводзінамі паміж асобна ўзятымі сутнасцямі (прыклад - уласцівасць is_build_mode ці ж адмалёўка паводзін). Можна было б выкарыстоўваць атрыманне ў спадчыну, хоць у Rust няма нармальнага спосабу яго рэалізацыі. Што мне сапраўды было патрэбна - гэта кампаноўка;
  • прылада для ўзаемадзеяння сутнасцяў паміж сабой быў патрэбен, каб прызначаць людзей у корты;
  • самі сутнасці ўяўлялі сабою сумесь дадзеных і логікі, што вельмі хутка выходзіла з-пад кантролю.

Я правяла дадатковае вывучэнне і выявіла архітэктуру ECS – Entity Component System, Якая звычайна выкарыстоўваецца ў гульнях. Вось перавагі ECS:

  • дадзеныя аддзеленыя ад логікі;
  • кампаноўка замест атрымання ў спадчыну;
  • архітэктура, арыентаваная на дадзеныя.

Для ECS характэрны тры базавыя канцэпты:

  • сутнасці - тып аб'екта, на які спасылаецца ідэнтыфікатар (гэта можа быць гулец, мячык ці нешта яшчэ);
  • кампаненты - з іх складаюцца сутнасці. Прыклад - кампанент рэндэрынгу, размяшчэння і іншыя. Гэта сховішчы дадзеных;
  • сістэмы - яны выкарыстоўваюць як аб'екты, так і кампаненты, плюс змяшчаюць паводзіны і логіку, якія грунтуюцца на гэтых дадзеных. Прыклад - сістэма рэндэрынгу, якая перабірае ўсе сутнасці з кампанентамі для рэндэрынгу і займаецца адмалёўкай.

Пасля вывучэння стала зразумела, што ECS вырашае такія праблемы:

  • ужыванне кампаноўкі замест атрымання ў спадчыну для сістэмнай арганізацыі сутнасцяў;
  • збавенне ад мешаніны кода за кошт сістэм кіравання;
  • выкарыстанне метадаў накшталт is_build_mode, каб захоўваць логіку вайрфрэйма ў адным і тым жа месцы - у сістэме рэндэрынгу.

Вось што атрымалася пасля ўкаранення ECS.

resources -> гэта where all the assets are (images)
SRC
- components
- position.rs
- person.rs
- tennis_court.rs
- floor.rs
- wireframe.rs
- mouse_tracked.rs
- resources
- mouse.rs
- systems
- rendering.rs
- constants.rs
- utils.rs
- world_factory.rs -> world factory functions
- main.rs -> main loop

Прызначаем людзей на корты

ECS зрабіла жыццё прасцей. Цяпер у мяне быў сістэмны шлях дадання дадзеных да сутнасцяў і даданне логікі, заснаванай на гэтых дадзеных. А гэта, у сваю чаргу, дазволіла арганізаваць размеркаванне людзей па кортах.

Што я зрабіла:

  • дадала дадзеныя аб прызначаных кортах у Person;
  • дадала дадзеныя аб размеркаваных людзях у TennisCourt;
  • дадала CourtChoosingSystem, якая дазваляе аналізаваць людзей і пляцоўкі, выяўляць даступныя корты і размяркоўваць гульцоў на іх;
  • дадала сістэму PersonMovementSystem, якая шукае людзей, прызначаных на корты, і калі іх тамака няма, то адпраўляе людзей куды трэба.

Гульня на Rust за 24 гадзіны: асабісты досвед распрацоўкі

Падводзім вынікі

Мне вельмі спадабалася працаваць над гэтай простай гульнёй. Больш за тое, я задаволеная, што выкарыстоўвала для яе напісання Rust, паколькі:

  • Rust дае вам тое, што неабходна;
  • у яго выдатная дакументацыя, Rust вельмі элегантны;
  • сталасць - гэта крута;
  • не прыходзіцца звяртацца да кланавання, капіяванні ці іншым падобным дзеянням, што я часта рабіла ў З++;
  • Options вельмі зручныя для працы, яны таксама цудоўна апрацоўваюць памылкі;
  • калі праект удалося скампіляваць, то ў 99% ён працуе, прычым менавіта так, як павінен. Паведамлення пра памылкі кампілятара, мне здаецца, лепшыя з тых, што я бачыла.

Распрацоўка гульняў на Rust зараз толькі пачынаецца. Але ўжо ёсць стабільнае і даволі вялікае кам'юніці, якое працуе над тым, каб адкрыць Rust для ўсіх. Таму я гляджу на будучыню мовы з аптымізмам, з нецярпеннем чакаючы вынікаў нашай супольнай працы.

Skillbox рэкамендуе:

Крыніца: habr.com

Дадаць каментар