67714


Nodeをタッチした際に動作させたいアクションがあるのですが、Nodeのタッチを検出致しません。  
課題としては別クラスからのメソッドの引用方法と解釈しています。  
情報不足ございましたらお申し付けください。

【実現したい事】  
class WorldNodeでは画面をスクロール動作する為のコードを記載しています。  
class GameScene内のtouchesEndedでclass WorldNodeのcontentNodeをタッチした際に、class
GameScene内のメソッドを動作させたいです。  
逆にclass WorldNode内のtouchesEndedでclass GameSceneのメソッドを動作させる方法でも問題ないと考えています。

・タッチしたいNode：class WorldNodeのcontentNode

[code]

    var viewSizeWidth: Int = 375
    var viewSizeHeight: Int = 812
    
    class GameScene: SKScene, SKPhysicsContactDelegate {
    
        var battleship: SKSpriteNode!
    
        let worldNode =  WorldNode(size: CGSize(width: viewSizeWidth * 8, height: viewSizeHeight * 8))
    
        override func didMove(to view: SKView) {
            self.addChild(worldNode)
        }
    
        override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
           let touch = touches.first
           let location = touch!.location(in: self)
    //ここにclass WorldNode内のcontentNodeをタッチした際、class GameScene内のメソッドを動作させたい
        }
    
    //このメソッドをタッチイベント(touchesBegan)で使用したい。
        func addBattleship() {
            let battleship = SKSpriteNode(imageNamed: "battleship")
            self.battleship = battleship
            battleship.position = CGPoint(x: 0, y: 0)
            battleship.size = CGSize(width: 50, height: 50)
            battleship.zPosition = 2.0
            self.worldNode.contentNode.addChild(battleship)
            let move = SKAction.moveTo(y: 2700, duration: 10)
            battleship.run(move)
        }
    
    }
    
    //WorldNodeでは、画面をtouchesMovedでスクロールする為のコードを書いています。
    //class WorldNodeのプロパティである、contentNodeをclass GameScene内のメソッドで使用したいです。
    class WorldNode: SKSpriteNode {
    
        var contentNode = SKNode()
    
        private var startX: CGFloat = 0.0
        private var lastX: CGFloat = 0.0
        private var startY: CGFloat = 0.0
        private var lastY: CGFloat = 0.0
    
        init(size: CGSize) {
            super.init(texture: nil, color: SKColor.gray, size: size)
            self.isUserInteractionEnabled = true
            self.contentNode.position = CGPoint(x: 0, y: 0)
            self.contentNode.name = "contentNode"
            let ConstraintYRange = SKRange (lowerLimit: self.frame.minY + 500, upperLimit: self.frame.maxY - 500)
            let ConstraintXRange = SKRange (lowerLimit: self.frame.minX + self.frame.maxX / 2, upperLimit: self.frame.maxX - self.frame.maxX / 2)
            let yconst = SKConstraint.positionY(ConstraintYRange)
            let xconst = SKConstraint.positionX(ConstraintXRange)
            contentNode.constraints = [yconst, xconst]
            self.addChild(contentNode)
    
        }
    
        required init?(coder aDecoder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
    
            override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    
                let touch = touches.first
                let location = touch!.location(in: self)
                startX = location.x
                lastX = location.x
                startY = location.y
                lastY = location.y
            }
    
        override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
            let touch = touches.first
            let location = touch!.location(in: self)
    //ここにclass GameSceneのメソッドを動作させてもOK？（GameSceneのメソッドを引っ張る方法分かりません）
    }
    
            override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
    
                let touch: UITouch = touches.first!
                let location =  touch.location(in: self)
    
                let currentX = location.x
                let currentY = location.y
    
                let scrollspeed: CGFloat = 1.0
                let newX = self.contentNode.position.x + ((currentX - lastX) * scrollspeed)
                let newY = self.contentNode.position.y + ((currentY - lastY) * scrollspeed)
    
                let limitFactir: CGFloat = 100
                let leftLimitX = self.size.width * (-limitFactir)
                let rightLimitX = self.size.width * limitFactir
                let lowLimitY = self.size.height * (-limitFactir)
                let heightLimitY = self.size.height * limitFactir
    
                if newX < leftLimitX && newY < lowLimitY {
                    self.contentNode.position = CGPoint(x: leftLimitX, y: self.contentNode.position.y)
                    self.contentNode.position = CGPoint(x: self.contentNode.position.x, y: lowLimitY)
                } else if newX > rightLimitX && newY > heightLimitY {
                    self.contentNode.position = CGPoint(x: rightLimitX, y: contentNode.position.y)
                    self.contentNode.position  = CGPoint(x: self.contentNode.position.y, y: heightLimitY)
                } else {
                    self.contentNode.position = CGPoint(x: newX, y: self.contentNode.position.y)
                    self.contentNode.position = CGPoint(x: self.contentNode.position.x, y: newY)
                }
    
    
                lastX = currentX
                lastY = currentY
    
            }
        }
    
    
    
