Jump to content

Машины взрываются при погружении в воду.


Fella

Recommended Posts

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

мне уже вот такой код клиентовский насоветовали:

  
function isPlayerDiedinWater () 
    vehicle = getPedOccupiedVehicle (getLocalPlayer()) 
    if isElementInWater(vehicle) then 
        blowVehicle (vehicle) 
    end 
end  
addEventHandler("onClientRender", getLocalPlayer(), isPlayerDiedinWater) 

но по моим наблюдениям, если он что-то и делает, то не то что мне нужно.

Мог бы кто-нибудь сказать, что с этим кодом не так и как его заставить работать?

Большое спасибо заранее.

Link to comment

Лучше это сделать в таймере, а не в рендере.

Рендер предназначен для других целей, такие как прорисовка и т.п.

addEventHandler("onClientRender", getLocalPlayer(), isPlayerDiedinWater) 

Второй аргумент должен быть root, а не локальный игрок т.к событие должно срабатывать для всех клиентов.

Также не забывайте, что у Lua динамическое распределение памяти и делать такие конструкции не имеет смысла.

vehicle = getPedOccupiedVehicle (getLocalPlayer()) 
if isElementInWater(vehicle) then 
    blowVehicle (vehicle) 
end 

переменная vehicle станет глобальной, но в данной ситуации это вообще не нужно.

Вот, накидал код.

setTimer( 
    function() 
        if isPedInVehicle( localPlayer ) then 
            local pVehicle = getPedOccupiedVehicle( localPlayer ); 
            if isElementInWater( pVehicle ) then 
                blowVehicle( pVehicle ); 
            end 
        end 
    end, 
1000, 0 ); 

Link to comment

да, это работает, спасибо за помощь, но тут загвоздка ; как бы объяснить...

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

возможно ли как-то убрать этот эффект? извиняюсь, что обременяю тебя.

Link to comment
Лучше это сделать в таймере, а не в рендере.

Рендер предназначен для других целей, такие как прорисовка и т.п.

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

addEventHandler("onClientRender", root, 
    function () 
        -- берем списочек машин в зоне видимости и проходимся по нему 
        for k, v in ipairs( getElementsByType( "vehicle", root, true ) ) do 
         
            -- я отвечаю за состояние машинки в игре? 
            if isElementSyncer( v ) then 
             
                -- она в воде и еще живая? 
                if isElementInWater( v ) and not isVehicleBlown( v ) then 
                 
                    -- ну и умирает пусть тогда 
                    blowVehicle( v, false ) 
                end 
            end 
        end 
    end 
) 

syncer тоже очень полезная вещь иногда. Имитировал таким способом умирание машин в воде, как в SA:MPе, когда писал свой мод.

Link to comment
И рендер это не только отрисовка, а событие обновления состояний объектов всего игрового мира.

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

P.S. Вот после таких вот действий в рендере люди жалуются: "А почему у меня фпс проседает?".

Link to comment

Более того частота кадров у каждого клиента разная, и она не обязательно будет в пределах лимита, у некоторых оно может быть таким низким, что таймер в 50ms будет куда эффективнее.

На самом деле куда лучше будет создать колшейпы в воде там где надо и привязать к ним Hit-событие

P.S. 36, а не 38

Link to comment
Вообще-то, рендер вызывается при каждой смене кадра.
таймер в 50ms будет куда эффективнее.

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

http://code.google.com/p/mtasa-blue/sou ... ce9.cpp#20:

CProxyDirect3DDevice9::CProxyDirect3DDevice9 ( IDirect3DDevice9 * pDevice  ) 

Здесь начало. В конструкторе класса CProxyDirect3DDevice9 захватывается объект Direct3D, выше мне копать тупо лень, поэтому глядим конструктор и по смыслу понимаем, что это так и есть. Метод этого же класса Present - не что иное, как сигнал того, что экранный буфер готов к отрисовке. Это проверяется гуглом и MSDN-ом. В теле метода - вызов

http://code.google.com/p/mtasa-blue/sou ... e9.cpp#358:

CDirect3DEvents9::OnPresent ( m_pDevice ); 

Здесь вызов статического метода класса CDirect3DEvents9::OnPresent. CDirect3DEvents9 это еще одна прослойка между движком MTA и Direct3D. В его теле нас интересует вызов

http://code.google.com/p/mtasa-blue/sou ... s9.cpp#134:

CCore::GetSingleton ().DoPostFramePulse (); 

Здесь: получение синглтона (это нагуглите в целях развития) класса CCore, и вызов его метода DoPostFramePulse().

В его теле:

http://code.google.com/p/mtasa-blue/sou ... e.cpp#1284:

m_pModManager->DoPulsePostFrame (); 

Вызов DoPulsePostFrame() в объекте-менеджере модов (один из модов - deathmatch).

Метод в CModManager::DoPulsePostFrame вызывает

http://code.google.com/p/mtasa-blue/sou ... er.cpp#289:

m_pClientBase->PostFrameExecutionHandler (); 

В теле CClient::PostFrameExecutionHandler:

http://code.google.com/p/mtasa-blue/sou ... nt.cpp#251:

g_pClientGame->DoPulsePostFrame (); 

В CClientGame::DoPulsePostFrame:

http://code.google.com/p/mtasa-blue/sou ... me.cpp#736:

Тут останавливаемся, и начинаем смотреть в тело этого метода.

http://code.google.com/p/mtasa-blue/sou ... me.cpp#925:

DoPulses (); 

Спускаемся ниже в теле уже этого метода.

http://code.google.com/p/mtasa-blue/sou ... e.cpp#1121:

// Call onClientRender LUA event 
CLuaArguments Arguments; 
m_pRootEntity->CallEvent ( "onClientRender", Arguments, false ); 

И теперь самое занимательное. Это и есть точка вызова события onClientRender в скриптах. А теперь покрутим над этим вызовом и посмотрим внимательно, и над ним находится целая куча вызовов вида

 m_pUnoccupiedVehicleSync->DoPulse (); 
m_pPedSync->DoPulse (); 
#ifdef WITH_OBJECT_SYNC 
m_pObjectSync->DoPulse (); 
#endif 

И тому подобное. А это, если посмотреть внутрь методов соответствующих классов - код обновления положений, состояний и прочего для разнообразных игровых элементов: машин, объектов, и т.п.. Так вот, к чему я это пишу? Из этого кода видно, что вызов onClientRender производится сразу же после обновления мира. И поэтому, если нужно отследить какие-то изменения в игровом мире, нет ничего удобнее, чем поглядеть в вызове onClientRender. И внести свои изменения в него. И не нужно городить убербыстрых таймеров на 50ms, когда можно сделать всё и без них, сразу на месте. Это не будет слишком накладно, если использовать функции (getElementsByType(type = "vehicle", streamedIn = true)) правильно. Плюс, таймеры ненадежны, и могут иметь задержку до выполнения, тогда как какие-то изменения в мир нужно произвести немедленно. И лучше их делать сразу же в этом кадре. Вот так.

Link to comment

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...