SKSpriteNode 定位数学让我无法理解?

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

SKSpriteNode 定位数学让我无法理解?

更具体地说,从

UIBezierPath
创建
CGRect
所需的定位。

接下来是一些简短的代码片段...

roomWidth = UIScreen.main.bounds.width
roomHeight = UIScreen.main.bounds.height

// trackOffset = a constant
tracksWidth  = roomWidth - 2*trackOffset   // inset from screen edge
tracksHeight = 0.40*roomHeight - trackOffset

// the `SKSpriteNode` = myTracks
myTracks.anchorPoint = CGPoint(x: 0.5, y: 0.5)

tracksPosX = 0.0
tracksPosY = -roomHeight/2 + trackOffset + tracksHeight/2
myTracks.position = CGPoint(x: tracksPosX, y: tracksPosY)

后来,我从我的

UIBezierPath
创建了一个
trackRect
,定义如下:

trackRect = CGRect(x: tracksPosX - tracksWidth/2,
                   y: tracksPosY,
                   width: tracksWidth,
                   height: tracksHeight)
trainPath = UIBezierPath(ovalIn: trackRect)

但是有些问题

y: tracksPosY
因为在iOS中,原点位于左上角,矩形向右下角延伸。

鉴于 Apple 的此规范,它不应该是:

y: tracksPosY + tracksHeight/2

uibezierpath
1个回答
0
投票

我们需要尝试简化事情——并逐步进行。

所以,让我们使用一个简单的游戏场景,我们将定义一个

rect
并添加一个具有矩形路径的
SKShapeNode
和一个具有椭圆形路径的
SKShapeNode

import UIKit
import GameplayKit

class SimpleGameScene: SKScene {
    
    override func didMove(to view: SKView) {
        
        guard let thisSKView = self.view else { return }
        
        let sz = thisSKView.frame.size
        
        let offset: CGFloat = 80.0
        
        let x: CGFloat = offset
        let y: CGFloat = offset
        let w: CGFloat = sz.width - (offset * 2.0)
        let h: CGFloat = sz.height * 0.4
        
        let myRect: CGRect = .init(x: x, y: y, width: w, height: h)
        
        let shapeNodeRect = SKShapeNode(path: UIBezierPath(rect: myRect).cgPath)
        shapeNodeRect.lineWidth = 7
        shapeNodeRect.strokeColor = .darkGray
        addChild(shapeNodeRect)
        
        let shapeNodeOval = SKShapeNode(path: UIBezierPath(ovalIn: myRect).cgPath)
        shapeNodeOval.lineWidth = 5
        shapeNodeOval.strokeColor = .systemBlue
        addChild(shapeNodeOval)
    }
    
}

看起来像这样:

并且,正如我们所知,y 轴与场景套件“反转”,因此我们的原点为

80,80
,宽度和高度为正值:

如果我们使用相同的值在“普通”应用程序中定义矩形,请使用

CAShapeLayer
而不是场景套件节点:

import UIKit

class ShapeTestVC: UIViewController {
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        view.backgroundColor = .init(red: 0.148, green: 0.148, blue: 0.148, alpha: 1.0)
        
        let thisSKView = UIView()
        thisSKView.frame = view.bounds
        view.addSubview(thisSKView)
        
        let sz = thisSKView.frame.size
        
        let offset: CGFloat = 80.0
        
        let x: CGFloat = offset
        let y: CGFloat = offset
        let w: CGFloat = sz.width - (offset * 2.0)
        let h: CGFloat = sz.height * 0.4
        
        let myRect: CGRect = .init(x: x, y: y, width: w, height: h)
        
        let shapeNodeRect = CAShapeLayer()
        shapeNodeRect.path = UIBezierPath(rect: myRect).cgPath
        shapeNodeRect.lineWidth = 7
        shapeNodeRect.strokeColor = UIColor.darkGray.cgColor
        shapeNodeRect.fillColor = UIColor.clear.cgColor
        thisSKView.layer.addSublayer(shapeNodeRect)
        
