phaserでアクションRPG Day2 フィールドの作成

f:id:naosim:20181010064547p:plain

前回phaserのセットアップをしました

naosim.hatenablog.jp

今日はフィールドを表示してみます

で...いきなり壁...
コードをcodepenで書いてたけどcodepenは画像を扱えなかった...
ワークアラウンドはあるけど本質的じゃないコードが入るといやなのでjsdo.itへお引越し
今回はでほぼ体力使い切った...

画像をpreloadで読み込む

まずはフィールドのマップチップ画像を読み込みます

http://jsrun.it/assets/K/e/w/N/KewNo.png

function preload() {
  // 背景画像の読み込み 16x16のマップチップの画像
  this.load.image('ground', 'http://jsrun.it/assets/K/e/w/N/KewNo.png');
  // thisはSceneクラス
  // https://photonstorm.github.io/phaser3-docs/Phaser.Scene.html
  
}

いきなりthisが出てきてビックリしますが、thisはSceneクラスです
Phaser 3 API Documentation - Class: Scene

Phaserのver2ではグローバル変数にgameオブジェクトをいれておいて使うのが定石ぽかったけど、ver3からはthisを使うんですねー
グローバル汚染が減ってよくなった

背景を表示する

次にGroundクラスを作ります
phaser系の記事を読むとだいたいcreateメソッドの中に直にいろいろ書いてますが、それだと辛くなってくるのでクラス化しておきます

Groundクラスを使う側の実装

function create() {
  const ground = new Ground(this);
  ground.create();
}

newしてcreateを呼んで終了
create()と同時に背景画像が表示される想定です
phaserはcreateと同時に表示する仕様がある意味特徴なのでそれに合わせます

他のフレームワーク(例えばenchantとか)だと、生成後にaddChild()すると表示されますが、phaserは一気に表示まで処理が進みます
強気だなぁと思うけど、慣れればまぁそんなもんって感じですね

Groundクラス

次にGroundクラスの実装です

class Ground {
  constructor(scene) {
      this.scene = scene;
      this.level = [
        [  6,  0,  0,  0,  0,  0,  0,  0,  0,  0,  6],
        [  6,  0,  0,  0,  0,  0,  0,  0,  0,  0,  6],
        [  6,  0,  0,  0,  0,  0,  0,  0,  0,  0,  6],
        [  6,  0,  0,  0,  0,  0,  0,  0,  0,  0,  6],
        [  6,  0,  0,  0,  0,  0,  0,  0,  0,  0,  6],
        [  6,  0,  0,  0,  0,  0,  0,  0,  0,  0,  6],
        [  6,  0,  0,  0,  0,  0,  0,  0,  0,  0,  6],
        [  6,  0,  0,  0,  0,  0,  0,  0,  0,  0,  6],
        [  6,  0,  0,  0,  0,  0,  0,  0,  0,  0,  6],
        [  6,  0,  0,  0,  0,  0,  0,  0,  0,  0,  6],
        [  6,  0,  0,  0,  0,  0,  0,  0,  0,  0,  6],
        [  6,  0,  0,  0,  0,  0,  0,  0,  0,  0,  6],
        [  6,  0,  0,  0,  0,  0,  0,  0,  0,  0,  6],
        [  6,  0,  0,  0,  0,  0,  0,  0,  0,  0,  6],
        [  6,  0,  0,  0,  0,  0,  0,  0,  0,  0,  6],
        [  6,  0,  0,  0,  0,  0,  0,  0,  0,  0,  6],
        [  6,  0,  0,  0,  0,  0,  0,  0,  0,  0,  6],
        [  6,  0,  0,  0,  0,  0,  0,  0,  0,  0,  6],
        [  6,  0,  0,  0,  0,  0,  0,  0,  0,  0,  6],
        [  6,  0,  0,  0,  0,  0,  0,  0,  0,  0,  6],
        [  6,  0,  0,  0,  0,  0,  0,  0,  0,  0,  6],
        [  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6],
      ];
      this.layer = null
  }

