Как создать игру в командной строке
Перейти к содержимому

Как создать игру в командной строке

  • автор:

Параметры командной строки

При создании игр важно протестировать исполняемый файл, чтобы убедиться, что окончательная компиляция корректна и не содержит непредвиденных ошибок или различий. Однако создание исполняемого файла означает, что, поскольку у вас нет доступа ни к консоли отладки, ни к окну компилятора, может быть трудно определить и отладить любые проблемы, которые вы можете найти. Однако для этого есть решение — использовать параметры командной строки для установки определенных свойств при запуске исполняемой версии игры, что, в свою очередь, позволяет создавать выходные журналы и тестировать другие специальные ситуации.

ПРИМЕЧАНИЕ Это доступно только для стандартного целевого модуля Windows .

Чтобы это заработало, сначала необходимо создать исполняемый файл игры Windows и установить его на тестовой машине. После этого необходимо создать ярлык игры на рабочем столе. Именно здесь мы будем добавлять параметры, которые будут передаваться на *.exe (чтобы создать ярлык, щелкните правой кнопкой мыши на исполняемом файле игры и выберите Отправить на > Рабочий стол (создать ярлык)).

После создания ярлыка вы можете использовать любой из следующих параметров в целевом пути:

-noaudio : Это отключает все звуки в вашей игре.

-inawindow : Заставляет игру запускаться в окне, даже если она запущена в полноэкранном режиме.

-output filename> : Отправляет вывод консоли на имя файла, включая любые отладочные сообщения, которые есть в вашей кодовой базе.

-debugoutput filename> : Отправляет консольный вывод в указанный файл, исключая любые пользовательские отладочные сообщения, но включая дополнительную информацию из бегущей строки для сообщения об ошибке.

-software : Заставляет игру использовать программную обработку вершин вместо аппаратной для рендеринга игровой графики. Это особенно полезно для тех ПК, которые используют встроенный чипсет Intel GFX или демонстрируют проблемы с отображением на старых машинах.

-intel : Это включит исправление для плохих драйверов Intel, независимо от того, какой GPU используется. Это решение для плохих драйверов замедлит работу, что даст заметное снижение производительности вашей игры, поэтому если вам не нужно исправление, не используйте его.

-vanillaGFX : Использование этого параметра отключает любую проверку производителя драйвера и может быть очень полезно для тестирования вашей игры «как есть».

Стоит отметить, что filename> в -output и -debugoutput при желании может быть одним и тем же, так как файл открывается и закрывается при каждой записи.

Теперь вы должны перейти к тому месту, где вы создали ярлык исполняемого файла игры, а затем щелкнуть на нем правой кнопкой мыши и выбрать пункт «Свойства».

Откроется окно, аналогичное приведенному выше. Здесь вы можете добавить в Target path вашу командную строку. Просто прокрутите до конца заданного пути, добавьте пробел, а затем строку параметров. Так, например, для создания вывода файла отладки вы можете сделать следующее:

D:\Users\Me\GameMaker\CustomLoadBar-Default-1.0.0.0\CustomLoadBar.exe -debugoutput debug.txt

В результате будет создан файл под названием » debug.txt » в том же месте, что и исполняемый файл (не в месте расположения ярлыка, а там, где установлена игра) с информацией об отладке и компиляторе вашей готовой игры. Нажмите на «OK», а затем используйте ярлык для запуска игры, чтобы она использовала эти параметры и помогла в отладке.

Более подробную информацию о том, как отладить вашу игру, вы найдете в разделе руководства » Отладка».

Бегущая строка YoYo также имеет специальный параметр командной строки:

-game filename> : загружает заданный файл игры

Зачем вам это нужно? Обычно вы этого не делаете, но если вы хотите иметь более одной версии вашей игры одновременно, это можно сделать с помощью этого метода, что значительно упрощает сравнительные тесты A/B. С помощью этого метода вы можете провести фокус-тестирование с кем-то другим или, как дизайнер, получить лучшее представление о вещах, проводя тесты бок о бок.

Чтобы настроить его, необходимо знать несколько вещей. Для начала вам понадобится путь к самому Runner. Его можно найти в папке Program Data вашей ОС Windows (по умолчанию она может быть скрыта, поэтому вам придется изменить свойства просмотра, чтобы увидеть ее), а типичный формат выглядит следующим образом:

Обратите внимание, что папка runtime_version будет зависеть от установленных у вас в данный момент времен выполнения. Вам также понадобится путь к файлу yourgame>.win , поскольку именно на него мы будем указывать бегунку. Самый простой способ найти этот путь — зайти в папку Temp (обычно она находится по адресу %localappdata%\Temp\GameMakerStudio\\ ) и просто найти *.win , так как это покажет вам все доступные файлы, и вы сможете просто выбрать тот, к которому вам нужно получить полный путь.

Теперь мы могли бы создать ярлык для бегущей строки, как мы это сделали для исполняемого файла, а затем изменять параметры по мере необходимости, но это означает необходимость открывать и редактировать ее между тестами, что сводит на нет цель использования этой функциональности для тестирования бок о бок. Вместо этого мы создадим пару маленьких «пакетных» файлов, чтобы справиться с этой задачей.

Для этого сначала создайте новый текстовый документ на рабочем столе и откройте его в Блокноте (или любом другом текстовом редакторе). Добавьте следующее:

START C:\ProgramData\GameMaker\Cache\runtimes\\windows\Runner.exe -game \.win

Вам нужно установить временный путь к файлу игры *.win , а затем сохранить его как » GM_Test_1.bat » на рабочем столе (имейте в виду, что Блокнот автоматически сохранит файл *.txt , если вы не выберете «все файлы» в опциях диалога сохранения). Пример того, как должна выглядеть ваша конечная команда:

START C:\ProgramData\GameMaker\Cache\runtimes\runtime-9.9.1.199\windows\Runner.exe -game D:\Users\Mark\AppData\Local\Temp\GameMaker\AlphaDog_Reboot\AlphaDog_Reboot.win

Теперь создайте другой текстовый файл и добавьте ту же строку, только на этот раз укажите путь ко второй версии игры, с которой вы хотите провести тестирование бок о бок, и сохраните его как » GM_Test_2.bat «. Вы можете дважды щелкнуть по любому из этих файлов , чтобы открыть различные версии вашей игры, а наличие этих файлов в виде пакетных файлов означает, что вы можете легко открыть их снова, чтобы отредактировать их и изменить тестируемую игру.

Аргументы командной строки

