Garry's Mod

Garry's Mod

65 ratings
Полный курс лекций (туториалов) по GLua. Лекция 2
By Stanislav Volkovich
На протяжении около одного года я занимался реализацией проекта гейммода для GMod, однако в связи с техническими проблемами обработки 3D моделей движком Source (я использовал неоригинальные для Source модели, а конвертированные из Gamebryo), он не был завершён. Когда я начинал эту деятельность, я не был осведомлён ни о том, как Source обрабатывает 3D модели, ни о том, что такое движок или гейммод вообще. В связи с тем что разработка гейммода закончилась ничем, я бы хотел поделиться полученными мной в процессе разработки знаниями с теми, кто был бы заинтересован в создании аддонов и гейммодов.

Данный курс позволит с нуля освоить программирование на Lua в целом и на GLua в частности, теорию функционирования Source и Garry’s Mod, что позволит создавать любые аддоны и гейммоды в рамках возможностей движка по желанию прошедшего курс.

Курс будет состоять из 9 лекций:
I - базовый уровень:
1 - переменные, операторы, функции - https://steamcommunity.com/sharedfiles/filedetails/?id=2236011000.
2 - условия, циклы, таймеры - здесь.
3 - классы, объекты - https://steamcommunity.com/sharedfiles/filedetails/?id=2249794255.
4 - контекст, сеть - https://steamcommunity.com/sharedfiles/filedetails/?id=2263853920.
II - стандартный уровень:
5 - основные классы, представляющие объекты в мире, Source и Garry’s Mod, цели и методы взаимодействия с ними - https://steamcommunity.com/sharedfiles/filedetails/?id=2278730936&preview=true;
6 - основные классы Garry’s Mod, представляющие объекты на экране (интерфейс).
7 - создание класса Garry’s Mod с помощью Entity.
8 - создание класса Lua с помощью MetaTable.
III - продвинутый уровень:
9 - создание аддона на примере аддона для смены дня и ночи.
10 - создание гейммода на примере гейммода с противостоянием двух команд.

Лекции будут публиковать по мере написания не реже одного раза в неделю за исключением лекций 8 и 9. Каждая лекция состоит из общей теории, теории применения в Lua и GLua, примеров, заданий. Вы можете задать в комментариях любые вопросы относительно контента лекции.

То, что я сделал с помощью GLua, вы можете увидеть по ссылке: https://www.youtube.com/watch?v=NxVDBptT2to.

Обратная связь. Пожалуйста, не оскорбляйте меня или любых других пользователей в комментариях в руководству. Пожалуйста, не пишите мне с предложениями сходить на прогулку, поиграть в ГМод, купить гараж. Если я захочу сделать что-то из вышеперечисленного, я обращусь в соответствующие инстанции, а не буду писать руководства по программированию для ГМода. Я буду рад ответить на ваши вопросы по программированию в комментариях и личных сообщениях и обсудить что бы то ни было, связанное с этой темой, но не более того.
2
   
Award
Favorite
Favorited
Unfavorite
Часть 1. Условия
Условия - это проверки определённых переменных в формате Bool на то, являются они true или false с выполнением соответствующих true или false операций.

Элемент условия
Описание
if FirstCondition
Первая переменная, которая будет проверяться.
then Operation1() Operation2()...
Операция или операции в случае, если проверяемая переменная равна true.
elseif SecondCondition then Operation1() Operation2()...
Операция или операции в случае, если вторая проверяемая переменная равна true.
else Operation1() Operation2()...
Операция или операции в случае, если ни одна из проверяемых переменных не равна true.
end
Конец условия.

Обязательными частями условия являются только части if FirstCondition then Operation() end. Дополнительными частями являются elseif SecondCondition then Operation(), который может повторяться неограниченное количество раз, и else, который может повторяться только один раз и должен находиться в самом низу условия. Условия проверяются последовательно, и в том случае, если одно из них верно, все идущие ниже не проверяются.
Необходимо также уточнить, что переменные, в связи с сравнительным оператором, возвращают переменную в формате Bool. Например, A = B возвращает true или false в зависимости от значений A и B.
В постановке условия также допустимо использование логических операторов. Например, A AND B возвращает true, если A и B - true, и false, если A или B - false.

Пример постановки условия для решения теоретической задачи:

if GameTimeInSeconds > 60 then print( “You are playing more than 1 minute.” ) end

В данном случае, условие сравнивает созданную в лекции 1 числовую переменную GameTimeInSeconds и число 60, и если GameTimeInSeconds больше, то печатает в консоль определённый текст.

Пример постановки условия для решения практической задачи:

if Player:LastHitGroup( ) == 1 then Player:Kill( ) end