        let shapeNodeOval = CAShapeLayer()
        shapeNodeOval.path = UIBezierPath(ovalIn: myRect).cgPath
        shapeNodeOval.lineWidth = 5
        shapeNodeOval.strokeColor = UIColor.systemBlue.cgColor
        shapeNodeOval.fillColor = UIColor.clear.cgColor
        thisSKView.layer.addSublayer(shapeNodeOval)
    }
    
}

我们得到这个:

具有非倒置 y 轴:

这个问题可能令人困惑的是,倒矩形或椭圆形看起来没有任何不同。

所以,让我们添加一个“箭头”贝塞尔曲线路径形状:

    let p1: CGPoint = .init(x: myRect.midX - 60.0, y: myRect.minY + 80.0)
    let p2: CGPoint = .init(x: myRect.midX, y: myRect.minY)
    let p3: CGPoint = .init(x: myRect.midX + 60.0, y: myRect.minY + 80.0)
    let p4: CGPoint = .init(x: myRect.midX, y: myRect.maxY)
    let p5: CGPoint = .init(x: myRect.minX, y: myRect.maxY)
    let p6: CGPoint = .init(x: myRect.maxX, y: myRect.maxY)

    let pth = UIBezierPath()
    
    pth.move(to: p1)
    pth.addLine(to: p2)
    pth.addLine(to: p3)
    pth.move(to: p2)
    pth.addLine(to: p4)
    pth.move(to: p5)
    pth.addLine(to: p6)

让我们将其添加为“普通”应用程序中的另一个

CAShapeLayer

    let shapeNodeArrow = CAShapeLayer()
    shapeNodeArrow.path = pth.cgPath
    shapeNodeArrow.lineWidth = 3
    shapeNodeArrow.strokeColor = UIColor.systemRed.cgColor
    shapeNodeArrow.fillColor = UIColor.clear.cgColor
    thisSKView.layer.addSublayer(shapeNodeArrow)

我们得到了这个(如预期的那样):

如果我们随后将该箭头贝塞尔曲线路径添加为场景套件应用程序中的另一个

SKShapeNode

    let shapeNodeArrow = SKShapeNode(path: pth.cgPath)
    shapeNodeArrow.lineWidth = 3
    shapeNodeArrow.strokeColor = .systemRed
    addChild(shapeNodeArrow)

我们得到这个结果:

现在很容易看到反转的 y 轴反转了我们创建的路径。


再次简化您正在做的事情,以便更容易学习和理解正在发生的事情。它还有助于添加

print(...)
语句或调试断点来评估您的计算。

使用您发布的代码,如果我设置

trackOffset = 80.0
然后最后设置
print(trackRect)
,我会得到(在 iPhone 15 Pro 上):

(-116.5, -215.6, 233.0, 260.8)

这几乎肯定不是你想要的矩形原点。


每个示例的完整代码...

“普通”应用程序,带有

CAShapeLayer

class ShapeTestVC: UIViewController {
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        view.backgroundColor = .init(red: 0.148, green: 0.148, blue: 0.148, alpha: 1.0)
        
        let thisSKView = UIView()
        thisSKView.frame = view.bounds
        view.addSubview(thisSKView)
        
        let sz = thisSKView.frame.size
        
        let offset: CGFloat = 80.0
        
        let x: CGFloat = offset
        let y: CGFloat = offset
        let w: CGFloat = sz.width - (offset * 2.0)
        let h: CGFloat = sz.height * 0.4
        
        let myRect: CGRect = .init(x: x, y: y, width: w, height: h)
        
        let shapeNodeRect = CAShapeLayer()
        shapeNodeRect.path = UIBezierPath(rect: myRect).cgPath
        shapeNodeRect.lineWidth = 7
        shapeNodeRect.strokeColor = UIColor.darkGray.cgColor
        shapeNodeRect.fillColor = UIColor.clear.cgColor
        thisSKView.layer.addSublayer(shapeNodeRect)
        
