В предыдущей части туториала мы установили фреймворк и выполнили его минимальную настройку.

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

Игра жизни Конвея

Я определенно должен был объяснить «Игру жизни Конвея» раньше, но лучше позже, чем никогда!

Создателем игры является Джон Хортон Конвей, игра не требует никаких действий со стороны игрока, по крайней мере, в ее классической версии, но вы всегда можете создать свои правила и вселенную, где возможно взаимодействие с клетками.

Во вселенной, которая в основном представляет собой двумерную сетку (x и y), созданные ячейки имеют два возможных состояния: живые (1) или мертвые (0). Каждый тик или эволюция каждая клетка взаимодействует со своими восемью соседями.

Ячейка использует следующие правила для расчета следующего поколения:

  1. Любая живая клетка с менее чем двумя живыми соседями умирает, как будто из-за недонаселения.
  2. Любая живая клетка с двумя-тремя живыми соседями живет до следующего поколения.
  3. Любая живая клетка с более чем тремя живыми соседями умирает, как бы от перенаселения.
  4. Любая мертвая клетка, имеющая ровно три живых соседа, становится живой клеткой, как бы путем размножения.

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

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

Объекты

На данный момент мы знаем правила нашей игры, и у нас есть начальная структура, состоящая из двух файлов main.lua и conf.lua. Мы можем начать организовывать наш код, для этого мы будем использовать объекты, сохраненные в отдельных файлах, каждый из которых выполняет свою конкретную работу.

Для этого мы можем использовать библиотеку под названием Classic, это модуль для создания объектов и классов в Lua, либо вы можете найти понравившуюся вам ООП-библиотеку.

Хотя в Lua нет классов или объектов, их все равно можно создавать без каких-либо библиотек. Я покажу оба способа создания объектов, первый — это чистый Lua, а второй — библиотека. Реализация Lua идет первой, так что не стесняйтесь пропустить ее и перейти ко второй части.

Приведенный ниже код вы можете написать отдельно от файла игры или в терминальной среде Lua, но для того, чтобы программировать на Lua отдельно от фреймворка LÖVE, вам нужно установить сам Lua, что довольно просто.

И, как всегда, код, написанный в руководстве, будет доступен в конце.

Lua-объект

Чтобы иметь собственную реализацию объекта, во-первых, вам нужно выделить таблицу с ее именем:

local Text = {}

после этого определите объект:

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

local sentence = Text:newObject()

Вызовите метод sayHello, который принимает строковый тип:

sentence:sayHello("Mikhail")

Результат будет: «Здравствуйте! Михаил, рад тебя видеть!»

Вы можете поиграть с объектом самостоятельно, напишите код выше в отдельном файле и вызовите его из терминала:

lua object.lua

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

Объект библиотеки

скачайте classic.lua, создайте новую папку, куда вы будете сохранять все библиотеки, которые будут использоваться в игре, и поместите туда файл.

Я создал папку libraries, в которой все мои библиотеки имеют свои собственные папки, потому что некоторые из них могут иметь более одного файла, в моем случае это полный путь к файлу classic.lua. будет:

~/love/GOF/libraries/classic/classic.lua

После того, как вы сохранили файл, загрузите его в игру, написав в main.lua вверху:

Object = require "libraries/classic/classic"

Где последний classic — это наш файл без расширения .lua, а предыдущие — папки, Object — это глобальная переменная, которую мы будем использовать для доступа к библиотекам. функционально, вы можете написать любое имя, которое вам нравится.

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

Внутри папки создайте файл Text.lua, который будет нашим файлом для объекта Text. Каждый созданный вами объект должен расширять Object из библиотеки Classic, чтобы мы могли обращаться с ним как с объектом, для этого напишите в начале файла:

Text = Object:extend()

Расширяя объект библиотеки, каждый файл в игре может иметь к нему доступ. После инициализации объекта создайте метод new объекта:

function Text:new()
    -- the body of the method
    -- available outside it
end

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

self.text = "Hello! "
self.greetingStr = ", glad to see you!"

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

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

