Jump to content

Não alterar tanto imagem com face virada para camera do client


Recommended Posts

Esse código cria uma imagem virada para camera do client quando o player tem certo elementdata, porém quando eu vou para muito longe a imagem fica grande demais, e perto fica bom, teria um jeito de deixar esse discrepância menos perceptível sem alterar muito o tamanho da imagem?

 

function desenharp()
    for _, player in ipairs(getElementsByType("player")) do
        if isElement(player) then
            if getElementData(player, "imortalp") == true then
                local alpha = getElementAlpha(player)
                local px, py, pz = getElementPosition(player)
                local camX, camY, camZ = getCameraMatrix()
                local dist = getDistanceBetweenPoints3D(camX, camY, camZ, px, py, pz)
                local notModifiedDist = dist
                if notModifiedDist < options.distance then
                    local headPosX, headPosY, headPosZ = getPedBonePosition(player, 4)
                    if headPosX and headPosY and headPosZ then
                        local screenX, screenY = getScreenFromWorldPosition(headPosX, headPosY, headPosZ, 25, false)
                        if screenX then
                            local width, height = (47 * 3) * 1, (47 * 3) * 1
                            local iconX, iconY = math.floor(screenX - width / 2), math.floor(screenY - 80 - height / 2)
                            dxDrawImage(iconX + 20, iconY + 120, width - 95, height - 95, "Patro.png", 0, 0, 0, tocolor(255, 255, 0, alpha))
                        end
                    end
                end
            end
        end
    end
end
addEventHandler("onClientRender", root, desenharp)

E caso o código apresente mais erros ou possíveis erros, porfavor sintam-se a vontade para aponta-los para quer eu possa corrigir.

Link to comment
  • Other Languages Moderators
Posted (edited)

Utilize DxDrawMaterialLine3D (3D) em vez de DxDrawImage (2D).

On 17/05/2024 at 20:29, Doongogar said:

E caso o código apresente mais erros ou possíveis erros, porfavor sintam-se a vontade para aponta-los para quer eu possa corrigir.

  1. Não precisa usar ipairs quando a ordem dos elementos não importa. Utilize pairs que é levemente mais rápido.
    for _, player in pairs(getElementsByType("player")) do
  2. Se sua elementData só pode ter valores true ou false, não precisa verificar se ela é == true.
    Neste caso, só isso já serve:
    if getElementData(player, "imortalp") then -- Se for qualquer valor, passa na condição. Se for false, não passa.

 

Edited by Lord Henry
Link to comment
3 hours ago, Lord Henry said:

Utilize DxDrawMaterialLine3D (3D) em vez de DxDrawImage (2D).

  1. Não precisa usar ipairs quando a ordem dos elementos não importa. Utilize pairs que é levemente mais rápido.
    for _, player in pairs(getElementsByType("player")) do
  2. Se sua elementData só pode ter valores true ou false, não precisa verificar se ela é == true.
    Neste caso, só isso já serve:
    if getElementData(player, "imortalp") then -- Se for qualquer valor, passa na condição. Se for false, não passa.

 

Eu dei uma verificada na função DxDrawMaterialLine3D e tentei fazer por meio deste, porém quando a tela do client se move, a imagem rotaciona junto com a tela do client proporcionando um posicionamento estranho da imagem em alguns angulos e também quando o player se move a imagem fica meio tremula meio que como se ela não atualizasse bem todo frame, e está reduzindo bastante o FPS também

 

local screenWidth, screenHeight = guiGetScreenSize()
local options = {
    scaleMultiplier = (screenWidth + 1920) / (1920 * 2),
    distance = 20,
    aimdistance = 0,
}

options.aimdistance = options.distance - options.distance * 0.15

function desenhar()
    for _, player in pairs(getElementsByType("player")) do
        if isElement(player) then
            if getElementData(player, "imortal") then
                local alpha = getElementAlpha(player)
                local px, py, pz = getElementPosition(player)
                local camX, camY, camZ = getCameraMatrix()
                local dist = getDistanceBetweenPoints3D(camX, camY, camZ, px, py, pz)
                local notModifiedDist = dist
                if notModifiedDist < options.distance then
                    local headPosX, headPosY, headPosZ = getPedBonePosition(player, 4)
                    if headPosX and headPosY and headPosZ then
                        local height = - 0.3
                        local width = 0.3
                        local halfHeight = height / 2
                        local texture = dxCreateTexture("Logo.png")
                        if texture then
                            local bottomX, bottomY, bottomZ = headPosX, headPosY, headPosZ - halfHeight
                            local topX, topY, topZ = headPosX, headPosY, headPosZ + halfHeight
                            dxDrawMaterialLine3D(bottomX, bottomY, bottomZ, topX, topY, topZ, texture, width, tocolor(255, 255, 255, alpha))
                        end
                    end
                end
            end
        end
    end
