SceneKitで作成した、立方体ジオメトリの各面タップ時に別々に画面遷移させたい!
プログラミング初心者で、swiftで開発を始めた者です。
SceneKitを使い、ジェスチャーで回転可能な、立方体のジオメトリを作成しました。
次に、この立方体の各面をタップした時に別のViewControllerに画面遷移させたいのですが、方法が分からず、困っています。
import UIKit
import SceneKit
class KuruKuruViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let scene = GameScene() //適用するsceneを設定する
// SCNView 設定
let scnView = self.view as! SCNView
scnView.backgroundColor = UIColor.black
scnView.scene = scene
scnView.showsStatistics = true
scnView.allowsCameraControl = true
// CUBEジオメトリ
let CUBE:SCNGeometry = SCNBox(width: 3, height: 3, length: 3, chamferRadius: 0.4)
let geometryNode = SCNNode(geometry: CUBE)
scene.rootNode.addChildNode(geometryNode)
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleTap(_:)))
scnView.addGestureRecognizer(tapGesture)
追記1 SCNHitTestResultのfaceIndexプロパティを使い画面遷移を試みたが、faceIndexが返す値を「各面に0~5の値が割り当てられている」と間違える。
@objc func handleTap(_ gestureRecognize: UIGestureRecognizer) {
let scnView = self.view as! SCNView
let p = gestureRecognize.location(in: scnView) //pにタップした座標を渡す
let hitResults = scnView.hitTest(p, options: [:]) //hitTestでタップした箇所のオブジェクトを検索
var hitfaceIndex = (hitResults.first)?.faceIndex //初めにヒットしたオブジェクトの面のインデックスを返す
if hitfaceIndex == 0 {
self.performSegue(withIdentifier: "Segue1", sender: nil)
}
if hitfaceIndex == 1 {
self.performSegue(withIdentifier: "Segue2", sender: nil)
}
if hitfaceIndex == 2 {
self.performSegue(withIdentifier: "Segue3", sender: nil)
}
if hitfaceIndex == 3 {
self.performSegue(withIdentifier: "Segue4", sender: nil)
}
if hitfaceIndex == 4 {
self.performSegue(withIdentifier: "Segue5", sender: nil)
}
if hitfaceIndex == 5 {
self.performSegue(withIdentifier: "Segue6", sender: nil)
}
}
追記2 faceIndexが返す値を調べる為にprintを使ってシュミレータで画面タップした時に値を表示するようにした。結果、各面に0と1の値が表示された。(chamferRadiusが0の場合)
各面で値が被るため、個別の画面遷移が出来ない。
import UIKit
import SceneKit
class KuruKuruViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let scene = GameScene()
let scnView = self.view as! SCNView
scnView.backgroundColor = UIColor.black
scnView.scene = scene
scnView.showsStatistics = true
scnView.allowsCameraControl = true
let CUBE:SCNGeometry = SCNBox(width: 3, height: 3, length: 3, chamferRadius: 0.0)
let geometryNode = SCNNode(geometry: CUBE)
scene.rootNode.addChildNode(geometryNode)
//各面毎にマテリアルを設定するためにマテリアル情報を準備
let m1 = SCNMaterial()
let m2 = SCNMaterial()
let m3 = SCNMaterial()
let m4 = SCNMaterial()
let m5 = SCNMaterial()
let m6 = SCNMaterial()
//マテリアルにイメージを設定
m1.diffuse.contents = UIImage(named: "1")
m2.diffuse.contents = UIImage(named: "2")
m3.diffuse.contents = UIImage(named: "3")
m4.diffuse.contents = UIImage(named: "4")
m5.diffuse.contents = UIImage(named: "5")
m6.diffuse.contents = UIImage(named: "6")
//CUBEの各面のマテリアルに設定
CUBE.materials = [m1, m2, m3, m4, m5, m6]
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleTap(_:)))
scnView.addGestureRecognizer(tapGesture)
}
//CUBEの面をタップするとfaceIndexの値をprintする
@objc func handleTap(_ gestureRecognize: UIGestureRecognizer) {
let scnView = self.view as! SCNView
let p = gestureRecognize.location(in: scnView)
let hitResults = scnView.hitTest(p, options: [:])
if let number = hitResults.first?.faceIndex {
print(number)
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
追記3( 解決 ) SCNGeometry
に対して複数のSCNMaterial
を設定した場合、同時にSCNGeometryElement
も複数になり、SCNHitTestResult
のプロパティfaceIndex
では、各面0と1の値しか返さない事が判明した。
そこで、同じSCNHitTestResult
のプロパティであるgeometryIndex
を使ったところ、各面の値を取得することが出来た。
そして、画面遷移のコードに取得した値を割り当てて、「立方体ジオメトリの各面タップ時に別々に画面遷移する」ことに成功した。
import UIKit
import SceneKit
class KuruKuruViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let scene = GameScene() //適用するsceneを設定する
// SCNView 設定
let scnView = self.view as! SCNView
scnView.backgroundColor = UIColor.white
scnView.scene = scene
scnView.showsStatistics = true //統計情報を表示
scnView.allowsCameraControl = true //ジェスチャーで様々なカメラ操作を可能に
// CUBEジオメトリ
let CUBE:SCNGeometry = SCNBox(width: 3, height: 3, length: 3, chamferRadius: 0.0)
let geometryNode = SCNNode(geometry: CUBE)
scene.rootNode.addChildNode(geometryNode) //チルドノードをルートノードに適応
//各面毎にマテリアルを設定するためにマテリアル情報を準備
let m1 = SCNMaterial()
let m2 = SCNMaterial()
let m3 = SCNMaterial()
let m4 = SCNMaterial()
let m5 = SCNMaterial()
let m6 = SCNMaterial()
//マテリアルにイメージを設定
m1.diffuse.contents = UIImage(named: "1")
m2.diffuse.contents = UIImage(named: "2")
m3.diffuse.contents = UIImage(named: "3")
m4.diffuse.contents = UIImage(named: "4")
m5.diffuse.contents = UIImage(named: "5")
m6.diffuse.contents = UIImage(named: "6")
//CUBEの各面のマテリアルに設定
CUBE.materials = [m1, m2, m3, m4, m5, m6]
//タップジェスチャーを追加
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleTap(_:)))
scnView.addGestureRecognizer(tapGesture) //viewをタップするとタップジェスチャーを追加 handleTap関数を実行
}
//<CUBE全体タップ>タップで画面遷移 handleTap関数を設定
@objc func handleTap(_ gestureRecognize: UIGestureRecognizer) {
let scnView = self.view as! SCNView
let p = gestureRecognize.location(in: scnView)
let hitResults = scnView.hitTest(p, options: [:])
let number = hitResults.first?.geometryIndex // ジオメトリのエレメントの値を取得
//画面遷移
if number == 0 {
self.performSegue(withIdentifier: "Segue1", sender: nil)
}
if number == 1 {
self.performSegue(withIdentifier: "Segue2", sender: nil)
}
if number == 2 {
self.performSegue(withIdentifier: "Segue3", sender: nil)
}
if number == 3 {
self.performSegue(withIdentifier: "Segue4", sender: nil)
}
if number == 4 {
self.performSegue(withIdentifier: "Segue5", sender: nil)
}
if number == 5 {
self.performSegue(withIdentifier: "Segue6", sender: nil)
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}