На каком языке программирования написан rust игра

Обновлено: 05.07.2024

В этом посте я расскажу о небольшой игре, которую я разработала всего за 24 часа (в основном по несколько часов по вечерам или выходным). Она ещё далека от идеала, но поделиться опытом, полученным при её разработке, уже вплоне можно.

Почему Rust?

Я выбрала Rust, потому что слышала о нём отличные отзывы и вижу, что он набирает обороты в области разработки игр. Должна сказать, что в тот момент, когда я начала эту игру, несколько небольших программ уже были написаны мной на Rust.

Почему именно игра и какая это игра?

Делать игры — весело! Хотелось бы, чтобы была более веская причина, но для любительских проектов я обычно предпочитаю вещи, которые весьма далеки от того, что я делаю ежедневно на работе. Так какая же игра? Я хотела сделать симулятор на тему тенниса с пиксельной графикой. Я ещё не всё продумала, но по сути это теннисная академия, куда люди приходят и играют в теннис.

Техническое исследование

Я нашла несколько полезных ресурсов, которыми хочу поделиться:

Я провела небольшое исследование игровых движков Rust и остановилась на двух вариантах: Piston и ggez. Я пробовала их в предыдущем небольшом проекте и в итоге выбрала ggez, потому что он кажется более простым для использования в маленькой 2D игре. Модульная структура Piston кажется немного непонятной для новичка.

Архитектура игры

Затем я провела немного времени, размышляя об игре. Первым шагом было бы сделать некоторую площадку на экране, затем несколько человек и несколько кортов. В конечном счёте, нам нужно будет назначать людей на корты, заставлять их передвигаться и ждать. У людей будут навыки, которые будут улучшаться с увеличением игровой практики. Ещё там должен быть своего рода режим сборки, чтобы вы могли добавлять людей и корты, и в итоге всё это будет стоить денег.

В этот момент у меня появилось достаточно идей, чтобы начать писать код.

Разработка игры

Начало: круги и абстракция

Я стащила готовый образец ggez и получила на экране окно с кружком. Удивительно! Далее немного абстракции. Я подумала, что было бы неплохо абстрагироваться от идеи игрового объекта. Каждый игровой объект можно рендерить и обновлять, что-то вроде этого:

Это позволит иметь хороший список объектов, которые я могла бы обновлять и рендерить в цикле.

На данном этапе main.rs необходим, потому что в нём содержится весь код. Поэтому я потратила некоторое время, разбивая его на отдельные файлы и немного упорядочивая структуру каталогов, так что теперь это выглядит так.

Люди, площадки и изображения

object Person

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

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

1 октября – 30 ноября, Онлайн, Беcплатно

Есть 2 уникальных плитки: 1 и 2.

Каждая секция корта состоит из плитки 1 или плитки 2 как они есть или повёрнутых на 180 градусов.

unique tiles

Основной режим сборки

Теперь, когда я могла рендерить площадки, людей и корты, я решила, что мне нужен базовый режим сборки. И я сделала так, что, когда клавиша нажата, — объект выбран, и затем клик разместил бы этот объект. Например, клавиша 1 позволит вам выбрать корт, а клавиша 2 — игрока.

Это было бы не очень удобно, так как вам нужно помнить, что такое 1 и 2. Поэтому я добавила вайрфрейм (от англ. wireframe – каркас) в режиме сборки, чтобы вы могли понять, какой у вас объект. Вот сборка в действии.

Сомнения по поводу архитектуры и рефакторинга

Теперь у меня было несколько игровых объектов: люди, корты и площадки. Но для того, чтобы заставить вайрфрейм работать, необходимо было бы сообщать каждой сущности объекта, находятся ли эти объекты в режиме отображения или же нарисован ограничивающий прямоугольник (рамка) вместо образа. Это не совсем правильно.

Я начала подвергать сомнению архитектуру и смогла увидеть некоторые ограничения:

  • Наличие сущности, отображающей и обновляющей саму себя, было бы проблематичным — сущность не будет знать, должна она рендерить изображение или вайрфрейм.
  • У меня не было хорошего способа обмена свойствами и поведением между сущностями (такого как свойство is_build_mode или отрисовки поведения, например). Я могла бы использовать наследование (в Rust нет хорошего способа реализовать это), но что я действительно хотела, так это компоновку.
  • У меня не было хорошего способа взаимодействия сущностей между собой — что мне определенно понадобилось позже, чтобы назначать людей в корты.
  • Сущности смешивали данные и логику, что очень быстро выходило из-под контроля.