end
addEventHandler("onClientRender", root, desenhar)

ali na altura eu deixei negativo pois a imagem estava sendo gerada de cabeça pra baixo, não sei o porque também...

Link to comment
  • Other Languages Moderators
Posted (edited)

Pra melhorar um pouco o FPS, deixe o dxCreateTexture fora da função, numa variável local.

E ali no getElementsByType, faça assim pra pegar só os jogadores que estão sendo sincronizados pelo localPlayer. Não faz sentido pegar player que está muito longe.

getElementsByType("player", root, true)

Sobre o ângulo estranho, como a imagem sempre ficará voltada para a câmera, então é melhor voltar ao 2D mesmo.


Uma coisa que você pode usar para calcular melhor a escala do dxDrawImage, é pegar 3 posições X,Y,Z ao redor da cabeça do jogador, onde começa e termina a imagem.

Mas essas posições levam em consideração a rotação da câmera, sendo assim, não adianta simplesmente somar ou subtrair os valores, pois fazendo isso, as posições ficarão relativas ao cenário fixo.

Para isso, você precisa criar um objeto qualquer invisível e sem colisão na cabeça do jogador (no osso). Depois disso você deve rotacionar esse objeto para sempre ficar apontado para a câmera utilizando a função útil findRotation3D. (o objeto não pode ser anexado no jogador, ele apenas tem sua posição setada na cabeça dele a cada frame.)

Só depois de você ter um objeto invisível e sem colisão na cabeça do jogador sempre apontado para a câmera, utilize getElementMatrix para obter as posições do lado e em cima desse objeto, essas posições você usará no getScreenFromWorldPosition para desenhar o dxDrawImage final.

Como eu faria:

local texture = dxCreateTexture("Logo.png") -- Cria a textura.
local objetos = {} -- Tabela onde os objetos invisíveis vão ficar.
local options = {
    distance = 100,
    escala = 1,
}

addEventHandler("onClientRender", root, function()
    for _, player in pairs(getElementsByType("player", root, true)) do -- Para cada jogador sendo sincronizado por este cliente, faça:
        if isElement(player) then -- Se o jogador existe, então: (evita erros quando o player desconecta neste frame)
            if getElementData(player, "imortalp") then -- Se essa elementData não for false, então:
                local alpha = getElementAlpha(player)
                local px, py, pz = getElementPosition(player)
                local camX, camY, camZ = getCameraMatrix()
                local dist = getDistanceBetweenPoints3D(camX, camY, camZ, px, py, pz)
                if dist < options.distance then -- Se a distância da câmera para o jogador for menor que o limite, então:
                    local headPosX, headPosY, headPosZ = getPedBonePosition(player, 4) -- Obtém a posição do osso da cabeça do jogador.
                    if headPosX then -- Se obteve uma das posições (significa que obteve as outras também), então:
                        if not isElement(objetos[player]) then -- Se ainda não existe o objeto invisível na cabeça desse jogador, então:
                            objetos[player] = createObject (3003, headPosX, headPosY, headPosZ, 0, 0, 0, true) -- Cria um objeto sem colisão associado ao jogador.
                            setElementAlpha(objetos[player], 0) -- Deixa o objeto invisível.
                        else -- Se já existe o objeto, apenas mantém ele na cabeça do jogador a cada frame.
                            setElementPosition(objetos[player], headPosX, headPosY, headPosZ)
                        end
                        local rx, ry, rz = findRotation3D(headPosX, headPosY, headPosZ, camX, camY, camZ) -- Encontra a rotação relativa do objeto com a câmera.
                        setElementRotation(objetos[player], rx, ry, rz) -- Aponta o objeto para a câmera.
              
                        local rightX, rightY, rightZ = getPositionFromElementOffset(objetos[player], options.escala / 2, 0, 0) -- Obtém a posição XYZ na direita da cabeça do jogador.
                        local leftX, leftY, leftZ = getPositionFromElementOffset(objetos[player], (options.escala / 2) * -1, 0, 0) -- Obtém a posição XYZ na esquerda da cabeça do jogador.
                        
                        local screenX1, screenY1 = getScreenFromWorldPosition(rightX, rightY, rightZ)
                        local screenX2 = getScreenFromWorldPosition(leftX, leftY, leftZ)
                        local _, screenY3 = getScreenFromWorldPosition(headPosX, headPosY, headPosZ + options.escala)
                        -- Apenas para testes
                        -- dxDrawText("1", screenX1 or 0, screenY1 or 0)
                        -- dxDrawText("2", screenX2 or 0, screenY1 or 0)
                        -- dxDrawText("3", screenX2 or 0, screenY3 or 0)
                        if screenX1 and screenX2 and screenY3 then
                            local width, height = screenX2 - screenX1, screenY1 - screenY3 -- Calcula os tamanhos subtraindo as posições da tela.
                            dxDrawImage(screenX1, screenY3, width, height, texture, 0, 0, 0, tocolor(255, 255, 0, alpha)) -- A imagem precisa ser quadrada, preferencialmente múltipla de 2. (16x16, 32x32, 64x64, 128x128, etc)
                        end
                    end
                elseif isElement(objetos[player]) then -- Se o jogador não está perto o suficiente e tem o objeto invisível em sua cabeça, então:
                    destroyElement(objetos[player]) -- Destrói o objeto.
                    objetos[player] = nil -- Limpa a variável dele para liberar espaço na memória.
                end
            end
        end
    end
end)

