SpriteKitのcontactTestBitMaskを用いた衝突判定を2つのサブクラス(SKSpriteNode)間で行いたい
【追記編集】
理解が進まないので自分の例を交えたいと思います。
現在、私が取り組んでいる2Dゲームのプロジェクトにおけるクラスやスプライトの存在などは以下のようになっています。
- GameScene: SKScene
- Player: SKSpriteNode
- Bullet: SKShapeNode(Player.swiftファイル内)
- Enemy: SKSpriteNode
実現したい流れ
流れを簡単に説明しますと、
- GameSceneが呼び出される
- player(Playerクラス)とenemy(Enemyクラス)の生成
- スペースキーを押すとplayerからbullet(Bulletクラス)が放出(※bulletは連打などにより複数生成される)
- enemyに当たったらgetDamage(Enemyクラス関数)により敵のライフ減少
僭越ながら、以下にファイルの中身を最大限削減した状態で載せさせていただきました。
質問の補足
ファイルの内容を見ていただければわかると思うのですが、contactTestBitMaskを各ファイルごとに1箇所ずつ設定(?)しておいてます。
どのように書けば呼び出されるのかなぁと色々なタイミング方法で試してはいますが、一度も呼び出されたことがありません。また色々なサイトを参考にしましたが、クラスをまたいだ当たり判定や、複数のオブジェクトに対してのものはなにぶん見つからず、自力で試してはみましたがそれらしい情報を得ることは叶いませんでした。
GameScene.swift
import SpriteKit
import GameplayKit
class GameScene: SKScene {
let player = Player(def_pos: CGPoint(x: 0.0, y: 0.0))
// 省略
override func didMove(to view: SKView) {
player.position = CGPoint(x: self.view!.bounds.maxX/2, y: (self.view!.bounds.maxY)/2)
player.setScene(scene: self)
self.addChild(player)
let enemy = Enemy(def_pos: CGPoint(x: 300, y: 300))
enemy.setScene(scene: self)
self.addChild(enemy)
player.physicsBody?.contactTestBitMask = enemy.physicsBody!.categoryBitMask
}
override func keyDown(with event: NSEvent) {
switch event.keyCode {
// 省略
case 49:
player.shoot()
default:
break
}
}
override func keyUp(with event: NSEvent) {
// 省略
}
override func update(_ currentTime: TimeInterval) {
// Called before each frame is rendered
// 省略
self.player.update()
}
}
extension GameScene: SKPhysicsContactDelegate {
func didBegin(_ contact: SKPhysicsContact) {
print("------------衝突しました------------")
}
}
Player.swift
import Foundation
import SpriteKit
class Player: SKSpriteNode, SKPhysicsContactDelegate {
var gameScene: SKScene!
func setScene(scene: SKScene) {
self.gameScene = scene
print(scene)
}
init(def_pos: CGPoint) {
// 省略
super.init(texture: textures[2], color: NSColor.clear, size: CGSize(width: 40, height: 40))
self.position = def_pos
// 省略
self.physicsBody = SKPhysicsBody(circleOfRadius: 10)
self.physicsBody?.affectedByGravity = false
self.physicsBody?.allowsRotation = false
self.physicsBody?.isDynamic = false
self.physicsBody?.categoryBitMask = 0b1000
self.physicsBody?.collisionBitMask = 0b1000
self.physicsBody?.contactTestBitMask = 0b0000
}
// 省略
func shoot() {
let bullet = SKShapeNode(circleOfRadius: 3)
bullet.fillColor = NSColor.yellow
bullet.position = self.position
bullet.physicsBody = SKPhysicsBody(circleOfRadius: 3)
bullet.physicsBody?.affectedByGravity = false
bullet.physicsBody?.categoryBitMask = 0b0100
bullet.physicsBody?.collisionBitMask = 0b0100
bullet.physicsBody?.velocity = CGVector(dx: 0, dy: 300)
self.gameScene.addChild(bullet)
}
// 省略
func update() {
}
func didBegin(_ contact: SKPhysicsContact) {
print("collision occured by player")
getDamaged()
}
required init?(coder aDecoder: NSCoder) {
fatalError()
}
}
Enemy.swift
import Foundation
import SpriteKit
class Enemy: SKSpriteNode, SKPhysicsContactDelegate {
var gameScene: SKScene!
func setScene(scene: SKScene) {
self.gameScene = scene
print(scene)
}
var health: Int!
init(def_pos: CGPoint) {
// 省略
super.init(texture: textures.first, color: NSColor.clear, size: CGSize(width: 200, height: 200))
self.position = def_pos
self.health = 100
self.physicsBody = SKPhysicsBody(circleOfRadius: 80)
self.physicsBody?.affectedByGravity = false
self.physicsBody?.allowsRotation = false
self.physicsBody?.isDynamic = false
self.physicsBody?.velocity = CGVector(dx: 0, dy: 0)
self.physicsBody?.categoryBitMask = 0b0001
self.physicsBody?.collisionBitMask = 0b0001
self.physicsBody?.contactTestBitMask = 0b0100
// 省略
}
func getDamaged() {
// ダメージを受けたときの処理
self.health -= 10
}
func didBegin(_ contact: SKPhysicsContact) {
print("collision occured by enemy")
getDamaged()
}
// 省略
required init?(coder aDecoder: NSCoder) {
fatalError()
}
}
それぞれのサブクラスはphysicsBodyをもち、スプライトひとつひとつはcategoryBitMaskとcollisionBitMaskを持っています。
しかしながら、どこにcontactTestBitMaskを書けばいいのかがわかりません。
いわゆる物理エンジンによる、自然な動き(衝突した後の動作など)は全く問題なく機能しますが、GameScene(最初に呼び出されるシーン)において定義した
extension GameScene: SKPhysicsContact Delegate {
func didBegin:(_ contact: SKPhysicsContact) {
print("衝突!")
}
といったものを動作させたいときに色々と試しましたがいずれも表示されませんでした。
とすることで全ての敵に対し衝突判定を出せるかと思いましたが、意図したように動きませんでした。
それに、私が試みようとしていることって、ゲーム作りにおいてはかなり基本的というか、誰もがやるようなことだと思うので、SpriteKitの方で何かしらメソッドがある気がしてなりません。
もし、ご助力いただけるのであれば些細なことでも感謝いたします。回答よろしくお願いします。