技術ブログ | 株式会社アイプランニング IPlanning corporation

アイプランニング社員が調査したこと、学んでいることが具体的にどんなものなのかを披露します。 Here is what the IPlanning corp employees surveyed and what they learned.

Java製のシューティングゲームをTypeScriptでWebに移植する

f:id:iplcojp:20180302203529g:plain

10年前にIPL社員が作ったJava製のシューティングゲームを、Web技術を使ってリメイクしました。 また、JavaからTypeScriptへの移植のポイントについても記載します。

何はともあれゲームを遊ぶ

以下のURLからプレイできます。

https://iplanning.heteml.net/2018-shooting/

Chrome, Firefox, Edgeブラウザで動作確認しております。スマートフォンには非対応です。

操作方法

スペースキー:ゲーム開始&弾を撃つ
カーソルキー:上下左右に移動

ハイスコアはお使いのブラウザ内(LocalStorage)に記録されます。

経緯

アイプランニングでは、毎年新人に自由課題を課しています。

ゲームやカロリー計算機など、人によって作るものはそれぞれですが、 10年前に筆者は、当時それなりに新しい技術だったJavaとAWT(当時既にSwingはありましたが)を使って、 簡単なシューティングゲームを作りました。

旧版はこちらで記事にしています。

見た目は下記のような、かなり寂しいものになっています。

f:id:iplcojp:20180302194949j:plain

10年の時を経て、今やJavaScriptの方が当時のJavaよりも高速に動作する時代です。 JavaScriptはブラウザなどのマルチプラットフォーム*1で動作するという利点があるため、 弊社でもJavaからJavaScriptへの移植案件を度々頂くことがあります。

一方で、JavaScriptは良くも悪くも柔軟なところがあり、バグを作り込みやすく、大規模な開発には向いていません。

これに対処するため、弊社で採用している言語が「TypeScript」です。

TypeScriptとは

www.typescriptlang.org

TypeScriptは、コンパイルするとJavaScriptを出力する言語です。 (このような言語を一般的にAltJSと呼びます。同種の言語として、ElmやFlowなども人気です)

TypeScriptの最も強力で便利な機能は、型チェックです。

TypeScriptでは、コンパイル時に型チェックを行なうことが出来ます。 最も簡単なサンプルを下記に例示します。

f:id:iplcojp:20180305202844p:plain

例示したコードでは、name引数にstring型を指定しています。 ここで、name引数に数値を指定すると、型が想定と異なるために、コンパイル時にエラーになります。 また、namenullundefinedが来ることも防ぐ事が出来ます。

このTypeScriptコンパイラの強力な型チェック機能により、開発者はより安全なコードを書くことが出来ます。

移植作業は、大きなひとかたまりのコードを移植して、エラーを直す作業の繰り返しなので、賢いコンパイラは非常に頼りになる存在です。

TypeScriptはJava開発者におすすめ

アイプランニングには、多くのJava経験者が在籍しています。 その為、Java経験者にとって学習が容易である点も重要です。

その点、TypeScriptはJavaからの移行もスムーズです。理由としては下記が挙げられます。

  • TypeScriptはC#に似ています*2が、C#に近い言語であるJavaにもある程度文法が似ています。
  • クラス*3やインターフェースがあるため、Javaのフィーリングで書くことが出来ます。
  • VSCodeを使うことで、Eclipseのような賢い補完やリファクタ機能を使う事ができます*4

ただし、下記の点について注意が必要です。

  • TypeScriptは型指定が後置です。
  • TypeScriptはStructural Subtyping(構造的部分型) を採用しています。
    • 型の構造が一致していれば派生型として扱われます。基本的にJavaは型に対して厳格ですが、TypeScriptの場合はより柔軟です。これはJavaからTypeScriptへの移植は簡単ですが、TypeScriptからJavaへの移植は難しいことを意味しています。
  • 計算精度について注意が必要です。

移植の方法

JavaからTypeScriptへのコンバータは知る限り存在しません。*5

ということで、ひたすら手で書き換えていきます。対象クラスは13個です。

f:id:iplcojp:20180305211013p:plain

他のクラスに依存していない、例えばデータクラスの移植を最初に行なうと良いでしょう。一括変換などは現実的ではありません。エラーが出ないことを確認しながら少しずつ置き換える作業になります。

