SpriteKit - 获取缩放场景上可见区域的实际大小/帧.AspectFill

问题描述 投票:3回答:4

我一直在SpriteKit制作游戏,在支持所有屏幕尺寸时遇到了问题。我尝试通过将所有场景缩放模式设置为.AspectFill并为所有宽度为480且高度为800的屏幕设置固定大小来解决此问题,如下所示:

让scene = GameScene(大小:CGSize(宽度:480,高度:800))

我去添加一个与游戏场景边缘对齐的边框(SKShapeNode),我发现self.frame.width和self.frame.height不会给出设备可见区域的尺寸。经过一些研究后,我通过使用UIScreen.mainScreen()。bounds.height和UIScreen.mainScreen()。bounds.width发现了很多建议。我实现了这种获取可见屏幕尺寸的方式,它仍然给我的尺寸与玩家可见的场景的实际尺寸不匹配。

我不确定这是否与我的所有场景有关,无论设备的分辨率是否具有固定的宽度和高度,并使用.AspectFill进行缩放。我一直坚持支持所有屏幕尺寸一段时间的问题,我不确定如何去做。如果它有助于我使用矢量图形文件而不是PNG。所以我的问题是:是否有任何支持所有屏幕尺寸的快捷方式,或者我是否必须使用硬编码每个设备上场景中每个节点的坐标?我不明白为什么这会是解决方案,因为它似乎太乏味而且代码看起来很混乱。

我还询问如何获得一个矩形/框架,在场景缩放后给出设备上场景可见区域的尺寸?

我很抱歉,如果我之前的任何一个问题被提出过,但我找到的每一个答案要么没有特别解决我的问题,要么写得太乱了。我想我已经提供了足够的信息来解决这个问题,但如果我没有,请向我询问所需的信息。

非常感谢,任何帮助表示赞赏

ios sprite-kit screen resolution visible
4个回答
8
投票

以下是Aspect Fill的工作原理:

场景的尺寸将始终保持不变,因此如果您说场景是480点乘800点,无论设备如何,它都将保持不变。

然后发生的是它将从SKView的中心开始缩放场景,直到它填满最远的墙壁。

这将反过来裁剪你的场景。

因此,在跨不同宽高比设计游戏时,您需要设计游戏来处理裁剪。

现在,在您的特定情况下,您需要弄清楚设备的宽高比是什么,并消除与场景方面的差异。

例如。您的场景是3x5,设备是9x16,这意味着您的设备将增长到9.6x16,以填充9x16设备的大小。我们得到这个因为我们的高度比我们的宽度有更多的距离,所以我们需要做3/5 = a / 16,3 * 16 = a * 5,(3 * 16)/ 5 = a所以a = 9.6。

这意味着你有宽度的.6裁剪场景,两边都有.3。

现在您要问,.3如何与各种屏幕尺寸相关联。

好吧,我们需要弄清楚.3是9.6的百分比。我们用分区来做,.3 / 9.6 = .03125或3.125%;这意味着我们需要将3.125%的边界推向场景的宽度,即15点。这意味着.03125 * 480 = 15.然后你的边界应该从15宽度和465开始。

伪代码:

let newSceneWidth = (scene.width * view.height) / (scene.height)
let sceneDifference = (newSceneWidth - view.Width)/2
let diffPercentage = sceneDifference / (newSceneWidth)

let leftEdge = diffPercentage * scene.width;
let rightEdge = scene.width - (diffPercentage * scene.width);

现在,如果高度上的距离比宽度更大,则必须交换宽度和高度变量。


6
投票

感谢您的回答,特别感谢KnightOfDragon,他帮我解决了我的问题。我所做的是创建一个产生CGRect的函数,该函数考虑了设备的尺寸以及场景的预定宽高比。这可以用作边界,并使场景中节点的相对定位更容易,以便它们对于所有设备都是可见的。再次特别感谢KnightOfDragon回答了解决方案代码的基础,如果没有它们,那将是困难的,我将无法分享这个整体解决方案。这是Swift中的最终代码:

