Jump to content

Ficou certo isso? Ou pesado demais?


Recommended Posts

  • Other Languages Moderators

Galera, fiz esse script para resolver um problema solicitado por um usuário aqui do fórum.
O objetivo é fazer com que os jogadores e veículos que estiverem dentro de uma zona de colisão fiquem no "modo ghost" (sem colisão) para que os veículos e jogadores que estiverem dentro da zona não colidam entre si e voltem ao normal quando saírem da zona.
O esquema é simples de se fazer usando setElementCollidableWith e onClientColShapeHit. O problema é que os veículos criados dentro da zona não eram afetados, pois ainda não existiam quando o jogador entrou na zona, sendo necessário o jogador sair da zona e entrar de novo nela para que este novo veículo fosse afetado também. Para contornar esse problema fiz tudo usando onClientRender, porém não sei se essa é a melhor maneira, levando em conta que consome maior processamento e existem muitos verificadores dentro dele.
O script ficou assim: (client-side)

Spoiler

ghostZone = createColSphere (2490, -1668, 12.5, 25) -- Cria uma esfera de colisão na Grove Street.

allVehicles = {} -- Cria uma variável vazia do tipo tabela.
allPlayers = {}
theVehicle = {}

function infiniteGhost () -- Executa isso a cada frame.
	if isElementWithinColShape (localPlayer, ghostZone) then -- Se o jogador local estiver na zona, então:
		theVehicle[localPlayer] = getPedOccupiedVehicle (localPlayer) -- O veículo em que o jogador está. (será nulo se o jogador estiver a pé.)
		allVehicles[localPlayer] = getElementsWithinColShape (ghostZone, "vehicle") -- Todos os elementos do tipo 'vehicle' que estão dentro da zona.
		allPlayers[localPlayer] = getElementsWithinColShape (ghostZone, "player") -- Todos os elementos do tipo 'player' que estão dentro da zona.
		if allVehicles[localPlayer][1] then -- Se existir algum elemento do tipo 'vehicle' dentro da zona, então:
			for i,vehicle in ipairs (allVehicles[localPlayer]) do -- Para cada veículo dentro da zona, faça isso:
				setElementCollidableWith (vehicle, localPlayer, false) -- Faz aquele veículo não colidir com o jogador local.
			end
			if theVehicle[localPlayer] then -- Se o jogador local está em um veículo, então:
				for i,vehicle in ipairs (allVehicles[localPlayer]) do -- Para cada veículo dentro da zona, faça isso:
					setElementCollidableWith (vehicle, theVehicle[localPlayer], false) -- Faz aquele veículo não colidir com o veículo que o jogador local está.
				end
			end
		end
		if allPlayers[localPlayer][1] then -- Se existir algum elemento do tipo 'player' dentro da zona, então:
			for i,player in ipairs (allPlayers[localPlayer]) do -- Para cada jogador dentro da zona, faça isso:
				setElementCollidableWith (player, localPlayer, false) -- Faz aquele jogador não colidir com o jogador local.
			end
			if theVehicle[localPlayer] then -- Se o jogador local está em um veículo, então:
				for i,player in ipairs (allPlayers[localPlayer]) do -- Para cada veículo dentro da zona, faça isso:
					setElementCollidableWith (player, theVehicle[localPlayer], false) -- Faz aquele jogador não colidir com o veículo que o jogador local está.
				end
			end
		end
	else -- Se o jogador local não estiver na zona, faz a mesma coisa só que deixa ele colidir com os veículos e jogadores novamente.
		theVehicle[localPlayer] = getPedOccupiedVehicle (localPlayer)
		allVehicles[localPlayer] = getElementsWithinColShape (ghostZone, "vehicle")
		allPlayers[localPlayer] = getElementsWithinColShape (ghostZone, "player")
		if allVehicles[localPlayer][1] then
			for i,vehicle in ipairs (allVehicles[localPlayer]) do
				setElementCollidableWith (vehicle, localPlayer, true)
			end
			if theVehicle[localPlayer] then
				for i,vehicle in ipairs (allVehicles[localPlayer]) do
					setElementCollidableWith (vehicle, theVehicle[localPlayer], true)
				end
			end
		end
		if allPlayers[localPlayer][1] then
			for i,player in ipairs (allPlayers[localPlayer]) do
				setElementCollidableWith (player, localPlayer, true)
			end
			if theVehicle[localPlayer] then
				for i,player in ipairs (allPlayers[localPlayer]) do
					setElementCollidableWith (player, theVehicle[localPlayer], true)
				end
			end
		end
		if theVehicle[localPlayer] then
			setElementCollidableWith (localPlayer, theVehicle[localPlayer], true)
		end
		removeEventHandler ("onClientRender", getRootElement(), infiniteGhost) -- Para de ficar executando isso a cada frame.
	end