  create() {
    const map = this.scene.make.tilemap({ data: this.level, tileWidth: 16, tileHeight: 16 });
    this.layer = map.createStaticLayer(0, map.addTilesetImage("ground"), 0, 0);
    // ここで背景画像が配置される
  }

ただの壁のある箱です
createStaticLayerを呼ぶとlayerが生成されて画面にフィールドが表示されます
ただ...createStaticLayerの意味がよくわからん...

Phaser 3 API Documentation - Class: Tilemap

他にもcreateDynamicLayerてのもある
何がStaticで何がDynamicなのか...物理演算的なやつかな... とりあえず今はStaticで実装しておきます

以上、

jsdo.it

ここまでで箱っぽいフィールドができました

phaserでアクションRPG Day1

f:id:naosim:20181010064547p:plain

アクションRPGが作りたくなった
あとゲームフレームワークのPhaserがバージョン3になってたので勉強も兼ねて開発経過をメモします

初期状態のおまじない

var config = {
  type: Phaser.AUTO,
  width: 400,
  height: 400,
  physics: {
    default: 'arcade',
    arcade: {
      debug: true// お好みで
    }
  },
  scene: {
    preload: preload,
    create: create,
    update: update
  },
  pixelArt: true// アンチエイリアスをoffにする
};
var game = new Phaser.Game(config);

function preload() {
  // assetの読み込み
}

function create() {
  // 初期化処理
}

function update() {
  // フレーム単位の処理
}

ここは「おまじない」だと思っていい
設定の仕方は以前はセッターを使ってたけど、ver3からはconfigオブジェクトにセットするように変更されたっぽい
その方が引数の拡張がしやすいからかな

設定のポイントはpixelArtを必ず設定すること

これを設定しないとアンチエイリアスが効いてピクセルが潰れてボケボケな絵になる
ファミコンぽいゲームを作りたいなら必ずpixelArtをtrueに設定しよう

その他の設定では、今回は物理的な衝突が使いたいのでarcadeという物理演算を使います arcadeは四角形の衝突等を適当に計算してくれます

あとhtml側はphserをCDNから読み込んでおけばok

<script src="//cdn.jsdelivr.net/npm/phaser@3.11.0/dist/phaser.js"></script>

便利だなぁ

See the Pen ActionRPG Day1 by なおしむ (@naosim) on CodePen.

実行すると真っ黒な画面が表示されました
とりあえず今日はここまで

問題: いい感じの日付取得

下記に月・日を入力するといい感じに日付(年月日)にしてくれる機能がある。
これと同じ機能のメソッドcreateDate()を完成させよ。
メソッドのインターフェースは↓この通り。

/**
 * @param {number} month 1-12
 * @param {number} dayOfMonth 1-31
 * @param {Date} currentDate
 * @return {Date}
 */
function createDate(month, dayOfMonth, currentDate) {
    //TODO: 実装
}

ただし言語はjsでなくてもなんでも良い。
また、monthやdayOfMonthに不正な値(マイナスなど)が入ることは考慮しなくて良い。

求められるスキル: 要件分析、実装

いい感じDate

月:
日:
現在:



結果:

先輩に「メソッドを実行して結果を変数に突っ込め」と言われたら2

前回の記事でメソッドを実行して変数に突っ込む時の思考を整理しました

naosim.hatenablog.jp

「そんなの出来て当然」と思った方も多いと思いますが、実はjavaはそんなに甘くないです
ってことで抜き打ちテスト!

以下の問いに答えよ

1. hogeメソッドを実行して変数に突っ込め

class Hoge {
public int hoge(String text) { /* 実装省略 */ }
}

2. hogeメソッドを実行して変数に突っ込め

class Hoge {
  public static int hoge(String text) { /* 実装省略 */ }
}

3. valueを取得して変数に突っ込め

class Hoge {
  @Getter
  private final String value;
  public String getDoubleValue() { return value + value; }
}

4. hogeメソッドを実行して変数に突っ込め

@Component
class Hoge {
public int hoge(String text) { /* 実装省略 */ }
}

5. invokeメソッドを実行して変数に突っ込め

※これはひっかけ問題です

@RestController
class Hoge {
  @RequestMapping(value = "/hello", method = RequestMethod.POST)
  public Map invoke() { /* 実装省略 */ }
}

解答

1

int hoge = new Hoge().hoge("あいうえお");

インスタンスに対してメソッドを呼べばOK

2

int hoge = Hoge.hoge("あいうえお");

クラスに対してメソッドを呼べばOK

3

ここからが本題
@Getterに注目できればOK @Getterlombokが提供しているアノテーションで、フィールドにつけると"getフィールド名()"の形式のメソッドを自動生成してくれる
今回の場合はvalueアノテーションが付いているのでgetValue()が生成されている
なので正解はこう

String value = new Hoge("あいうえお").getValue();

こんな感じでアノテーションが目に見えないメソッドを生成していることがある
アノテーションの意味を理解しよう
ちなみに今回の問題は@Getterを理解してないと
「問題はvalueを取れって言ってるけどprivateだから取れない。。お?getDoubleValue()てメソッドがあるぞ。とりあえずこれ使っとこと」

String value = new Hoge("あいうえお").getDoubleValue();

みたいなな違いが起きる
これはアノテーションを理解していない場合に現場でありがちな勘違い
気をつけよう

4

@Componentに注目できればOK
@Componentの場合はDIが絡んでいるのでインスタンス生成に@Autowiredを用いる
なのでどこかで生成してからメソッドを呼べばok

@Autowired
Hoge hoge;
int hoge = hoge.hoge("あいうえお");

ちなみに@Autowiredしてから使う系は @Componentの他にも@Service@Repositoryなどがある
いちおう文法上は普通にnewしても使えるけど、たぶんチームメンバーに怒られる

5

これはもはやRestApiなので普通にnewして呼ぶものじゃない
curlコマンドで呼ぶとか、webClient的なもので呼ぶとか、テストツールで呼ぶとか

まとめ

「メソッドを実行して結果を変数に突っ込め」と言われた時、その実現方法は状況に応じていろいろあることがわかった
とくにlombokやDIなどのアノテーションが絡むと状況判断が難しくなる
アノテーションに気をつけて正しい状況判断ができるようになろう

先輩に「メソッドを実行して結果を変数に突っ込め」と言われたら

javaのコーディング中にこんな風に言われた時に、どんなことを考えているのかを整理しておきます

調査

まず対象となるメソッドが何なのかを調査します

