如何集成UIDevice旋转并在旋转后创建新的UIBezierPath?

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

如何集成UIDevice旋转并在旋转后创建新的UIBezierPath?

我的挑战是成功集成

UIDevice
旋转并在每次 UIBezierPath 旋转时创建一个
new
UIDevice

编辑: 根据 DonMag 的建议更改为

viewDidLayoutSubviews
。这是有道理的,因为我希望在调整所有 SKSpriteNode 的大小并重新定位后,生成一个新的 UIBezierPath 旋转后

哦,我多么希望这能奏效..但它确实奏效了没有

作为序言,我在之间来回跳动

    NotificationCenter.default.addObserver(self,
                                           selector: #selector(rotated),
                                           name: UIDevice.orientationDidChangeNotification,
                                           object: nil)

在我的

viewDidLoad()
内与

一起调用
    @objc func rotated() {

    }

override func viewDidLayoutSubviews() {

    // please see code below

}

当我实现

viewDidLayoutSubviews()
时,与
rotated()
相比,我的成功要好得多。所以让我提供
 viewDidLayoutSubviews()
的详细代码。

我得出的结论是,每次旋转

UIDevice
时,都需要生成 new
UIBezierPath
,因为我的各种
positions
sizes
SKSprieNodes
会发生变化。

我绝对不是说我必须在每次旋转时创建一个新的

UIBezierPath
..只是说我认为我必须这样做。

代码开始

// declared at the top of my `GameViewController`:
var myTrain: SKSpriteNode!
var savedTrainPosition: CGPoint?
var trackOffset = 60.0
var trackRect: CGRect!
var trainPath: UIBezierPath!

我的

UIBezierPath
创作和
SKAction.follow
代码如下:

// called with my setTrackPaths() – see way below
func createTrainPath() {
    
    // savedTrainPosition initially set within setTrackPaths().
    // We no longer keep tabs on this Position because
    // UIBezierPath's built-in .currentPoint does that for us.
    trackRect = CGRect(x: savedTrainPosition!.x,
                       y: savedTrainPosition!.y,
                       width: tracksWidth,
                       height: tracksHeight)
    trainPath = UIBezierPath(ovalIn: trackRect)
    trainPath = trainPath.reversing()   // makes myTrain move CW
                                    
}   // createTrainPath


func startFollowTrainPath() {
   
    let theSpeed = Double(5*thisSpeed)

    var trainAction = SKAction.follow(
                                  trainPath.cgPath,
                                  asOffset: false,
                                  orientToPath: true,
                                  speed: theSpeed)
    trainAction = SKAction.repeatForever(trainAction)
    createPivotNodeFor(myTrain)
    myTrain.run(trainAction, withKey: runTrainKey)

}   // startFollowTrainPath


func stopFollowTrainPath() {
    
    guard myTrain == nil else {
        myTrain.removeAction(forKey: runTrainKey)
        savedTrainPosition = myTrain.position
        return
    }
    
}   // stopFollowTrainPath

这是详细的

viewWillLayoutSubviews
我之前承诺过的:

override func viewDidLayoutSubviews() {
    
    super.viewDidLayoutSubviews()
    
    if (thisSceneName == "GameScene") {

        // code to pause moving game pieces

        setGamePieceParms()   // for GamePieces, e.g., trainWidth
        setTrackPaths()       // for trainPath
        reSizeAndPositionNodes()
            
        // code to resume moving game pieces

    }   // if (thisSceneName == "GameScene")
            
}   // viewDidLayoutSubviews


    func setGamePieceParms() {
        
        if (thisSceneName == "GameScene") {
        
            roomScale = 1.0
            let roomRect = UIScreen.main.bounds
            roomWidth    = roomRect.width
            roomHeight   = roomRect.height
            roomPosX = 0.0
            roomPosY = 0.0

            tracksScale = 1.0
            tracksWidth  = roomWidth - 4*trackOffset   // inset from screen edge
#if os(iOS)
            if UIDevice.current.orientation.isLandscape {
                tracksHeight = 0.30*roomHeight
            }
            else {
                tracksHeight = 0.38*roomHeight
            }
#endif
            // center horizontally
            tracksPosX = roomPosX
            // flush with bottom of UIScreen
            let temp = roomPosY - roomHeight/2
            tracksPosY = temp + trackOffset + tracksHeight/2

            trainScale = 2.8
            trainWidth  = 96.0*trainScale   // original size = 96 x 110
            trainHeight = 110.0*trainScale
            trainPosX = roomPosX
#if os(iOS)
            if UIDevice.current.orientation.isLandscape {
                trainPosY = temp + trackOffset + tracksHeight + 0.30*trainHeight
            }
            else {
                trainPosY = temp + trackOffset + tracksHeight + 0.20*trainHeight
            }
#endif

    }   // setGamePieceParms

// a work in progress
func setTrackPaths() {
   
    if (thisSceneName == "GameScene") {
        
        if (savedTrainPosition == nil) {                
            savedTrainPosition = CGPoint(x: tracksPosX - tracksWidth/2, y: tracksPosY)
        }
        else {
            savedTrainPosition = CGPoint(x: tracksPosX - tracksWidth/2, y: tracksPosY)
        }
        
        createTrainPath()

    }   // if (thisSceneName == "GameScene")

}   // setTrackPaths

func reSizeAndPositionNodes() {

    myTracks.size = CGSize(width: tracksWidth, height: tracksHeight)
    myTracks.position = CGPoint(x: tracksPosX, y: tracksPosY)

    // more Nodes here ..

}

代码结束

我的理论表明,当我在每次

setTrackPaths()
旋转时调用
UIDevice
时,应该调用
createTrainPath()

UIBezierPath
而言,视觉上没有发生任何有意义的事情..直到我打电话给
 startFollowTrainPath()

底线

就在那时,我确信已经创建了一个新的

UIBezierPath
,因为当我旋转createTrainPath()时调用
UIDevice
时,它应该是这样的。

new

UIBezierPath 不是新的,而是旧的。

如果您已经通过我的长代码做到了这一点,

问题是我需要做什么才能制作一个新的

UIBezierPath来适应调整大小和重新定位的

SKSpriteNode

    

uibezierpath
1个回答
0
投票

使用

scene.scaleMode = .resizeFill

时,我们可以在

override func didChangeSize(_ oldSize: CGSize)
类中实现
SKScene
。当场景大小发生变化时(例如设备旋转时),将调用此函数。
因此,对于一个非常简单的示例,如下所示:

我们可以使用这张图片(名为“arrow2”):

以及这个示例代码...


GameViewController 类

import UIKit import SpriteKit import GameplayKit class GameViewController: UIViewController { var scene: GameScene! override func viewDidLoad() { super.viewDidLoad() scene = GameScene(size: view.frame.size) scene.scaleMode = .resizeFill if let skView = view as? SKView { skView.presentScene(scene) } } override var supportedInterfaceOrientations: UIInterfaceOrientationMask { return .all } override var prefersStatusBarHidden: Bool { return true } }


游戏场景类

import SpriteKit import GameplayKit class GameScene: SKScene { var spOval: SKShapeNode! var myTrain: SKSpriteNode! var trainPath: UIBezierPath! var currentSize: CGSize = .zero override func didMove(to view: SKView) { // ellipse frame will be set in updateFraming spOval = SKShapeNode(ellipseIn: .zero) spOval.lineWidth = 5 spOval.strokeColor = .lightGray addChild(spOval) myTrain = SKSpriteNode(imageNamed: "arrow2") addChild(myTrain) updateFraming() startAnim() } override func didChangeSize(_ oldSize: CGSize) { if let v = self.view { // this can be called multiple times on device rotation, // so we only want to update the framing and animation // if the size has changed if currentSize != v.frame.size { currentSize = v.frame.size updateFraming() startAnim() } } } func updateFraming() { // self.view is optional, so safely unwrap guard let thisSKView = self.view else { return } let sz = thisSKView.frame.size // make the ellipse width equal to view width minus 120-points on each side let w: CGFloat = sz.width - 240.0 // if view is wider than tall (landscape) // set ellipse height to 30% of view height // else (portrait) // set ellipse height to 38% of view height let h: CGFloat = sz.width > sz.height ? sz.height * 0.3 : sz.height * 0.38 // center horizontally let x: CGFloat = (sz.width - w) * 0.5 // put bottom of ellipse 40-points from bottom of view let y: CGFloat = 40.0 let r: CGRect = .init(x: x, y: y, width: w, height: h) // create the "path to follow" trainPath = UIBezierPath(ovalIn: r).reversing() // update the visible oval spOval.path = trainPath.cgPath } func startAnim() { var trainAction = SKAction.follow( trainPath.cgPath, asOffset: false, orientToPath: true, speed: 200.0) trainAction = SKAction.repeatForever(trainAction) myTrain.run(trainAction, withKey: "myKey") } }

这是完整项目的链接:
https://github.com/DonMag/SpriteKitRotation

不完全确定这会给你想要的东西,因为你有很多不清楚的代码......但希望它至少能让你朝着正确的方向前进。

© www.soinside.com 2019 - 2024. All rights reserved.