Я покопалась в Сети и обнаружила ECS (Entity Component System) — архитектура, которая в основном используется в играх. Суть ECS заключается в следующем:

  • Отделять данные от логики.
  • Компоновка вместо наследования.
  • Дизайн, ориентированный на данные.

В терминологии ECS есть 3 основных понятия:

  • Сущности: это просто тип объекта, на который ссылается идентификатор (например, игрок, мяч и т. д.).
  • Компоненты: это то, из чего состоят ваши сущности. Например, компонент рендеринга, компонент расположения и т. д. Это просто хранилище данных.
  • Системы: системы используют объекты и компоненты, содержат поведение и логику на основе этих данных. Например, у вас может быть система рендеринга, которая просто перебирает все сущности, содержащие компоненты для рендеринга, и рисует их все.

Чем больше я читала об этом, тем больше понимала, что это решит мои текущие проблемы:

  • Я могла бы использовать компоновку вместо наследования, чтобы раскладывать сущности более систематично.
  • Я могла бы использовать системы для управления поведением, не делая “спагетти” из кода.
  • Я могла бы делиться методами вроде is_build_mode и иметь логику вайрфрейма в одном месте (в системе рендеринга).

Вот что я получила после внедрения ECS (что, честно говоря, всё практически переписано).

Назначение игроков на корты

После перехода на ECS всё стало относительно легко. Теперь у меня был систематический способ добавления данных в мои объекты и добавления логики на основе этих данных. Это позволило мне очень легко назначать людей на корты.

Что было сделано:

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

Всё это можно увидеть в действии.

Заключение

Я действительно отлично провела время, создавая эту маленькую игру. Но я особенно рада, что написала её с помощью Rust, потому что:

  • Rust позволяет делать именно то, что вам нужно.
  • Он очень элегантен и имеет отличную документацию.
  • Неизменность по умолчанию прекрасна.
  • Никаких неловких движений, клонирования или копирования (как я часто делала в C++).
  • С Options работать очень комфортно и они делают обработку ошибок прекрасной.
  • Если проект скомпилировался, то 99 % случаев он работает так, как вы и ожидаете. А ошибки компилятора, вероятно, самые лучшие, которые я когда-либо видела.

Что касается разработки игр на Rust, я думаю, что это только начало. Но я вижу большое сообщество, активно работающее над тем, чтобы сделать Rust более доступным для разработчиков. Так что я с оптимизмом смотрю в будущее и не могу дождаться, чтобы увидеть, как всё это будет развиваться.

Хинт для программистов: если зарегистрируетесь на соревнования Huawei Cup, то бесплатно получите доступ к онлайн-школе для участников. Можно прокачаться по разным навыкам и выиграть призы в самом соревновании.

Перейти к регистрации

Мне нужен язык программирования на котором я бы смог создать похожую выживалку. Просто говорят что сейчас все современные игры созданы на с++. Я собираюсь делать выживалку на джижке Unity 5 pro. Посоветуйте язык программирования на котором я бы смог создать нормальную игру. Не надо мне советовать то что проще. Говорите то что более функциональнее. Короче я думаю вы меня поняли. Мне нужен хороший язык програмирования. И кстати, если не трудно то киньте на его обучалку ссылку. Буду благодарен

ну в википедии же всё есть.
всё написано на юнити кроме ARK, он на анреале 4, тот на C++.
приличное что-то всегда пишется на C++

Передача данных клиент-сервер в игре java
Здравствуйте. Пишу маленькую сетевую игру как на картинке. На сервере создаются списки пуль и.

В игре Rust непонятные просадки FPS
Здравствуйте . В игре Rust у меня непонятные просадки фпс . Вот играю фпс 50-80+ но в какие то.

Взаимодействие WinCC с Labview 2013, как сервер-клиент, так и клиент-сервер
Здравствуйте. Интересует информация о взаимодействии WinCC с Labview 2013, как сервер-клиент, так и.


На каком языке и в каком приложении писалась эта програмка?
мне надо отредактировать и скомпилировать чтобы в exe файле писались моя фамилия


Недавно я написал статью Трясём стариной — или как вспомнить Ассемблер, если ты его учил 20 лет назад. В статье рассказывается о том, как изучать ассемблер на примере игрушки 2048. Возможно для целей самой статьи игрушка была подходящая, но конечный результат меня немного удручил. Бинарник размером в 10 килобайт, который потребляет 2 мегабайта памяти, из-за неправильно слинкованной библиотеки резал глаза.

Посему я задался вопросом, а как это можно было бы сделать правильнее? Наверняка есть намного более удачное решение. (И организовал ещё один конкурс с призами в конце статьи)

