モーリーのメモ

アプリ開発等(プログラミング、CG作成)、興味を持ったことを実践してまとめるブログです。

モーリーのメモ

マニュアルのチュートリアルをやってみる! その2:Cocos Creator

<今回やること!>

 
 前回は、ゲームシーンを作成して『Canvas』に背景、地面、キャラクタを配置しました。
 今回は、スクリプトを書いて、ゲームに必要な機能を実装します。

使用環境

 私が使用している環境です。

キャラクターのスクリプトを作成

スクリプトを作成

  1. 『Assets』パネルで、『assets』フォルダを右クリックし、『Create』→『Folder』をクリックしてフォルダを作成
    f:id:mmorley:20190425171213p:plain
  2. 作成したフォルダを右クリックし、『Rename』を選択して、名前を"scripts"に変更
    今回は、この中にスクリプトを保存します。
  3. 『scripts』フォルダを右クリックし、『Create』→『JavaScript』を選択
  4. 作成したスクリプトの名前を『Player』に変更
    f:id:mmorley:20190425171946p:plain
  5. 『Player』スクリプトをダブルクリックして、コードエディタを開く

プロパティを設定

  1. コードエディタで『Player』スクリプトの『properties』の部分を下記のように変更

    // Player.js
        //...省略
        properties: {
            // キャラクターのジャンプの高さ(ピクセル)
            jumpHeight: 0, 
            // キャラクターがジャンプで最高地点に達するまでの時間:(秒)
            jumpDuration: 0, 
            // 水平方向の最大移動速度(ピクセル/秒)
            maxMoveSpeed: 0, 
            // 水平方向の加速度(ピクセル/秒^2)
            accel: 0, 
        },
        //...省略
    

  2. 『command + s』キー(Windowsなら『ctrl + s』キー)を押してスクリプトを保存
  3. 『Node Tree』パネルで、『Player』ノードを選択
  4. 『Properties』パネルで、『Add Component』ボタンをクリックし、『Custom Component』→『Player』をクリック
    f:id:mmorley:20190425203920p:plain
    『Player』スクリプトコンポーネントとしてノードに追加されます。
  5. 『Properties』パネルの『Player』コンポーネントに、先程のコードで『properties』に記載した変数が表示されるので下図のように値を設定する
    f:id:mmorley:20190425205738p:plain
    スクリプトをで変更しなくても、『Properties』パネルで値を変更が出来ます。

