XGM Forum
Сайт - Статьи - Проекты - Ресурсы - Блоги

Форуме в режиме ТОЛЬКО ЧТЕНИЕ. Вы можете задать вопросы в Q/A на сайте, либо создать свой проект или ресурс.
Вернуться   XGM Forum > Warcraft> Академия: форум для вопросов> Желтая пресса: обучающие статьи
Ник
Пароль
Войти через VK в один клик
Сайт использует только имя.

 
Doc

offline
Опыт: 63,163
Активность:
[Info] Локальные действия
Все мы знаем о существовании native-функции GetLocalPlayer:
constant native GetLocalPlayer takes nothing returns player
Она ничего не берет, но возвращает локального игрока, то есть того, на компьютере которого запущена карта. То есть, если вы запустите ее в мультиплеере, на компьютере первого игрока функция вернет хендл идентичный Player(0), на компьютере второго - Player(1) и так далее.
Все мы когда-нибудь использовали GetLocalPlayer(), к примеру, для перемещения камеры игрока или для показа локального плавающего текста, но знали ли вы, какой на самом деле потенциал скрывает эта простая функция?
Позвольте мне пояснить. Мы знаем, что неправильное использование данной функции приводит к десинхронизации клиентов в мультиплеере и последующему разрыву связи. Но знаете ли вы, какие именно действия являются неправильными? Это любые действия, создающие объекты, наследующие тип handle, будь это юнит или destructable или влекущие за собой их создание. Действия, относящиеся к визуальной части игры, не вызывают десинхронизации.
Приведу пример.
if (GetLocalPlayer() == Player(0)) then
	call CreateUnit(Player(0), 'hpea', 0., 0., 0.)
endif
Вызовет десинхронизацию, так как данным действием мы локально создаем работника для первого игрока, не создавая его у остальных.
if (GetLocalPlayer() == Player(0)) then
	call SetCameraPosition(200., 200.)
endif
Данный код переместит камеру красного игрока в позицию 200 200 в декартовой системе координат. Десинхронизации не будет, так как никаких объектов не создается.
Но это лишь поверхностная сторона, рассмотрим более интересные возможности:
Почти все мы играли в карту DotA и знаем, что некоторые способности в этой карте имеют визуальный эффект, видимый не всем игрокам. Но как реализовать такое, без угрозы десинхронизации? Воспользуемся этим кодом:
function AddLocalEffect takes string eff, real x, real y, player pl returns effect
	local string e = ""
	if (GetLocalPlayer() == pl) then
		set e = eff
	endif
	return AddSpecialEffect(e, x, y)
endfunction
Примечание: вообще, довольно полезно было бы занести значение GetLocalPlayer() в переменную, дабы избежать лишние вызовы. Например, так:
globals
	constant player LocalPlayer = GetLocalPlayer()
endglobals
Далее, с помощью cJass'а, мы можем оптимизировать уже имеющуюся карту, без ручной замены переменных, так:
#define <GetLocalPlayer()> = LocalPlayer
Примечание: до этого вы видели использование GetLocalPlayer только в условиях, но что мешает использовать его и в других местах? Например, так:
local multiboard mb = CreateMultiboard()
call MultiboardDisplay(mb, GetLocalPlayer() == Player(0))
Эти действия создадут мультиборд, видимый только красному игроку.
Или так:
local force f = CreateForce()
local unit u = CreateUnit(Player(0), 'hpea', 0., 0., 0.)
call ForceAddPlayer(f, Player(1))
call ForceAddPlayer(f, Player(3))
if (IsPlayerInForce(GetLocalPlayer(), f)) then
	call SetUnitVertexColor(u, 255, 255, 255, 128)
