Статья предназначается для всех любителей старой-доброй Total Annihilation и ее открытой реализации в виде SpringRTS + Balanced Annihilation.
Несмотря на то, что виджет Air Screen Keeper оказался, по большому счету, бесполезной затеей, на его примере ввиду небольшого размера можно отразить основные идеи построения расширений к играм на основе движка Spring.
Итак, суть виджета (т.е. расширения) — сообщить игроку в той или иной форме о том, что т.н. воздушный экран, состоящий из множества самолетов, выполняющих команду «патруль», а��акован врагом с земли. Обычно такие атаки в разгаре битвы (8 на 8 игроков) не очень заметны и можно легко прохлопать, как противник таким образом уничтожит до 70% самолетов, если отвлечься на что-то сиюминутное.
Поэтому в качестве заметного сообщения будем использовать маркер на карте, видимый только игроку, и будем ставить его в месте, где был атакован наш самолет. Кроме того, чтобы не заполонить маркерами все на свете, будем выставлять их с задержкой в несколько секунд.
К сожалению, отсутствует видео, где это можно было бы посмотреть вживую. Вместо этого приведу пример более сложного виджета, управляющего конструкторами для автоматической застройки шахт по добыче металла в пределах периметра базы игрока. Кстати, последний вычисляется автоматически благодаря алгоритму, описанному в замечательной статье о построении минимальных выпуклых оболочек.
Для начала инициализируем виджет и отсортируем имеющиеся воздушные суда, используя арсенал функций, предоставляемых движком:
В таблице units у нас теперь располагаются все самолеты, имеющиеся в распоряжении игрока на момент инициализации виджета. Этого, пр��вда, недостаточно, т.к. нам нужен именно воздушный экран, т.е. самолеты, выполняющие команду патруль. Некоторые могут прохлаждаться на земле, как, например, бомбардировщики, или транспортные самолеты, но нам нужны именно патрулирующие в данный момент.
Пройдемся по имеющимся самолетам и посмотрим их очереди команд на предмет наличия «патруля», после чего поместим найденные самолеты в таблицу airscreen (вне всяких сомнений, есть более эффективный способ реализации этой процедуры, но на момент создания виджета я не придумал ничего лучше, как каждый 128-й кадр игры проделывать такую сортировку. К счастью, это происходит достаточно быстро, чтобы игрок ничего не заметил).
Возможно, более эффективная реализация заключалась бы в обработке события UnitCommand — команды, поступающей для какого-либо юнита. Если это патруль и других команд нет, то можно поместить самолет в таблицу airscreen. Ну да ладно.
Также надо иметь ввиду, что самолеты строятся, уничтожаются, кроме того, кто-то из команды может отдать нам парочку, или, мы сами можем кому-то их отдать, за этим придется следить с помощью соответствующих событий UnitFinished и UnitDestroyed:
При приеме/передаче юнитов между игроками с точки зрения виджета происходит примерно то же самое, что при создании/уничтожении, так что просто сошлемся на уже имеющиеся методы:
Функция notify ставит маркер на карте с задержкой и достаточно проста:
Полный код виджета можно посмотреть здесь: github.com/spike-spb/air-screen-keeper/blob/master/air-screen-keeper.lua
Чтобы установить этот виджет, нужно скопировать его в <ПутьУстанокиSpring>/LuaUI/Widgets. К сожалению, до сих пор многие сталкиваются с проблемой не то, чтобы установки виджетов… многим просто не удается запустить саму игру, поэтому было составлено видео-руководство по этому, в целом, нехитрому процессу: springrts.ru/howto
Кроме того, в сети существует более подробное видео-руководство по созданию виджетов, длиною более часа, созданное Александром Липатовым, которое в свое время помогло мне быстро сориентироваться в процессе разработки расширений для Spring, так что я также приведу его здесь: youtu.be/eMEEa9imx3g
Вообще, надо сказать, что с помощью подобных расширений можно создавать замечательные возможности, временами существенно влияющие на стратегию и тактику игры, благодаря чему у игроков старой школы с навыками программирования имеется замечательная возможность не только играть в старую добрую игру, но и самостоятельно создавать ее.
Ссылки:
Lua scripting (для Spring) — здесь API, где можно найти описание всех функций, упомянутых в статье.
SpringRTS.ru
Сообщество Spring вконтакте
Несмотря на то, что виджет Air Screen Keeper оказался, по большому счету, бесполезной затеей, на его примере ввиду небольшого размера можно отразить основные идеи построения расширений к играм на основе движка Spring.
Итак, суть виджета (т.е. расширения) — сообщить игроку в той или иной форме о том, что т.н. воздушный экран, состоящий из множества самолетов, выполняющих команду «патруль», а��акован врагом с земли. Обычно такие атаки в разгаре битвы (8 на 8 игроков) не очень заметны и можно легко прохлопать, как противник таким образом уничтожит до 70% самолетов, если отвлечься на что-то сиюминутное.
Поэтому в качестве заметного сообщения будем использовать маркер на карте, видимый только игроку, и будем ставить его в месте, где был атакован наш самолет. Кроме того, чтобы не заполонить маркерами все на свете, будем выставлять их с задержкой в несколько секунд.
К сожалению, отсутствует видео, где это можно было бы посмотреть вживую. Вместо этого приведу пример более сложного виджета, управляющего конструкторами для автоматической застройки шахт по добыче металла в пределах периметра базы игрока. Кстати, последний вычисляется автоматически благодаря алгоритму, описанному в замечательной статье о построении минимальных выпуклых оболочек.
Для начала инициализируем виджет и отсортируем имеющиеся воздушные суда, используя арсенал функций, предоставляемых движком:
local function dispatchUnit(unitID, unitDefID)
local udef = UnitDefs[unitDefID] -- находим описание юнита по id
-- если юнит воздушный, то помещаем его в глобальный массив
if udef.isAirUnit then
units[unitID] = true
end
end
function widget:Initialize()
local allunits = spGetTeamUnits(spGetMyTeamID())
for _, uid in ipairs(allunits) do
dispatchUnit(uid, spGetUnitDefID(uid))
end
end
В таблице units у нас теперь располагаются все самолеты, имеющиеся в распоряжении игрока на момент инициализации виджета. Этого, пр��вда, недостаточно, т.к. нам нужен именно воздушный экран, т.е. самолеты, выполняющие команду патруль. Некоторые могут прохлаждаться на земле, как, например, бомбардировщики, или транспортные самолеты, но нам нужны именно патрулирующие в данный момент.
Пройдемся по имеющимся самолетам и посмотрим их очереди команд на предмет наличия «патруля», после чего поместим найденные самолеты в таблицу airscreen (вне всяких сомнений, есть более эффективный способ реализации этой процедуры, но на момент создания виджета я не придумал ничего лучше, как каждый 128-й кадр игры проделывать такую сортировку. К счастью, это происходит достаточно быстро, чтобы игрок ничего не заметил).
local function UnitHasPatrolOrder(unitID)
local queue=spGetCommandQueue(unitID,2)
for i,cmd in ipairs(queue) do
if cmd.id==CMD.PATROL then
return true
end
end
return false
end
function updateAirScreenUnits()
for id, v in pairs(units) do
if UnitHasPatrolOrder(id) then
airscreen[id] = true
end
end
end
function widget:GameFrame(frameNum)
if (frameNum % 128 ) == 0 then
updateAirScreenUnits()
end
end
Возможно, более эффективная реализация заключалась бы в обработке события UnitCommand — команды, поступающей для какого-либо юнита. Если это патруль и других команд нет, то можно поместить самолет в таблицу airscreen. Ну да ладно.
Также надо иметь ввиду, что самолеты строятся, уничтожаются, кроме того, кто-то из команды может отдать нам парочку, или, мы сами можем кому-то их отдать, за этим придется следить с помощью соответствующих событий UnitFinished и UnitDestroyed:
function widget:UnitFinished(unitID, unitDefID, unitTeam)
if (unitTeam ~= spGetMyTeamID()) then
return
end
dispatchUnit(unitID, unitDefID) -- сортируем
end
function widget:UnitDestroyed(unitID, unitDefID, unitTeam, attackerID, attackerDefID, attackerTeam)
if airscreen[unitID] then
if attackerID then
notify(attackerID) -- юнит уничтожен, мы ставим маркер на месте атакующего, если известен его id
else
notify(unitID) -- или в месте уничтожения, если id незвестен
end
airscreen[unitID] = nil -- удаляем самолет из воздушного экрана
end
units[unitID] = nil -- удаляем самолет из списка самолетов
end
При приеме/передаче юнитов между игроками с точки зрения виджета происходит примерно то же самое, что при создании/уничтожении, так что просто сошлемся на уже имеющиеся методы:
function widget:UnitTaken(unitID, unitDefID, unitTeam, newTeam)
widget:UnitDestroyed(unitID, unitDefID)
end
function widget:UnitGiven(unitID, unitDefID, unitTeam, oldTeam)
widget:UnitFinished(unitID, unitDefID, unitTeam)
end
Функция notify ставит маркер на карте с задержкой и достаточно проста:
function widget:Update(dt)
lastMarkTime = lastMarkTime - dt
end
function notify(unitID)
if (lastMarkTime < 0) then
lastMarkTime = MARK_DELAY
local msg = "AA"
local x, y, z = Spring.GetUnitPosition(unitID)
spMarkerAddPoint(x, y, z, msg, true)
end
end
Полный код виджета можно посмотреть здесь: github.com/spike-spb/air-screen-keeper/blob/master/air-screen-keeper.lua
Чтобы установить этот виджет, нужно скопировать его в <ПутьУстанокиSpring>/LuaUI/Widgets. К сожалению, до сих пор многие сталкиваются с проблемой не то, чтобы установки виджетов… многим просто не удается запустить саму игру, поэтому было составлено видео-руководство по этому, в целом, нехитрому процессу: springrts.ru/howto
Кроме того, в сети существует более подробное видео-руководство по созданию виджетов, длиною более часа, созданное Александром Липатовым, которое в свое время помогло мне быстро сориентироваться в процессе разработки расширений для Spring, так что я также приведу его здесь: youtu.be/eMEEa9imx3g
Вообще, надо сказать, что с помощью подобных расширений можно создавать замечательные возможности, временами существенно влияющие на стратегию и тактику игры, благодаря чему у игроков старой школы с навыками программирования имеется замечательная возможность не только играть в старую добрую игру, но и самостоятельно создавать ее.
Ссылки:
Lua scripting (для Spring) — здесь API, где можно найти описание всех функций, упомянутых в статье.
SpringRTS.ru
Сообщество Spring вконтакте