Затем определите метод sayHello с аргументом name, внутри метода выведите на терминал желаемое предложение, используя локальные переменные и аргумент:

function Text:sayHello(name)
    print(self.text .. name .. self.greetingStr)
end

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

function Text:destroy()
    self.text = nil
    self.greetingStr = nil
end

Последний шаг перед запуском, нам нужно запросить наш объект в main.lua, запрос объекта аналогичен библиотекам после Classic пишем:

require "objects/Text"

Где objects — папка, а Text — файл Text.lua.

В функции love.load вызовите созданный нами объект:

Наконец, запустите игру LÖVE, в терминале вы должны увидеть два предложения с разными именами, или же вы можете использовать функцию LÖVE graphics.print для вывода текста на экран игры, для этого вам нужно удалить печать в методе sayHello(name) и замените его на return:

return self.text .. name .. self.greetingStr

и в функции love.draw напечатайте наш текст:

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

Комната

Что такое комната?

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

Размер комнаты может быть больше, чем экран окна, в этой ситуации мы можем выбрать поведение нашей камеры, она также фиксирована в одном месте, что означает, что мы видим только внутри границы окна и можем изменить вид камеры, когда игрок нажимает на триггер. — выходит за границу окна — или следим за нашим игроком, конечно, есть много других комбинаций камеры, там мы ограничены только фантазией программиста, например игра «Рыцарь-лопатка»

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

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

Зачем вам нужна комната?

  1. Для загрузки и выгрузки экрана с объектами внутри них
  2. Переключение между сценами

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

  1. Кнопки смены комнаты на игру или настройки,
  2. Кнопка выхода из игры,
  3. Фоны,
  4. Звуки,
  5. Спецэффекты и многое другое.

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

Оформить комнату

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

Внутри папки rooms создайте Menu.lua, объект, который будет нашей комнатой главного меню в игре, откуда мы можем перейти дальше в комнату игры или опций.

В Menu.lua определите Menu как объект:

Menu = Object:extend()

Стандартный макет для всех наших объектов в рамках LÖVE будет иметь методы new, update, draw, и destroy:

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

love.graphics.setBackgroundColor(1,1,1)

Это изменит весь фон экрана на белый, затем, чтобы наш круг был в центре экрана, определяем две локальные переменные с шириной и высотой окна:

self.screenWidth = love.graphics.getWidth()
self.screenHeight = love.graphics.getHeight()

graphics.getWidth и getHeight берут видимую ширину и высоту окна в пикселях — без каких-либо полос панели — в момент вызова.

Теперь при рисовании круга в методе draw измените цвет круга, я выбираю синий:

love.graphics.setColor(0.258, 0.525, 0.956)

После изменения цвета создайте круг с помощью graphics.circle:

love.graphics.circle("fill", self.screenWidth / 2, self.screenHeight / 2, 100)

Первый аргумент — определяет метод рисования объекта геометрии, fill хорошо заполнит фигуру цветом, а line нарисует только границу фигуры.

Второй и третий аргументы — это x и y позиция круга, я беру половину ширины и высоты окна, чтобы круг был в центре экрана.

Четвертый аргумент — это радиус окружности.

Чтобы увидеть наш круг, нам нужно изменить комнату с main.lua на menu.luamain.luaна самом деле не комната , но фреймворк LÖVE стартует с этого файла, так что по сути это комната и не одновременно — для смены комнаты мы можем вызвать ее прямо в функции load, но это не так интересно , вместо этого мы делаем клавишу F1 триггером смены комнаты.

В main.lua требуется наш объект Menu:

require "objects/rooms/Menu"

Кстати, все предыдущие эксперименты с объектом Text вы можете удалить, поэтому в конце у вас будет пустой main.lua с двумя требованиями, Object и Меню.

Теперь создадим новую функцию love.keypressed(key):

function love.keypressed(key)
end

Прописать в функцию условие «если», если была нажата F1 — вызвать объект Меню, и вывести сообщение в терминал; Позже я объясню, зачем нам нужно это сообщение:

if key == "f1" then
    Menu()
    print("The Menu room created!")
