Игра на 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 за MainState {
    fn update(&mut self, context: &mut Context) -> GameResult {
         Обновяване на всички обекти
        for object in self.objects.iter_mut() {
            object.update(context)?;
        }
 
        Ok(())
    }
 
    fn draw(&mut self, context: &mut Context) -> GameResult {
        graphics::clear(context);
 
         Рендериране на всички обекти
        for object in self.objects.iter_mut() {
            object.draw(context)?;
        }
 
        graphics::present(context);
 
        Ok(())
    }
}

main.rs е необходим, защото в него са всички редове от кода. Похарчих малко време, за да разделя файловете и оптимизирам структурата на директориите. Ето как изглеждат нещата след това:
resources -> тук се намират всички активи (изображения)
src
— entities
— game_object.rs
— circle.rs
— main.rs -> основен цикъл

Хора, игрища и изображения

Следващата стъпка е създаването на игровия обект Person и зареждането на изображения. Всичко трябва да се изгражда на базата на плочки 32*32.

Игра на Rust за 24 часа: личен опит в разработката

Тенис кортове

След като проучих как изглеждат тенис кортовете, реших да ги направя от плочки 4*2. Първоначално можех да направя изображение с такъв размер или да комбинирам 8 отделни плочки. Но след това разбрах, че са нужни само две уникални плочки и ето защо.

Имаме общо две такива плочки: 1 и 2.

Всяка част от корта се състои от плочка 1 или плочка 2. Те могат да бъдат разположени както обикновено или да бъдат обърнати на 180 градуса.

Игра на Rust за 24 часа: личен опит в разработката

Основен режим на строителство (сглобяване)

След като успях да постигна рендеринг на площадките, хората и картите, осъзнах, че е необходим и основен режим на сглобяване. Реализирах го така: когато бутонът е натиснат — обектът е избран, а кликът го поставя на нужното място. Така бутон 1 позволява да се избере корт, а бутон 2 позволява да се избере играч.

Но все пак е нужно да запомним какво означава 1 и 2, затова добавих wireframe, за да е ясно кой обект е избран. Ето как изглежда.

Игра на Rust за 24 часа: личен опит в разработката

Въпроси относно архитектура и рефакторинг

Сега имам няколко игрови обекта: хора, корти и етажи. Но за да работят вайрфреймовете, всяка структура трябва да знае дали самите обекти са в режим на демонстрация, или просто е нарисувана рамка. Това не е много удобно.

Установих, че е необходимо да преразгледам архитектурата, за да идентифицирам някои ограничения:

  • наличието на структура, която се отразява и актуализира сама, е проблем, тъй като тази структура не може да „разбере“, какво трябва да рендерира — изображение и вайрфрейм;
  • липсата на инструмент за обмен на свойства и поведение между отделните структури (пример — свойството is_build_mode или рисуване на поведението). Можеше да се използва наследяване, но в Rust няма нормален начин за реализация на това. Наистина, което ми трябваше — беше компоновка;
  • инструмент за взаимодействие между структурите беше необходим, за да се назначават хората в корта;
  • самите структури представляваха смес от данни и логика, което бързо излизаше извън контрол.

Направих допълнително проучване и открих архитектурата ECS — Entity Component System, която обикновено се използва в игрите. Ето предимствата на ECS:

  • данните са отделени от логиката;
  • компоновка вместо наследяване;
  • архитектура, ориентирана към данните.

За ECS са характерни три основни концепции:

  • структури — тип обект, на който се позовава идентификатор (това може да бъде играч, топка или нещо друго);
  • компоненти — от тях се състоят структури. Пример — компонент за рендериране, положение и други. Това са хранилища на данни;
  • системи — те използват както обекти, така и компоненти, плюс съдържат поведение и логика, базирани на тези данни. Пример — система за рендериране, преминаваща през всички структури с компоненти за рендериране и отговаряща за графичното изобразяване.

След проучването стана ясно, че ECS решава такива проблеми:

  • прилагане на компоновка вместо наследяване за системна организация на структурите;
  • освобождаване от хаоса на кода чрез управляващи системи;
  • използване на методи като is_build_mode, за да се съхранява логиката на вайрфрейма на едно и също място — в системата за рендериране.

Ето какво се получи след внедряването на ECS.

resources -> тук се намират всички активи (изображения)
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 -> основен цикъл

Назначав хора на игрищата

ECS улесни живота. Сега имах системен подход за добавяне на данни към обектите и интегриране на логика, основаваща се на тези данни. Това, от своя страна, позволи организиране на разпределението на хората по игрищата.

Какво направих:

  • добавих данни за назначените игрища в Person;
  • добавих данни за разпределените хора в TennisCourt;
  • добавих CourtChoosingSystem, позволяваща анализ на хората и игрищата, откриване на налични игрища и разпределение на играчите на тях;
  • добавих система PersonMovementSystem, която търси хора, назначени на игрищата, и ако ги няма, изпраща хората където е нужно.

Игра на Rust за 24 часа: личен опит в разработката

Обобщение

Много ми хареса да работя върху тази проста игра. Освен това, удовлетворена съм, че я написах на Rust, тъй като:

  • Rust ти дава точно това, от което имаш нужда;
  • има отлична документация, Rust е изключително елегантен;
  • постоянството е велика работа;
  • не е нужно да прибягвам до клониране, копиране или други подобни действия, които често правех в C++;
  • Options са много удобни за работа, те също така отлично обработват грешките;
  • ако проектът е успял да се компилира, то в 99% от случаите той работи точно така, както трябва. Според мен, съобщенията за грешки на компилатора са най-добрите, които съм виждала.

Разработката на игри на Rust сега едва започва. Но вече има стабилна и доста голяма общност, работеща над това да отвори Rust за всички. Затова гледам с оптимизъм към бъдещето на езика, нетърпеливо очаквайки резултатите от нашата съвместна работа.

Skillbox препоръчва:

Източник: habr.com

Купете надежден хостинг за сайтове с защита от DDoS, VPS VDS сървъри 🔥 Купете надежден хостинг за сайтове с защита от DDoS, VPS VDS сървъри | ProHoster