end

function ghostmode_on (hitElement, matchingDimension)
	if hitElement == localPlayer then
		addEventHandler ("onClientRender", getRootElement(), infiniteGhost) -- Começa a executar aquela função a cada frame.
	end
end    
addEventHandler( "onClientColShapeHit", ghostZone, ghostmode_on)

 

Alguém tem alguma ideia de fazer esse script "mais leve"? Ou esse jeito é o único possível para evitar o problema?

Edited by Lord Henry
Link to comment
  • Moderators

Você poderia fazer assim, 1º passo: ter o controle de todos elementos dentro da colshape. Adicione na tabela os elementos que colidirem e remova quando sair da colshape (onClientColShapeHit/Leave).

Aí vem aquele porém que você citou, e a solução é com "onClientRender". Acho que a melhor alternativa é usar getElementsWithinColShape mas ao mesmo tempo não seria muito bom pra performance dar loop na tabela a cada frame - só que é a única solução, o quão negativo deve ser, isso eu não sei porque depende do volume de jogadores/veículos no servidor e requer um teste pra saber mais precisamente se vale a pena.

Talvez também tenham outras formas lidar com os outros veículos criados, por exemplo, um evento que é chamado quando cria um veículo por meio de um painel.

Não entendi muito bem o uso dessas tabela no código, fora isso, vejo algumas coisas que precisam de otimização. Você está usando setElementCollidableWith mesmo quando não há necessidade, te recomendo usar uma verificação antes:

if isElementCollidableWith( theElement, withElement ) then setElementCollidableWith( theElement, withElement, bool ) end

O que você tá fazendo é quase o mesmo que isto:

addEventHandler( "onClientRender", root,
	function()
		setElementCollidableWith( localPlayer, elem, false )
	end
)

Outro conselho também, sempre use variáveis locais sempre que puder.

Também, muitos dizem que usar pairs é pouca coisa mais rápido pra executar do que ipairs (deve ser uma diferença realmente baixa).

Edited by DNL291
Link to comment
  • Other Languages Moderators
5 hours ago, DNL291 said:

te recomendo usar uma verificação antes:


if isElementCollidableWith( theElement, withElement ) then setElementCollidableWith( theElement, withElement, bool ) end

 

Vou colocar isso.

No caso das tabelas ali é pq eu pretendo depois usar com mais de uma zona ao mesmo tempo, dai os elementos de uma serão diferentes de outra.

Link to comment
  • Other Languages Moderators
16 hours ago, raynner said:

eu acho que usar "onClientRender" nesta função não é correto há outras formas de fazer isso e que não exigem tanto ^^ porém os proprios eventos do colShape e utilize isElementInColShape para reavaliar os valores para os velhos e novos players ^^

Como eu detectaria um elemento que acabou de ser criado dentro da zona se os elementos criados dentro de uma zona de colisão só são detectados após uma nova verificação?

Link to comment
Just now, Lord Henry said:

Como eu detectaria um elemento que acabou de ser criado dentro da zona se os elementos criados dentro de uma zona de colisão só são detectados após uma nova verificação?