[/code]


CC BY-SA 4.0
2020-06-15T15:44:40.440
67694
2020-06-16T07:41:05.457
39881
question
swift xcode spritekit
Swift SpriteKit メソッド、プロパティを別クラスで使用したい


タッチイベントは、プロパティ`isUserInteractionEnabled`が`true`のノードが受け取ると、そこで寿命を完結させ、消滅します。質問者さんは、「いやいや、そんなケチなこと言わずに、`GsmeScene`にもタッチイベントを渡してくださいよ」と思われているのでしょうが、`SpriteKit`の（というか、`UIResponder`クラスの）仕組みは、そうなっていません。その仕組みを変更させるよりは、だったら他の手段を考えた方が良さそうです。

[code]

    class WorldNode: SKSpriteNode {
    
    // 省略
    
        override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
            // 省略
            self.scene?.touchBegan(touches, event)
        }
    
    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
            // 省略
            self.scene?.touchEnded(touches, event)
        }
    
        override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
            // 省略
            self.scene?.touchMoved(touches, event)
        }
    }
    
[/code]

`GameScene`クラスの、タッチイベントを受け取る各メソッドを、直接`WorldNode`クラスのタッチイベントを受け取るクラスで呼んでやるという手段を考えてみました。


CC BY-SA 4.0
2020-06-16T05:22:38.660
67709
2020-06-16T05:22:38.660
18540
67694
answer


まず最初に、 ** _Nodeのタッチを検出致しません_**
と書かれていますが、これでは「`touchesBegan(_:with:)`などのタッチイベント処理メソッドが呼ばれない」と言うふうに読み取れます。まずは、「タッチイベント処理メソッドが呼ばれない」のか、「タッチイベント処理メソッドの中でうまく処理を記述できない」のか、はっきりさせて下さい。

次に、 ** _WorldNodeのcontentNodeをタッチ_** とか、 ** _タッチしたいNode：class
WorldNodeのcontentNode_**
とありますが、`WorldNode`クラスの`contentNode`は、最初は空の`SKNode`で初期化されていますので、画面に表示される対象を持ちません。画面に表示される領域がないものに対する「タッチを検出」することは出来ませんので、この部分は意味を持ちません。

* * *

と言うわけで、意味が理解できない部分は一旦忘れて、「別クラスからのメソッドの引用」に関する部分だけを考えます。

まず、`addBattleship()`メソッドですが、追加したnodeを`GameScene`クラスの`battleship`プロパティに設定する部分以外は`WorldNode`クラス内の処理だけで完結するのですから、`WorldNode`クラスのメソッドとするべきでしょう。

`WorldNode`クラスから`GameScene`のインスタンスにアクセスするには`scene`と言うプロパティが使えるのですが、型が`SKScene`になっていて使いにくいので、ちょっとした計算型プロパティを定義して、`GameScene`型としてアクセス出来るようにしておきます。

[code]

    class WorldNode: SKSpriteNode {
    
        var gameScene: GameScene? { scene as? GameScene }
    
        //...
    }
    
[/code]

その上で`addBattleship()`メソッドを`WorldNode`内に定義してやれば、特に問題なく呼び出せるでしょう。

[code]

        override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
            //print(type(of: self), #function) //デバッグ用
    
            let battleship = addBattleship()
            self.gameScene?.battleship = battleship
        }
    
        func addBattleship() -> SKSpriteNode {
            let battleship = SKSpriteNode(imageNamed: "battleship")
            battleship.position = CGPoint(x: 0, y: 0)
            battleship.size = CGSize(width: 50, height: 50)
            battleship.zPosition = 2.0
            self.contentNode.addChild(battleship)
            let move = SKAction.moveTo(y: 2700, duration: 10)
            battleship.run(move)
    
            return battleship
        }
    
[/code]

* * *

インスタンスメソッドであろうが、インスタンスプロパティであろうが、他クラスのものを呼び出してやりたければ、`self.gameScene?.battleship
= battleship`のように`(そのクラスのインスタンス).`をメソッド名、インスタンス名の前に付けてやるだけです。

(この例の場合はOptionalなんで、`.`ではなく`?.`になっていますが。)

どのクラスか、よりも強く「どのインスタンスか」を強く意識して下さい。

* * *

あちこちよくわからないところを無視して書いたので、全く使い物にならないことを書いてしまった可能性もあります。その際にはコメントしていただくなり、ご質問を改善していただくなりをお願いします。


CC BY-SA 4.0
2020-06-16T07:41:05.457
67714
2020-06-16T07:41:05.457
13972
67694
answer