Более чистый/более производительный способ сопоставить точку за кадром с краем экрана?

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

В идеале значок должен быть постоянно виден где-то на краю экрана, чтобы вид мог смотреть на этот значок, чтобы снова выровнять цель с их окном просмотра.

Я нашел способ сделать это методом проб и ошибок. Этот метод включает проецирование вектора от камеры к цели на плоскость экрана и использование скалярных произведений для получения компонентов x и y направления в пространстве пикселей/пространстве экрана. Мы записываем знаки этих компонентов, чтобы они не потерялись в предстоящей тригонометрии. затем мы вычисляем точку на краю экрана, которая будет пересечена (если окажется, что это край, который вообще будет пересечен) и ограничим ее границей с отступами. когда мы вычисляем пересечения, мы в конечном итоге теряем соответствующие знаки в наших числах из-за [тригонометрии], поэтому мы повторно применяем знаки, которые мы записали ранее. остальное просто совмещает нашу иконку и маленькую стрелку-указатель, указывающую за пределы экрана.

этот метод работает отлично, но это немного беспорядок и, вероятно, расточительный. Я не уверен, насколько это необходимо и можно ли что-то упростить, лучше используя триггерные тождества. Я думаю, что можно было бы свернуть if-else в один блок, применяя минимальные и максимальные значения к перехватам, но я думаю, что это будет менее эффективно. Я видел несколько других вопросов с различными ответами на эту проблему, но не нашел и не пропустил этот конкретный вопрос, который единственный, который сработал для меня.

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

Заранее спасибо за любую информацию! или если этот вопрос будет сочтен излишним, не беспокойтесь

      local camera = workspace.CurrentCamera
local worldPoint = self.target.CFrame.Position;
local vector, inViewport = camera:WorldToScreenPoint(worldPoint);
local screenPoint = Vector2.new(vector.X, vector.Y);
local depth = vector.Z;

local screenGuiSizePixels = self.screenGui.AbsoluteSize;
local halfHeight = screenGuiSizePixels.Y/2-100;
local halfWidth = screenGuiSizePixels.X/2-100;
local maxlength = math.sqrt(math.pow(halfWidth,2) + math.pow(halfHeight,2)); -- the diagonal of one screen quadrant

local screenPointOffsetDirection = Vector2.new(screenPoint.X-screenGuiSizePixels.X/2, screenPoint.Y-screenGuiSizePixels.Y/2).Unit

local screenPosition = clampUdim2InPixels(UDim2.new(0, screenPoint.x, 0, screenPoint.y), screenGuiSizePixels.x, screenGuiSizePixels.y);
self.imageLabel.Position = screenPosition;
if inViewport then
    self.pointImageLabel.ImageTransparency = 1; 
else
    
    local v = (worldPoint-camera.CFrame.Position).Unit;
    local n = camera.CFrame.LookVector;
    local vec_p = (v - (v:Dot(n)) * n).Unit
    
    local cameracframe = camera.CFrame
    
    local x = vec_p:Dot(cameracframe.rightVector)
    local y = -vec_p:Dot(cameracframe.upVector)
    
    local viewportDirection = Vector2.new(x, y);
    local signs = Vector2.new(math.sign(x), math.sign(y));
    local angle = math.atan2(viewportDirection.Y, viewportDirection.X);
    local horizontal_intercept = halfHeight * math.abs(math.cos(angle)/math.sin(angle))*signs.X;--this is cotangent btw
    local vertical_intercept = halfWidth * math.abs(math.tan(angle))*signs.Y;
    
    local screen_edgepoint;
    if signs.x > 0 and signs.y > 0 then
        print("bottom right")
        screen_edgepoint = Vector2.new(math.min(halfWidth, horizontal_intercept), math.min(halfHeight, vertical_intercept));
    elseif signs.x > 0 and signs.y <=0 then
        print("top right")
        screen_edgepoint = Vector2.new(math.min(halfWidth, horizontal_intercept), math.max(-halfHeight, vertical_intercept));
    elseif signs.x < 0 and signs.y > 0 then
        print("bottom left")
        screen_edgepoint = Vector2.new(math.max(-halfWidth, horizontal_intercept), math.min(halfHeight, vertical_intercept));
    elseif signs.x < 0 and signs.y <= 0 then
        print("top left")
        screen_edgepoint = Vector2.new(math.max(-halfWidth, horizontal_intercept), math.max(-halfHeight, vertical_intercept));
    end
    
    local offsetDirection = Vector2.new(screenPosition.X.Offset-screenGuiSizePixels.X/2, screenPosition.Y.Offset-screenGuiSizePixels.Y/2).UnitscreenGuiSizePixels.x, screenGuiSizePixels.y);
    screenPosition = UDim2.fromOffset(screenGuiSizePixels.X/2+ screen_edgepoint.x, screenGuiSizePixels.Y/2 + screen_edgepoint.y);
    
    self.imageLabel.Position = screenPosition;
    
    self.pointImageLabel.ImageTransparency = 0;
    local offsetDirection = Vector2.new(screenPosition.X.Offset-screenGuiSizePixels.X/2, screenPosition.Y.Offset-screenGuiSizePixels.Y/2).Unit
    self.pointImageLabel.Position = UDim2.fromOffset(screenPosition.X.Offset+offsetDirection.X*(waypointRadius+arrowRadius), screenPosition.Y.Offset+offsetDirection.Y*(waypointRadius+arrowRadius));
    local angle = math.atan2(offsetDirection.Y, offsetDirection.X);
    self.pointImageLabel.Rotation = math.deg(angle)+90;
end

0 ответов

Другие вопросы по тегам