また、作業量を削減するため、最初はany型を許可する設定(noImplicitAnyを指定しない)にしておきましょう。*6

例として、プレイヤークラスの移動処理を掲載します。

移植前(Java

   /**
    * 移動処理
    * @param mx x方向の入力(-1 ... +1)
    * @param my y方向の入力(-1 ... +1)
    */
    public void move(int mx, int my)
    {
        //Canvasの外には移動できないようにする
        double postX = x + mx * speed;
        double postY = y + my * speed;
        
        if ((0 < postX)&&(postX < 500))
        {
            x = postX;
        }
        if ((0 < postY)&&(postY < 480))
        {
            y = postY;
        }
    }

移植後(TypeScript)

  movePlayer(mx: number, my: number) {
    //Canvasの外には移動できないようにする
    var postX: number = this.x + mx * this.speed;
    var postY: number = this.y + my * this.speed;

    var margin = 50
    if (0 + margin < postX && postX < 500- margin) {
      this.x = postX;
    }
    if (0 + margin < postY && postY < 480 - margin) {
      this.y = postY;
    }
    this.mesh.position.x = this.x;
    this.mesh.position.y = Util.normalize(this.y);
  }

3D化

折角なので、見た目を豪華にするため、表示を3Dに変更しました。

Web上で3D表示を行なう際は、必ずと言っていいほどThree.jsが採用されます。今回も、Three.jsを使います。

three.js - Javascript 3D library

3Dとはいえ、当たり判定や移動ロジックなどは2Dのものをそのまま流用出来ます。2Dと同じ座標に3Dの物体を配置するようにするだけです。Java版は描画を各オブジェクトに任せるという抽象化を行っていたため、描画部分のみの変更で対応できました。

移植しての感想

  • 元バージョンの設計がある程度抽象化されていたため、それほど苦労せずに移植出来ました。
  • やはり3Dならではの描画の難しさがありました。ライティングや表示サイズを確認しながら作れないとかなり厳しいです。3DゲームでUnity等のオーサリングツールが主流になっている理由がよく分かります。
  • 3Dモデリング能力があればもっと見た目の良いものが出来たのでは無いかと思います。
  • Three.jsにはオンラインエディタが存在し、シーンをインタラクティブに作成することが出来ます。そちらを使うようにしたほうが良かったかもしれません。
  • 元バージョンではFlyweightパターンを意図したオブジェクトの使い回しを行っていましたが、今となっては不要なテクニックかもしれません。

オンラインエディタは下記のようなインタラクティブなものです。スクリプトも書けるため、使いこなすことで製作効率は上がりそうです。

f:id:iplcojp:20180305105507p:plain

ソースコード

ソースコードは下記からダウンロード出来ます。

https://iplanning.heteml.net/2018-shooting/ipl-shooting-src.zip

終わりに

JavaからWebアプリへ移行するに当たって、TypeScriptはおすすめの選択肢です。 弊社では業務アプリケーションのWebアプリへの移植も多く行っております。ご相談は弊社メールフォームよりお問い合わせ下さい。

*1:CordovaやElectronなどを使うことで、ネイティブアプリケーションのようなものを作ることが出来ます

*2:C#と言語デザイナーが同じという経緯があります。実際にはTypeScriptはJavaScriptの拡張として作られているので、あくまでスタイルが似ているということです

*3:JavaScriptはプロトタイプベースの言語であり、JavaScriptやTypeScriptのクラスシンタックスはプロトタイプのシンタックスシュガーです。Javaと同じ動作はしません。詳しくはJavaScriptのプロトタイプについて調べてみて下さい

*4:これ自体はVSCodeの機能というわけではなく、TypeScriptが備えているLanguage Serverという機能に対応しているエディタであれば利用可能です。例えばVimなどでも補完を使うことが出来ます

*5:JavaからJavaScriptへのコンバータは昔から存在します。ただしJavaAPIを実行するためのランタイムを抱え込む、JavaScriptのモダンなツールを使うことが出来ない、などのデメリットもあり、普段はコンバートよりもフルスクラッチに近い移植を選択しています

*6:勿論最終的にはanyがなくなることが望ましいです。anyを使うと型チェックや補完機能を活かすことが出来ません