end

Прямо сейчас запустите игру и посмотрите на результат, это должен быть сначала черный экран, который является нашей комнатой Шредингера, который после нажатия кнопки «F1» изменится на белый экран, это наша комната Меню, но подождите, где круг?

Что пойдет не так? Когда вы вызываете Меню, оно загружается, но только с помощью метода new с двумя переменными и перерисовкой фона, все остальные методы вне new не вызывается, для исправления напишем функцию смены комнаты с вызовом всех ее методов.

Для этого вернитесь в основной файл и определите глобальную переменную current_room со значением nil в файле love.load. :

current_room = nil

Эта переменная будет содержать строковое значение текущей активной комнаты. В love.update напишите условие, что если текущая комната существует, то активируйте для нее метод update:

if current_room then
    current_room:update(dt)
end

В love.draw то же активируем метод draw, если у нас есть комната:

if current_room then
    current_room:draw()
end

Почти готово! Определите новую функцию gotoRoom с аргументами room_type и три точки (…),что сделает ее variadic function — обычная функция, но вместо заданного количества аргументов, эта может принимать любые — мы не будем использовать дополнительные аргументы, вместо этого мы будем использовать Vararg в определении current_room этого способ, которым мы можем вызывать методы объекта:

function gotoRoom(room_type, ...)
end

Внутри функции определите переменную current_room с аргументом room_type, который является строковым типом наших комнат, например, Menu.lua будет просто Меню или Game.lua — Игра и так далее:

current_room = _G[room_type](...)

_G в Lua — это таблица с глобальными переменными, их все можно увидеть, напечатав в терминале:

for n in pairs(_G) do print(n) end

а если добавить [room_type] то вы увидите все наши методы комнаты:

for n in pairs(_G[room_type]) do
    print(n)
end

Наконец, у нас все еще есть метод destroy, который пока не используется, но все же его можно использовать в будущем:

Функция gotoRoom после всех этих манипуляций должна выглядеть так:

Последний шаг — заменить непосредственный вызов объекта Menu созданной нами функцией:

gotoRoom("Menu")

А пока вы можете изменить комнату и увидеть круг, который раньше не отображался, попробуйте!

В момент создания меню номера, я написал печатное сообщение и обещаю, что объясню, что ему нужно, время пришло! В игре смените комнату на меню как обычно, но сейчас! Нажимайте клавишу снова и снова, каждый раз, когда вы нажимаете клавишу, вы будете получать одно и то же сообщение в терминале, хотя ничего не происходит, и вы больше не находитесь в главной комнате, так почему вы все еще есть?

Ответ прост: каждый объект, который у нас есть, будет скомпилирован в файле main.lua, таким образом, функции в файле будут иметь глобальное влияние на каждый объект.

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

Чтобы исправить проблему нажатия клавиш, SSYGEN написал небольшую библиотеку под названием Boipushy, которая поможет нам, как использовать и файл библиотеки вы можете найти здесь, теперь давайте перепишем взаимодействие с кнопку, скачать input.lua, поместить в папку с библиотекой и потребовать от main.lua:

Input = require "libraries/boipushy/input"

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

Чтобы изменить ввод с keypressed на библиотеку, в love.load напишите:

Здесь мы объявляем объект Input в глобальную переменную input, затем к клавише F1 привязываем изменение действия комнаты и печать сообщения.

Как пишет SSYGEN в своей документации к библиотеке, вы можете объявить объект Input для других игроков, например, переменная input будет использоваться для основной игрок, при этом:

для второго игрока.

Не забудьте полностью удалить функцию love.keypressed(key), затем в menu.lua добавить в метод new функцию отвязки для ключ, который мы используем:

input:unbind("f1")

Таким образом, вы удаляете весь связанный функционал ключа, вот и все! Разбейте кнопку в любое время, когда вам нравится, чтобы увидеть результат.

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

Первая половина файлов туториала: object.lua, main.lua, Text.lua.

Вторая половина: main.lua, Menu.lua.

TL;DR

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

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

Прежде чем перейти к следующему

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

Добрый день!