В данном случае, Player - это Userdata, представляющая определённого игрока. LastHitGroup( ) - функция, применяемая к Userdata, возвращающая последнюю группу повреждений, получившую урон, в числовом формате. 1 - это голова. Kill - это функция, применяемая к Userdata Player, вызывающая смерть игрока (то есть отключение переход Physical Object для которого Player является Parent, в режим ragdoll, отключение возможности для Player правлять Physical Object). Таким образом, условие проверяет, получил ли определённый игрок урон в голову. Если да, то игрок умирает. Вам совершенно необязательно в настоящее время понимать, что такое Userdata и любые другие вопросы, не касающиеся темы лекции. Этот пример просто демонстрирует практическое использование условий для создания игрового процесса, что невозможно без обращения к Userdata.
Часть 2. Циклы
Циклы - это определённая последовательность действий, выполняющихся, пока выполняется определённое условие. Существует два цикла, а именно: for и while.

Элемент цикла
Описание
for i = 1, 2, 1
for - это начало цикла. i - это новая объявляемая локальная переменная со значением 1. Вместо i можно использовать любое другое имя, а вместо 1 - любое другое значение типа Number. Можно также использовать созданную заранее переменную. На этом значении i цикл начнётся. 2 - это значение i, на котором цикл завершится. 1 - это значение, которое будет прибавляться к i каждое выполнение цикла. Его можно не указывать, и тогда к i будет прибавляться один.
do Operation1() Operation2()...
do - это разделение между условием цикла и операцией или операциями цикла.
end
end - это конец цикла.
[/td]

Пример цикла for для решения теоретической задачи:

for i = 1, GameTimeInSeconds do print( “Game time in seconds is greater than or equals to “ .. i .. “.” ) end

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

Пример цикла for для решения практической задачи:

for i = 1, 2 do local Bar = vgui.Create( “ParameterBar” ) Bar:CenterHorizontal( 0.1 ) if i == 1 then Bar:CenterVertical( 0.8 ) Bar:SetValue( Player:Health( ) ) else Bar:CenterVertical( 0.9 ) Bar:SetValue( Player:Armor( ) ) end end

Данный цикл создаст два виджета гипотетического класса “Bar”, который программист создал для отображения в виде шкалы от - до 100 определённой переменной игрока. Оба виджета имеют один класс, и оба расположены на 0.1 по ширине экрану. Но один отображает здоровье, а другой - броню, один находится на 0.8 по высоте экрана, а другой - на 0.9. В данном случае, общего у виджетов немного, однако чем его больше, тем больше смысл использования цикла. Однако, если в этом случае программист захочет изменить положение обоих шкал по ширине экрана, он должен будет исправить одну переменную в Bar:CenterHorizontal( 0.1 ), а если бы оба виджета были созданы отдельно, то уже две.

Элемент цикла
Описание
while A
while - это начало цикла. A - это условие, переменная типа Bool.
do Operation1() Operation2()...
do - это разделение условия и операции или операций цикла.
end
end - это конец цикла.

Пример цикла while для решения теоретической задачи:

while GameTimeInSeconds == 60 do print( “Game time in seconds equals to 60.” ) GameTimeInSeconds = GameTimeInSeconds + 1 end

Данный цикл напечает в консоль “Игровое время в секундах равно 60” при условии, что оно действительно равно, а затем прибавит к игровому времени в секундах 1. То есть он будет выполнен всего 1 раз, и не отличается от условия if.

Пример цикла while для решения практической задачи:

local ID = 1 while Entities:GetObjects( )[ID] ~= nil do ID = ID + 1 end self:SetObjectID( tostring( ID ) )

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

В данном примере я использовал функцию из своего гейммода, где для того, чтобы снизить нагрузку на движок и обеспечить большую управляемость классами и объектами, я создаю аналоги движковых энтити в виде таблиц для обработки логики, а сами движковые энтити использую только для симуляции физики. Эта функция вызывается, когда новое энтити-таблица создаётся, и ему присваивается ID. Я рассмотрю эту логику в 7 и 9 лекциях, однако сейчас скажу: она почти нигде и никем не используется, где-то это оправданно, так как для аддона, добавляющего одно уникальное оружие, создавать свою систему классов и объектов, конечно же, не нужно, а где-то - нет, большому гейммоду, превращающему физическую песочницу в симулятор жизни или глобальную стратегию, жизненно, в целях оптимизации, необходима своя система классов и объектов.
Часть 3. Таймеры
Таймеры - это функции, выполняемые с определённой задержкой определённое количество раз. В GLua существует библиотека (то есть таблица определённых функций, сохранённых в одну переменную) timer, функции которой используются для управления таймерами.

Создание таймера
timer.Create( String_Name, Number_Delay, Number_Repetitions, function( ) Operation1 Operation2 ... end)

В данном случае, timer - это имя библиотеки, в которой находится функция. Create - это имя функции. String_Name - это имя таймера, оно должно быть уникальным. Если создать два таймера с одним именем, созданный последним перезапишет созданный первым. По имени осуществляется управление таймером через другие функции из библиотеки timer. Number_Delay - это задержка в секундах между повторениями. Number_Repetitions - это количество повторений. function( ) end - это объявление новой функции, которая и будет выполняться каждое повторение.

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

Удаление таймера
timer.Remove( String_Name )