А почему бы не сделать на Rust, и правильно прикрученных библиотеках? При этом, если вы знаете, что делаете, то вы можете запросто уменьшить количество потребляемой оперативной памяти, но при этом написать визуальную игрушку с использованием Windows API.

Причём это не значит, что вы будете использовать какую-то нестандартную библиотеку. Встречайте — windows-rs, проект поддерживаемый Microsoft. Ваш билет в мир Windows, если вы пишете на Rust.

При написании этой статьи я понял, что мы берём понемногу из двух миров — Windows API и rust. Определённые моменты будут казаться очевидными для rust разработчиков, другие моменты будут казаться очевидными для Windows разработчиков. Так как это — статья для всех, то я решил что будет лучше, если объясню больше, чем меньше.

▍ Введение

Для тех кто незнаком с Rust — вам необходимо будет подтянуть свои знания. Хотя бы потому что с момента своего создания в 2010 году язык завоёвывает популярность. С 2016 года язык появлялся в отчётах stackoverflow в списках самых любимых языков. В 2021 году Rust остаётся самым любимым языком на stackoverflow, что не мешает ему оставаться очень нишевым языком, доступным только избранным. В основном потому, что сам по себе Rust это низкоуровневый язык, который хоть и красив и приятен, но всё же учится не за 20 минут по видео из ютуба.

Rust используется в ядре Linux. Естественно, так как Rust был изначально создан в Mozilla Foundation, то большое количество компонентов Firefox написано именно на нём. В Microsoft решили, что не стоит отставать, и начали использовать Rust в части своих проектов.


И если год назад вы могли увидеть в репозитории упоминания, что проект не находится в стабильном состоянии и не рекомендуется к использованию в работе, то сейчас эти упоминания из проекта пропали. (Хотя, для того чтобы вы могли воспользоваться результатами этого проекта, вам всё равно придётся установить Rust Nightly билд, и вы успеете насмотреться на предупреждения от компилятора).

Итак, в чём заключается проект? Все Windows API импортированы в Rust и вы можете использовать любые стандартные функции прямо из вашей программы, без большого количества колдовства.

▍ Начало работы

Итак, что же нам нужно сделать, для того чтобы начать работать с Windows API в Rust.

Для начала, нам потребуется rust nightly.

rustup default nightly
rustup update

После этого вам нужно будет создать новый проект на rust и сразу создать дополнительную библиотеку под названием bindings.
cargo new testproject
cd testproject
cargo new --lib bindings .
И, после этого добавить в cargo.toml в нашем проекте зависимости для подключения библиотеки Microsoft.

[dependencies]
bindings = < path = "bindings" >
windows = "0.21.1"

На момент написания статьи, актуальная версия библиотеки 0.21.1, посему везде будем использовать именно эту версию.

После этого в самой папке с библиотекой bindings нам нужно будет добавить в Cargo.toml следующий текст:
[package]
name = "bindings"
version = "0.1.0"
edition = "2018"

[dependencies]
windows = "0.21.1"

[build-dependencies]
windows = "0.21.1"
Итак, что у нас тут получается? У нас есть проект под названием testproject, в нём есть библиотека bindings. Цель этой библиотеки — подключать зависимости для того, чтобы вы могли работать с Windows API в вашем приложении.

Сам файл bindings/src/lib.rs будет состоять из одной команды:


Это — вызов макроса, который подключит все необходимые зависимости.

И теперь, самое интересное, файл bindings/build.rs

▍ Написание программы

Итак, мы закончили с подготовкой. Что делать теперь? Теперь всё просто. Здесь можно брать документацию Windows API и искать то, что нам интересно.

Первое — функция main теперь должна возвращать windows::Result<()>. В данном случае мы будем возвращать пустой кортеж, но так как мы находимся в Windows, мы можем вернуть очень много вариантов значений. Для тех, кому это может понадобиться, Result принимает error, подробности здесь.


Второе — все вызовы самих Windows API должны производиться через директиву unsafe.

Я видел множество каких-то непонятный священных войн по поводу того, что использование директивы unsafe должно быть запрещено законом. Если честно, я никогда не понимал из-за чего происходят подобные войны. Давайте посмотрим на официальную документацию. Здесь нам ясно и чётко рассказывают, что в использовании unsafe нет ничего дьявольского, и оно разрешено Женевской конвенцией. Вам просто необходимо знать, что да как использовать.

Ок. Лезем дальше. Давайте откроем документацию Microsoft и найдём в ней MessageBoxA.

