如何定位 SCNCamera 以使对象“刚好适合”视图?

问题描述 投票:0回答:1

我正在使用 SceneKit 制作 3D 棋盘游戏。棋盘本身是平坦的,但上面的棋子是 3D 的。我想定位相机,使其以这样的角度向下看板:

                camera
               /
              /
             /
            /
           /
board     /
___________

我想放置相机,使整个板都在视野中,而且不小。我在运行时知道棋盘的尺寸,但在编译时不知道,因为用户可以从许多不同的棋盘中进行选择来玩游戏。

我试图计算相机至少需要多远才能看到木板的整个宽度。我画了一个这样的图:

enter image description here

计算出距离是板宽度的一半除以 tan(FOV/2)。

将其转换为代码:

private func setupCamera(boardWidth: CGFloat, maxBoardZ: CGFloat, boardCenterX: CGFloat) {
    cameraNode = SCNNode()
    let camera = SCNCamera()
    cameraNode.camera = camera

    let boardRadius = boardWidth / 2
    let cameraDistance = boardRadius / tan(degreesToRadians(camera.fieldOfView) / 2)
    let cameraHeight: Float = 10

    cameraNode.position = SCNVector3(x: Float(boardCenterX), y: cameraHeight, z: Float(maxBoardZ + cameraDistance))

    cameraNode.eulerAngles.x = -0.5
}

cameraNode
是场景的一个属性,我稍后将场景视图的
pointOfView
设置为。

这在纵向模式下看起来不错(这只是整个手机屏幕的一部分):

enter image description here

在不切断电路板部分的情况下,相机不可能离电路板更近。

但在横向中,与它所处的

SCNView
相比,棋盘非常小:

enter image description here

相机本来可以移得更近一些!我本来期望的是这样的:

enter image description here

注意白色背景区域 - 这就是

SCNView

我认为 FOV 可能在横向上发生了变化,但当我检查调试器时,FOV 始终为 60 度,无论方向如何。似乎即使

SCNCamera
fieldOfView
属性,
SCNView
也有自己的、独立的 FOV,这取决于它的宽度和高度,我不知道如何访问它。

如何定位相机,使整个板正好适合在

SCNView
内?

ios swift camera scenekit
1个回答
3
投票

好的 - 将其发布在这里 - 希望没问题。

我只在几张大地图上使用扫射,但最终把它们拿掉了。 我想要所有的地图都适合 - 它看起来与你的非常相似。所以是的,我逆向工作。 我把相机放在我想要的地方,然后摆弄地图和面板尺寸,所以我一次只处理一件事。

我有三角形、四边形和六角形的面板。 这是一款塔防游戏,因此攻击者可以根据面板的类型以多种方式移动。

class Camera
{
    var data = Data.sharedInstance
    var util = Util.sharedInstance
    var gameDefaults = Defaults()
    
    var cameraEye = SCNNode()
    var cameraFocus = SCNNode()
        
    var centerX: Int = 100
    var strafeDelta: Float = 0.8
    var zoomLevel: Int = 35
    var zoomLevelMax: Int = 35              // Max number of zoom levels
    
    //********************************************************************
    init()
    {
        cameraEye.name = "Camera Eye"
        cameraFocus.name = "Camera Focus"
        
        cameraFocus.isHidden = true
        cameraFocus.position  =  SCNVector3(x: 0, y: 0, z: 0)
        
        cameraEye.camera = SCNCamera()
        cameraEye.constraints = []
        cameraEye.position = SCNVector3(x: 0, y: 15, z: 0.1)
        
        let vConstraint = SCNLookAtConstraint(target: cameraFocus)
        vConstraint.isGimbalLockEnabled = true
        cameraEye.constraints = [vConstraint]
    }
    //********************************************************************
    func reset()
    {
        centerX = 100
        cameraFocus.position  =  SCNVector3(x: 0, y: 0, z: 0)
        cameraEye.constraints = []
        cameraEye.position = SCNVector3(x: 0, y: 32, z: 0.1)
        cameraFocus.position = SCNVector3Make(0, 0, 0)
        
        let vConstraint = SCNLookAtConstraint(target: cameraFocus)
        vConstraint.isGimbalLockEnabled = true
        cameraEye.constraints = [vConstraint]
    }
    //********************************************************************
    func strafeRight()
    {
        if(centerX + 1 < 112)
        {
            centerX += 1
            cameraEye.position.x += strafeDelta
            cameraFocus.position.x += strafeDelta
        }
    }
    //********************************************************************
    func strafeLeft()
    {
        if(centerX - 1 > 90)
        {
            centerX -= 1
            cameraEye.position.x -= strafeDelta
            cameraFocus.position.x -= strafeDelta
        }
    }
    //********************************************************************
}

我这样使用GKGraph:

var graphNodes: [GKPanelNode] = [] // All active graph nodes with connections
var myGraph = GKGraph()   // declaring the Graph

然后您的典型面板负载,可能与您的类似

func getQuadPanelNode(vPanelType: panelTypes) -> SCNNode
    {
        let plane = SCNBox(width: 1.4, height: 0.001, length: 1.4, chamferRadius: 0)
        
        plane.materials = []
        plane.materials = setQuadPanelTextures(vPanelType: vPanelType)
        plane.firstMaterial?.isDoubleSided = false
        return SCNNode(geometry: plane)
    }

然后从我创建的地图加载面板。

func loadPanels()
    {
        removePanelNodes()
        for vPanel in mapsDetail.getDetail(vMap: data.mapSelected)
        {
            let vPanel = Panel.init(vName: "Panel:" + vPanel.name, vPanelType: vPanel.type, vPosition: vPanel.pos, vRotation: vPanel.up)
            gridPanels[vPanel.panelName] = vPanel
            
            if(vPanel.type == .entry) { entryPanelName = vPanel.panelName }
            if(vPanel.type == .exit)  { exitPanelName  = vPanel.panelName }
        }
    }
© www.soinside.com 2019 - 2024. All rights reserved.