  1. メソッドはインスタンスメソッドかクラスメソッドか
  2. 引数の型は何か
  3. 戻り値の型は何か

たとえば

class StringUtil {
int getLength(String text) { /* 実装省略 */ }
}

ならこう

実装

調査が終わったら次は実装です
最初に書くのは戻り値の型です

int

個人的にはここが最もハードルが高いように感じます
どうしてもメソッドの実行を最初に書きたくなるみたい
ただ文法上、変数宣言が先なのでしょうがない

次に書くのはメソッドの結果を入れる変数名

int length

クーーーッ!
まだメソッド実行させてくれないのかー!
て感じですが変数名を決める必要があります
ここで変数名を迂闊にhogeとかにすると叱られます
適切な名前をつけましょう

次にようやくメソッドの実行です
メソッドがインスタンスメソッドなら変数名.メソッド名()
クラスメソッドならクラス名.メソッド名()の形式で書きます

今回はインスタンスメソッドなので前者の形式ですが、インスタンスが入ってる変数がないのでそれを先に書きます

StringUtil util = new StringUtil();
int length

次にメソッドを書きます
とりあえず引数は空で良い

StringUtil util = new StringUtil();
int length = util.getLength();

最後に引数を書きます
引数の型を意識して書きます

StringUtil util = new StringUtil();
int length = util.getLength("あいうえお");

以上

まとめ

コーディング中に「メソッドを実行して結果を変数に突っ込め」と言われたときの思考を整理しました
普段は体が勝手に反応するレベルでコーディングしてますが、整理してみるといろいろ考えてるものだな
とりあえず調査してから実装することが重要だと思います

タスクの依存性を表現したタスクボードをmermaidjsで書くと良い

タスク管理の方法としてバックログタスクかんばん(TODO,DOING,DONEのやつ)を使っていますが、ちょっとツラみを感じています
これらの方法はタスクの優先順位が見える意味では良いけど、タスク間の依存関係が見えない
最近やってる案件がウォーターフォール的なので、優先順位よりもタスクの依存の方が知りたい
そうゆう意味でタスクを管理する方法としてはバリューストリームマップやアローダイアグラムのような図で表現して、終わったらチェックマークをつけていく感じにしたいなぁと
ただなかなかイイツールが無くて...
と思ってたらmermaidjsでイイ感じにかけたーー!

百聞は一見に如かず

説明するより見た方が早いので今回作った図を載せます

9月
1w
2w
3w
リリース
サービスイン
テスト
リリース準備
設計
コーディング

タスクの依存関係がわかる!

図から以下のことがわかります

  • タスク同士が矢印で繋がっている => の依存関係がわかる
  • 大きな枠が月、小さな枠が週 => 週ごとのスケジュールがわかる
  • 色 => タスクの状態がわかる
    • 赤: 私にアサインされたタスク
    • グレー: 完了したタスク

mermaidjsのコード

書いたコードはこちら

graph LR
T0[設計]
T1[コーディング]
T2[テスト]
T3[リリース準備]
T4[リリース]
T5[サービスイン]

T0 --> T1
T1 --> T2
T2 --> T4
T3 --> T4
T4 --> T5

subgraph 9月
  subgraph 1w
    T0
    T1
  end
  subgraph 2w
    T2
    T3
  end
  subgraph 3w
    T4
    T5
  end
end

classDef done fill:#aaa;
classDef naosim fill:#faa;
class T0,T1,T3 done;
class T2 naosim;
  • タスクの内容と依存関係はフローチャートの記法で書く
  • スケジュールはsubgraphで書く
  • タスクの状態は独自のclassを定義する

タスクで表現したいこと毎にかける

で、mermaidの何が良いかって、コードを見ればわかる通りタスクの内容・依存・スケジュール・状態を別々に管理きる!
やべーーmermaid最高やーー!

まとめ

  • mermaidでタスクの依存性表現したタスクボードが作れる
  • mermaidならタスクで表現したいこと毎にかけるから良さそう

【GAS + kotlin】コードを分割してメンテしやすくする

前回、GASをkotlinで開発する方法を書きました

naosim.hatenablog.jp

この記事で「3万行を超えるkotlin.jsをコードに直接コピペする」と書きましたが、さすがにそれでは辛い

幸いGASは同一プロジェクトのスクリプトを簡単に呼べるのでそれを利用してファイル分割してみました
以下、その方法

前回の最終形態

コード.gs (呼び出し元スクリプト)

// おまじない
var ArrayBuffer = Int8Array = Int16Array = Uint16Array = Int32Array = Float32Array = Float64Array = function(){};

// kotlin.jsのコード 3万行超え

// gaskotlin.jsのコード 20行くらい

// 呼び出し
function myFunction() {
  gaskotlin.com.naosim.gaskotlin.run();
}

ファイルを3つに分割する

kotlin.js.gs

function initKotlin() {
  // おまじない
  var ArrayBuffer = Int8Array = Int16Array = Uint16Array = Int32Array = Float32Array = Float64Array = function(){};

  // kotlin.jsのコード 3万行超え



  return kotlin; // 最後にkotlinオブジェクトを返す
}

gaskotlin.js.gs

function initGaskotlin() {
  var kotlin = initKotlin(); // kotlinを読み込む


  // gaskotlin.jsのコード 20行くらい


  return gaskotlin;// 最後にgaskotlinオブジェクトを返す
}

コード.gs (呼び出し元スクリプト)

var gaskotlin = initGaskotlin();
function myFunction() {
  gaskotlin.com.naosim.gaskotlin.run();
}

結果

可読性が上がった

当たり前ですが...
3万行のコードは読みにくいので...

ファイルの保存時間が早くなった

3万行のコードを保存した時は保存だけで結構時間がかかっていましたが、それを外だししたら早くなった
GASは保存時に何してるんだろう...
保存するファイルだけシンタックスエラーのチェックが走るのかな?

まとめ

  • kotlin.js等を別gsファイルに外だしした
  • 保存時間が短くなったりしてよかった