キャラクタのジャンプのコードを作成

  1. 『Player』スクリプトを下記のように変更
    『setJumpAction』というメソッドを『properties: {...},』ブロックの下に作ります。
    キャラクターのジャンプアクションを作成するメソッドです。

    // Player.js
        properties: {
            //...省略
        },
    
        setJumpAction: function () {
            // ジャンプアップ(ジャンプの最高地点まで移動)するアクションを定義
            var jumpUp = cc.moveBy(this.jumpDuration, cc.v2(0, this.jumpHeight)).easing(cc.easeCubicActionOut());
            // ジャンプダウン(ジャンプで地上まで落下)するアクションを定義
            var jumpDown = cc.moveBy(this.jumpDuration, cc.v2(0, -this.jumpHeight)).easing(cc.easeCubicActionIn());
            // リピート(ジャンプアップ→ジャンプダウン→ジャンプアップ→・・・の繰り返し)するアクションを返す
            return cc.repeatForever(cc.sequence(jumpUp, jumpDown));
        },
    

    補足:スクリプトについて
    • 『cc.moveBy(時間, 距離)』:現在の位置から指定時間で指定距離移動するアクションを返す
    • 『cc.v2(x, y)』:ベクトル、座標を返す
    • 『.easing(加減速タイプ)』:下表の加減速を設定
    • 『cc.sequence(アクション1, アクション2, ...)』:複数のアクションをつなげたアクションを返す
    • 『cc.repeatForever(アクション)』:指定したアクションを繰り返すアクションを返す
      *下表のグラフはマニュアルをトレースしただけで正確ではありません。あくまで目安です。
      linear
      横軸:時間、縦軸:位置
      f:id:mmorley:20190426093743p:plain
      easeSineIneaseSineOuteaseSineInOut
      f:id:mmorley:20190426094007p:plainf:id:mmorley:20190426094044p:plainf:id:mmorley:20190426094110p:plain
      easeCubicActionIneaseCubicActionOuteaseCubicActionInOut
      f:id:mmorley:20190426094214p:plainf:id:mmorley:20190426094324p:plainf:id:mmorley:20190426094420p:plain
      easeQuinticActionIneaseQuinticActionOuteaseQuinticActionInOut
      f:id:mmorley:20190426094504p:plainf:id:mmorley:20190426094539p:plainf:id:mmorley:20190426094615p:plain
      easeCircleActionIneaseCircleActionOuteaseCircleActionInOut
      f:id:mmorley:20190426094647p:plainf:id:mmorley:20190426094714p:plainf:id:mmorley:20190426094743p:plain
      easeQuadraticActionIneaseQuadraticActionOuteaseQuadraticActionInOut
      f:id:mmorley:20190426094826p:plainf:id:mmorley:20190426094903p:plainf:id:mmorley:20190426094947p:plain
      easeQuarticActionIneaseQuarticActionOuteaseQuarticActionInOut
      f:id:mmorley:20190426095036p:plainf:id:mmorley:20190426095115p:plainf:id:mmorley:20190426095156p:plain
      easeExponentialIneaseExponentialOuteaseExponentialInOut
      f:id:mmorley:20190426095233p:plainf:id:mmorley:20190426095258p:plainf:id:mmorley:20190426095327p:plain
      easeElasticIneaseElasticOuteaseElasticInOut
      f:id:mmorley:20190426095400p:plainf:id:mmorley:20190426095449p:plainf:id:mmorley:20190426095518p:plain
  2. 『Player』スクリプトの『onLoad』メソッドを下記のように変更
    シーンのロード直後に、追加した『setJumpAction』メソッドを呼び出してアクションを実行します。

    // Player.js
        onLoad: function () {
            // ジャンプアクションを作成
            this.jumpAction = this.setJumpAction();
            // ジャンプアクションを開始
            this.node.runAction(this.jumpAction);
        },
    

    補足:スクリプトについて
    • 『onLoad: function () {実行内容)』:シーンがロードされた直後に実行
    • 『this』:『Playerコンポーネント(=スクリプト)』
    • 『this.node』:『Playerコンポーネントを追加したノード(=『Player』ノード)』
    • 『runAction(アクション)』:『Player』ノードでアクションを実行
  3. コードエディタで『Player』スクリプトを保存
    シーンのロード後に『Player』ノードのジャンプアクションを実行するところまで作成したので、実際に動かしてみます。
    『Cocos Creator』のエディタの上部のプレビューボタンを押すとデフォルトのブラウザが開いて、ゲームを実行します。
    f:id:mmorley:20190426120831p:plain