Bom este é o grande problema do nosso amado MTA :( ele não tem um evento para "onVehicleSpawn" correto o que temos deveria se chamar "onVehicleRespawn" pois ele apenas é desencadeado quando um veículo e respawnado e não temos um "onVehicleSpawn" verdadeiro porém você pode usar um setTimer para verificar que será menos prejudicial ao invés de utilizar "onClientRender".
Bom mais é apenas uma sugestão você pode sim utilizar Render porém não acho correto tendo em mente que ele exige um pouco do processador.

Edited by raynner
Link to comment
  • Other Languages Moderators
4 hours ago, raynner said:

Bom este é o grande problema do nosso amado MTA :( ele não tem um evento para "onVehicleSpawn" correto o que temos deveria se chamar "onVehicleRespawn" pois ele apenas é desencadeado quando um veículo e respawnado e não temos um "onVehicleSpawn" verdadeiro porém você pode usar um setTimer para verificar que será menos prejudicial ao invés de utilizar "onClientRender".
Bom mais é apenas uma sugestão você pode sim utilizar Render porém não acho correto tendo em mente que ele exige um pouco do processador.

Pensei em usar timer, só que como a verificação deve acontecer em curtíssimo período de tempo, não achei que valeria a pena. Até porque cada jogador dentro da zona criaria seu próprio timer.
Por isso que eu cancelo o "onClientRender" do jogador quando ele sai da zona, para que ele pare de consumir esse processamento por frame.

E sim, realmente um "onVehicleSpawn" seria perfeito para essa ocasião, dai não precisaria de nenhum timer nem render.

Edited by Lord Henry
Link to comment
  • Other Languages Moderators
12 hours ago, Banex said:

O próprio evento onClientColShapeHit já é acionado quando um elemento é criado dentro de sua área. Certamente, o motivo do veículo não ser afetado, é  que possui algo errado em seu código.

Não funciona para veículos criados no jogador. Por exemplo os veículos do painel Admin.

Link to comment
  • Other Languages Moderators

Então fiz assim, usando também o "onClientVehicleEnter":
 

ghostZone = createColSphere (2490, -1668, 12.5, 25) -- Cria uma esfera de colisão no CJ.
allVehicles = {}
allPlayers = {}
theVehicle = {}

function ghostChecker (theElement, matchingDimension)
    theVehicle[localPlayer] = getPedOccupiedVehicle (localPlayer) -- O veículo em que o jogador está. (será nulo se o jogador estiver a pé.)
    allVehicles[localPlayer] = getElementsWithinColShape (ghostZone, "vehicle") -- Todos os elementos do tipo 'vehicle' que estão dentro da zona.
    allPlayers[localPlayer] = getElementsWithinColShape (ghostZone, "player") -- Todos os elementos do tipo 'player' que estão dentro da zona.
    if isElementWithinColShape (localPlayer, ghostZone) then
        outputChatBox ("O novo objeto não está colidindo em você.") -- Use só pra testes.
        if allVehicles[localPlayer][1] then -- Se existir algum elemento do tipo 'vehicle' dentro da zona, então:
            for i,vehicle in ipairs (allVehicles[localPlayer]) do -- Para cada veículo dentro da zona, faça isso:
                setElementCollidableWith (vehicle, localPlayer, false) -- Faz aquele veículo não colidir com o jogador local.
            end
            if theVehicle[localPlayer] then -- Se o jogador local está em um veículo, então:
                for i,vehicle in ipairs (allVehicles[localPlayer]) do -- Para cada veículo dentro da zona, faça isso:
                    setElementCollidableWith (vehicle, theVehicle[localPlayer], false) -- Faz aquele veículo não colidir com o veículo que o jogador local está.
                end
            end
        end
        if allPlayers[localPlayer][1] then -- Se existir algum elemento do tipo 'player' dentro da zona, então:
            for i,player in ipairs (allPlayers[localPlayer]) do -- Para cada jogador dentro da zona, faça isso:
                setElementCollidableWith (player, localPlayer, false) -- Faz aquele jogador não colidir com o jogador local.
            end
            if theVehicle[localPlayer] then -- Se o jogador local está em um veículo, então:
                for i,player in ipairs (allPlayers[localPlayer]) do -- Para cada veículo dentro da zona, faça isso:
                    setElementCollidableWith (player, theVehicle[localPlayer], false) -- Faz aquele jogador não colidir com o veículo que o jogador local está.
                end
            end
        end
    else
        outputChatBox ("O novo objeto está colidindo em você.") -- Use só pra testes.
        if allVehicles[localPlayer][1] then
            for i,vehicle in ipairs (allVehicles[localPlayer]) do
                setElementCollidableWith (vehicle, localPlayer, true)
            end
            if theVehicle[localPlayer] then
                for i,vehicle in ipairs (allVehicles[localPlayer]) do
                    setElementCollidableWith (vehicle, theVehicle[localPlayer], true)
                end
            end
        end
        if allPlayers[localPlayer][1] then
            for i,player in ipairs (allPlayers[localPlayer]) do
                setElementCollidableWith (player, localPlayer, true)
            end
            if theVehicle[localPlayer] then
                for i,player in ipairs (allPlayers[localPlayer]) do
                    setElementCollidableWith (player, theVehicle[localPlayer], true)
                end
            end
        end
        if theVehicle[localPlayer] then
            setElementCollidableWith (localPlayer, theVehicle[localPlayer], true)
        end
    end
end
addEventHandler ("onClientColShapeHit", ghostZone, ghostChecker)
addEventHandler ("onClientColShapeLeave", ghostZone, ghostChecker)

addEventHandler ("onClientVehicleEnter", getRootElement(), function (thePlayer, seat) -- Para casos de veículos criados no jogador.
    outputChatBox ("Entrou em algum veículo.") -- Use só para testes.
    ghostChecker () -- Executa aquela função acima.
end)

Parece funcionar, mas precisa testar com mais de um jogador ao mesmo tempo.

Link to comment

Não é necessário usar tabelas, mesmo se você quiser usar esse sistema em várias áreas 

Tente isso, deve funcionar bem.

function onClientColShapeHit(theElement, matchingDimension)
	if (matchingDimension) then
		for _,element in ipairs(getElementsWithinColShape(source,"player")) do
			setElementCollidableWith(element, theElement, false)
		end
		for _,element in ipairs(getElementsWithinColShape(source,"vehicle")) do
			setElementCollidableWith(element, theElement, false)
		end
	end
end
addEventHandler("onClientColShapeHit", resourceRoot, onClientColShapeHit)

function onClientColShapeLeave(theElement, matchingDimension)
	if (matchingDimension) then
		for _,element in ipairs(getElementsWithinColShape(source),"player") do
			setElementCollidableWith(element, theElement, true)
		end
		for _,element in ipairs(getElementsWithinColShape(source),"vehicle") do
			setElementCollidableWith(element, theElement, true)
		end
	end
end
addEventHandler("onClientColShapeLeave", resourceRoot, onClientColShapeLeave)

 

Link to comment
  • Other Languages Moderators

Já me ocorreu diversas vezes de eu criar uma variável local client-side, e ela ser compartilhada com todos os clientes, quando na verdade era pra ser única ao jogador local. Por isso prefiro usar tabela para obrigar a ocorrer separadamente.

Link to comment
  • Moderators

Eu fiz um teste com uma Colshape no servidor e realmente o "onClientColShapeHit" também é acionado quando cria o elemento (o veículo, no caso) dentro dele.
Nesse caso, é só usar os eventos onClientColShapeHit/Leave que deve funcionar; e ao mesmo tempo vai ser bem melhor em questão de performance.


A propósito, o código do @Banex têm resourceRoot nos eventos, isso quer dizer que só é chamado para os elementos do próprio resource, então lembre-se de trocar por root.

Edited by DNL291
Link to comment
  • Other Languages Moderators
3 hours ago, DNL291 said:

Eu fiz um teste com uma Colshape no servidor e realmente o "onClientColShapeHit" também é acionado quando cria o elemento (o veículo, no caso) dentro dele.
Nesse caso, é só usar os eventos onClientColShapeHit/Leave que deve funcionar; e ao mesmo tempo vai ser bem melhor em questão de performance.


A propósito, o código do @Banex têm resourceRoot nos eventos, isso quer dizer que só é chamado para os elementos do próprio resource, então lembre-se de trocar por root.

Sim, também testei e ele detecta. Só não detectou quando criei o veículo pelo painel Admin. Por isso que usei o "onClientVehicleEnter".
Mas se trocar por root dai vai ativar em todas as zonas de colisão do servidor, não é?

Edited by Lord Henry
Link to comment
  • Moderators

Sim, é resourceRoot, não tinha lembrado que é só pra colshape do script xD

Então, eu testei dando um veículo pelo painel admin; criei o elemento de colisão, adicionei o evento com a saída no chat e funcionou (usei setDevelopmentMode() + showcol pra ver a colshape).

Edited by DNL291
Link to comment
  • Other Languages Moderators
4 hours ago, DNL291 said:

Sim, é resourceRoot, não tinha lembrado que é só pra colshape do script xD

Então, eu testei dando um veículo pelo painel admin; criei o elemento de colisão, adicionei o evento com a saída no chat e funcionou (usei setDevelopmentMode() + showcol pra ver a colshape).

Fiz exatamente a mesma coisa. Usando outputChatBox pra testar. Mas não detectou o veículo criado em mim. Mas detectou quando eu coloquei o "onClientVehicleEnter".

Edited by Lord Henry
Link to comment
  • Moderators

Você quer dizer quando você cria um veículo e warpa nele usando o admin né? Eu tinha falado do teste que fiz sobre o evento onClientColShapeHit ser chamado quando cria um elemento já dentro dele.

Quanto a esse problema de não chamar o evento quando warpa no veículo, acho que não é simples de se resolver, mas você pode fazer um código dentro do "onClientRender" e o evento onClientVehicleEnter com mais alguns outros meios para saber se o jogador entrou no veículo normalmente ou se foi com warp. Ou editar o admin e adicionar um evento pra esta acão.

Bem que o MTA já podia ter feito algo pra isso, tipo chamar onClientVehicleEnter  quando a função warpPedIntoVehicle é usada. No entanto, pode ser que tenha outro jeito mais simples de detectar isso, pois estou meio por fora do MTA ultimamente.

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...