◆ Cocos Creatorでスーパーマリオみたいな2Dアクションゲームを作ります。◆
◆ このシリーズの他の記事を見るには『PlatformerGame』タグをクリックして下さい。◆
◆ 最初から読みたい場合はココをクリックして下さい。◆
こちらの記事の続きです。
今回は、プレイヤーがブロック(地面など構造物のノード)に着地しているかどうかを接触イベントで判定する処理を追加します。連続するブロックを走り抜ける時に、着地を誤判定しないように工夫します。
【 注意 】この記事で使用しているCocos Creator v2.3.3は、VS Codeのデバッグ実行でブレークポイントが機能しないバグがあので、v.2.3.2を使用して下さい。
v.2.3.2は、Cocos DashboardのEditorのDownloadボタンからインストール出来ます。
既存のプロジェクトのEditor Versionは、下図のように切り替えます。一度確認のメッセージが表示されます。
使用環境
私が使用している環境です。
- OS:Windows 10 Home
- ブラウザ:Google Chrome (64-bit)
- 開発環境:Cocos Creator v2.3.3
- コードエディタ:Visual Studio Code(以下VS Code)
プレイヤーが着地しているか判定する
接触イベントで、接点(接触した場所)の座標を取得できます。
接点の座標が足元(足裏)かどうかで、プレイヤーが着地しているかどうかを判定します。
また着地しているブロック(地面など構造物のノード)を数えることで誤判定を防ぎます。
下記のように実装します。
- プレイヤーに接触イベントを設定します。
接触イベントには下記の2つがあります。 - プレイヤーの足元がブロックに触れたかどうかを判定します。
接触イベントでプレイヤーと対象物の接点(下図の赤い点)の座標が得られます。
この座標が足元かどうかで判定します。下部 上部 横 - ブロックかどうかはコライダーのプロパティの『tag』の値で判別することにします。
今後、ブロックのコライダーのみ『tag』=0に設定します。
- プレイヤーが着地しているブロック(地面など構造物のノード)を数えます。
全てのノードはUUID(Universal Unique Identifier)と呼ばれる固有の識別子を持っています。
着地しているブロックのUUIDを保持する配列を作って下記のように処理します。配列の長さが1以上なら着地していることになります。
ちなみに、誤判定が生じるやり方の例です。
BeginContactで着地中フラグ=true、EndContactで着地中フラグ=falseとした場合、
2つのブロックの境界で下記の順序でイベントが起きる場合があります。- 『②のブロックのBeginContact』が発生、着地中フラグ=true
- 『①のブロックのEndContact』が発生、着地中フラグ=false
『①のブロックのEndContact』と『②のブロックのBeginContact』の発生順が入れ替わることがあるので誤判定が生じます。 - 『②のブロックのBeginContact』が発生、着地中フラグ=true
- 『Node Tree』パネルで『player』を選択して、『Properties』パネルで『RigidBody』の『Enabled Contact Listener』を設定します。
- 『Assets』パネルで『assets/script/player』をダブルクリックします。
VS Codeが起動します。VS Codeで作業
- 『onBeginContact()』と『onEndContact()』を追加します。
onKeyUp () { /* 省略 */ } landingArray: { [key: string]: boolean; } = {}; // 接触しているブロックのUUIDを保持する連想配列 isLanging: boolean = true; // 着地中かどうか true:着地中 onBeginContact (contact: cc.PhysicsContact, selfCollider: cc.PhysicsCollider, otherCollider: cc.PhysicsCollider) { // 接触開始時の処理 // 接点の場所を判定する let points = contact.getWorldManifold().points; // 接点のワールド座標を取得 let playerBody: cc.RigidBody = selfCollider.body; // プレイヤーのボディを取得 let relativePoint: cc.Vec2 = cc.Vec2.ZERO; // 変換後の座標 let isPlayerBottom: boolean = true; // プレイヤーの足元かどうか、true:足元 for (let i = 0; i < points.length; i++) { // 接点の数だけループする playerBody.getLocalPoint(points[i], relativePoint); // 接点をワールド座標からプレイヤーのローカル座標に変換する relativePoint.divSelf(this.node.scaleY); // プレイヤーノードのスケールを反映 if (relativePoint.y >= -60.5) { // 足元ではない場合 isPlayerBottom = false; // falseにする } } // 足元に接触したブロックのUUIDを配列に追加する if (otherCollider.tag == 0) { // ブロックのコライダー(tag=0)の場合 if (isPlayerBottom) { // 足元の場合 this.landingArray[otherCollider.uuid] = true; // UUIDを配列に追加する this.isLanging = true; // 着地中にする // this.jumpCount = 0; } } } onEndContact (contact: cc.PhysicsContact, selfCollider: cc.PhysicsCollider, otherCollider: cc.PhysicsCollider) { // 接触終了時の処理 // 接触終了したブロックのUUIDを配列から削除する delete this.landingArray[otherCollider.uuid]; // 配列にUUIDがあれば削除する if (Object.keys(this.landingArray).length == 0) { // 接触しているブロックの数が0の場合 this.isLanging = false; // 着地中フラグをfalseにする } }
- 『onKeyDown()』のジャンプ処理の着地判定の部分を変更します。
/* 省略 */ onKeyDown (event: cc.Event.EventKeyboard) { // キーを押した時の処理 switch(event.keyCode) { // 押されたキーの種類で分岐 /* 省略 */ case cc.macro.KEY.w: // 『w』キーの場合 case cc.macro.KEY.up: // 『↑』キーの場合 case cc.macro.KEY.space: // 『スペース』キーの場合 // ジャンプ処理 if (!this.inputJump) { // 『ジャンプ』の入力が0の場合 this.inputJump = 1; //『ジャンプ』の入力を1にする let velocity: cc.Vec2 = this.getComponent(cc.RigidBody).linearVelocity; // 現在の速度を取得 if (this.isLanging) { // 着地している場合 velocity.y = this.jumpSpeed; // ジャンプする(y+方向にジャンプ速度を与える) } this.getComponent(cc.RigidBody).linearVelocity = velocity; // 速度を更新する } break; } }
変更前は、着地してからY方向の速度が0になるまで若干の遅延があるせいで、操作の反応が悪い時がありましたが、変更後は改善されました。
Cocos Creatorで作業
今回作成したファイル
今回の作業によって下記のファイルのようになります。
今回はここまでです。お疲れさまでした。
続きは、こちらの記事です。