キャラクターのキー操作のコードを作成

 キャラクターの移動を操作するための『A』と『D』のキー操作を追加します。

  1. 『Player』スクリプトに『setJumpAction』メソッドの下にキーダウン時、キーアップ時に呼び出すメソッドを追加

    // Player.js
        setJumpAction: function () {
           //...省略
        },
    
        onKeyDown (event) {  // キーを押した時の処理
            // キー入力時の情報がevent(コールバックパラメータ)入ります
            // event.keyCodeには押されたキーのコードが入ります
            switch(event.keyCode) {  // 押されたキーで条件分岐
                case cc.macro.KEY.a: // 『a』キーの場合(cc.macro.KEY.aはシステムで定義された『a』キーのコード)
                    this.accLeft = true; // 『右に加速フラグ』をtrueにする
                    break;
                case cc.macro.KEY.d: //  『d』キーの場合(cc.macro.KEY.dはシステムで定義された『a』キーのコード)
                    this.accRight = true;// 『左に加速フラグ』をtrueにする
                    break;
            }
        },
    
        onKeyUp (event) { //キーを放した時の処理
            switch(event.keyCode) {
                case cc.macro.KEY.a: // 『a』キーの場合
                    this.accLeft = false; // 『右に加速フラグ』をfalseにする
                    break;
                case cc.macro.KEY.d: //  『d』キーの場合
                    this.accRight = false; // 『左に加速フラグ』をfalseにする
                    break;
            }
        },
    

  2. 『Player』スクリプトの『onLoad』メソッドを下記のように変更し、『 onDestroy』メソッドを追加
    左右の加速フラグおよび水平速度を初期化し、キー入力イベントのリスニング(受付)開始します。

    // Player.js
        onLoad: function () {
            // ジャンプアクションを作成
            this.jumpAction = this.setJumpAction();
            // ジャンプアクションを開始
            this.node.runAction(this.jumpAction);
    
            this.accLeft = false; // 左の加速フラグをfalseにする
            this.accRight = false;; // 右の加速フラグをfalseにする
            this.xSpeed = 0; // キャラクタの現在の水平速度をにする
    
            // キーダウンイベントのリスニング(受付)を開始 
            cc.systemEvent.on(cc.SystemEvent.EventType.KEY_DOWN, this.onKeyDown, this);
            // キーアップイベントのリスニング(受付)を開始  
            cc.systemEvent.on(cc.SystemEvent.EventType.KEY_UP, this.onKeyUp, this); 
        },
    
        onDestroy () { // ノードが破棄される時の処理
            // キーダウンイベントのリスニング(受付)を終了
            cc.systemEvent.off(cc.SystemEvent.EventType.KEY_DOWN, this.onKeyDown, this);
            // キーアップイベントのリスニング(受付)を終了
            cc.systemEvent.off(cc.SystemEvent.EventType.KEY_UP, this.onKeyUp, this); 
        },
    

    補足:システムイベント
    • 『cc.systemEvent.on(
       イベントの種類,
       イベント発生時に呼び出すメソッド,
       イベントを登録するノード)』:イベントを登録
    • 『cc.SystemEvent.EventType.KEY_DOWN』:キーダウンイベントを示す定数
    • 『cc.SystemEvent.EventType.KEY_UP』:キーアップイベントを表す定数
  3. 『Player』スクリプトの『update』メソッドを下記のように変更
    加速フラグの状態によって現在の水平速度を計算して、キャラクターの位置を設定します。

    // Player.js
        update: function (dt) { // 1フレームごとに実行される処理(dtは前のフレームからの経過時間)
            if (this.accLeft) { // 左の加速フラグがtrueの場合
                this.xSpeed -= this.accel * dt; // 現在の水平速度を更新
            } else if (this.accRight) { // 右の加速フラグがtrueの場合
                this.xSpeed += this.accel * dt; // 現在の水平速度を更新
            }
    
            // 現在の水平速度が最大速度を超える場合
            if ( Math.abs(this.xSpeed) > this.maxMoveSpeed ) {
                // 方向はそのままで、最大速度にする
                this.xSpeed = this.maxMoveSpeed * this.xSpeed / Math.abs(this.xSpeed);
            }
    
            this.node.x += this.xSpeed * dt; // 現在のキャラクタの水平位置を更新
        },
    

  4. コードエディタで『Player』スクリプトを保存
    キー入力によって、キャラクターを左右に移動する処理を追加したので、また実際に動かしてみます。
    プレビューボタンを押して、ゲームを実行します。
    *ブラウザの制約により、1度マウスでクリックするまでキー入力に反応しません。

     『properties』パネルで『Player』コンポーネントのプロパティ値を変更することで、キャラクターのスピードを調整することが出来ます。
    例えば、下記のように設定するとキャラクターがすばやく動きます。好みに応じて調整して下さい。

    Jump Height: 150
    Jump Duration: 0.3
    Max Move Speed: 400
    Accel: 1000
    

 キャラクターの動きについては、以上で完成です。

星を作成

 星は下記のような機能を持ちます。

  • ランダムな位置に出現する
  • キャラクターが触れると得点になる
  • キャラクターが触れた星は消えて、新しい星が作成される

