<今回やること!>
- こちらの記事の続きを行います。
mmorley.hatenablog.com
- スクリプト(ソースコード)を書いて、アニメーション、キー入力、ループ処理等を実装します。
前回は、ゲームシーンを作成して『Canvas』に背景、地面、キャラクタを配置しました。
今回は、スクリプトを書いて、ゲームに必要な機能を実装します。
目次
使用環境
私が使用している環境です。
- Mac OS X El Capitan Version 10.11.6
- Cocos Creator Version 2.1.1
- ブラウザ:Google Chrome Version 73.0.3683.103 (64-bit)
キャラクターのスクリプトを作成
スクリプトを作成
- 『Assets』パネルで、『assets』フォルダを右クリックし、『Create』→『Folder』をクリックしてフォルダを作成
- 作成したフォルダを右クリックし、『Rename』を選択して、名前を"scripts"に変更
今回は、この中にスクリプトを保存します。
- 『scripts』フォルダを右クリックし、『Create』→『JavaScript』を選択
- 作成したスクリプトの名前を『Player』に変更
- 『Player』スクリプトをダブルクリックして、コードエディタを開く
プロパティを設定
- コードエディタで『Player』スクリプトの『properties』の部分を下記のように変更
// Player.js //...省略 properties: { // キャラクターのジャンプの高さ(ピクセル) jumpHeight: 0, // キャラクターがジャンプで最高地点に達するまでの時間:(秒) jumpDuration: 0, // 水平方向の最大移動速度(ピクセル/秒) maxMoveSpeed: 0, // 水平方向の加速度(ピクセル/秒^2) accel: 0, }, //...省略
- 『command + s』キー(Windowsなら『ctrl + s』キー)を押してスクリプトを保存
- 『Node Tree』パネルで、『Player』ノードを選択
- 『Properties』パネルで、『Add Component』ボタンをクリックし、『Custom Component』→『Player』をクリック
『Player』スクリプトがコンポーネントとしてノードに追加されます。
- 『Properties』パネルの『Player』コンポーネントに、先程のコードで『properties』に記載した変数が表示されるので下図のように値を設定する
スクリプトをで変更しなくても、『Properties』パネルで値を変更が出来ます。
キャラクタのジャンプのコードを作成
- 『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
横軸:時間、縦軸:位置easeSineIn easeSineOut easeSineInOut easeCubicActionIn easeCubicActionOut easeCubicActionInOut easeQuinticActionIn easeQuinticActionOut easeQuinticActionInOut easeCircleActionIn easeCircleActionOut easeCircleActionInOut easeQuadraticActionIn easeQuadraticActionOut easeQuadraticActionInOut easeQuarticActionIn easeQuarticActionOut easeQuarticActionInOut easeExponentialIn easeExponentialOut easeExponentialInOut easeElasticIn easeElasticOut easeElasticInOut
- 『cc.moveBy(時間, 距離)』:現在の位置から指定時間で指定距離移動するアクションを返す
- 『Player』スクリプトの『onLoad』メソッドを下記のように変更
シーンのロード直後に、追加した『setJumpAction』メソッドを呼び出してアクションを実行します。// Player.js onLoad: function () { // ジャンプアクションを作成 this.jumpAction = this.setJumpAction(); // ジャンプアクションを開始 this.node.runAction(this.jumpAction); },
補足:スクリプトについて - コードエディタで『Player』スクリプトを保存
シーンのロード後に『Player』ノードのジャンプアクションを実行するところまで作成したので、実際に動かしてみます。
『Cocos Creator』のエディタの上部のプレビューボタンを押すとデフォルトのブラウザが開いて、ゲームを実行します。
キャラクターのキー操作のコードを作成
キャラクターの移動を操作するための『A』と『D』のキー操作を追加します。
- 『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; } },
- 『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』:キーアップイベントを表す定数
- 『cc.systemEvent.on(
- 『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; // 現在のキャラクタの水平位置を更新 },
- コードエディタで『Player』スクリプトを保存
キー入力によって、キャラクターを左右に移動する処理を追加したので、また実際に動かしてみます。
プレビューボタンを押して、ゲームを実行します。
*ブラウザの制約により、1度マウスでクリックするまでキー入力に反応しません。『properties』パネルで『Player』コンポーネントのプロパティ値を変更することで、キャラクターのスピードを調整することが出来ます。
例えば、下記のように設定するとキャラクターがすばやく動きます。好みに応じて調整して下さい。Jump Height: 150 Jump Duration: 0.3 Max Move Speed: 400 Accel: 1000
星を作成
星は下記のような機能を持ちます。
プレハブ(Prefab)を作成
プレハブはノードを生成するためのテンプレート(型)です。
星の持つ機能をプレハブとして保存しておくと、その機能を持ったノードを動的(ゲームの実行中)に生成出来ます。
星のノードのように繰り返し生成するものに適しています。
- 『Assets』パネルの『textures』フォルダにある『star』を『Node Tree』パネルの『Canvas』ノードにドラッグ
プレハブ作成のために『Node Tree』に配置しますが、作成後に削除します。『Node Tree』に配置する位置はどこでも構いません。また『Scene』パネルで位置を調整刷る必要もありません。
- 『Assets』パネルの『scripts』フォルダを右クリックし、『Create』→『JavaScript』を選択
- 作成したスクリプトの名前を『Star』に変更
- 『Star』スクリプトをダブルクリックして、コードエディタを開く
- 『Star』スクリプトの『properties』の部分を下記のように変更
// Star.js properties: { // キャラクターと星の距離がこの値より小さい場合に得点とする pickRadius: 0 },
『star』プレハブが生成されます。
『star』ノードは使用しないので削除します。
ゲーム制御スクリプトを追加
ゲーム制御スクリプトでは、得点、失敗、リスタートの処理を行います。
- 『Assets』パネルの『scripts』フォルダを右クリックし、『Create』→『JavaScript』を選択
- 作成したスクリプトの名前を"Game"に変更
- 『Game』スクリプトをダブルクリックして、コードエディタを開く
- 『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 // プロパティの型をノード型に設定 } },
- コードエディタで『Game』スクリプトを保存
- 『Node Tree』パネルで、『Canvas』ノードを選択
- 『Properties』パネルで、『Add Component』ボタンをクリックし、『Custom Component』→『Game』をクリック
- 『Assets』パネルの『star』プレハブを、『Properties』パネルの『Game』コンポーネントの『Star Prefab』にドラッグ
プロパティに設定した型に該当するものだけ登録出来ます。
- 『Node Tree』パネルの『ground』ノードを、『Properties』パネルの『Game』コンポーネントの『Ground』にドラッグ
- 『Node Tree』パネルの『Player』ノードを、『Properties』パネルの『Game』コンポーネントの『Player』にドラッグ
- 『Properties』パネルの『Game』コンポーネントの『Min Star Duration』を3にする
- 『Properties』パネルの『Game』コンポーネントの『Max Star Duration』を5にする
星を生成する時に、『Min Star Duration』から『Max Star Duration』の間の乱数値で星が消える時間を設定します。
ランダムな位置で星を生成
- 『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); },
補足:使用したメソッドについて - コードエディタで『Game』スクリプトを保存
プレハブを利用して1つ目の星を生成する処理を追加しました。
また実際に動かしてみます。プレビューボタンを押して、ゲームを実行します。
実行する(ブラウザを再読込でも可)度に、ランダムな位置に星が生成されます。
キャラクターが星を採取するアクションを追加
キャラクターが星を採取する処理を追加します。
1フレームごとに、キャラクターと星のノードから位置を取得して距離を計算し、採取可能な距離より短ければ採取したとみなします。
- 『Game』スクリプトの『spawnNewStar』メソッドの最後に次のコードを追加
// Game.js spawnNewStar: function() { // ...省略 // 生成した『Star』ノードの『Star』コンポーネントに、『Game』コンポーネントの実体を渡す。 newStar.getComponent('Star').game = this; },
『Game』コンポーネントの実体とは、ゲーム実行時に稼働中の『Game』コンポーネントです。『Game.js』内のthisがそれに相当します。
- コードエディタで『Game』スクリプトを保存
- 『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();』:ノードを破棄し、他のオブジェクトへの参照を解放する
- 『ノード.getPosition()』:ノードの位置をVec2型で返す
- 『Star』スクリプトに、『update』メソッドを追加
1フレームごとにキャラクターと星の距離が『pickRadius』の値より小さいなら、採取の処理を行います。// Star.js update: function (dt) { if (this.getPlayerDistance() < this.pickRadius) { // キャラクターと星の間の距離がpickRadiusより小さい場合 this.onPicked(); // onPickedメソッド(採取の処理)を呼び出す return; } },
- コードエディタで『Star』スクリプトを保存
また実際に動かしてみます。プレビューボタンを押して、ゲームを実行します。
キャラクターが星に近づくと、星が消えてランダムな位置に新しい星が生成されます。
あとがき
ノードやコンポーネントの受け渡しが少しややこしいかもしれませんが、順を追って考えるとなるほどなと思います。
おすすめ・関連する記事
- mmorley.hatenablog.com
この記事の続きです。