◆ 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は、下図のように切り替えます。一度確認のメッセージが表示されます。
目次
- 目次
- 使用環境
- コリジョングループ(Collision Group)を設定する
- スライムの画像をAssetsに追加する
- スライムの実装方法
- スライムのノードを作成する
- RigidBodyとColliderを設定する
- 移動中のアニメーションを作成する
- 踏みつけられた時のアニメーションを作成する
- デフォルトのアニメーションを設定する
- スクリプトを追加する
- slimeBlueノードをプレハブにする
- playerノードのコリジョングループを設定する
- coinプレハブのコリジョングループを設定する
- タイルマップでスライムの位置を指定する
- ステージにslimeBlueを設置する
- playerスクリプトにスライムの踏みつけ処理を追加する
- 今回作成したファイル
使用環境
私が使用している環境です。
- OS:Windows 10 Home
- ブラウザ:Google Chrome (64-bit)
- 開発環境:Cocos Creator v2.3.2
- コードエディタ:Visual Studio Code(以下VS Code)
- Tiled Version 1.3.4
コリジョングループ(Collision Group)を設定する
衝突するものと衝突しないものをグループ単位で分けます。
例えば、プレイヤーはアイテムと接触しますが、敵はアイテムと接触しないようにします。
不要な衝突を無くすので、処理の負荷を下げることにもなります。
下記のようにグループを分けます。
グループ名 | 対象 |
---|---|
platform | 足場のブロック、壁等どの地形 |
player | プレイヤー |
enemy | 敵 |
item | コイン等のアイテム |
下記のグループの組み合わせのみ接触イベントが発生します。
スライムの画像をAssetsに追加する
移動中の画像2枚と、踏みつけられた時の画像1枚を追加します。
スライムの実装方法
スライムのノードを編集して最終的にプレハブにします。
下記のように実装します。
- スライムの主な仕様です。
- スライムは一定速度で移動します。スタートは左向きです。
- 壁や段差、他の敵にぶつかると方向転換します。
- 画面外では停止して、画面内に入ると動きます。
- プレイヤーに踏みつけられると倒されて消えます。
- スライムは一定速度で移動します。スタートは左向きです。
- ノードは下記の構造にします。
今回、コライダーのどこが接触したかを判定するのですが、ぶつかる速度が速いとコライダー同士が貫通してしまい、正しく判定出来ません。RigitBodyで『Bullet』を有効にすると貫通を防ぐことが出来ます。ただし処理は重くなるので注意が必要です。
Spriteだけ子ノードにしているのは、方向転換の時に絵だけを反転させるためです。壁にぶつかった時にコライダーを反転させると接触時の処理が難しくなります。
スライムのノードを作成する
Spriteコンポーネントは、デフォルトでは画像の外側の透明部分を除外します。
これだと倒された画像に差し替えた時におかしくなるので、透明部分も含むようにします。
アンカーポイントの位置を下端する対処もありますが、ステージに配置する際に位置の調整が必要になります。
Spriteのオプション設定 | slimeBlue | slimeBlue_Hit |
---|---|---|
Size Mode:TRIMMED、Trim:チェックあり (デフォルト) | ||
Size Mode:RAW、Trim:チェックなし (元画像のサイズ) |
- 『Node Tree』パネルで『Canvas』を右クリック→『Create』→『Create Empty Nodes』をクリックしてノードを作成し、名前を”slimeBlue”にします。
- 『Assets』パネルから『slimeBlue』を『Node Tree』パネルにドラッグ&ドロップします。
下図のようになります。
- 『Node Tree』パネルで『Canvas/slimeBlue』を選択して、『Properties』パネルの『Node』を下図のように設定します。
親ノードのほうの『slimeBlue』です。
『Group』を変更した時に、『全ての子ノードのグループも同様に変更しますか?』と問われるので『Confirm』をクリックします。
- 『Node Tree』パネルで『Canvas/slimeBlue/slimeBlue』を選択して、『Properties』パネルの『Node』の『Size』を確認します。
子ノードのほうの『slimeBlue』です。
画像の外側の透明部分を除くサイズです。後でコライダーを作る際に参考になります。
- 『Properties』パネルの『Sprite』を下図のように設定します。
- 『Size Mode』:『RAW』
スプライトを元のサイズ(透明部分を含む)にします。
- 『Trim』:チェックを外します。
透明部分を除外しません。
- 『Size Mode』:『RAW』
Cocos Creatorで作業
RigidBodyとColliderを設定する
『RigidBody』コンポーネントは『Bullet』の設定は重要です。『Bullet』を有効にすると処理の負荷は高くなりますが、接触の判定がより厳密になります。
コライダーの形状は、プレイヤーと同じくブロックの継ぎ目に引っかからない形状にします。
- 『Node Tree』パネルで『Canvas/slimeBlue』を選択して、『Properties』パネルの『Add Component』→『Physics Component』→『Collider』→『Polygon』をクリックします。
親ノードのほうの『slimeBlue』です。
- 『Properties』パネルの『RigidBody』を下図のように設定します。
- 『Enabled Contact Listener』:チェックを入れます。
接触(衝突)コールバック(onBeginContact、onEndContact』を使えるようにします。
- 『Bullet』:チェックを入れます。(透明部分を除外しません。)
ボディ(コライダー)を貫通しないようにします。ただし、処理の負荷が高くなります。
- 『Fixed Rotation』:チェックを入れます。
ボディ(コライダー)が回転しないようにします。
- 『Enabled Contact Listener』:チェックを入れます。
- 『Properties』パネルで『PhysicsPolygonCollider』の『Editing』にチェックを入れます。
- 『Scene』パネルで下図の赤丸の位置をクリックしてポイントを追加します。
- 『Properties』パネルで『PhysicsPolygonCollider』を下図のようにします。
Cocos Creatorで作業
移動中のアニメーションを作成する
移動中にスライムが収縮するようにします。
- 『Properties』パネルの『Add Component』→『Other Component』→『Animation』をクリックします。
親ノードのほうの『slimeBlue』です。
- 『Timeline』パネルで、『Create a new Clip file.』をクリックします。
- 『assets/animation』フォルダに”slimeBlueMove”と名前を付けて『保存』をクリックします。
- 『Timeline』パネルで『Open/Close Record mode』ボタンをクリックします。
- 『Timeline』パネルで『slimeBlue/slimeBlue』を選択して『Add Property』→『cc.Sprite.spriteFrame』をクリックします。
- 『Assets』パネルの『assets/texture/slimeBlue〜slimeBlue_Move(2個のファイル)』を『Ctrl』キーを押しながら選択して、『Timeline』パネルの『cc.Sprite.spriteFrame』の列の『0:00』にドラッグ&ドロップします。
『slimeBlue』の方をドラッグします。どちらをドラッグするかでフレームへの登録順が変わります。
- 『Timeline』パネルで下記のように設定します。
- 『WrapMode:』で『Loop』を選択します。
アニメーションを繰り返し再生します。
- 『Sample:』を"2"にして『Enter』キーを押します。
再生速度を2FPS(1秒間に画像を切り替える回数)にします。
『Enter』を押すか、別の場所をクリックするまで入力が確定されません。
- 『WrapMode:』で『Loop』を選択します。
- 『Scene』パネルで『Save』をクリックしてアニメーション(Animation Clip)を保存します。
Cocos Creatorで作業
踏みつけられた時のアニメーションを作成する
潰れて消えるようにします。
消えた後にノードを削除する関数を呼び出します。
- 『『Timeline』パネルで『+』をクリックします。
- 『assets/animation』フォルダに”slimeBlueHit”と名前を付けて『保存』をクリックします。
- 『Clip:』で『slimeBlueHit』を選択します。
- 『Timeline』パネルで『slimeBlue/slimeBlue』を選択して、『Add Property』→『cc.Sprite.spriteFrame』をクリックします。
- 『Timeline』パネルで『slimeBlue/slimeBlue』を選択して、『Add Property』→『opacity』をクリックします。
- 『Assets』パネルの『assets/texture/slimeBlue_Hit』を『Timeline』パネルの『cc.Sprite.spriteFrame』の列の『0:00』にドラッグ&ドロップします。
- 『Timeline』パネルで『coinGold』をクリックして選択して、タイムラインの赤線を『0:00』に置いた後、『opacity』の横の『≡』→『Insert keyframe』をクリックします。
0:00の位置にキーフレームが追加されます。
- 『Timeline』パネルで『slimeBlue/slimeBlue』を選択して、タイムラインの赤線を『0:00』に置いた後、『opacity』の横の『≡』→『Insert keyframe』をクリックします。
- 続けて『0:01』の位置にキーフレームを追加します。
- 『Timeline』パネルでタイムラインの赤線を『0:01』に置いた後、『Properties』パネルで『Node』の『opacity』を下図のように設定します。
- 『Timeline』パネルでタイムラインの赤線を『0:10』に置いた後、『Insert frame's event』ボタンをクリックして、タイムラインに追加された白いマークをダブルクリックします。
- 『FUNCTION』に"destroyNode"と入力して、保存ボタンをクリックした後、閉じるボタンで閉じます。
後でスクリプトに『destroyNode()』を定義します。
- 『Timeline』パネルで『Sample:』を下図のように設定します。
- 『Scene』パネルで『Save』をクリックしてアニメーション(Animation Clip)を保存します。
- 『Timeline』パネルで『Open/Close Record mode』ボタンをクリックします。
Cocos Creatorで作業
デフォルトのアニメーションを設定する
最初から移動中のアニメーションを再生するようにします。
スクリプトを追加する
下記の機能を実装します。
- 一定速度で移動します。
- コライダーの右または左部分が接触した場合、逆方向に反転します。
- 画面外にいる場合は停止して、画面内に入った時に動き出します。
- 倒された時に停止して、倒された時用のアニメーションを再生します。
- 倒された時にノードを破棄します。
- 『Assets』パネルで『script』フォルダを右クリック→『Create』→『Typescript』をクリックします。
- 作成されたファイルの名前を"slimeBlue"に変更します。
- 『Node Tree』パネルで『Canvas/slimeBlue』を選択して、『Properties』パネルの『Add Component』→『Custom Component』→『slimeBlue』をクリックします。
- 『Assets』パネルで『assets/script/slimeBlue』をダブルクリックします。
VS Codeが起動します。VS Codeで作業
- 下記のようにコードを変更します。
const {ccclass, property} = cc._decorator; @ccclass export default class slimeBlue extends cc.Component { // クラス名をNewClassからslimeBlueにする scaleTotal: number = 0; // 適用されているスケール gravityScale: number = 6; // プレイヤーの重力スケール camera: cc.Node = null; // カメラのノード moveDistanceX: number = 0; // 動き始める距離 start () { this.node.getComponent(cc.RigidBody).gravityScale = this.gravityScale; // 重力スケールを設定する this.scaleTotal = this.node.scaleX * cc.find("Canvas/stage01").scaleX; // 適用されているスケール this.camera = cc.find("Canvas/Main Camera"); this.moveDistanceX = cc.winSize.width / 2 * 1.2; } direction: number = -1; // 移動方向 inputDir: boolean = false; // 方向転換中かどうか、true=方向転換中 onBeginContact (contact: cc.PhysicsContact, selfCollider: cc.PhysicsCollider, otherCollider: cc.PhysicsCollider) { // 接触開始時の処理 // 接点の場所を判定する let points = contact.getWorldManifold().points; // 接点のワールド座標を取得 let relativePoint: cc.Vec2 = cc.Vec2.ZERO; // 変換後の座標 let isLeft: number = 1; // 左かどうか、1:左 let isRight: number = 1; // 右かどうか、1:右 for (let i = 0; i < points.length; i++) { // 接点の数だけループする selfCollider.body.getLocalPoint(points[i], relativePoint); // 接点をワールド座標から自身のローカル座標に変換する relativePoint.divSelf(this.scaleTotal); // 座標にスケールを反映する if (relativePoint.x >= -42) { // 左ではない場合 isLeft = 0; // 左フラグを0にする } if (relativePoint.x <= 42) { // 右ではない場合 isRight = 0; // 右フラグを0にする } } // 方向転換処理 let newDirection: number = isLeft - isRight; // 新しい方向を計算、左が接触したら右(newDirection=1)に行く if (!this.inputDir && newDirection) { // 方向転換中でない、かつ方向が0ではない場合 this.inputDir = true; let childNode: cc.Node = this.node.getChildByName("slimeBlue"); // 子ノードのslimeBlueを取得する childNode.scaleX = Math.abs(childNode.scaleX) * -1 * newDirection; // ノードの向きを変える、元の絵が左向き this.direction = newDirection; // 方向を更新する } } onEndContact (contact: cc.PhysicsContact, selfCollider: cc.PhysicsCollider, otherCollider: cc.PhysicsCollider) { // 接触終了時の処理 this.inputDir = false; // 方向転換中フラグをfalseにする } speed: number = 120; // 移動速度 _paused: boolean = false; // 停止状態を保持する curVelocity: cc.Vec2 = cc.Vec2.ZERO; // 停止時に速度を保持する curGravityScale: number = 0; // 停止時に重力スケールを保持する set paused (value: boolean) { // _pausedのセッター if (this._paused == value) return; // 同じ値なら処理を抜ける this._paused = value; // 値を更新する let rigidBody: cc.RigidBody = this.node.getComponent(cc.RigidBody); // RigidBodyを取得する if(value){ // 停止中にする場合 this.curVelocity = rigidBody.linearVelocity; // 現在の速度を保持する this.curGravityScale = rigidBody.gravityScale; // 現在の重力スケールを保持する rigidBody.linearVelocity = cc.Vec2.ZERO; // 速度をゼロにする rigidBody.gravityScale = 0; // 重力スケールをゼロにする } else { // 停止中を解除する場合 rigidBody.linearVelocity = this.curVelocity; // 停止前の速度に戻す rigidBody.gravityScale = this.curGravityScale; // 停止前の重力スケールに戻す } } isHit: boolean = false; // 倒されたかどうか、true=倒されている update (dt: number) { // 毎フレームの描画前の処理(dt:前フレームからの経過時間) if (this.isHit) return; // 倒されている場合、処理を抜ける // 画面外にいる場合、停止する let selfWorldPos = this.node.convertToWorldSpaceAR(cc.Vec2.ZERO); // 自ノードのワールド座標 let cameraWorldPos = this.camera.convertToWorldSpaceAR(cc.Vec2.ZERO); // カメラのワールド座標 let distance: cc.Vec2 = selfWorldPos.sub(cameraWorldPos); // 自ノードとカメラの距離を取得 if (Math.abs(distance.x) > this.moveDistanceX) { // 自ノードが画面外の場合 this.paused = true; // 一時停止する return; // 処理を抜ける } else { // 自ノードが画面内の場合 this.paused = false; // 一時停止を解除する } // 左右の移動 let velocity: cc.Vec2 = this.node.getComponent(cc.RigidBody).linearVelocity; // 現在の速度を取得 velocity.x = this.speed * this.direction; // 速度を計算する this.node.getComponent(cc.RigidBody).linearVelocity = velocity; // 速度を更新する } hit () { // 倒された時の処理 this.isHit = true; // 倒されたフラグをtrueにする this.paused = true; // 停止中フラグをtrueにする this.node.getComponent(cc.Animation).play("slimeBlueHit"); // 『hit』用のアニメを再生する } destroyNode () {// キーフレームイベント:親ノードを破棄する this.node.destroy(); // 親ノードを破棄する } }
- 『ctrl + s』キーを押して、コードを保存します。
Cocos Creatorで作業
slimeBlueノードをプレハブにする
スライムの機能を一通り実装したのでプレハブにします。
coinプレハブのコリジョングループを設定する
プレハブのファイルをダブルクリックすると再編集出来ます。
コインのプレハブにもコリジョングループを設定します。
- 『Assets』パネルで『prefab/coinGold』をダブルクリックします。
- 『Node Tree』パネルで『coinGold』を選択して、『Properties』パネルの『Node』を『item』に変更します。
- 『全ての子ノードのグループも同様に変更しますか?』と問われるので『Confirm』をクリックします。
- 『Scene』パネルで『Save』をクリックした後、『Close』をクリックします。
Cocos Creatorで作業
タイルマップでスライムの位置を指定する
スライムを設置する場所にオブジェクトを設置します。
設置するのはprefabレイヤーです。
コインのオブジェクトと重なっても大丈夫です。
- 『~/PlatformerGame/assets/texture/stage01.tmx』を開きます。
- 『レイヤー』パネルで『pfefab』を選択して、『四角形を追加』ツールで、『Ctrl』キーを押しながらマウスドラッグして、下図のようにオブジェクトを作成した後、『プロパティ』パネルで名前を”slimeBlue”にします。
『ctrl』キーを押すと、マウスカーソルがグリッドに吸着します。
- 『オブジェクトを選択』ツールで『slimeBlue』オブジェクトを選択して、『Ctrl + c』→『Ctrl + v』キーを押して複製した後、『Ctrl』キーを押しながらマウスドラッグして下図の位置に移動します。
- 『Ctrl + s』キーを押して保存します。
Tiledで作業
ステージにslimeBlueを設置する
Cocos Creatorのエディタ上でslimeBlueのプレハブを受け取って、連想配列に追加します。
- 『Assets』パネルで『assets/script/stage』をダブルクリックします。
VS Codeが起動します。VS Codeで作業
- 下記のように『addPrefab ()』を変更します。
/* 省略 */ @property(cc.Prefab) // Cocos Creatorのエディタに表示する slimeBluePrefab : cc.Prefab = null; // slimeBlueのプレハブを取得する addPrefab () { // 『Tiled』のオブジェクト情報を元にプレハブを配置 let objects = this.tiledMap.getObjectGroup("prefab").getObjects(); // 『Tiled』のオブジェクトを取得 let layerNode = this.tiledMap.node.getChildByName("prefab"); // タイルマップの子ノードを取得 let prefabArray: { [key: string]: cc.Prefab; } = {}; // 名前をkeyにしてプレハブを連想配列に収める prefabArray["coinGold"] = this.coinGoldPrefab; // コインのプレハブを配列に追加する prefabArray["slimeBlue"] = this.slimeBluePrefab; // コインのプレハブを配列に追加する for (let i = 0; i < objects.length; i ++) { // オブジェクトの数だけループ /* 省略 */ } }
- 『ctrl + s』キーを押して、コードを保存します。
Cocos Creatorで作業
- 『Node Tree』パネルで『Canvas/stage01』を選択して、『Properties』パネルの『stage』に『Assets』パネルの『assets/prefab/slimeBlue』をドラッグ&ドロップします。
playerスクリプトにスライムの踏みつけ処理を追加する
プレイヤーの足元(足裏)でスライムを踏んだ場合に、そのスライムを倒す処理を追加します。
踏んだ後にわずかに跳ねるようにします。コライダーが消えるまでプレイヤーと再接触しないようにする意味合いがあります。
- 『Assets』パネルで『assets/script/player』をダブルクリックします。
VS Codeが起動します。VS Codeで作業
- 下記のように『onBeginContact()』を変更します。
/* 省略 */ onBeginContact (contact: cc.PhysicsContact, selfCollider: cc.PhysicsCollider, otherCollider: cc.PhysicsCollider) { // 接触開始時の処理 // 接点の場所を判定する /* 省略 */ // 足元に接触したブロックのUUIDを配列に追加する /* 省略 */ // 接触相手のノードごとの処理 switch (otherCollider.node.name) { // 接触相手のノードの名前で処理を分岐する case "coinGold": // coinGoldの場合 otherCollider.node.getComponent('coin').getCoin(); // coinスクリプトのgetCoin()を実行する break; case "slimeBlue": // slimeBlueの場合 if (isPlayerBottom) { // 足で踏んだ場合 otherCollider.node.getComponent('slimeBlue').hit(); // slimeBlueスクリプトのhit()を実行する playerBody.linearVelocity = cc.v2(playerBody.linearVelocity.x, 500); // 踏んだ後に跳ねる } break; } }
- 『ctrl + s』キーを押して、コードを保存します。
Cocos Creatorで作業
- 『ctrl + p』キー(またはプレビューボタン)を押して、プレビューを実行します。
ゲーム画面を一度クリックした後、プレイヤーをキー操作で移動出来ます。
【 注意 】クリックでゲーム画面をフォーカスしていないとキー操作出来ません。
*下図はgifです。クリックで再生します。(ブログの仕様でgifがループします。)
*キーボードとマウスの操作を表示するソフトを使っています。
今回作成したファイル
今回の作業によって下記のファイルのようになります。
- https://github.com/githubmorley/blog-files/blob/master/platformergame011/slimeBlue.ts
- https://github.com/githubmorley/blog-files/blob/master/platformergame011/stage.ts
- https://github.com/githubmorley/blog-files/blob/master/platformergame011/player.ts
今回はここまでです。お疲れさまでした。
続きは、こちらの記事です。