        let shapeNodeOval = CAShapeLayer()
        shapeNodeOval.path = UIBezierPath(ovalIn: myRect).cgPath
        shapeNodeOval.lineWidth = 5
        shapeNodeOval.strokeColor = UIColor.systemBlue.cgColor
        shapeNodeOval.fillColor = UIColor.clear.cgColor
        thisSKView.layer.addSublayer(shapeNodeOval)
        
        let p1: CGPoint = .init(x: myRect.midX - 60.0, y: myRect.minY + 80.0)
        let p2: CGPoint = .init(x: myRect.midX, y: myRect.minY)
        let p3: CGPoint = .init(x: myRect.midX + 60.0, y: myRect.minY + 80.0)
        let p4: CGPoint = .init(x: myRect.midX, y: myRect.maxY)
        let p5: CGPoint = .init(x: myRect.minX, y: myRect.maxY)
        let p6: CGPoint = .init(x: myRect.maxX, y: myRect.maxY)

        let pth = UIBezierPath()
        
        pth.move(to: p1)
        pth.addLine(to: p2)
        pth.addLine(to: p3)
        pth.move(to: p2)
        pth.addLine(to: p4)
        pth.move(to: p5)
        pth.addLine(to: p6)

        let shapeNodeArrow = CAShapeLayer()
        shapeNodeArrow.path = pth.cgPath
        shapeNodeArrow.lineWidth = 3
        shapeNodeArrow.strokeColor = UIColor.systemRed.cgColor
        shapeNodeArrow.fillColor = UIColor.clear.cgColor
        thisSKView.layer.addSublayer(shapeNodeArrow)
    }
    
}

每个示例的完整代码...

场景套件应用程序与

SKShapeNode

class SimpleGameScene: SKScene {
    
    override func didMove(to view: SKView) {
        
        guard let thisSKView = self.view else { return }
        
        let sz = thisSKView.frame.size
        
        let offset: CGFloat = 80.0
        
        let x: CGFloat = offset
        let y: CGFloat = offset
        let w: CGFloat = sz.width - (offset * 2.0)
        let h: CGFloat = sz.height * 0.4
        
        let myRect: CGRect = .init(x: x, y: y, width: w, height: h)
        
        let shapeNodeRect = SKShapeNode(path: UIBezierPath(rect: myRect).cgPath)
        shapeNodeRect.lineWidth = 7
        shapeNodeRect.strokeColor = .darkGray
        addChild(shapeNodeRect)
        
        let shapeNodeOval = SKShapeNode(path: UIBezierPath(ovalIn: myRect).cgPath)
        shapeNodeOval.lineWidth = 5
        shapeNodeOval.strokeColor = .systemBlue
        addChild(shapeNodeOval)
        
        let p1: CGPoint = .init(x: myRect.midX - 60.0, y: myRect.minY + 80.0)
        let p2: CGPoint = .init(x: myRect.midX, y: myRect.minY)
        let p3: CGPoint = .init(x: myRect.midX + 60.0, y: myRect.minY + 80.0)
        let p4: CGPoint = .init(x: myRect.midX, y: myRect.maxY)
        let p5: CGPoint = .init(x: myRect.minX, y: myRect.maxY)
        let p6: CGPoint = .init(x: myRect.maxX, y: myRect.maxY)
        
        let pth = UIBezierPath()
        
        pth.move(to: p1)
        pth.addLine(to: p2)
        pth.addLine(to: p3)
        pth.move(to: p2)
        pth.addLine(to: p4)
        pth.move(to: p5)
        pth.addLine(to: p6)
        
        let shapeNodeArrow = SKShapeNode(path: pth.cgPath)
        shapeNodeArrow.lineWidth = 3
        shapeNodeArrow.strokeColor = .systemRed
        addChild(shapeNodeArrow)
    }
    
}
© www.soinside.com 2019 - 2024. All rights reserved.