endif
Данные действия создадут крестьянина и сделают его полупрозрачным для 2-го и 4-го игрока.
Или вот так:
local multiboard mb = CreateMultiboard()
local integer id = GetPlayerId(GetLocalPlayer())
call MultiboardSetTitleText(GetUnitName(Hero[id])) // Hero - массив юнитов
Эти действия создадут мультиборд и в заголовке для каждого игрока покажут имя его героя.
Примечание: старайтесь не использовать GetLocalPlayer в действиях показа текста. Во-первых в определенных условиях это может вызвать непредвиденную десинхронизацию, а во-вторых - текст, показанный таким образом не будет виден в реплее игры.
Вы уже наверняка знаете про существование Preload Exploit, позволяющего сохранять данные на диск. Но что если, например, нужно сохранить некие данные определенному игроку? GetLocalPlayer поможет нам и тут! Воспользуемся следующим кодом:
if (GetLocalPlayer() == Player(0)) then
	call PreloadGenClear()
	call PreloadGenStart()
	call PreloadGenEnd("\\code.txt")
endif
Это сохранит на диск с игрой первого игрока почти пустой файл code.txt. Довольно полезная возможность для карт типа ORPG, например.
Рассмотрим еще вот какую вещь:
Предположим, мы хотим узнать положение камеры игрока и пользуемся функцией такого рода:
function GetPlayerCameraX takes player p returns real
	if (GetLocalPlayer() == p) then
		return GetCameraTargetPositionX()
	endif
	return 0.
endfunction
Но что делать, если мы хотим, например, создать в позиции камеры юнита? Данные-то будут локальны. В таком случае, нам придется воспользоваться синхронизацией локальных данных. Вещь это довольно сложная и, что довольно важно, не мгновенная. Воспользуемся следующим кодом:
globals
	gamecache cache = InitGameCache("cache")
endglobals

function SyncReal takes player p, real val returns real
	if (GetLocalPlayer() == p) then
		call StoreReal(cache, "", "", val)
	endif
	call TriggerSyncStart()
	if (GetLocalPlayer() == p) then
		call SyncStoredReal(cache, "", "")
	endif
	call TriggerSleepAction(2.)
	call TriggerSyncReady()
	return GetStoredReal(cache, "", "")
endfunction

// Используем примерно так:
local player p = Player(0)
local real local_val = GetPlayerCameraX(p)
local real result_var = SyncReal(p, local_val)
call CreateUnit(p, 'hfoo', result_var, 0., 0.)
Внимание: Код функции SyncReal очень примерный, и неуниверсальный, читателю рекомендуется самому доработать и протестировать данную функцию для получения возможности, например, одновременной синхронизации нескольких переменных.
Вот возможность, которая была бы полезна, например, в картах жанра "Мафия". В таких картах мы имеем некоего убийцу и граждан, которым нужно найти убийцу. Чтобы сохранять анонимность, убийца имеет абсолютно идентичную с гражданами модель и имя. Но что если мы хотим, чтобы игрок-убийца визуально воспринимал своего героя по-другому? Поступим так:
Создадим двух абсолютно идентичных юнитов на основе нашего "гражданина". Пускай их равкоды будут 'h000' и 'h001' соответственно. Изменим у второго юнита имя на "Убийца" и модель на грабителя.
При создании юнитов, воспользуемся следующим кодом:
local integer i = 'h000'
local integer l = 0
if (GetLocalPlayer() == Player(0)) then
	set i = 'h001'
endif
loop
	exitwhen (l == 11)
	call CreateUnit(Player(l), i, 0., 0., 0.)
	set l = l + 1
endloop
Для каждого игрока в центре карты будут созданы юниты. Но внимание, первый игрок будет видеть у своего юнита модель грабителя и имя "Убийца", остальные же игроки этого видеть не будут.
Внимание: у этого метода есть опасная сторона: если мы будем проводить сравнения с GetUnitTypeId(unit), выдающей равкод юнита или с GetUnitName(unit), будут выданы локальные значения, пользуйтесь этим с осторожностью.
То же самое работает с предметами, декорациями, разрушаемыми объектами и способностями. Причем способности могут использовать даже разные баффы, что дает возможность одному игроку видеть, например, буран, а второму огненный дождь.
Надеюсь данная статья помогла вам понять поистине огромный потенциал использования локального игрока в вашем коде и понять какие возможности может дать эта функция. Удачи в ваших начинаниях, но помните, что главное - осторожность и рациональность.