Обычно, Unity будет запущен, если дважды кликнуть по ярлыку на рабочем столе, но его также можно запустить из командной строки (т.е. из терминала MacOS или из командной строки Windows). При запуске таким образом, можно задать Unity команды и информацию на старте, которые могут быть очень полезны для целей тестирования, автоматизированных сборок и других производственных целей.

В MacOS, вы можете запустить Unity из терминала введя:-

 /Applications/Unity/Unity.app/Contents/MacOS/Unity "C:\Program Files\Unity\Editor\Unity.exe" "C:\Program Files (x86)\Unity\Editor\Unity.exe" 

Игры, сделанные в Unity, могут быть запущены похожим путём.

Activating Unity Silently

В MacOS, вы можете запустить Unity из терминала введя:-

 /Applications/Unity/Unity.app/Contents/MacOS/Unity -quit -batchmode -serial R3-XXXX-XXXX-XXXX-XXXX-XXXX -username 'JoeBloggs@example.com' -password 'MyPassw0rd' 

…в то время, как в Windows вам надо ввести

 "C:\Program Files\Unity\Editor\Unity.exe" -quit -batchmode -serial R3-XXXX-XXXX-XXXX-XXXX-XXXX -username "JoeBloggs@example.com" -password "MyPassw0rd" "C:\Program Files (x86)\Unity\Editor\Unity.exe" -quit -batchmode -serial R3-XXXX-XXXX-XXXX-XXXX-XXXX -username "JoeBloggs@example.com" -password "MyPassw0rd" 

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

-assetServerUpdate ]> Вызывает обновление проекта в Сервере ассетов, в заданном IP:port. Порт может быть необязательным, и если он не дан, то это предполагает то, что он стандартный (10733). Рекомендуется использовать эту команду в связке в аргументом -projectPath, чтобы убедиться, что вы работаете с нужным проектом. Если имя проекта не задать, то будет открыт последний проект, использовавший Unity. Если по заданному пути -projectPath проекта не существует, то автоматически будет создан новый.
-batchmode Запускает Unity в режиме batch. Это всегда следует использовать в связке с другими аргументами командной строки, так как он обеспечивает то, что при старте не появятся никакие выскакивающие окна и это убирает необходимость вмешательства человека. Когда происходит исключение во время исполнения кода скрипта, сервер ассетов сообщает о падении или другие операции падают, Unity незамедлительно закроется с return кодом 1. Обратите внимание, что в режиме batch Unity будет отсылать в консоль минимально возможную версию лога. Однако файлы лога будут всё также содержать полные логи. Учтите, что открытие проекта в режиме batch, когда в редакторе открыт этот же проект, не поддерживается. Одновременно может быть запущена только одна копия Unity.
-buildLinux32Player Собрать 32-битное приложение для Linux (например, -buildLinux32Player path/to/your/build).
-buildLinux64Player Собрать 64-битное приложение для Linux (например, -buildLinux64Player path/to/your/build).
-buildLinuxUniversalPlayer Собрать совмещённое 32-битное и 64-битное приложение для Linux (например, -buildLinuxUniversalPlayer path/to/your/build).
-buildOSXPlayer Собрать 32-битное приложение для Mac OSX (например, -buildOSXPlayer path/to/your/build.app).
-buildOSX64Player Собрать 64-битное приложение для Mac OSX (например, -buildOSX64Player path/to/your/build.app).
-buildOSXUniversalPlayer Собрать совмещённое 32-битное и 64-битное приложение для Mac OSX (например, -buildOSXUniversalPlayer path/to/your/build.app).
-buildTarget Allows the selection of an active build target before a project is loaded. Possible options are: win32, win64, osx, linux, linux64, ios, android, web, webstreamed, webgl, xbox360, xboxone, ps3, ps4, psp2, wsa, wp8, tizen, samsungtv.
-buildWebPlayer Собрать приложение для WebPlayer (например, -buildWebPlayer path/to/your/build).
-buildWebPlayerStreamed Собрать потоковое приложение для WebPlayer (например, -buildWebPlayerStreamed path/to/your/build).
-buildWindowsPlayer Собрать 32-битное приложение Windows (например, -buildWindowsPlayer path/to/your/build.exe).
-buildWindows64Player Собрать 64-битное приложение Windows (например, -buildWindows64Player path/to/your/build.exe).
-cleanedLogFile Detailed debugging feature. StackTraceLogging allows features to be controlled to allow detailed logging. All settings allow None, Script Only and Full to be selected.
-createProject Создать пустой проект по заданному пути.
-editorTestsCategories Filter editor tests by categories. Separate test categories with a comma.
-editorTestsFilter Filter editor tests by names. Separate test names with a comma.
-editorTestsResultFile Path where the result file should be placed. If the path is a folder, a default file name will be used. If not specified, the results will be places in project’s root folder.
-executeMethod Исполняет статичный метод сразу, как только Unity запустится, проект откроется и, опционально, произойдёт обновление сервера ассетов. Это можно использовать для постоянной интеграции, проведения Unit тестов, сборки, подготовки данных и т.д. Если вы хотите, чтобы была возвращена ошибка из командной строки процесса, вы можете либо запустить исключение, которое вынудит Unity закрыться с результатом 1, либо вызвать EditorApplication.Exit с ненулевым кодом. Если вы хотите передать параметры, то вы можете добавить их в командную строку и получить их внутри метода используя System.Environment.GetCommandLineArgs.
-exportPackage Экспортирует пакет по заданному пути (или набору путей). exportAssetPath это папка (относительно корневой папки Unity проекта), куда будет проводиться экспорт из Unity проекта и exportFileName это имя пакета. На данный момент, эта опция позволяет экспортировать только все папки за раз. Это команда обычно используется вместе с аргументом -projectPath .
-force-d3d9 (Windows only) Принуждает редактор использовать Direct3D 9. Это и так делается по умолчанию, поэтому обычно нет причин использовать эту команду.
-force-d3d11 (Windows only) Принуждает редактор использовать Direct3D 9. Это и так делается по умолчанию, поэтому обычно нет причин использовать эту команду.
-force-glcore (Windows only) Make the editor use OpenGL 3/4 core profile for rendering. The editor tries to use the best OpenGL version available and all OpenGL extensions exposed by the OpenGL drivers. If the platform isn’t supported, Direct3D is used.
-force-glcoreXY (Windows only) Similar to -force-glcore but request a specific OpenGL context version. Accepted values for XY: 32, 33, 40, 41, 42, 43, 44 or 45.
-force-gles (Windows only) Make the editor use OpenGL for Embedded Systems for rendering. The editor tries to use the best OpenGL ES version available and all OpenGL ES extensions exposed by the OpenGL drivers.
-force-glesXY (Windows only) Similar to -force-gles but request a specific OpenGL ES context version. Accepted values for XY: 30, 31 or 32.
-force-clamped (Windows only) Used together with -force-glcoreXY, but it prevents checking for additional OpenGL extensions allowing to run between platforms with the same code paths
-force-free Make the editor run as if there is a free Unity license on the machine, even if a Unity Pro license is installed.
-importPackage Импортирует выбранный пакет. Окно импорта показываться не будет.
-logFile Задаёт, куда редактор, или приложение будет записывать логи.
-nographics При работе в режиме batсh, вообще не инициализирует графическое устройство. Это позволяет запустить автоматизированные рабочие процессы на устройствах, у которых даже нет графического процессора (автоматизированные рабочие процессы работает только тогда, когда окно редактора выделено, иначе вы не сможете отправлять симулируемые команды ввода).
-password The password of the user — needed when activating. This option is new in Unity 5.1.
-projectPath Открыть проект по заданному пути.
-quit Закрывает редактор Unity после того, как другие команды были исполнены. Учтите, что из-за этого сообщения об ошибках могут быть скрыты (однако они появятся в файле Editor.log).
-returnlicense Return the currently active license to the license server. Please allow a few seconds before license file is removed, as Unity needs to communicate with the license server. This option is new in Unity 5.0.
-runEditorTests Run editor tests from the project. This argument requires projectPath and it’s good to run it with batchmode argument. quit is not required as the editor will automatically close down after the run is finished.
-serial Activates Unity with the specified serial key. It is recommended to pass “-batchmode -quit” arguments as well, in order to quit Unity when done, if using this for automated activation of Unity. Please allow a few seconds before license file is created, as Unity needs to communicate with the license server. Make sure that license file folder exists, and has appropriate permissions before running Unity with this argument. In case activation fails, see the Editor.log for info. This option is new in Unity 5.0.
-silent-crashes Don’t display crash dialog.

