WWDC 2017 の SceneKit サンプル Fox 2 を調べる その12
Character.swift を見てゆく。
この Swift ファイルはプレイヤーキャラクターである Max の設定とグローバルの関数で構成されている。
キャラクター設定は NSObjct となっており、
GameController.swift の GameController のイニシャライズ時に setupCharacter() が呼ばれ、その中でこのキャラクター設定が呼ばれる。
流れ
Character クラス の初期化時に Max の設定、パーティクル、サウンド(SE)、アニメーション情報を読み込んでいる。
それを元に update 関数で移動、ジャンプ、攻撃、アニメーション、SE 再生、障害物の判定が設定されており、 Character クラスの呼び先である GameController クラスの 描画時に呼ばれる SceneRenderer で毎フレームこちらの update 関数が呼ばれている。
コード内容
import
Foundation、SceneKit、simd が呼ばれている。
import Foundation import SceneKit import simd
グローバル関数
地面とキャラクターの接点を求める。
地面である平面の地点、法線、キャラ接地面の法線と接地点と重力を加味した値を元に、
平面の法線とキャラの接地点、平面の法線とキャラの接地面の法線を内積し、割って平面の地点から引く。
この Swift ファイルで設定されている Character クラスの最後の方でこちらを使ってキャラの移動場所を求めている。
func planeIntersect(planeNormal: float3, planeDist: Float, rayOrigin: float3, rayDirection: float3) -> Float { return (planeDist - simd_dot(planeNormal, rayOrigin)) / simd_dot(planeNormal, rayDirection) }
Character クラス
NSObject で設定されており、GameController でこちらをイニシャライズされる時、SCNScene を受け取る。
class Character: NSObject { ... }
以下、Character クラスの中身
定数 / 変数
static private let speedFactor: CGFloat = 2.0 static private let stepsCount = 10
変数名 | 説明 |
---|---|
speedFactor | Max の歩くスピードの初期値 |
stepsCount | 後で設定している steps の配列の上限。Step_rock_00 から 09 の mp3 ファイルを SCNAudioSource として設定する。 |
static private let initialPosition = float3(0.1, -0.2, 0)
シーン上に Max が登場する初期位置、またはリセットされた時元に戻すために使う値。
ちなみに地面よりある程度下に行った場合にリセットされるが、基本的にはステージのコリジョンが行く手を阻むため Max が有効範囲外に行く事はなくリセットはされない。
static private let gravity = Float(0.004) static private let jumpImpulse = Float(0.1) static private let minAltitude = Float(-10) static private let enableFootStepSound = true static private let collisionMargin = Float(0.04) static private let modelOffset = float3(0, -collisionMargin, 0) static private let collisionMeshBitMask = 8
変数名 | 説明 |
---|---|
gravity | 重力の値。Max の downwardAcceleration で高度を設定しおり、この値で引く |
jumpImpulse | ジャンプ時の弾み具合。downwardAcceleration にこの値を足す |
minAltitude | 高度の最小値。これより下に行くとキャラクターの位置がリセットされる |
enableFootStepSound | 歩行音を鳴らすフラグ |
collisionMargin | コリジョンとキャラとの Y 軸のマージンの設定値 |
modelOffset | キャラと地面のオフセット値を SCNVector3 で設定。 Y 軸にマイナスした collisionMargin を設定している |
collisionMeshBitMask | コリジョン用のメッシュで当たり判定を行うためのビットマスク |
enum GroundType: Int { case grass case rock case water case inTheAir case count }
Max の現在の状態を調べるための列挙。
キャラクター (Max)
private var characterNode: SCNNode! private var characterOrientation: SCNNode! private var model: SCNNode!
Max の設定はシーン上では characterNode > characterOrientation > model というノードの構成となっている。
変数名 | 説明 |
---|---|
characterNode | シーンの addChild されている。移動や効果音再生のアクションなどを設定するトップレベルのノード |
characterOrientation | characterNode の子となるノードで回転を設定している |
model | characterOrientation の子となるノードで Max の scn ファイルのジオメトリがあるノードをこちらに設定している。また、ボーンを使用したアニメーションのアクションもこのノードで行なっている |
物理アニメーション設定
private var characterCollisionShape: SCNPhysicsShape? private var collisionShapeOffsetFromModel = float3.zero private var downwardAcceleration: Float = 0
変数名 | 説明 |
---|---|
characterCollisionShape | キャラクターのコリジョンを設定する |
collisionShapeOffsetFromModel | キャラクターのコリジョンのオフセット値を設定する |
downwardAcceleration | 物理シミュレーションじの高度設定 |
ジャンプ動作設定
private var controllerJump: Bool = false private var jumpState: Int = 0 private var groundNode: SCNNode? private var groundNodeLastPosition = float3.zero var baseAltitude: Float = 0 private var targetAltitude: Float = 0
変数名 | 説明 |
---|---|
controllerJump | ジャンプ操作時のフラグ |
jumpState | ジャンプ時の状態 |
groundNode | 地面のからノード |
groundNodeLastPosition | groundNode の最終位置を設定する |
baseAltitude | 基準となる Max の高度。GameController でも変更される |
targetAltitude | 目標となる Max の高度 |
private var lastStepFrame: Int = 0 private var frameCounter: Int = 0
フレーム毎に frameCounter が1増え、lastStepFrame など比較した際音を鳴らすためのもの。
private var previousUpdateTime: TimeInterval = 0 private var controllerDirection = float2.zero
previousUpdateTime は現在より前の更新時間を表す。
controllerDirection は使用箇所不明。
状態
private var attackCount: Int = 0 private var lastHitTime: TimeInterval = 0 private var shouldResetCharacterPosition = false
変数名 | 説明 |
---|---|
attackCount | 攻撃判別用。0でなければ攻撃している |
lastHitTime | 最後に敵に当たった時の時間 |
shouldResetCharacterPosition | リセットされる際のフラグ。true になった際 Max の位置がリセットされる |
パーティクル
private var jumpDustParticle: SCNParticleSystem! private var fireEmitter: SCNParticleSystem! private var smokeEmitter: SCNParticleSystem! private var whiteSmokeEmitter: SCNParticleSystem! private var spinParticle: SCNParticleSystem! private var spinCircleParticle: SCNParticleSystem! private var spinParticleAttach: SCNNode! private var fireEmitterBirthRate: CGFloat = 0.0 private var smokeEmitterBirthRate: CGFloat = 0.0 private var whiteSmokeEmitterBirthRate: CGFloat = 0.0
変数名 | 説明 |
---|---|
jumpDustParticle | ジャンプの際出る砂煙のパーティクル |
fireEmitter | 溶岩に入った際に尻尾から出る炎のパーティクル |
smokeEmitter | 溶岩に入った際に尻尾から出る黒煙のパーティクル |
whiteSmokeEmitter | 尻尾の炎が鎮火した際に出る白煙のパーティクル |
spinParticle | 攻撃の際のパーティクル。使用不明 |
spinCircleParticle | 攻撃の際の衝撃波のパーティクル |
spinParticleAttach | max.scn の Max のノード particles_spin_circle を設定する |
fireEmitterBirthRate | fireEmitter の再生レートの設定 |
smokeEmitterBirthRate | smokeEmitter の再生レートの設定 |
whiteSmokeEmitterBirthRate | whiteSmokeEmitter の再生レートの設定 |
SE
private var aahSound: SCNAudioSource! private var ouchSound: SCNAudioSource! private var hitSound: SCNAudioSource! private var hitEnemySound: SCNAudioSource! private var explodeEnemySound: SCNAudioSource! private var catchFireSound: SCNAudioSource! private var jumpSound: SCNAudioSource! private var attackSound: SCNAudioSource! private var steps = [SCNAudioSource](repeating: SCNAudioSource(), count: Character.stepsCount )
変数名 | 説明 |
---|---|
aahSound | 溶岩に入り炎が鎮火した際の Max の声 |
ouchSound | 溶岩に入った際のの Max の声 |
hitSound | 敵に当たった時の音 |
hitEnemySound | 敵に攻撃を当てた時の音 |
explodeEnemySound | 敵がやられる時爆発する音 |
catchFireSound | 溶岩に入った際の炎の音 |
jumpSound | Max がジャンプした時の音 |
attackSound | Max が攻撃した時の音 |
steps | Max 歩行時の足音を格納する配列 |
private(set) var offsetedMark: SCNNode?
使用用途不明
アクション
GameController から渡されるパラメーター。
var isJump: Bool = false var direction = float2() var physicsWorld: SCNPhysicsWorld?
変数名 | 説明 |
---|---|
isJump | ジャンプ状態の判定用変数 |
direction | Max の方向をを決める |
physicsWorld | シーンの physicsWorld が渡される |
このプロジェクトの中で1番定数と変数が多いため結構な量となっている。
次回は Character クラスで実装されている関数について見てゆく。