addEventHandler("onClientElementStreamOut", root, function() -- Ativa esse evento quando um elemento para de ser sincronizado por este client.
    if getElementType(source) == "player" then -- Se foi um jogador, então:
        if isElement(objetos[source]) then -- Se existe o objeto invisível daquele jogador, então:
            destroyElement(objetos[source]) -- Destrói o objeto.
            objetos[source] = nil -- Limpa a variável dele para liberar espaço na memória.
        end
    end
end)

-- Funções úteis.
function findRotation3D(x1, y1, z1, x2, y2, z2)
    local rotx = math.atan2 (z2 - z1, getDistanceBetweenPoints2D (x2, y2, x1, y1))
    rotx = math.deg(rotx)
    local rotz = -math.deg(math.atan2(x2 - x1, y2 - y1))
    rotz = rotz < 0 and rotz + 360 or rotz
    return rotx, 0, rotz
end

function getPositionFromElementOffset(element,offX,offY,offZ)
    local m = getElementMatrix(element)
    local x = offX * m[1][1] + offY * m[2][1] + offZ * m[3][1] + m[4][1]
    local y = offX * m[1][2] + offY * m[2][2] + offZ * m[3][2] + m[4][2]
    local z = offX * m[1][3] + offY * m[2][3] + offZ * m[3][3] + m[4][3]
    return x, y, z
end

 

Edited by Lord Henry
Link to comment
  • 2 weeks later...
On 21/05/2024 at 15:07, Lord Henry said:

Pra melhorar um pouco o FPS, deixe o dxCreateTexture fora da função, numa variável local.

E ali no getElementsByType, faça assim pra pegar só os jogadores que estão sendo sincronizados pelo localPlayer. Não faz sentido pegar player que está muito longe.

getElementsByType("player", root, true)

Sobre o ângulo estranho, como a imagem sempre ficará voltada para a câmera, então é melhor voltar ao 2D mesmo.


Uma coisa que você pode usar para calcular melhor a escala do dxDrawImage, é pegar 3 posições X,Y,Z ao redor da cabeça do jogador, onde começa e termina a imagem.

Mas essas posições levam em consideração a rotação da câmera, sendo assim, não adianta simplesmente somar ou subtrair os valores, pois fazendo isso, as posições ficarão relativas ao cenário fixo.

Para isso, você precisa criar um objeto qualquer invisível e sem colisão na cabeça do jogador (no osso). Depois disso você deve rotacionar esse objeto para sempre ficar apontado para a câmera utilizando a função útil findRotation3D. (o objeto não pode ser anexado no jogador, ele apenas tem sua posição setada na cabeça dele a cada frame.)

Só depois de você ter um objeto invisível e sem colisão na cabeça do jogador sempre apontado para a câmera, utilize getElementMatrix para obter as posições do lado e em cima desse objeto, essas posições você usará no getScreenFromWorldPosition para desenhar o dxDrawImage final.

Como eu faria:

local texture = dxCreateTexture("Logo.png") -- Cria a textura.
local objetos = {} -- Tabela onde os objetos invisíveis vão ficar.
local options = {
    distance = 100,
    escala = 1,
}