//Returns a CGRect that has the dimensions and position for any device with respect to any specified scene. This will result in a boundary that can be utilised for positioning nodes on a scene so that they are always visible
func getVisibleScreen(var sceneWidth: Float, var sceneHeight: Float, viewWidth: Float, viewHeight: Float) -> CGRect {
    var x: Float = 0
    var y: Float = 0

    let deviceAspectRatio = viewWidth/viewHeight
    let sceneAspectRatio = sceneWidth/sceneHeight

    //If the the device's aspect ratio is smaller than the aspect ratio of the preset scene dimensions, then that would mean that the visible width will need to be calculated
    //as the scene's height has been scaled to match the height of the device's screen. To keep the aspect ratio of the scene this will mean that the width of the scene will extend
    //out from what is visible. 
    //The opposite will happen in the device's aspect ratio is larger.
    if deviceAspectRatio < sceneAspectRatio {
        let newSceneWidth: Float = (sceneWidth * viewHeight) / sceneHeight
        let sceneWidthDifference: Float = (newSceneWidth - viewWidth)/2
        let diffPercentageWidth: Float = sceneWidthDifference / (newSceneWidth)

        //Increase the x-offset by what isn't visible from the lrft of the scene
        x = diffPercentageWidth * sceneWidth
        //Multipled by 2 because the diffPercentageHeight is only accounts for one side(e.g right or left) not both
        sceneWidth = sceneWidth - (diffPercentageWidth * 2 * sceneWidth)
    } else {
        let newSceneHeight: Float = (sceneHeight * viewWidth) / sceneWidth
        let sceneHeightDifference: Float = (newSceneHeight - viewHeight)/2
        let diffPercentageHeight: Float = fabs(sceneHeightDifference / (newSceneHeight))

        //Increase the y-offset by what isn't visible from the bottom of the scene
        y = diffPercentageHeight * sceneHeight
        //Multipled by 2 because the diffPercentageHeight is only accounts for one side(e.g top or bottom) not both
        sceneHeight = sceneHeight - (diffPercentageHeight * 2 * sceneHeight)
    }

    let visibleScreenOffset = CGRect(x: CGFloat(x), y: CGFloat(y), width: CGFloat(sceneWidth), height: CGFloat(sceneHeight))
    return visibleScreenOffset
}

1
投票

我不知道为什么:

struct Constants {
    static let vcHeight = UIScreen.mainScreen().bounds.height
    static let vcWidth = UIScreen.mainScreen().bounds.width
}

没有给出适当的界限。但我不知道你为什么要使用.aspectFill来获得合适的尺寸。 当我制作我的游戏时,我使用了这个

let position.x = vcHeight/3   //to putting him on a third of the screen for all iPhone's 

这也可用于获得合适的尺寸。我对矢量图形一无所知,所以我无法帮助你。


0
投票

实际上有一种非常简单的方法可以做到这一点。 SKScene上的convertPoint(fromView:)函数允许将点从包含SKView的坐标系转换为场景空间坐标。因此,您可以获取视图的原点和等于其最远范围的点,将它们转换为场景的坐标空间,并像这样测量差异:

extension SKScene {
    func viewSizeInLocalCoordinates() -> CGSize {
        let reference = CGPoint(x: view!.bounds.maxX, y: view!.bounds.maxY)
        let bottomLeft = convertPoint(fromView: .zero)
        let topRight = convertPoint(fromView: reference)
        let d = topRight - bottomLeft
        return CGSize(width: d.x, height: d.y)
    }
}

(请注意,我也使用了CGPoint的扩展,它允许我直接使用点进行加法和减法。见下文。另请注意,在场景出现后调用此函数是安全的;我有点懒散的强制展开view,如果现场还没有显示,那将是nil。)

extension CGPoint {
    static func +(lhs: CGPoint, rhs: CGPoint) -> CGPoint {
        return CGPoint(x: lhs.x + rhs.x, y: lhs.y + rhs.y)
    }
    static func -(lhs: CGPoint, rhs: CGPoint) -> CGPoint {
        return CGPoint(x: lhs.x - rhs.x, y: lhs.y - rhs.y)
    }
}

重要提示:这假定场景没有分配摄像机节点,或者摄像机节点的比例是1:1。如果您添加自己的相机节点并允许其缩放以创建缩放效果,请使用下面的代码。另请注意,连接到摄像机节点的节点本身不会与场景的其余部分一起缩放。我通过提供ignoreCameraScale在我的代码中允许这样做。如果测量值与使用相机移动和缩放的HUD元素相关,则将此参数传递给true。

extension SKScene {
    func viewSizeInLocalCoordinates(ignoreCameraScale: Bool = false) -> CGSize {
        let reference = CGPoint(x: view!.bounds.maxX, y: view!.bounds.maxY)
        var bottomLeft = convertPoint(fromView: .zero)
        var topRight = convertPoint(fromView: reference)

        if ignoreCameraScale, let camera = camera {
            bottomLeft = camera.convert(bottomLeft, from: self)
            topRight = camera.convert(topRight, from: self)
        }

        let d = topRight - bottomLeft
        return CGSize(width: d.x, height: -d.y)
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.