У нас есть четыре параметра, которые нам надо передать этой функции.

  • hWnd — дескриптор основного окна, в котором будет показано уведомление. Так как MessageBoxA — это модальное окно, то выполнение команд в основном окне заблокируется, пока будет активно уведомление. Так как у нас нет никакого окна, то сюда можно смело передавать NULL.
  • lpText/lpCaption — указатель типа Long на строку, строки, которые будут показывать в заголовке и теле окна.
  • uType — набор констант, которые зададут поведение, тип кнопок и иконок в этом MessageBox


Как вы видите, нам не пришлось изобретать велосипед, и пытаться создавать тип NULL, которого не существует в Rust, да и передача строк не требует шести преобразований их в разные форматы. Поэтому нам не нужно извращаться и писать что-то типа:


Всё достаточно цивильно.

Так, давайте подключим наши биндинги, соберём программу и посмотрим, как она работает. Конечный результат выглядит вот так:


Замечательно! Повсюду кракозябры, как в 1999 году. Что делать? Разбираемся и понимаем, что MessageBoxA работает c ANSI строками, в то время как MessageBoxW работает с Unicode (Wide strings). Собственно говоря, эта нотация сохранилась во всех Windows API. Я не знаю причин не использовать W версии функций. Но, будьте аккуратны, большое количество писателей мануалов на английском языке не понимают разницы, и вы увидите загруженность A версиями функций, в то время как вам необходимо использовать W версии этих функций.

Заменяем всё в lib.rs и main.rs. Пробуем ещё раз.


При весе в 164 килобайт на диске, программа занимает 940 килобайт оперативной памяти. И при этом работает с Windows API. Достаточно скромно. Лучше, чем ассемблер с кривыми биндингами, который работает только в консоли.

Итак, давайте пройдёмся по основным моментам этой главы:

  1. Вызов WinAPI это всегда unsafe вызов.
  2. fn main должна возвращать результат, понятный Windows.
  3. Вы знаете где и как найти документацию по Windows API и можете использовать их в вашей программе. (Все данные о WinAPI можно достать здесь, а данные о функциях, спортированных в rust находятся здесь.

▍ Пишем программу

Ну что же, теперь мы умеем создавать программу на rust, которая может тянуть WinAPI. Дело за малым — создать окно, понакидать в него что-то, что будет представлять тайлы в нашей игре, и написать логику.

Ну что же, давайте создадим окно и понакидаем в него всякого разного.

Для начала, давайте обновим build.rs и добавим кое-какие функции, которые нам понадобятся.


После этого заменим импорты и код Main на следующий метод:


Здесь мы вызываем GetModuleHandleA. Эта функция возвращает нам называние модуля в котором мы работаем. Каждое окно должно быть привязано к определённому модулю. В данном случае это будет название нашего бинарного файла.

После этого создаём структуру WNDCLASSA и заполняем её параметрами, с которыми наше окно будет запускаться.

Далее, мы регистрируем этот класс.

После чего мы запускаем это окно, вызывая CreateWindowExW.

Компилируем и пробуем всё это запустить.

На экране нет ничего. Более того, если мы попробуем перекомпилировать проект, то у нас всё рухнет и ругнётся на линкер с ошибкой, что конечный бинарный файл для записи недоступен.

Идём в диспетчер задач и видим там, что наша программа безбожно висит.

Сколько раз вам приходилось видеть вот такое?


Именно поэтому наша программа превратилась в программу-невидимку.

Первым делом добавляем после CreateWindowExW следующий код


А в описании структуры WNDCLASSA заменим:

Теперь давайте запустим и проверим всё это.

Ура! Наконец-то! У нас есть окно!


Только не пытайтесь его закрыть. У вас всё рухнет. Окно закроется, а программа останется висеть в памяти. И не пытайтесь изменять размер этого окна. Если вдруг чего, вы увидите чёрные прямоугольники, вместо контента.

Обновляем код и получаем:


При закрытии окна мы выходим из программы, а при изменении размера мы его перерисовываем. Теперь окно не будет виснуть, и вы можете запустить и закрыть программу.

Проверяем. Бинарник размером в 160 килобайт, занимает 1 мегабайт оперативной памяти.

Проверяем знания:

▍ Что дальше?

И всё это доступно прямо сейчас в rust. Надеюсь вы узнали для себя немного нового и возможно у вас появились идеи о том, как вы могли бы это применить в вашей работе. Что же, если это так, то я очень рад, что вам пригодилось.

И, как обычно, конкурс. На этот раз задача будет отстоять в реализации самой компактной игры 2048 написанной на rust. Первое место — эквивалент $25 в пейпал. Второе место — $15, третье $5.

Мерять будем потребление оперативки. Игра должна быть написана на rust и в ней обязательно использовать winapi. Она должна быть графической. Но конкретный движок на выбор.

Читайте также: