ブロックチェーンゲーム制作チェーン -Minimal Arcade Medal-

ブロックチェーン技術、ゲーム作成のためのコード、ドット絵やアニメーションの作り方を解説するブログです。最終的に記事の内容を組み合わせることで誰でもブロックチェーンゲームが作れるようにしたいです。Micro Arcade MedalというdApps開発中です。

座標システムを理解するーCocosCreator

今回はNodeの座標を相対座標から絶対座標に変換するというサンプルコードを解説します。
サンプルプロジェクトはCocosCreatorのNewProjectからExample Collectionでアクセスできます。


CocosCreatorでは画像などテキストを格納するNodeというものがあるのですが、そこにComponentというプログラミングコードを貼り付けることでゲームを作っていきます。


簡単に言うと、Nodeの座標とは画像やテキストの位置のことです。


基本的にどのゲームエンジンでも同じような座標システムを採用しているので、この記事の内容は応用が利きます。


記事の前半だけ読んでおけば、普通に開発してる分にはあまり困らないと思います。


サンプルコードの場所は以下の通り。
example/assets/cases/01_graphics/01_sprite/

このSceneについて


f:id:kusakabob:20180709165721p:plain


これはAnchorPointというSceneです。
Nodeの座標を表しているのですが、括弧書きの中の数字は絶対座標を表しており、上がAnchorRelated、下がAnchorUnrelatedの場合の値です。


ちなみにこの画像、またコードはオリジナルのものとは値が違います。
色々と試行錯誤した結果、オリジナルコードが間違っているのではないかという結論に達したので、理解しやすいように変更しました。


コードの中身を理解する上で必要なのが絶対座標、相対座標、そしてアンカーポイントです。
順に説明していきます。

相対座標と絶対座標とは


Node座標の値はどこを起点、つまり(0,0)とするかで変わります。


絶対座標の場合、起点は左下です。
相対座標の場合、起点は親の座標です。


絶対座標は車のハンドルの位置を地球儀の緯度と経度で指定するようなものです。
相対座標は車から見てハンドルがどの場所にあるのかといった方法です。


具体例を出します。


f:id:kusakabob:20180709165830p:plain


このSceneの編集画面です。
左側のNode Tree、またSceneパネルを見てもらうと上側のgoldを選択しているのがわかります。
画面右側のPropertiesパネルはそのgoldの画像を納めた Nodeの情報が表示されています。


座標に着目するとgoldの位置はx=0,y=0と書いてあります。
これはgoldの親がsheep_run_0であり、sheep_run_0の座標から見るとちょうど(0,0)の場所にあるということです。


sheep_run_0が親であるということは画面左側のNode Treeを見てもらうと、Sheep_run_0とgoldが入れ子の関係性にあることからわかります。


全ての親であるCanvas以外のNodeの位置は基本的に相対座標で管理されています。


f:id:kusakabob:20180709165830p:plain


一方絶対座標というのは、この画面の左下を(0,0)とした場合の位置になります。
羊やコインが配置されている画面の左下に0と書かれている場所が起点です。
上側のコインは大体(500,500)あたりにあることが画面から見て取れますが、その正確な値が(480,470)というわけです。


f:id:kusakabob:20180709165721p:plain

アンカーポイントとは


ところで、上記のコインの位置なのですが、コインの画像自体はどこを起点としてるのでしょうか。


コインの左下をコインの位置と定義するか、コインの右上をコインの位置とするかで、見かけ上の位置は変わります。


例えば、スマホが机の上においてあります。
机の左下の角を(0,0)とします。
するとスマホの位置は(x,y)で表すことができます。
(50,50)とすると机の左下から右に50cm、左に50cmの位置にあると言えます。


この際にスマホの左下の角が50cm,50cmにあるのか、スマホの右上の角が50cm,50cmにあるのかでスマホの見かけ上の位置は、スマホの幅と高さ分ずれます。


つまりスマホのどこを指して50cm,50cmの場所にあるかという、そのポイントを決める必要があります。


それがアンカーポイントです。


アンカーポイントはデフォルトではx=0.5,y=0.5の位置に設定されています。
これは画像の真ん中の地点がアンカーポイントであるということです。


0,0ならば右上、1,1ならば左下の角が画像の中心となります。


画像を回転させたり大きくするときは、ここを起点に画像が編集されます。


f:id:kusakabob:20180709165830p:plain


上の画面の場合、羊のアンカーポイントは1,1
コインのアンカーポイントは0.5,0.5に設定しています。


それぞれ見かけ上の位置は違いますが、Positionは一緒です。


Nodeの構造について


ここからコードの解説に入っていきます。


おさらいすると、今回のサンプルは各goldの相対座標を絶対座標に変換するプログラムです。


f:id:kusakabob:20180709165721p:plain


相対座標は上のコインも下のコインも、それぞれの親である羊からみて(0,0)の地点にあります。
(0,0)を左下の角からみた絶対座標である(480,470)と(480,200)に変換して表示しています。


さてこのSceneのNodeの構造は

  • Canvas
    • BackGround
    • sheep_run_0
      • gold
    • sheep_run_0
      • gold
    • posAR
    • NewLabel
    • NewLabel
    • pos

となっており、ARというComponentファイル(ゲームロジック)が、全てのNodeの親であるCanvasと紐づけられています。

コードの中身


以下ARの中身です。

cc.Class({
    extends: cc.Component,

    properties: {
        
        posAR: cc.Label,
        pos: cc.Label,
        goldAR: cc.Node,
        gold: cc.Node
    },

    onLoad: function () {
        var sheep = this.goldAR.parent;
        var posAR = sheep.convertToWorldSpaceAR(cc.v2(this.goldAR.x, this.goldAR.y));
        this.posAR.string = '(' + parseInt(posAR.x) + ', ' + parseInt(posAR.y) + ')';
        
        sheep = this.gold.parent;
        var pos = sheep.convertToWorldSpace(cc.v2(this.gold.x, this.gold.y));
        this.pos.string = '(' + parseInt(pos.x) + ', ' + parseInt(pos.y) + ')';
    },

});


基本的にComponentの構造は

  • properties: データの定義
  • onLoad: データの初期化
  • update: 毎フレーム実行される処理

なのでこれらに着目するとコードの理解が捗ります。
ちなみにこのコードにはupdateがありません。

properties: {    
        posAR: cc.Label,
        pos: cc.Label,
        goldAR: cc.Node,
        gold: cc.Node
    },
  • posAR: AnchorRelativeであるNodeの位置を表示するテキスト
  • pos: AnchorRelativeでないNodeの位置を表示するテキスト
  • goldAR: AnchorRelativeであるNode
  • gold:AnchorRelativeであるNode
  onLoad: function () {
        var sheep = this.goldAR.parent;
        var posAR = sheep.convertToWorldSpaceAR(cc.v2(this.goldAR.x, this.goldAR.y));
        this.posAR.string = '(' + parseInt(posAR.x) + ', ' + parseInt(posAR.y) + ')';
        
        sheep = this.gold.parent;
        var pos = sheep.convertToWorldSpace(cc.v2(this.gold.x, this.gold.y));
        this.pos.string = '(' + parseInt(pos.x) + ', ' + parseInt(pos.y) + ')';
    },
  • var sheep: goldARの親のNode
  • var posAR:goldARのxとyをVec2*に変換&goldの相対座標を絶対座標に変換

Vec2は2Dのベクトルと位置を表すclassタイプです。

  • this.posAR.string:このComponentのposAR propertyのStringをposARに置き換える


同じようなことの繰り返しなので後半は省きます。

convertToWorldSpaceARとconvertToWorldSpaceの違いについて


上記のコードは同じようなコードが2回記述されています。


違いはconvertToWorldSpaceかconvertToWorldSpaceARかどうかです。


どちらも相対座標を絶対座標に変換するメソッドですが、
変換する際に親のアンカーポイントを考慮するか考慮しないかの違いがあります。


文字だけだと非常にわかりづらいので、以下の画像で解説します。


f:id:kusakabob:20180709165721p:plain


上の画面では、上のコインも下のコインも絶対座標は同じです。
下の画面では、上のコインの座標と下のコインの絶対座標は違います。


f:id:kusakabob:20180709183406p:plain


違いは羊のアンカーポイントです。
上の画面ではどちらの羊もアンカーポイントは(0,0)
下の画面ではどちらの羊も(1,1)です。


羊の画像のサイズは168px * 113pxなのでアンカーポイントを(0,0)から(1,1)に変えると、
そのサイズ分羊の位置がずれます。


羊の見かけ上の位置は羊一個分ずれたわけなんですが、コインの見かけ上の位置は変わっていません。
羊のアンカーポイントから見てコインは(0,0)の地点にあるという事実は変わってないので、コインはそこに止まります。


convertToWorldSpaceを使って子供の相対座標を絶対座標に変換すると、その子供は親のアンカーポイントを考慮せずに座標を算出します。
実際はアンカーポイントを変えたことによって発生した画像のずれなのですが、親が移動した分だけ自分の座標をずらして変換してしまいます。


converToWorldSpaceARは親のアンカーポイントが変わっただけで座標自体は変わっていないということを子供が考慮して座標を算出します。
つまり、親のアンカーポイントをどれだけ変えようと、子供の絶対座標は変わりません。


めっちゃざっくりいうと、convertToWorldSpaceARの方が賢いってことですね。

所感


自分の中でもはっきりわかってない部分が多かったのですが、今回この記事を書くことでだいぶ整理されました。


サンプルコードの間違い?のおかげで意味のわからない数字がでてきてパニックでした笑
変更した箇所は以下の通りです。

  sheep = this.gold.parent; //this.goldAR.parentになっていた。
  var pos = sheep.convertToWorldSpace(cc.v2(this.gold.x, this.gold.y)); //convertToWorldSpaceARだった。


相対座標を絶対座標に変換するのは、異なる親をもつ子供同士の位置を比較するときに役立ちます。
具体的には宇宙船のレーザーガンから発射されるレーザーが敵に当たったかどうかを判定するときとかですかね。


ちなみにconvertToNodeSpaceとそのAR版があります。
これは逆の働きをするメソッドです。