将屏幕外的点映射到屏幕边缘的更干净/更高性能的方法?

Cleaner/more performant way to map a point offscreen to the edge of the screen?

提问人:Swanijam 提问时间:2/22/2023 最后编辑:KylaaaSwanijam 更新时间:2/22/2023 访问量:25

问:

因此,我正在尝试实现一个屏幕航点系统,就像您可能在数十个不同的游戏中看到的那样,在玩家视口中目标的任何地方,都可以透过墙壁看到图标。幸运的是,在我的情况下,这可以自动完成,但是为了覆盖屏幕上的物体,我们遇到了困难的部分。

理想情况下,图标应始终在屏幕边缘的某个位置可见,以便视图可以朝向该图标,以便再次将目标与其视口对齐。

我找到了一种方法,通过一些试验和错误来做到这一点。该方法涉及将矢量从相机投影到目标上,并使用点积在像素空间/屏幕空间中获取方向的 x 和 y 分量。我们记录这些组件的迹象,这样它们就不会在即将到来的三角函数中丢失。然后,我们计算屏幕边缘上将要交叉的点(如果事实证明这是将要交叉的边缘),并将其约束在填充边界内。当我们计算截距时,我们最终会丢失数字上的相关符号,因为[三角函数],所以我们重新应用了我们之前记录的符号。剩下的就是对齐我们的图标和指向屏幕外的小指针箭头。

这种方法非常有效 - 但它有点混乱,而且可能很浪费。我不确定其中有多少是必要的,或者是否可以通过更好地使用 trig 标识来简化其中任何一个。我认为可以通过对截距应用最小值和最大值来将 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
性能 Lua Roblox 可读性 Luau

评论


答: 暂无答案