addEventHandler("onClientRender", root, function()
    for _, player in pairs(getElementsByType("player", root, true)) do -- Para cada jogador sendo sincronizado por este cliente, faça:
        if isElement(player) then -- Se o jogador existe, então: (evita erros quando o player desconecta neste frame)
            if getElementData(player, "imortalp") then -- Se essa elementData não for false, então:
                local alpha = getElementAlpha(player)
                local px, py, pz = getElementPosition(player)
                local camX, camY, camZ = getCameraMatrix()
                local dist = getDistanceBetweenPoints3D(camX, camY, camZ, px, py, pz)
                if dist < options.distance then -- Se a distância da câmera para o jogador for menor que o limite, então:
                    local headPosX, headPosY, headPosZ = getPedBonePosition(player, 4) -- Obtém a posição do osso da cabeça do jogador.
                    if headPosX then -- Se obteve uma das posições (significa que obteve as outras também), então:
                        if not isElement(objetos[player]) then -- Se ainda não existe o objeto invisível na cabeça desse jogador, então:
                            objetos[player] = createObject (3003, headPosX, headPosY, headPosZ, 0, 0, 0, true) -- Cria um objeto sem colisão associado ao jogador.
                            setElementAlpha(objetos[player], 0) -- Deixa o objeto invisível.
                        else -- Se já existe o objeto, apenas mantém ele na cabeça do jogador a cada frame.
                            setElementPosition(objetos[player], headPosX, headPosY, headPosZ)
                        end
                        local rx, ry, rz = findRotation3D(headPosX, headPosY, headPosZ, camX, camY, camZ) -- Encontra a rotação relativa do objeto com a câmera.
                        setElementRotation(objetos[player], rx, ry, rz) -- Aponta o objeto para a câmera.
              
                        local rightX, rightY, rightZ = getPositionFromElementOffset(objetos[player], options.escala / 2, 0, 0) -- Obtém a posição XYZ na direita da cabeça do jogador.
                        local leftX, leftY, leftZ = getPositionFromElementOffset(objetos[player], (options.escala / 2) * -1, 0, 0) -- Obtém a posição XYZ na esquerda da cabeça do jogador.
                        
                        local screenX1, screenY1 = getScreenFromWorldPosition(rightX, rightY, rightZ)
                        local screenX2 = getScreenFromWorldPosition(leftX, leftY, leftZ)
                        local _, screenY3 = getScreenFromWorldPosition(headPosX, headPosY, headPosZ + options.escala)
                        -- Apenas para testes
                        -- dxDrawText("1", screenX1 or 0, screenY1 or 0)
                        -- dxDrawText("2", screenX2 or 0, screenY1 or 0)
                        -- dxDrawText("3", screenX2 or 0, screenY3 or 0)
                        if screenX1 and screenX2 and screenY3 then
                            local width, height = screenX2 - screenX1, screenY1 - screenY3 -- Calcula os tamanhos subtraindo as posições da tela.
                            dxDrawImage(screenX1, screenY3, width, height, texture, 0, 0, 0, tocolor(255, 255, 0, alpha)) -- A imagem precisa ser quadrada, preferencialmente múltipla de 2. (16x16, 32x32, 64x64, 128x128, etc)
                        end
                    end
                elseif isElement(objetos[player]) then -- Se o jogador não está perto o suficiente e tem o objeto invisível em sua cabeça, então:
                    destroyElement(objetos[player]) -- Destrói o objeto.
                    objetos[player] = nil -- Limpa a variável dele para liberar espaço na memória.
                end
            end
        end
    end
end)

addEventHandler("onClientElementStreamOut", root, function() -- Ativa esse evento quando um elemento para de ser sincronizado por este client.
    if getElementType(source) == "player" then -- Se foi um jogador, então:
        if isElement(objetos[source]) then -- Se existe o objeto invisível daquele jogador, então:
            destroyElement(objetos[source]) -- Destrói o objeto.
            objetos[source] = nil -- Limpa a variável dele para liberar espaço na memória.
        end
    end
end)

-- Funções úteis.
function findRotation3D(x1, y1, z1, x2, y2, z2)
    local rotx = math.atan2 (z2 - z1, getDistanceBetweenPoints2D (x2, y2, x1, y1))
    rotx = math.deg(rotx)
    local rotz = -math.deg(math.atan2(x2 - x1, y2 - y1))
    rotz = rotz < 0 and rotz + 360 or rotz
    return rotx, 0, rotz
end

function getPositionFromElementOffset(element,offX,offY,offZ)
    local m = getElementMatrix(element)
    local x = offX * m[1][1] + offY * m[2][1] + offZ * m[3][1] + m[4][1]
    local y = offX * m[1][2] + offY * m[2][2] + offZ * m[3][2] + m[4][2]
    local z = offX * m[1][3] + offY * m[2][3] + offZ * m[3][3] + m[4][3]
    return x, y, z
end

 

 

Dessa forma o client está conseguindo ver que a imagem e 2D se visualizar de baixo para cima ou de cima para baixo, e quando o player se movimenta a imagem aparentemente não segue o player em tempo real pois ela fica dando umas mini travadas, além de consumir muito FPS. Não teria como manter o meio pelo qual a imagem está sendo criada somente usando algum calculo para diminuir a imagem com relação na distancia de visão do client? Quando um player se distancia demais além de a imagem ficar grande demais ela "sai de perto do player" atingindo um distanciamento considerável do player que está com a imagem

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