В данном случае, String_Name - это имя, таймер с которым будет удалён.

Пример таймера для решения теоретической задачи:

timer.Create( “CountGameTimeInSeconds”, 1, 0, function( ) GameTimeInSeconds = GameTimeInSeconds + 1 end)

В данном случае, создаётся таймер с названием “CountGameTimeInSeconds”, задержкой - 1 и бесконечным количеством повторений. Каждое повторение он увеличивает переменную GameTimeInSeconds на 1, то есть считает игровое время в секундах.

Пример таймера для решения практической задачи:

timer.Create( self:GetObjectID( ) .. "FunctioningTimer", 1, 0, function( ) self:Functioning( ) end)

В данном случае, создаётся таймер с названием “FunctioningTimer”, но перед ним выносится ID создающего таймер объекта, чтобы каждый объект имел свой таймер, задержкой - 1 и бесконечным количеством повторений. Каждое повторение таймер вызывает функцию Functioning для объекта, создавшего таймер. Что находится в данном функции - неизвестно, и для каждого объекта она отличается. Но для игрока в жанре survival, там может находиться, например, уменьшение воды, пищи и сна.
Дополнительная литература
  1. Условия:
  2. Циклы:
  3. Таймеры:
Задание 2
Задача. Написать скрипт, создающий таймер, считающий игровое время в минутах, и каждую 60 минуту печатающий в консоль по 1 сообщению “One more hour” на каждый час, а затем сообщение: “Isn’t it time to stop?”

Ответ. Если задача решена верно, то после 2 часов игры, игрок увидит в консоли:
One more hour.
One more hour.
Isn’t it time to stop?

Примечание. Если Вы не хотите ждать 2 часа, чтобы проверить работоспособность скрипта, а я уверен, что Вы не хотите, можете, например, условно сократить время часа до 2 минут (или как либо ещё изменить единицы измерения так, чтобы время ожидания при тесте не было слишком велико) при проверке правильности выполнения задания.
17 Comments
Schweppes Jul 20, 2023 @ 2:32am 
Но вопрос остается открытым. Почему так?
Schweppes Jul 20, 2023 @ 2:30am 
UPD к моему предыдущему комментарию: Я переделал немного код просто поменяв while на if, после чего всё начало стабильно работать.

GameTimeInSeconds = 0

timer.Create("CountGameInMinutes", 1, 0, function()
GameTimeInSeconds = GameTimeInSeconds + 1
print(GameTimeInSeconds)

if GameTimeInSeconds == 12 then
print("Isn't it time to stop right there?")
end
end)
Schweppes Jul 20, 2023 @ 2:23am 
Здравствуйте, я написал код. Не знаю правильно ли я его написал, но вроде отчет времени работает. Вот только, когда наступает время написать "Isn't it time to stop right there?" Garry's Mod полностью зависает и перестает отвечать. Надеюсь на помощь.

GameTimeInSeconds = 0

timer.Create("CountGameInMinutes", 1, 0, function()
GameTimeInSeconds = GameTimeInSeconds + 1
print(GameTimeInSeconds)

while GameTimeInSeconds == 120 do
print("Isn't it time to stop right there?")
end
end)
NGDL Media Dec 19, 2021 @ 5:50am 
Куда делась 1-я лекция?
Solovei Empty Feb 4, 2021 @ 3:01am 
рандомный таймер с интервалами как в hammer editor
timer.Create( “id”-- персональный индитификатор таймера подобно имени переменных
1, -- задержка между повторами (в данном случаи минемальный интервал)
0, -- количество повторений. при числе 0 бесконечный таймер
function( )
timer.Stop( “id”) -- индитификатор таймера тод же что и timer.Create
timer.Simple(math.random(0,10),function() timer.Start('id') end)
--место для вашего кода
- где 10 + мин. интервал равен максимальному интервалу
end)
amnesia Oct 30, 2020 @ 1:45am 
Спасибо, заработало.
Stanislav Volkovich  [author] Oct 30, 2020 @ 1:23am 
Insxne, локальная функция SomeTimers объявлена (то есть создана), но нигде не вызывается. Необходимо вызвать функцию в самом конце с помощью кода: "SomeTimers( )". Также обрати внимание, что имя таймера следует писать в кавычках, чтобы там же была создана новая переменная типа String. В противном случае скрипт будет искать переменную Timer и Timer2, которые не существуют.
amnesia Oct 29, 2020 @ 1:06pm 
Привет.
Ошибок никаких не выдаёт, сообщение просто не отсылаются. Вот код

local function SomeTimers( )
timer.Create(Timer, 1, 2, function()
print("One more hour.")
end )
timer.Create(Timer2, 15, 1, function()
print("Isn’t it time to stop?")
end )
end
Stanislav Volkovich  [author] Oct 23, 2020 @ 10:06am 
Вышла четвёртая лекция: https://steamcommunity.com/sharedfiles/filedetails/?id=2263853920 .
Stanislav Volkovich  [author] Oct 6, 2020 @ 6:27am