Отредактировано Doc, 14.07.2011 в 23:25.
Старый 05.06.2011, 23:13
Clamp
Lost in space
offline
Опыт: 71,258
Активность:
[:|||||||:]
не удержался:(
было и сто раз мусолилось
Старый 05.06.2011, 23:31
Doc

offline
Опыт: 63,163
Активность:
Clamp, про локальных юнитов и баффы нет.
Старый 05.06.2011, 23:33
Clamp
Lost in space
offline
Опыт: 71,258
Активность:
лень искать, но я где то писал это по аналогии с созданием локального эффекта.
Старый 05.06.2011, 23:40
naxim

offline
Опыт: 10,186
Активность:
Doc:
if (GetLocalPlayer() == Player(0)) then
call SetCameraPosition(200., 200.)
endif
А если GetLocalPlayer() !== Player(0)?
SetCameraPosition задает камеру только для игрока с индексом 0?
И можно получить текущую точку расположения камеры нужного игрока?
Старый 06.06.2011, 00:15
Doc

offline
Опыт: 63,163
Активность:
naxim, если не равно, то установит для всех остальных. Да, можно, но данные будут локальны.
Старый 06.06.2011, 00:22
naxim

offline
Опыт: 10,186
Активность:
Doc:
Да, можно, но данные будут локальны.
Дай как мне такую функцию. Т.К. знаю тока на гуях Current Camera которая, и в ней нельзя выбирать игрока.
Старый 06.06.2011, 00:42
Doc

offline
Опыт: 63,163
Активность:
constant native GetCameraTargetPositionX takes nothing returns real
Старый 06.06.2011, 01:07
Hanabishi
COOL STATUS
offline
Опыт: отключен
Doc, а разве из-за разных юнитов не будет десинка от pathing? Или аур на соседних юнитов (если есть)?
Старый 06.06.2011, 07:07
Doc

offline
Опыт: 63,163
Активность:
Hanabishi, прочиатй внимательнее, юниты нужны идентичные, но с разными визуальными данными.
Старый 06.06.2011, 11:47
Doc

offline
Опыт: 63,163
Активность:
Clamp, суть статьи в том и состоит, чтобы обобщить разрозненные куски данных, по отдельности до которых никому нет дела.
Старый 06.06.2011, 21:04
MyRtZ

offline
Опыт: 6,530
Активность:
Дополните до краев первый пост и в статьи, буду весьма благодарен
Старый 07.06.2011, 00:53
Doc

offline
Опыт: 63,163
Активность:
Можно написать про локальные эффекты и опасность использование локального рандома, что еще?
Старый 07.06.2011, 01:16
_Red

offline
Опыт: 4,095
Активность:
Doc:
что еще
Синхронизация локальных данных была бы замечательным дополнением
Старый 07.06.2011, 06:46
Clamp
Lost in space
offline
Опыт: 71,258
Активность:
напиши им про создание декора на примере локальной порчи деревьев, например
про безопасное локальное изменение глобалок
про невозможность локальных приказов
....
про локальное сохранение данных на диск, наконец
Старый 07.06.2011, 06:50
EvolutionArena
League of Legends
offline
Опыт: 3,219
Активность:
Можно написать про локальные эффекты
Мне бы очень хотелось посмотреть.
Старый 12.07.2011, 15:21
Helpmeplz

offline
Опыт: 11,698
Активность:
а 100500 тем на форуме не позволяют?
Код:
function AddLocalEffectTarget takes unit u, string eff, string attach, player pl returns effect
local string e = ""
if GetLocalPlayer() == pl then
 set e = eff
endif
return AddSpecialEffectTarget(e, u, attach)
endfunction
Старый 12.07.2011, 19:55
Doc

offline
Опыт: 63,163
Активность:
нате. +куча примеров +куча фишек.
Doc добавил:
Старый 14.07.2011, 17:49
Doc

offline
Опыт: 63,163
Активность:
Теперь и на глагне ! Закрыто, перемещено в желтую прессу.
Старый 15.07.2011, 13:20

Опции темы Поиск в этой теме
Поиск в этой теме:

Расширенный поиск

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

BB-коды Вкл.
[IMG] код Вкл.
HTML код Выкл.
Быстрый переход



Часовой пояс GMT +3, время: 17:42.