Пример использования

// C# example using UnityEditor; class MyEditorScript < static void PerformBuild () < string[] scenes = < "Assets/MyScene.unity" >; BuildPipeline.BuildPlayer(scenes, . ); > > // JavaScript example static void PerformBuild () < string[] scenes = < "Assets/MyScene.unity" >; BuildPipeline.BuildPlayer(scenes, . ); > 

Следующая команда запускает Unity в режиме batch, исполняет метод MyEditorScript.MyMethod , а затем закрывается, после выполнения.

Windows: C:\program files\Unity\Editor\Unity.exe -quit -batchmode -executeMethod MyEditorScript.MyMethod

Mac OS: /Applications/Unity/Unity.app/Contents/MacOS/Unity -quit -batchmode -executeMethod MyEditorScript.MyMethod

Следующая команда запускает Unity в режиме batch и производит обновление с сервера ассетов используя заданный путь к проекту. После завершения загрузки и импорта всех ассетов с сервера ассетов, запускается метод. После завершения исполнения метода, Unity автоматически закрывается.

/Applications/Unity/Unity.app/Contents/MacOS/Unity -batchmode -projectPath ~/UnityProjects/AutobuildProject -assetServerUpdate 192.168.1.1 MyGame AutobuildUser l33tpa33 -executeMethod MyEditorScript.PerformBuild -quit

Аргументы командной строки для самостоятельного приложения Unity

These should only be used under special circumstances, or when directed by Support.

-enableIncompatibleAssetDowngrade Use when you have content made by a newer, incompatible version of Unity, that you want to downgrade to work with your current version of Unity. When enabled, Unity will present you with a dialog asking for confirmation of such a downgrade if you attempt to open a project that would require it. This procedure is unsupported and highly risky, and should only be used as a last resort.

Аргументы командной строки для самостоятельного приложения Unity

Приложения собранные в Unity так же воспринимают некоторые аргументы командной строки:

-adapter N (Windows only) Позволяет игре работать в режиме full-screen на другом дисплее. Переменная N указывает на графический адаптер Direct3D. В большинстве случаев, есть связь типа “один-на-один” между адаптерами и видеокартами. На картах с мультиголовной поддержкой (они могут управлять несколькими мониторами одной картой) каждая “голова” может быть собственным адаптером.
-batchmode Запускает игру в “безголовом” режиме. Игра не будет ничего отображать или реагировать на действия пользователя. Это, по большей части, полезно для запуска серверов сетевых игр.
-force-d3d9 (Windows only) Принуждает игру использовать Direct3D 9. Это так и делается по умолчанию, поэтому обычно нет причин использовать эту команду.
-force-d3d9-ref (Windows only) Принуждает игру запуститься используя программный Direct3D “Reference” рендерер. The DirectX SDK должен быть установлен, чтобы эта команда работала. Это, по большей части, полезно для собирания автоматизированных тестовых единиц, где вы захотите убедиться, что рендеринг происходит одинаково, независимо от того, какая видеокарта используется.
-force-d3d11 (Windows only) Принуждает игру использовать для рендеринга Direct3D 11.
-force-d3d11-no-singlethreaded Включает DirectX 11.0 без указателя D3D11_CREATE_DEVICE_SINGLETHREADED
-force-glcore (Windows only) Make the editor use OpenGL core profile for rendering. The editor tries to use on the best OpenGL version available and all OpenGL extensions exposed by the OpenGL drivers. If the platform isn’t supported, Direct3D is used.
-force-glcoreXY (Windows only) Similar to -force-glcore but request a specific OpenGL context version. Accepted values for XY: 32, 33, 40, 41, 42, 43, 44 or 45.
-force-clamped (Windows only) Used together with -force-glcoreXY, but it prevents checking for additional OpenGL extensions allowing to run between platforms with the same code paths
-nographics При работе в режиме batсh, вообще не инициализирует графическое устройство. Это позволяет запустить автоматизированные рабочие процессы на устройствах, у которых даже нет графического процессора.
-nolog (Linux & Windows only) Не записывать логи. Обычно output_log.txt записан в папке *_Data рядом с исполняемым файлом игры, где печатается Debug.Log.
-popupwindow Окно будет создано как всплывающее окно (без рамки).
-window-mode mode (Windows only) Overrides the default behavior of the Fullscreen Mode setting. Possible options are: exclusive, borderless.
-screen-fullscreen Заменяет стандартную ширину экрана. Это должно целое число из диапазона поддерживаемого разрешения.
-screen-height Заменяет стандартную высоту экрана. Это должно целое число из диапазона поддерживаемого разрешения.
-screen-width Заменяет стандартную ширину экрана. Это должно целое число из диапазона поддерживаемого разрешения.
-screen-quality Заменяет стандартное качество изображения. Пример использования: /path/to/myGame -screen-quality Beautiful
-show-screen-selector Forces the screen selector dialog to be shown.
-single-instance (Linux & Windows only) Позволяет запускать только одну копию игру одновременно. Если другая копия уже запущена, то повторный запуск с -single-instance просто переключит внимание на уже существующую копию.
-parentHWND (Windows only) Embeds Windows Standalone application into another application, you have to pass parent application’s window handle to Windows Standalone application. See this example EmbeddedWindow.zip for more information.