プレハブ(Prefab)を作成

 プレハブはノードを生成するためのテンプレート(型)です。
 星の持つ機能をプレハブとして保存しておくと、その機能を持ったノードを動的(ゲームの実行中)に生成出来ます。
 星のノードのように繰り返し生成するものに適しています。

  1. 『Assets』パネルの『textures』フォルダにある『star』を『Node Tree』パネルの『Canvas』ノードにドラッグ
    プレハブ作成のために『Node Tree』に配置しますが、作成後に削除します。『Node Tree』に配置する位置はどこでも構いません。また『Scene』パネルで位置を調整刷る必要もありません。
    f:id:mmorley:20190426201852p:plain
  2. 『Assets』パネルの『scripts』フォルダを右クリックし、『Create』→『JavaScript』を選択
  3. 作成したスクリプトの名前を『Star』に変更
    f:id:mmorley:20190426202018p:plain
  4. 『Star』スクリプトをダブルクリックして、コードエディタを開く
  5. 『Star』スクリプトの『properties』の部分を下記のように変更

    // Star.js
        properties: {
            // キャラクターと星の距離がこの値より小さい場合に得点とする
            pickRadius: 0
        },
    

  • コードエディタで『Star』スクリプトを保存
  • 『Node Tree』パネルで、『star』ノードを選択
  • 『Properties』パネルで、『Add Component』ボタンをクリックし、『Custom Component』→『star』をクリック
  • 『Properties』パネルの『Star』コンポーネントで『Pick Radius』の値を"60"にする
    f:id:mmorley:20190426203639p:plain
  • 『Node Tree』パネルの『star』ノードを『Assets』パネルの『assets』にドラッグ
    f:id:mmorley:20190426213938p:plain
    『star』プレハブが生成されます。
    f:id:mmorley:20190426214127p:plain
  • 『Node Tree』パネルで『star』ノードを右クリックし、『Delete』をクリック
    『star』ノードは使用しないので削除します。

  • ゲーム制御スクリプトを追加

     ゲーム制御スクリプトでは、得点、失敗、リスタートの処理を行います。

    1. 『Assets』パネルの『scripts』フォルダを右クリックし、『Create』→『JavaScript』を選択
    2. 作成したスクリプトの名前を"Game"に変更
    3. 『Game』スクリプトをダブルクリックして、コードエディタを開く
    4. 『Game』スクリプトの『properties』の部分を下記のように変更

      // Game.js
          properties: {
              starPrefab: { // 星のプレハブ取得用の変数
                  default: null, // デフォルト値をnullに設定
                  type: cc.Prefab // プロパティの型をプレハブ型に設定
              },
              maxStarDuration: 0, // 星が消えるまでの時間の最大値
              minStarDuration: 0, // 星が消えるまでの時間の最小値
              ground: {  // 『ground』ノード取得用の変数
                  default: null, // デフォルト値をnullに設定
                  type: cc.Node // プロパティの型をノード型に設定
              },
              player: { // 『Player』ノード取得用の変数
                  default: null, // デフォルト値をnullに設定
                  type: cc.Node // プロパティの型をノード型に設定
              }
          },
      

    5. コードエディタで『Game』スクリプトを保存
    6. 『Node Tree』パネルで、『Canvas』ノードを選択
    7. 『Properties』パネルで、『Add Component』ボタンをクリックし、『Custom Component』→『Game』をクリック
    8. 『Assets』パネルの『star』プレハブを、『Properties』パネルの『Game』コンポーネントの『Star Prefab』にドラッグ
      プロパティに設定した型に該当するものだけ登録出来ます。
    9. 『Node Tree』パネルの『ground』ノードを、『Properties』パネルの『Game』コンポーネントの『Ground』にドラッグ
    10. 『Node Tree』パネルの『Player』ノードを、『Properties』パネルの『Game』コンポーネントの『Player』にドラッグ
    11. 『Properties』パネルの『Game』コンポーネントの『Min Star Duration』を3にする
    12. 『Properties』パネルの『Game』コンポーネントの『Max Star Duration』を5にする
      星を生成する時に、『Min Star Duration』から『Max Star Duration』の間の乱数値で星が消える時間を設定します。
      f:id:mmorley:20190427090118p:plain

    ランダムな位置で星を生成

    1. 『Game』スクリプトの『on Load』メソッドを変更し、『spawnNewStar』と『getNewStarPosition』を追加

      // Game.js
          onLoad: function () {
              // 『ground』のアンカーポイントのy座標を取得
              this.groundY = this.ground.y + this.ground.height / 2;
              // 新しい星を生成
              this.spawnNewStar();
          },
      
          spawnNewStar: function() {
              // 事前に設定したテンプレートでシーンに新しいノードを生成
              var newStar = cc.instantiate(this.starPrefab);
              // 新しく追加したノードを『Canvas』ノードの下に置く
              this.node.addChild(newStar);
              // 星にランダムな位置を設定
              newStar.setPosition(this.getNewStarPosition());
          },
      
          getNewStarPosition: function () {
              // 星のアンカーポイントのy座標=地面の高さ+キャラクターのジャンプの高さの範囲の乱数+地面にめり込ませないための調整値
              var randY = this.groundY + Math.random() * this.player.getComponent('Player').jumpHeight + 50;
              // x座標の最大値(絶対値)=画面幅の半分
              var maxX = this.node.width / 2; 
              // 星のアンカーポイントのx座標=(-1から1の乱数)×(x座標の最大値)
              var randX = (Math.random() - 0.5) * 2 * maxX;
              // 星のアンカーポイントの位置を返す
              return cc.v2(randX, randY);
          },
      

      補足:使用したメソッドについて
    2. コードエディタで『Game』スクリプトを保存
      プレハブを利用して1つ目の星を生成する処理を追加しました。
      また実際に動かしてみます。プレビューボタンを押して、ゲームを実行します。
      実行する(ブラウザを再読込でも可)度に、ランダムな位置に星が生成されます。
      f:id:mmorley:20190427105116p:plain

    キャラクターが星を採取するアクションを追加

     キャラクターが星を採取する処理を追加します。
     1フレームごとに、キャラクターと星のノードから位置を取得して距離を計算し、採取可能な距離より短ければ採取したとみなします。

    1. 『Game』スクリプトの『spawnNewStar』メソッドの最後に次のコードを追加

      // Game.js
          spawnNewStar: function() {
              // ...省略
              // 生成した『Star』ノードの『Star』コンポーネントに、『Game』コンポーネントの実体を渡す。
              newStar.getComponent('Star').game = this;
          },
      

      『Game』コンポーネントの実体とは、ゲーム実行時に稼働中の『Game』コンポーネントです。『Game.js』内のthisがそれに相当します。

    2. コードエディタで『Game』スクリプトを保存
    3. 『Star』スクリプトに、『onPicked』メソッドを追加

      // Star.js
          getPlayerDistance: function () {
              // 『Player』ノードの位置を取得
              var playerPos = this.game.player.getPosition();
              // this.node(星ノード)と『Player』ノードの距離を計算
              var dist = this.node.position.sub(playerPos).mag();
              return dist; // 計算した距離を返す
          },
      
          onPicked: function() {
         // 『Game』コンポーネントの『spawnNewStar』メソッドを呼び出して、新しく星を生成
              this.game.spawnNewStar();
              // 『star』ノードを破棄
              this.node.destroy();
          },
      

      補足:使用したメソッドについて
      • 『ノード.getPosition()』:ノードの位置をVec2型で返す
      • 『座標1.sub(座標2)』:座標1 - 座標2をVec2型で返す
      • 『ベクトル.mag()』:ベクトルの長さを返す
      • 『ノード.destroy();』:ノードを破棄し、他のオブジェクトへの参照を解放する
    4. 『Star』スクリプトに、『update』メソッドを追加
      1フレームごとにキャラクターと星の距離が『pickRadius』の値より小さいなら、採取の処理を行います。

      // Star.js
          update: function (dt) {
              if (this.getPlayerDistance() < this.pickRadius) { // キャラクターと星の間の距離がpickRadiusより小さい場合
                  this.onPicked(); // onPickedメソッド(採取の処理)を呼び出す
                  return;
              }
          },
      

    5. コードエディタで『Star』スクリプトを保存
      また実際に動かしてみます。プレビューボタンを押して、ゲームを実行します。
      キャラクターが星に近づくと、星が消えてランダムな位置に新しい星が生成されます。
     チュートリアルはまだ続きますが、今回はここまでです。

    あとがき

     ノードやコンポーネントの受け渡しが少しややこしいかもしれませんが、順を追って考えるとなるほどなと思います。

    おすすめ・関連する記事