Аргументы командной строки приложений Windows Store

По умолчанию, приложения Windows Store не принимают аргументы командной строки, так что, чтобы применить их, вам надо вызвать специальную функцию из App.xaml.cs/cpp или App.cs/cpp. Например,

appCallbacks.AddCommandLineArg("-nolog"); 

| | | |:—|:—| |-nolog| Не записывает UnityPlayer.log.| |-force-driver-type-warp| Включает DirectX 11.0 WARP device (Больше информации http://msdn.microsoft.com/en-us/library/gg615082.aspx)| |-force-d3d11-no-singlethreaded| Включает DirectX 11.0 без указателя D3D11_CREATE_DEVICE_SINGLETHREADED| |-force-gfx-direct| Включает одно-потоковый рендеринг. | |-force-feature-level–9–1| Включает DirectX 11.0 уровня функционала 9.1.| |-force-feature-level–9–2| Включает DirectX 11.0 уровня функционала 9.2.| |-force-feature-level–9–3| Включает DirectX 11.0 уровня функционала 9.3.| |-force-feature-level–10–0| Включает DirectX 11.0 уровня функционала 10.0.| |-force-feature-level–10–1| Включает DirectX 11.0 уровня функционала 10.1.| |-force-feature-level–11–0| Включает DirectX 11.0 уровня функционала 11.0.| …в то время, как в Windows вам надо ввести …в то время, как в Windows вам надо ввести … в командую строку. On PC you can use Command prompt: …в то время, как в Windows вам надо ввести

Опции

Вам следует сделать этот вызов до функции appCallbacks.Initialize*() .

Полноценная игра, сделанная мною в обычной windows консоли

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

Игровой движок

Итак, начнем с того как игра устроена в корне, и какова ее идея работы.

Сначала я определился с тем, как будет выводится игровой мир в консоль. Понял, что для вывода игровых объектов, нам нужен список, который хранит в себе другие списки, которые хранят в себе символы, которые в последующем выводятся на игровое поле циклом for .

Вот таким кодом:

for line_words in OUTPUT_IMAGE: for word in line_words: print(word, end="") print("\n", end="") 

Здесь мы рисуем все символы из списка, и переходим на новую строку, чтоб нарисовать следующий список символов.

Вот так выглядит переменная, которая хранит списки символов:

image

Тут cразу мы получаем решение как нам выводить по X и Y объекты, мы теперь можем указывать:

X — символ в списке
Y — список в котором содержится X
Тем самым нарисовать на поле какой-нибудь символ. Это мы будем использовать при рисовании игровых объектов.

Можем попробовать нарисовать на поле «мяч», подставив на место X и Y букву «O».

Для этого напишем такой код:

import os OUTPUT_IMAGE = [ [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",], [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",], [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",], [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",], [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",], [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",], [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",], [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",], [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",], [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",], [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",], ] OUTPUT_IMAGE[4][6] = "O" os.system("cls||clear") for line_words in OUTPUT_IMAGE: for word in line_words: print(word, end="") print("\n", end="") 

image

И вот, мы нарисовали объект на нашем игровом поле. Правда координаты X и Y получились не классическими. Во первых, мы указываем сначала Y, потом X, что не совсем по классике, во вторых, координата Y должна увеличиваться чтоб поднять объект, у нас же наоборот, она должна уменьшатся.

График X и Y в игре:

image

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

Теперь мы можем попробовать перемещать наш объект по игровому полю, т.е. создавать движение.

Нам понадобится очищать консоль, для того чтобы стирать старую картинку игрового поля.
Это мы сделаем командой:

os.system("cls||clear") 

Также, нам понадобится переопределять переменную OUTPUT_IMAGE , для того чтобы очищать все ранее нарисованные в игровом поле объекты.

Также нам все это нужно будет поместить в while True .

Добавим в while True функцию time.sleep(1) , для того чтобы ограничить FPS.

И вот, код нарисовался на глазах:

from time import sleep from os import system OUTPUT_IMAGE = [ [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",], [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",], [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",], [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",], [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",], [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",], [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",], [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",], [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",], [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",], [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",], ] x = 0 y = 0 while True: sleep(1) system("cls||clear") OUTPUT_IMAGE[y][x] = "O" for line_words in OUTPUT_IMAGE: for word in line_words: print(word, end="") print("\n", end="") y += 1 x += 1 OUTPUT_IMAGE = [ [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",], [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",], [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",], [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",], [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",], [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",], [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",], [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",], [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",], [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",], [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",], ] 

Теперь у нас есть возможность распределять объекты по полю.

Правда эти объекты слишком примитивы, и надо бы научится рисовать сложные объекты по типу игроков, домов, еды…

Для того чтобы нарисовать сложный объект, нам нужно понять и придумать, как нарисовать объект указав лишь один раз его X и Y.

Для этого нам понадобится функция, которая принимает картинку (символы), X, Y;

def SetImage(image: str, x: int, y: int): pass 

Теперь нам нужно ее реализовать. Для этого нужно решить, как нарисовать изображение, которое растягивается по оси X и Y, я придумал так:
рисовать объект разделяя его на символы, и как только встретится символ «\n», прибавить ось Y.

Ось Y как мы уже говорили неправильная, перевернутая наоборот, поэтому к ней мы прибавляем чтобы опустить объект.

Пример изображения который рисуется по моему принципу:

image = " O\n'|'\n |"#игрок 

Теперь давайте это опишем в нашей функции:

def SetImage(x: int, y: int, image: str): x_start = x x = x y = y for word in image: if word == "\n": x = x_start y += 1 else: x += 1 try: OUTPUT_IMAGE[y][x] = word except IndexError: break 

Добавим try: except() для того чтобы небыло ошибок если объект имеет X и Y слишком мальенькие или слишком большие.

x_start Это X, с которого нужно начинать рисовать при увеличении Y (при символе «\n»)

Теперь мы можем использовать нашу функцию, падать в нее X и Y, и картинку которую нужно рисовать:

from time import sleep from os import system OUTPUT_IMAGE = [ [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",], [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",], [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",], [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",], [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",], [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",], [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",], [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",], [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",], [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",], [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",], ] def SetImage(x: int, y: int, image: str): x_start = x x = x y = y for word in image: if word == "\n": x = x_start y += 1 else: x += 1 try: OUTPUT_IMAGE[y][x] = word except IndexError: break while True: sleep(1) system("cls||clear") SetImage(x=3,y=4,image=" O\n'|'\n |") for line_words in OUTPUT_IMAGE: for word in line_words: print(word, end="") print("\n", end="") OUTPUT_IMAGE = [ [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",], [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",], [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",], [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",], [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",], [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",], [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",], [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",], [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",], [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",], [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",], ] 

И вот что у нас получилось:

image

абсолютно также как и шарик который мы рисовали, его можно двигать по оси X и Y.

from time import sleep from os import system OUTPUT_IMAGE = [ [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",], [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",], [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",], [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",], [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",], [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",], [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",], [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",], [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",], [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",], [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",], ] px = 0 py = 0 def SetImage(x: int, y: int, image: str): x_start = x x = x y = y for word in image: if word == "\n": x = x_start y += 1 else: x += 1 try: OUTPUT_IMAGE[y][x] = word except IndexError: break while True: sleep(1) system("cls||clear") SetImage(x=px,y=py,image=" O\n'|'\n |") for line_words in OUTPUT_IMAGE: for word in line_words: print(word, end="") print("\n", end="") px += 1 py += 1 OUTPUT_IMAGE = [ [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",], [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",], [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",], [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",], [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",], [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",], [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",], [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",], [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",], [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",], [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",], ] 

И вот, у нас уже двигает игрок по карте.

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

Для начала нам нужно сделать функцию получение широты и высоты объекта, для того чтобы расчитать его хитбокс.

Итак, функцию я решил сделать по такой логике:

X — хитбокс объекта по X ширине, это самое больше количество символов между знаками «\n» в картинке
Y — хитбокс по Y это число символов «\n» в картинке

По этой логике не сложно сделать функцию, которая принимает картинку, считает у нее все символы между «\n», и выбирает из этого самое больше число символов — получилась широта.
И если посчитать символы «\n», как я уже написал — получится высота.

Функция получилась такой:

def GetSizeObject(img: str): w = 0 weights = [] h = [word for word in img if word == "\n"] for word in img: if word == "\n": weights.append(w) w = 0 else: w += 1 try: return except ValueError: return

Зачем здесь ValueError except?
Он здесь чтобы предотвратить ошибку при запуске игры.

Итак, давайте нарисуем нашего игрока, и вычислил его ширину и длину.

код с рисовкой и вычислением широты и высоты

from time import sleep from os import system OUTPUT_IMAGE = [ [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",], [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",], [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",], [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",], [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",], [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",], [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",], [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",], [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",], [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",], [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",], ] px = 3 py = 3 def SetImage(x: int, y: int, image: str): global OUTPUT_IMAGE x_start = x x = x y = y for word in image: if word == "\n": x = x_start y += 1 else: x += 1 try: OUTPUT_IMAGE[y][x] = word except IndexError: break def GetSizeObject(img: str): w = 0 weights = [] h = [word for word in img if word == "\n"] h.append(1) for word in img: if word == "\n": weights.append(w) w = 0 else: w += 1 try: return except ValueError: return player_image = " O\n'|'\n |" def draw(): global OUTPUT_IMAGE sleep(1) system("cls||clear") for line_words in OUTPUT_IMAGE: for word in line_words: print(word, end="") print("\n", end="") OUTPUT_IMAGE = [ [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",], [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",], [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",], [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",], [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",], [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",], [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",], [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",], [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",], [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",], [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",], ] while True: SetImage(x=px,y=py,image=player_image) print(GetSizeObject(img=player_image)) draw() 

Ура! у нас есть функция вычисления широты и высоты, теперь нам предстоит сделать функцию вычисления хитбокса и столкновений объектов.

Вспомним что у нас система координат не классическая, поэтому классическую функцию увы использовать не сможем, придется делать свою. Я для этого я нарисовал на графике 2 квадрата, которые сталкиваются, и по этой картинке можно придумать условие по которому будет высчитано столкновение

Для простоты понимания я нарисовал хитбоксы, Т.Е. квадраты:

image

Логика на словах

Для вычисления мы подаем

x — X первого объекта
y — Y первого объекта
h — Высота первого объекта
w — Широта первого объекта
x2 — X второго объекта
y2 — Y второго объекта
h2 — Высота второго объекта
w2 — Широта второго объекта

если
y больше y2 — h2 + h и y — h меньше чем y2 + h2 — h
или же
y2 больше y — h + h2 и y2 — h2 меньше чем y + h — h2

Зачем проверять 2 раза?

Мы сделали проверку 2 раза, просто из-за того чтобы посмотреть на столкновение/не столкновение с разных объектов.

Объекты соприкасаются по оси Y

Дальше смотри соприкосновение по оси X, она такое же что и по оси Y, но вместо y — x , а вместо h — w .

x больше x2 — w2 + w и x — w меньше чем x2 + w2 — w

x2 больше x — w + w2 и x2 — w2 меньше чем x + w — w2

объекты соприкасаются по оси X

Логика в коде

Логика такая же как и на словах, только в функции:

def IsClash(x: int, y: int, h: int, w: int,x2: int, y2: int, h2: int, w2: int): if (y >= y2 - h2 + h and y - h = y - h + h2 and y2 - h2 = x2 - w2 + w and x - w = x - w + w2 and x2 - w2  

Функция возвращает True если объекты соприкасаются, и False если нет.

Я нарисовал дополнительно куб на нашем игровом поле, для того чтобы игроку было с кем сталкиваться.

И попробовал как работает функция высчета столкновения.

Вот игрок соприкасается и кубом:

image

А вот нет соприкасаются:

image

Код соприкосновения

Это полный код соприкосновения/не соприкосновения:

from time import sleep from os import system OUTPUT_IMAGE = [ [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",], [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",], [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",], [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",], [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",], [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",], [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",], [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",], [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",], [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",], [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",], ] def SetImage(x: int, y: int, image: str): global OUTPUT_IMAGE x_start = x x = x y = y for word in image: if word == "\n": x = x_start y += 1 else: x += 1 try: OUTPUT_IMAGE[y][x] = word except IndexError: break def GetSizeObject(img: str): w = 0 weights = [] h = [word for word in img if word == "\n"] h.append(1) for word in img: if word == "\n": weights.append(w) w = 0 else: w += 1 try: return except ValueError: return def IsClash(x: int, y: int, h: int, w: int,x2: int, y2: int, h2: int, w2: int): if (y >= y2 - h2 + h and y - h = y - h + h2 and y2 - h2 = x2 - w2 + w and x - w = x - w + w2 and x2 - w2  

Теперь у нас все стартовые функции для игры, собственно их основе я писал свою игру.

Игра

Идея игры в такая:

Есть игрок, вокруг появляется еда, которую он вынужден собрать чтоб не умереть. В игре также присутствуют функции: поднять еду, положить в инвентарь, съесть ее из инвентаря, положить на пол предмет из инвентаря

Я начал с того что сделал игровой цикл в 3 строчки, это просто While True :

from time import sleep while True: sleep(0.1) 

Дальше я посчитал нужным, создать класс, в котором будут хранится все функции будующий объектов. Поэтому создал файл main.py и папку lib, в которую поместил файл lib.py в котором был класс игры. Т.Е. файлы игры выглядели так:

+----game | + -- | | -- main.py | \ --lib | +--lib.py -> class Game() | \ | +---

В дальнейшем я работал в основном с классом Game(), в main.py просто вызывал его, создавал стартовые объекты, запускал игру.

В классе game сделал функцию run(), которая заупускает игровой цикл. Также сделал функцию draw_all(), она стирает все прошлые объекты, рисует новые, и печатает на игровое поле.

И так выглядел класс:

from time import sleep class Game(): def __init__(self): self.OUTPUT_IMAGE = [] # здесь игровое поле def draw_all(self): for line_words in self.OUTPUT_IMAGE: for word in line_words: print(word, end="") print("\n", end="") def run(self): while True: self.draw_all() sleep(0.1) 

Добавил все основные функции, по типу set_image() , size_object() , is_clash() , и все те которые являются игровым движком, и которые я описал выше.

Сделал новую функцию create_object() и переменную self.OBJECTS , функцию create_object() я использую для создания объектов, она принимает параметры img , name , x , y , up , rigid , data .

img — картинка объекта
name — имя объекта (дом, трава, житель, еда и.т.п.)
x — X объекта
y — Y объекта
up — если этот параметр True, то объект рисуется над игроком, иначе игрок его перекрывает собой
rigid — твердость, игрок не может пройти через этот объект (еще не реализовано)
data — личные данные объекта, его личные характеристики

create_object()

Эта функцию которая сейчас у меня в игре:

def CreateObject(self,x: int, y: int, img: str, name: str = None, up: bool = False, rigid: bool = False, data: dict = <>): size_object = self.GetSizeObject(img=img) self.OBJECTS.append( ) 

На тот момент я уже добавил игрока, дом, траву, и жителя.

И решил использовать тот самый параметр в объекте up , использовать его в объекте Home , Т.Е. чтоб дом закрывал собой игрока. Для этого я сделал функцию CheckAll(), циклом for проходился по всем объектам, и рисовал их на исходящей картинке, Т.Е. использовать функцию SetImage(x: int, y: int, img:str), подавая в нее X и Y объекта, и картинку.

Тем самым рисовал объекты которые мог закрыть собой игрок. В этом же цикле я объявил список up_of_payer_objects , и если у объекта стоял up=True, то я добавлял его в список, не рисуя его на поле. После рисовал самого игрока, и только этого я проходил циклом for по объектам в up_of_payer_objects, рисуя их, тем самым они были над игроком.

def CheckAll(self): up_of_payer_objects = [] for object_now in range(len(self.OBJECTS)): if object_now["up"]: up_of_payer_objects.append(object_now) continue self.SetImage(x=object_now["x"],y=object_now["y"],image=object_now["img"]) 

Дальше я занялся движением игрока. Для этого я создал его как отдельный объект, который не находится в списке self.OBJECTS , но который хранится в переменной self.PLAYER .

Все его параметры, по типу X , Y , img , и.т.п. получить можно с помощью ключей, проще говоря это словарь (dict). С таким игроком и объектами уже можно было работать, двигать, вычислить столкновения. Я начал с движения.
Начал создавать движение с того что сделал функцию CheckKeysObjects(), которая отвечает за отслеживание нажатия клавиш, и которую я вызываю в функции CheckAll() в самом начале

def CheckAll(self): self.CheckKeysObjects() . 

Для отслеживания нажатий на клавиши я использовал библиотеку keyboard, и 4 переменные:

self.WALK_LEFT_PLAYER
self.WALK_RIGHT_PLAYER
self.WALK_UP_PLAYER
self.WALK_DOWN_PLAYER

И все оказалось просто, отслеживаем клавиши, и если нажата допустим d , то мы переменную self.WALK_RIGHT_PLAYER делаем True .

В самом начале функции объявляем все переменные в False , для того чтобы сбросить все прошлые результаты, а-то игрок не остановится.

CheckKeysObjects()

def CheckKeysObjects(self): #делаю все переменные в False, чтоб сбросить прошлые результаты self.WALK_LEFT_PLAYER = False self.WALK_RIGHT_PLAYER = False self.WALK_UP_PLAYER = False self.WALK_DOWN_PLAYER = False #а тут уже проверяю нажатия if keyboard.is_pressed("a"): self.WALK_LEFT_PLAYER = True elif keyboard.is_pressed("d"): self.WALK_RIGHT_PLAYER = True if keyboard.is_pressed("w"): self.WALK_UP_PLAYER = True elif keyboard.is_pressed("s"): self.WALK_DOWN_PLAYER = True 

После этого я в функции CheckAll() проверяю все перменные отвечающие за движение, узнаю, куда двигается игрок.

Если какая-то в True , узнаем какая, и двигаем предмет в противоположную сторону.

Получившийся код движения

def CheckAll(self): self.CheckKeysObjects() # check moves up_of_payer_objects = [] for object_now in range(len(self.OBJECTS)): self.PLAYER["img"] = self.PLAYER["image_normal"] if self.WALK_LEFT_PLAYER: self.OBJECTS[object_now]["x"] += 1 elif self.WALK_RIGHT_PLAYER: self.OBJECTS[object_now]["x"] -= 1 if self.WALK_UP_PLAYER: self.OBJECTS[object_now]["y"] += 1 elif self.WALK_DOWN_PLAYER: self.OBJECTS[object_now]["y"] -= 1 

Да, мы двигаем предметы в противоположную сторону, для того чтобы создать иллюзию движения. Если игрок идет на право, то все предметы окружения смещаются налево.

Дальше я добавил еще предметов окружения, и занялся спавном еды, у игрока цель собирать еду, чтобы не умереть.

Для отсчета времени спавна еды, я использовал простой time.sleep() , и библиотеку threading — для того чтобы запустить 2 функции одновременно, спавн еды и основной игровой цикл. Функция спавна еды SpawnEat() — это просто функция которая при запуске генерирует на случайных местах еду, вызывая для каждой единицы еды функцию CreateObject() .

Также, как только я сделал функцию спавна еды, я сделал переменную у игрока self.PLAYER["hungry"] , это его голод, в самом начале он равен — 100 ед., его я буду уменьшать если игрок ходит и тратит энегрию (типа энергию, ее в игре нет) или увеличивать если игрок что-то съел.

Также я сделал функцию MinimizeHungry() , она вызывается каждые 5 секунд, и просто отнимает у игрока 2 единицы голода. Это я сделал для того чтобы игроку пришлось двигаться, а не стоять на месте.

И наконец в функции Eat() , эта функция которая вызывается в отдельном потоке от игрового цикла. Она проверяет не слишком ли много еды на карте, если еды больше 10 ед. то НЕ вызывает функцию SpawnEat() , если меньше 10 ед. то вызывает SpawnEat() .

Вот какой она получилась:

def Eat(self): while True: sleep(4) if len([i for i in self.OBJECTS if i["name"] == "meat"]) < 10: self.SpawnEat() sleep(1) self.MinimizeHungry() 

Функция Start() , для запуска основного цикла:

def Start(self): while True: self.CheckAll() self.DrawAll() sleep(0.01) 

И функция run() , которая запускает всю игру.

def run(self): proc1 = threading.Thread(target=self.Start) proc1.start() proc2 = threading.Thread(target=self.Eat) proc2.start() 

Сам процесс поедания, я реализовал просто в функции CheckAll() и CheckKeysObjects() . В CheckKeysObjects() я проверял не нажал ли игрок на кнопку E . Если нажал, то ставил переменную self.PRESS_E в True .

В цикле CheckAll() , проверял, не еда ли нынешний объект в цикле for , если еда то проверял не сталкивается ли с ним игрок, если сталкивается то проверял переменную self.PRESS_E , и если она в True то тогда просто удалял объект, и увеличивал голод, Т.Е. переменную self.PLAYER["hungry"] .

Вот так это в коде

for object_now in range(len(self.OBJECTS)): . if self.OBJECTS[object_now]["name"] == "meat": items_objects.append(object_now) is_clash = self.IsClash( x=self.OBJECTS[object_now]["x"], y=self.OBJECTS[object_now]["y"], h=self.OBJECTS[object_now]["h"], w=self.OBJECTS[object_now]["w"], x2=self.PLAYER["x"], y2=self.PLAYER["y"], h2=self.PLAYER["h"], w2=self.PLAYER["w"], ) if is_clash: if self.PRESS_E: try: self.PLAYER["hungry"] += self.HUNGRUY_ADD del self.OBJECTS[object_now] break except IndexError: pass 

Скажу наперед, это все мне нужно будет переписовать, когда я буду делать инветнарь

Делаю инвентарь

Итак, настало сложное, нам нужно сделать инвентарь.

Сложность в том что все предметы нужно будет отображать, хранить историю, удалять, ставить на пол объекты.

Я начал с того что добавил игроку новый ключ, это был self.PLAYER["inventory"] , там хранятся 4 яцчейки, вот в таком виде:

"inventory":< "0":, "1":, "2":, "3":, > 

цифры — просто номера ячеек.

status — этот ключ хранит в себе значение, пуста яйчейка или нет. Если пуста то «space», если же там есть предмет, то там хранится имя предмета.

name — хранит в себе имя предмета, оно будет использовано когда игрок будет класть предмет.

minimize_image — эта уменьшенная картинка предмета которая изображается в инвентаре игрока.

После, сделал новые проверки в нашем CheckKeysObjects() , при нажатии на X предмет будет бросаться на землю, и также при нажатии на кнопку E будет вызываться функция self.UseEat() , которую мы сейчас будем разбирать.

Итак, функция self.UseEat() представляет из себя проход по всем ячейкам инвентаря, в поисках еды, и если еда найдена, то она удаляется из инвентаря, и к голоду добавляется 10 единиц. Для удаление предмета из инвентаря я сделал функцию self.DestroyItem() , в которую подается индекс ячейки, и вся ячейкой просто становится по «дефолту» пустой и без ничего.

self.DestroyItem()

def DestroyItem(self,index_item: str): item = self.PLAYER["inventory"][index_item] self.PLAYER["inventory"][index_item] = self.PLAYER["default_inventory_item"](index_item) self.PLAYER["inventory_must_update"] = True return item

self.CheckKeysObjects()

def CheckKeysObjects(self): self.WALK_LEFT_PLAYER = False self.WALK_RIGHT_PLAYER = False self.WALK_UP_PLAYER = False self.WALK_DOWN_PLAYER = False if key("a"): self.WALK_LEFT_PLAYER = True elif key("d"): self.WALK_RIGHT_PLAYER = True if key("w"): self.WALK_UP_PLAYER = True elif key("s"): self.WALK_DOWN_PLAYER = True if key("f"): self.KEY_F = True else: self.KEY_F= False if key("e"): self.UseEat()

self.UseEat()

def UseEat(self): for inventory_item in range(len(self.PLAYER["inventory"])): if self.PLAYER["inventory"][str(inventory_item)]["name"] == "meat": if self.PLAYER["hungry"] + self.ADD_HUNGRY_COUNT < 100.0: self.PLAYER["hungry"] += self.ADD_HUNGRY_COUNT self.DestroyItem(index_item=str(inventory_item)) 

Дальше функция бросания предмета на землю.

Там впрочем ничего сложного, при нажатии на X вызывается функция self.QuitItem() , в ней проходит цикл for по всем ячейкам инвентаря, и если ключ ["status"] не ровняется "space" , то эту ячейку удаляем с помощью ранее рассмотренной функции self.DestroyItem() , и создаем объект на основе того что был в ячейке, X и Y ставит игрока, как бы он бросил его возле себя.

self.Quititem()

def QuitItem(self): for inventory_item in range(len(self.PLAYER["inventory"])): if self.PLAYER["inventory"][str(inventory_item)]["status"] != "space": self.CreateObject( img=self.PLAYER["inventory"][str(inventory_item)]["img"], x=self.PLAYER["x"], y=self.PLAYER["y"], name=self.PLAYER["inventory"][str(inventory_item)]["name"], data=self.PLAYER["inventory"][str(inventory_item)]["data"], ) self.DestroyItem(index_item=str(inventory_item)) break 

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

На этом все?

Нет, я собираюсь в игру добавить нейросеть, используя библиотеку который я писал на Python,
Собираюсь сделать взаимодействие игрока с NPC оснащенными нейросетью,
небольшой, но какой-нибудь сюжет, и также какие-то припасы для игрока типа брони, еды. предметов, возможность строить блоками.

Попробовать игру

Ее можно свободно скачать с моего GitHub, для запуска потребуется лишь Python3, и библиотека keyboard. Запускать нужно файл main.py .

Game directory/ru

Эта подстраница используется на странице Game directory для отображения содержимого на русский (Russian).

Если вы внесли изменения в языковую подстраницу, не забудьте очистить основную страницу, нажав на кнопку справа. В противном случае её содержимое не обновится.

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

Contents

  • 1 Что такое Директория Игры?
  • 2 Настройка текущей директории игры
    • 2.1 Указание директории игры через Source SDK Launcher
    • 2.2 Указание директории игры через VConfig
    • 3.1 Урок о "ERROR-Unable to find GameInfo" (Russian)

    Что такое Директория Игры?

    Директория игры - это папка, к которой обращаются инструменты при поиске игрового контента. К примеру, Hammer Editor должен знать, где найти материалы, чтобы отобразить их. Studiomdl будет компилировать модели, но он должен знать, куда их сохранять. Vrad.exe будет рассчитывать освещение на карте, но он должен знать, где найти prop модели, чтобы загрузить их и определить, каким образом они заслоняют свет. Ни один из SDK инструментов не будет работать, если не указать путь к игре.

    Примеры игровых директорий для основных Source игр:

    Half-Life 2 C:\Program Files\Valve\Steam\SteamApps\username\half-life 2\hl2
    Counter-Strike: Source C:\Program Files\Valve\Steam\SteamApps\username\counter-strike source\cstrike
    Half-Life 2: Deathmatch C:\Program Files\Valve\Steam\SteamApps\username\half-life 2 deathmatch\hl2mp

    В последующих примерах, мы будем упоминать игровую директорию как C:\Program Files\Valve\Steam\SteamApps\SourceMods\MyMod\Blasters . Это произойдет в том случае, если вы выбрали Create a Mod через SDK launcher, введя директорию: C:\Program Files\Valve\Steam\SteamApps\SourceMods\MyMod , и введя название мода: Blasters .

    Один из способов определить игровую директорию игры - найти файл GameInfo.txt . Папка, в которой он лежит, и есть директория игры. Если вы создали C:\Program Files\Valve\Steam\SteamApps\SourceMods\MyMod\Blasters , как в приведенном выше примере, то вы найдете файл C:\Program Files\Valve\Steam\SteamApps\SourceMods\MyMod\Blasters\GameInfo.txt . Как только вы нашли необходимую директорию, вам необходимо указать ее в Source SDK.

    Для получения информации о том, как создать GameInfo.txt для своей собственной модификации, и каким образом использовать контент, обратитесь к статье Структура Файла GameInfo.txt.

    Настройка текущей директории игры

    Необходимо устанавливать директорию активной игры, чтобы все инструменты знали, где находится игровой контент. Выпадающий список Current Game в Source SDK Launcher является основным способом указания активной директории игры.

    Указание директории игры через Source SDK Launcher

    Чтобы указать директорию активной игры через Source SDK Launcher:

    1. Дважды кликните на Source SDK в Steam.
    2. Далее вы должны выбрать правильную текущую игру (Current Game) в Source SDK Launcher.
    3. Теперь запустите один из инструментов SDK, например, Hammer Editor, дважды кликнув на нем.

    Установка директории активной игры через Source SDK Launcher.

    Указание директории игры через VConfig

    Приложение VConfig так же может быть использовано для указания директории активной игры. У него есть те же функции настройки текущей игры (Current Game) как и в SDK Launcher.

    Приложение vconfig.exe находится в директории \sourcesdk\bin .

    Для примера, если вы установили Steam в C:\Program Files\Valve\Steam , VConfig находится здесь:

    C:\Program Files\Valve\Steam\SteamApps\username\sourcesdk\bin\vconfig.exe

    Чтобы указать директорию активной игры через VConfig:

    1. Дважды кликните на приложение vconfig.exe .
    2. Далее вы должны выбрать правильную текущую игру (Current Game) из выпадающего списка.
    3. Нажмите OK.
    4. Теперь запустите один из инструментов SDK, например, Hammer Editor, дважды кликнув на нем.

    Установка директории активной игры через VConfig.

    Качестве альтернативы, вы можете указать директорию игры без запуска VConfig, и выбора игры через меню. Чтобы сделать это, просто запустите VConfig с параметром командной строки -game [путь] . Это может быть особенно полезно для указания игровой директории через .bat файл или другие автоматизированные процессы.

    Например, следующая команда установит директорию игры по умолчанию, в виде пути к Half-Life 2, без необходимости запускать VConfig:

    vconfig -game "C:\Program Files\Valve\Steam\SteamApps\username\half-life 2"

    Указание директории игры через командную строку

    Альтернативный способ указания директории игры - напрямую указать путь в командной строке при каждом запуске инструментов SDK. Все SDK инструменты поддерживают параметр командной строки -game . Преимущество указания директории игры через командную строку в том, что вы напрямую указываете инструментам, где должен быть найден игровой контент.

    Вот несколько примеров запуска инструментов, где вы указываете директорию игры в явном виде:

    %sourcesdk%\bin\studiomdl -game C:\Program Files\Valve\Steam\SteamApps\SourceMods\MyMod\Blasters mymodel.qc
    %sourcesdk%\bin\vrad -game C:\Program Files\Valve\Steam\SteamApps\SourceMods\MyMod\Blasters mymap
    %sourcesdk%\bin\hammer -game C:\Program Files\Valve\Steam\SteamApps\SourceMods\MyMod\Blasters

    Примечание: Если в пути есть пробелы, вы должны использовать кавычки:

    %sourcesdk%\bin\hammer -game "C:\Program Files\Valve\Steam\SteamApps\username\counter-strike source\cstrike"
    %sourcesdk%\bin\vrad -game "C:\Program Files\Valve\Steam\SteamApps\username\counter-strike source\cstrike" MyCstrikeMap

    Использование параметра командной строки -game позволяет работать над несколькими играми или модификациями одновременно, и для каждой использовать свою конфигурацию. Этого легко добиться, создав ярлыки для инструментов, записав параметр -game в свойствах ярлыка. Не забудьте, что если в пути есть пробелы, его нужно писать в кавычках.

    Создание ярлыка к инструменту с параметром -game для указания директории игры.

    Урок о "ERROR-Unable to find GameInfo" (Russian)

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *