~GWTでゲームを作る~

日本語(Japanese page) 英語(English page)

このページではGWTでゲーム作成するときに必要となるであろう情報についてまとめています。GWTで作ったサンプルとプログラムをお見せし、具体的に説明したいと思います。

まず、GWTで作成したサンプルをお見せします。Java Scriptを利用しているので、Java Scriptを利用できないと機能しません。矢印キーやクリック、ダブルクリックに反応します。もし、反応しないのであれば、アクティブになっていないのかもしれません。サンプルをクリックしてアクティブにしてください。各要素については途中で部分的に取り上げて説明します。実際のプログラムは最後に掲載します。

Eclipseへのインストール

Eclipseへのインストールの手順について説明します。

  1. Eclipse マーケットプレースを選択
    ヘルプ>Eclipse マーケットプレースを選択します。
    Eclipse マーケットプレースを実際に選ぶ画像
  2. Eclipse マーケットプレースでGWTを検索
    赤枠の箇所にGWTと入力し、緑枠の"Go"をクリックします。
    Eclipse マーケットプレースでGWTを検索している画像
  3. GWT Eclipse pluginをインストール
    赤枠の箇所をクリックして、出てきた画面でインストールするものを選択してインストールします。画像では赤枠の箇所はインストール済みになっていますが、インストールしていなければインストールと表示されています。
    EclipseへGWT Eclipse pluginを実際にインストールしている画像
    私の場合、Eclipse 4.8(Photon)に導入しましたが、SDK2.7.0、SDK 2.8.1を両方インストールしようとすると、エラーでできませんでした。結局、SDK 2.8.1しかインストールしませんでした。
    GWT Eclipse plugin関係でEclipseへインストールしたもの
インストール手順は以上です。

GWTアプリケーションプロジェクト作成の手順

GWTアプリケーションプロジェクトの作成手順について説明します。

  1. GWTアプリケーションプロジェクトを新規作成するためのウィンドウを表示する
    手順は2通りあります。まず1つ目について説明します。
    ファイル>新規>プロジェクトを選択します。
    新規プロジェクト作成を実際に選ぶ画像
    次にウィザード選択画面で赤枠の"GWT Web Application Project"をクリックします。次に、緑枠の"次へ"をクリックします。
    ウィザード選択画面でGWT Web Application Projectを実際に選ぶ画像
    次に2つ目について説明します。
    緑枠の箇所をクリックして出てきたメニューの"New GWT Application Project"を選択します。
    新規GWT Application Projectの作成を実際に選ぶ画像
  2. 必要項目を記入してGWTアプリケーションプロジェクトを新規作成
    1のどちらの方法でも下の画像のようなウィンドウが出てきます。青枠部分の記入、選択を行います。パッケージ名にルールがあるのかは不明です。好きに入れて動かないということはなさそうです。また、緑枠の"プロジェクト・サンプル・コードを生成する"は必ずチェックを入れてください。私の環境ではこれを外すとエラーが発生して作成できませんでした。(2018/08/26現在)最後に、赤枠部分の"完了"をクリックします。
    必要事項の記載、選択を行い、新規GWT Application Projectを実際に作成する画像
GWTアプリケーションプロジェクト作成の手順は以上です。

プログラム作成前の作成されたGWTアプリケーションプロジェクトの調整

プログラムの作成の前に行う作成されたGWTアプリケーションプロジェクトの調整について説明します。

上の新規GWTアプリケーションプロジェクトの作成手順で作成すると、下の画像のような構成で自動で作成されます。

自動で作られた新規GWT Application Projectの構成

もし、サーバー側で何か処理をする必要がなく、サイトにアクセスしてきたユーザーのパソコン内でJava Scriptが処理を実行してくれればよいのであれば、client以外のものは不要です。上の画像で言うと、howToCreate.server、howToCreate.sharedは不要です。サンプルコードも不要なので削除します。上の画像で言うと、howToCreate.client内のGreetingService.java、GreetingServiceAsync.javaは不要です。それらを実行すると下の画像のような構成になります。
不要なファイル、パッケージを削除した場合のGWT Application Projectの構成

サーバーと連携する場合に必要なものは削除したので他のファイルの中身を調整する必要があります。以下でそれを説明します。

  1. gwt.xmlの調整
    このサンプルで言うと、howToCreate.HowToCreateAGame.gwt.xmlを調整します。下の画像の青枠部を削除します。
    変更前
    変更前のgwt.xmlファイル
    変更後
    変更後のgwt.xmlファイル
  2. web.xmlの調整
    このサンプルで言うと、war.WEB-INF.lib.web.xmlを調整します。下の画像の青枠部を削除します。
    変更前
    変更前のweb.xmlファイル
    変更後
    変更後のweb.xmlファイル

また、GWTアプリケーションプロジェクトをコンパイルするとキャッシュがTempフォルダに溜まっていきます。何もしなければ普段のインターネットのキャッシュと同様に保存されますが、キャッシュのせいで変更が反映されないということがあるようです。その場合にキャッシュを削除したくなるので、保存場所を指定しておきます。デバッグの構成、あるいは実行の構成からGWT Development modeの設定を行います。GWT Development modeで実行したことがないと設定できないようなので、その場合は後の記述を見ながら実行してください。
設定前
Tempフォルダの設定前
設定後
Tempフォルダの設定後

これでいよいよGWTアプリケーションの作成に入ることができます。

GWTアプリケーションのmain関数的な関数

このサンプルで言うと、howToCreate.client.HowToCreateAGame.java内のpublic void onModuleLoad()がmain関数的な関数です。サンプルコードにも自動的に作成されていますので、参考になります。

public class HowToCreateAGame implements EntryPoint,AnimationCallback {
private AnimationHandle handler;

public void onModuleLoad()
{
init();
if(bInitHasFailed==true)
{
return;
}
execute(0);
// executeByTimer();
}

@Override
public void execute(double timestamp)
{
handler=AnimationScheduler.get().requestAnimationFrame(this);
run();
}

private void executeByTimer()
{
final Timer timer = new Timer()
{
@Override
public void run()
{
loopProcess();
}
};
timer.scheduleRepeating(1);
}
}

onModuleLoad()内では初期化とループ(execute関数)をしています。executeとexecuteByTimerはどちらもループ処理ができますが、Timerを使うより、AnimationSchedulerを使うほうがパフォーマンス的には良いようです。timeStampはどのように使うのかわかりません。何を入れてもとりあえずループするようです。AnimationCallbackをimplementsしていることにご注意ください。Timerを使うときはAnimationCallbackをimplementsする必要はありません。

init()は初期化の関数、loopProcess()はループ処理の関数です。何もOverrideをしていない、自作の関数で、今の説明では重要ではないです。中身が知りたければ、最後に載せたサンプルのコードをご覧ください。

canvasの説明

canvasについて説明します。canvasは図形の描画や画像の描画、文字の描画ができます。ゲーム画面として使うことになると思います。

初期化は以下の通りです。howToCreate.client.HowToCreateAGame.java内のinit関数内でprepareCanvas()を呼び出しています。
public class HowToCreateAGame implements EntryPoint,AnimationCallback {
Canvas canvas;
Context2d context;
public static final int canvasHeight = 300;
public static final int canvasWidth = 300;
static final String divTagId = "canvasExample"; // must match div tag in html file

private void init()
{
prepareCanvas();
}

private void prepareCanvas()
{
canvas = Canvas.createIfSupported();
if (canvas == null)
{
RootPanel.get().add(new Label("Sorry, your browser doesn't support the HTML5 Canvas element"));
bInitHasFailed=true;
return;
}
canvas.setStyleName("canvasExample");
canvas.setWidth(canvasWidth + "px");
canvas.setCoordinateSpaceWidth(canvasWidth);
canvas.setHeight(canvasHeight + "px");
canvas.setCoordinateSpaceHeight(canvasHeight);
context = canvas.getContext2d();
KeyEventListener=new KeyEventListener();
MouseEventListener=new MouseEventListener();
canvas.addKeyDownHandler(KeyEventListener);
canvas.addKeyUpHandler(KeyEventListener);
canvas.addClickHandler(MouseEventListener);
canvas.addDoubleClickHandler(MouseEventListener);
canvas.setFocus(true);
RootPanel.get( divTagId ).add(canvas);
}
}

注意点を以下に記載します。

1つ目は、canvasはコンストラクタを呼び出すのではなく、(newするのではなく)createIfSupported関数で作成するということです。Canvasを作成できない場合、canvasがnullになるのでその対応もしておきます。

2つ目は、divタグのIDを設定するということです。divTagId="canvasExample"として定義して、RootPanel.get( divTagId ).add(canvas);というように設定しています。これでhtmlファイルを適切に作成することでcanvasをブラウザ上で表示することができます。またcssファイルを適切に作成することで見た目の調整を行うことができます。
サンプルコードのhtmlファイル
サンプルコードのhtmlファイル
上から変更して今回のサンプル用にしたhtmlファイル
今回のサンプルのhtmlファイル
サンプルコードのcssファイル
サンプルコードのcssファイル
上から変更して今回のサンプル用にしたcssファイル
今回のサンプルのcssファイル
htmlに何を書いてcssに何を書くのかということが分からなければ、とりあえずメモ帳に書いても変わらないことはhtmlに、変わることはcssに記載するというように理解すればいいと思います。例えば、太文字にしたいというときにはメモ帳に太文字はないのでcssを調整することで実現します。また、中央揃えにしたいというときにも同様にcssを調整することで実現します。

このように、見た目の調整はすべてcssを調整することで実現します。見た目を調整したいのかどうかを考えて見た目を調整したければcssでどのように調整すればいいかgoogleなどで調べ、見た目を調整したいのでなければhtmlでどのように調整すればいいかgoogleなどで調べればいいと思います。cssやhtmlについて一生懸命勉強しなくても、必要なときに必要なことだけ調べればいいと思います。

3つ目は、キー入力やクリックのイベントの取得の方法です。canvas.addXXXHandler(引数)の引数にXXXHandlerをimplementsしたクラスを入れることによってイベントを取得できるようにしています。一例としてKeyEventListenerの中身は次のようになっています。
public class KeyEventListener implements KeyUpHandler,KeyDownHandler{

@Override
public void onKeyDown(KeyDownEvent event)
{
event.preventDefault();
switch(event.getNativeKeyCode())
{
case KeyCodes.KEY_LEFT: Key.left=true; break;
case KeyCodes.KEY_RIGHT: Key.right=true; break;
case KeyCodes.KEY_UP: Key.up=true; break;
case KeyCodes.KEY_DOWN: Key.down=true; break;
case KeyCodes.KEY_S: Key.s=true; break;
}
}

@Override
public void onKeyUp(KeyUpEvent event)
{
event.preventDefault();
switch(event.getNativeKeyCode())
{
case KeyCodes.KEY_LEFT: Key.left=false; break;
case KeyCodes.KEY_RIGHT: Key.right=false; break;
case KeyCodes.KEY_UP: Key.up=false; break;
case KeyCodes.KEY_DOWN: Key.down=false; break;
case KeyCodes.KEY_S: Key.s=false; break;
}
}

}

Key.leftやKey.rightはpublicのstatic boolean変数でキーが押されているとtrue、キーが押されていないとfalseになるプログラムになっています。

また、event.preventDefault()でイベント発生時に通常のブラウザの動作を行わないようにしています。したがって、今の矢印キーの場合、スクロールされなくなっています。

4つ目はcanvasのContext2dを取得しておくことです。これをしないと図形や文字が描画できません。Context2dのcontextに取得して代入しています。具体的にはcontext = canvas.getContext2d()としています。

canvasへの描画

canvasへの描画について説明します。

まず、共通することとして塗りつぶしの色を指定するときはcontext.setFillStyle(CssColor.make("rgba(255,0,0,1)")、枠線の色を指定するときはcontext.setStrokeStyle(CssColor.make("rgba(255,0,0,1)")とします。色の指定はCssColor.make("rgba(255,0,0,1)")というようにrgb値とアルファ値(透明度)を指定します。アルファ値は0~1の値を設定します。0だと完全に透明で見えなくなります。1だと完全に不透明です。今回のサンプルでは青色の三角形でアルファ値0.5を設定しました。

図形の線の太さはcontext.setLineWidth(double lineWidth)で指定できます。lineWidthは太さを表します。

  1. 線の描画
    context.setStrokeStyle(Color);
    context.beginPath();
    context.moveTo(dx1, dy1);
    context.lineTo(dx2, dy2);
    context.closePath();
    context.Stroke();
    と書きます。context.moveTo(x1,y1)は座標x1、y1を始点とします。その点からcontext.lineTo(x2,y2)は始点からx2、y2まで線を引くと宣言します。context.Stroke()で実際に線が引かれます。
    線を定義する直前にcontext.beginPath()、線を定義した直後にcontext.closePath()を書くのを忘れないようにしましょう。
  2. 多角形の描画
    塗りつぶしの多角形を描画する場合は
    context.setFillStyle(Color);
    context.beginPath();
    if(dVertexX.length!=dVertexY.length)
    {
    return;
    }
    context.moveTo(dVertexX[0], dVertexY[0]);
    for(int i=1;i<dVertexX.length;i++)
    {
    context.lineTo(dVertexX[i], dVertexY[i]);
    }
    context.closePath();
    context.fill();
    枠だけの多角形を描画する場合は
    context.setStrokeStyle(Color);
    context.beginPath();
    if(dVertexX.length!=dVertexY.length)
    {
    return;
    }
    context.moveTo(dVertexX[0], dVertexY[0]);
    for(int i=1;i<dVertexX.length;i++)
    {
    context.lineTo(dVertexX[i], dVertexY[i]);
    }
    context.closePath();
    context.Stroke();
    とします。
    基本的には線を引いていき、図形を描いているイメージです。1つ前の線の描画も参照ください。
    dVertexX,dVertexYは多角形の各頂点の座標を格納しています。
  3. 四角形の描画
    枠だけの四角形を描く場合は
    context.setStrokeStyle(CssColor.make("rgba(255,0,0,1)"));
    context.strokeRect(10, 10, 100, 40);
    塗りつぶしの四角形を描く場合は
    context.setFillStyle(CssColor.make("rgba(255,0,0,1)"));
    context.fillRect(10, 10, 100, 40);
    のようにします。 context.strokeRect(x,y,width,height)、context.fillRect(x,y,width,height)となっており、x、yは左上頂点の座標、widthが四角形の幅、heightが四角形の高さです。
  4. 円、円弧の描画
    context.arc(x,y,radius,startAngle,endAngle)でx、yが円の中心の座標、radiusは円の半径、startAngle,endAngleは描かれる円周の範囲を指定していてこの差が2PIを超えると円を描きます。
    枠だけの円、円弧を描く場合は
    context.setStrokeStyle(CssColor.make("rgba(255,0,0,1)"));
    context.beginPath();
    context.arc(190, 75, 25, Math.PI/4, Math.PI*3/4);
    context.closePath();
    context.stroke();
    塗りつぶしの円、円弧を描く場合は
    context.setFillStyle(CssColor.make("rgba(0,0,255,1)"));
    context.beginPath();
    context.arc(190, 75, 25, Math.PI/4, Math.PI*3/4);
    context.closePath();
    context.fill();
    のようにします。
    context.arcは5つ変数を指定できて、5つ目の変数は時計回りか反時計回りかを指定できます。context.arc(190, 75, 25, Math.PI/4, Math.PI*3/4)はcontext.arc(190, 75, 25, Math.PI/4, Math.PI*3/4,false)と同じ時計周りに線が引かれて、context.arc(190, 75, 25, Math.PI/4, Math.PI*3/4,true)は半時計周りに線が引かれます。角度も時計回りが正になるか、反時計回りが正になるかこの変数に影響されるようです。
  5. 文字の描画
    文字の色を指定し、フォントを指定し、所定の位置に指定した文字列を描画する例です。
    context.setFillStyle(CssColor.make("rgba(0,0,0,1)"));
    context.setFont("bold 12pt 'Arial'");
    context.fillText("draw string test", 10, 20);
    context.fillText(str,x,y)でstrが描画する文字列、x、yが描画する位置です。
    ちなみに、文字列の幅は
    TextMetrics TM
    double dLength
    context.setFont("bold 12pt 'Arial'");
    TM=context.measureText("draw string test");
    dLength=TM.getWidth();
    とすると取得できます。
  6. 画像の描画
    まず、画像のロードの方法です。/war/img/test.pngというファイルをロードするとします。htmlファイルからの相対パスで指定できるようです。絶対パスだと一番上からの指定になるようです。今だとwarからですが、アップロードするとindex.htmlのある位置からになると思います。相対パスで指定するのがよいと思います。
    Image test;
    test=new Image("./img/test.png");
    画像の描画は以下の3通りです。
    context.drawImage(ImageElement.as(test.getElement()),dx,dy)
    context.drawImage(ImageElement.as(test.getElement()),dx,dy,dw,dh)
    context.drawImage(ImageElement.as(test.getElement()),sx,sy,sw,sh,dx,dy,dw,dh)
    dx、dyは画像を張り付ける位置の左上隅のx、y座標、dw、dhは画像を張り付けるときの幅、高さを表します。
    また、sx、sy、sw、shを指定した場合は画像の一部を抜き出して張り付けられて、これらの引数によって抜き出す位置を指定します。sx、syは画像の抜き出す位置の左上隅のx、y座標、sw、shは画像の抜き出す範囲の幅、高さを表します。

音声ファイルの操作

音声ファイルの操作について説明します。

まず、音声ファイルのロードについて説明します。war/sound/test.mp3というファイルをロードするとします。画像ファイルと同様相対ファイルで指定するのがいいと思います。
public void load()
{
Audio Test;
Test=Audio.createIfSupported();
if(Test==null)
{
return false;
}
Test.setSrc("./sound/test.mp3");
}
コンストラクタではなく、Audio.createIfSupported()を呼び出します。Audioを作成できない場合、Testがnullになるのでその対応もしておきます。音声ファイルの指定はTest.setSrcで行います。

他に使う関数としては、以下が挙げられます。

デバッグ

デバッグの方法について説明します。

  1. デバッグしたいプロジェクトのファイル上で右クリックし、GWT Development mode with Jettyを選択し、デバッグを実行
    GWT Development mode with Jettyを選択している画像
  2. HTMLファイルを開く
    しばらく待つと、"開発モード"タブにURLが表示される(青枠)ので、ダブルクリックする。あるいは、右クリックで出現したメニューから詳細を指定して選択する。 HTMLファイルを開く画像
  3. デバッグ作業
    ブラウザでHTMLファイルが開かれるのでF12でデバッグモードに移行し、デバッグします。私の環境(Chromium)で実行すると、javaのプログラム上でBreakPointを設定して処理を停止することができました。chromeだからかもしれません。
    Chromiumでデバッグしている画面。javaのプログラム上でBreakPointを設定し、実行を停止している。
  4. デバッグ終了
    デバッグが終わったら、HTMLファイルを閉じてGWT Development mode with Jettyを停止します。下の画像の青枠をクリックします。
    GWT Development mode with Jettyを停止している画像

Chromeをダウンロードしてeclipse.iniに-Dchrome.location=C:/Program Files/xxxx/chrome.exeなどとchromeファイルのパスを指定して、デバッグに使用するのが一番いいかもしれません。

サーバーにアップロードするJava Scriptを作成

プログラムが完成したら、サーバーにアップロードするためのJava Scriptを作成します。下の画像のように、Java Scriptを作成したいプロジェクトのファイル上で右クリックしてGWT Compilerで実行するだけです。
GWT Compilerで実行している画像

サーバーにアップロードする

サーバーにアップロードすべきものは下の画像の青枠で囲ったものです。つまり、サンプルの場合、warフォルダー内にあるJava Scriptファイルの入ったhowtocreateagameフォルダ、imgフォルダ、soundフォルダ、HowToCreateAGame.css、HowToCreateAGame.htmlとなります。実際は、cssファイルとhtmlファイルはこのホームページに合わせて別のものを用意しましたが、このように適切に編集したものでも問題ありません。
アップロードするもの

その他

その他に分かったことについて説明します。

サンプルのプログラム

参考のため、サンプルのプログラムを掲載します。このプログラムについてはGWTのプログラムを作成するためなら自由に複製して使っていただいて構いませんが、自己責任でご利用ください。

howToCreate.client.HowToCreateAGame.java

package howToCreate.client;

import com.google.gwt.animation.client.AnimationScheduler;
import com.google.gwt.animation.client.AnimationScheduler.AnimationCallback;
import com.google.gwt.animation.client.AnimationScheduler.AnimationHandle;
import com.google.gwt.canvas.client.Canvas;
import com.google.gwt.canvas.dom.client.Context2d;
import com.google.gwt.canvas.dom.client.CssColor;
import com.google.gwt.canvas.dom.client.TextMetrics;
import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.RootPanel;

import howToCreate.client.bgm.BGM;
import howToCreate.client.hiddenCommand.HiddenCommand;
import howToCreate.client.image.ArrowKeyImage;
import howToCreate.client.key.KeyEventListener;
import howToCreate.client.mouse.MouseEventDrawer;
import howToCreate.client.mouse.MouseEventListener;
import howToCreate.client.movingRect.XMovingRect;
import howToCreate.client.polygonDrawer.PolygonDrawer;

/**
* Entry point classes define <code>onModuleLoad()</code>.
*/
public class HowToCreateAGame implements EntryPoint,AnimationCallback {
Canvas canvas;
Context2d context;
public static final int canvasHeight = 300;
public static final int canvasWidth = 300;
static final String divTagId = "canvasExample"; // must match div tag in html file

private AnimationHandle handler;
private KeyEventListener KeyEventListener;
private MouseEventListener MouseEventListener;
private boolean bInitHasFailed;
private boolean bSupportAudio;
private XMovingRect Rect;
private double[] dStrokeTriangleVertexX;
private double[] dStrokeTriangleVertexY;
private double[] dFillTriangleVertexX;
private double[] dFillTriangleVertexY;
private double[] dStrokeLinePointX;
private double[] dStrokeLinePointY;
private double[] dFillLinePointX;
private double[] dFillLinePointY;
private TextMetrics TM;
private HiddenCommand hiddenCommand;

public void onModuleLoad()
{
init();
if(bInitHasFailed==true)
{
return;
}
execute(0);
// executeByTimer();
}

private void init()
{
prepareCanvas();
bSupportAudio=BGM.loadAudio();
ArrowKeyImage.loadImage();
Rect=new XMovingRect(30.0,100.0,10.0,CssColor.make("rgba(0,255,0,1"));
hiddenCommand=new HiddenCommand();
dStrokeTriangleVertexX=new double[3];
dStrokeTriangleVertexY=new double[3];
dFillTriangleVertexX=new double[3];
dFillTriangleVertexY=new double[3];
dStrokeLinePointX=new double[2];
dStrokeLinePointY=new double[2];
dFillLinePointX=new double[2];
dFillLinePointY=new double[2];
dStrokeTriangleVertexX[0]=22.5;
dStrokeTriangleVertexX[1]=85;
dStrokeTriangleVertexX[2]=53.75;
dStrokeTriangleVertexY[0]=50;
dStrokeTriangleVertexY[1]=50;
dStrokeTriangleVertexY[2]=100;
dFillTriangleVertexX[0]=dStrokeTriangleVertexX[0]+(dStrokeTriangleVertexX[1]-dStrokeTriangleVertexX[0])/2+10.0;
dFillTriangleVertexX[1]=dStrokeTriangleVertexX[1]+(dStrokeTriangleVertexX[1]-dStrokeTriangleVertexX[0])/2+10.0;
dFillTriangleVertexX[2]=dStrokeTriangleVertexX[2]+(dStrokeTriangleVertexX[1]-dStrokeTriangleVertexX[0])/2+10.0;
dFillTriangleVertexY[0]=100;
dFillTriangleVertexY[1]=100;
dFillTriangleVertexY[2]=50;
dStrokeLinePointX[0]=10;
dStrokeLinePointX[1]=30;
dStrokeLinePointY[0]=20;
dStrokeLinePointY[1]=20;
dFillLinePointX[0]=10;
dFillLinePointX[1]=30;
dFillLinePointY[0]=40;
dFillLinePointY[1]=40;

}

@Override
public void execute(double timestamp)
{
handler=AnimationScheduler.get().requestAnimationFrame(this);
run();
}

private void executeByTimer()
{
final Timer timer = new Timer()
{
@Override
public void run()
{
loopProcess();
}
};
timer.scheduleRepeating(1);
}

private void run()
{
loopProcess();
}

private void loopProcess()
{
context.clearRect(0, 0, canvasWidth, canvasHeight);
PolygonDrawer.DrawStrokePolygon(context, dStrokeLinePointX, dStrokeLinePointY, CssColor.make("rgba(255,0,0,1)"));
context.setFillStyle(CssColor.make("rgba(0,0,0,1)"));
context.setFont("bold 12pt 'Arial'");
TM=context.measureText("draw string test");
context.fillText("draw string test", (canvasWidth-TM.getWidth())/2, 20);
Rect.Draw(context);
PolygonDrawer.DrawStrokePolygon(context, dStrokeTriangleVertexX, dStrokeTriangleVertexY, CssColor.make("rgba(255,0,0,1)"));
PolygonDrawer.DrawFillPolygon(context, dFillTriangleVertexX, dFillTriangleVertexY, CssColor.make("rgba(0,0,255,0.5)"));
context.setStrokeStyle(CssColor.make("rgba(255,0,0,1)"));
context.beginPath();
context.arc(190, 75, 25, Math.PI/4, Math.PI*3/4);
context.closePath();
context.stroke();
context.setFillStyle(CssColor.make("rgba(0,0,255,1)"));
context.beginPath();
context.arc(250, 75, 25, Math.PI/4, Math.PI*3/4,true);
context.closePath();
context.fill();
context.setFillStyle(CssColor.make("rgba(0,0,0,1)"));
context.setFont("bold 10pt 'Arial'");
if(bSupportAudio==true&&BGM.isPaused()==true)
{
TM=context.measureText("Press s button to turn on the BGM.");
context.fillText("Press s button to turn on the BGM.", (canvasWidth-TM.getWidth())/2, 120);
}
else if(bSupportAudio==true)
{
TM=context.measureText("Press s button to turn off the BGM.");
context.fillText("Press s button to turn off the BGM.", (canvasWidth-TM.getWidth())/2,120);
}
BGM.play();
BGM.pause();
ArrowKeyImage.DrawKeys(context, 10.0, canvasHeight-70.0);
MouseEventDrawer.Draw(context, 110, canvasHeight-70.0);
hiddenCommand.DrawCorrectlyPushedCommand(context);
hiddenCommand.Judge();
if(hiddenCommand.hasInputHiddenCommand()==true)
{
context.setFillStyle(CssColor.make("rgba(0,0,0,1)"));
context.setFont("bold 14pt 'Arial'");
TM=context.measureText("Congraturations!");
context.fillText("Congraturations!", (canvasWidth-TM.getWidth())/2,150);
TM=context.measureText("You found the hidden command.");
context.fillText("You found the hidden command.", (canvasWidth-TM.getWidth())/2,170);
}
MouseEventListener.buildClickEventFlag();
MouseEventListener.breakClickFlag();
MouseEventListener.breakDoubleClickFlag();
}

private void prepareCanvas()
{
canvas = Canvas.createIfSupported();
if (canvas == null)
{
RootPanel.get().add(new Label("Sorry, your browser doesn't support the HTML5 Canvas element"));
bInitHasFailed=true;
return;
}
canvas.setStyleName("canvasExample");
canvas.setWidth(canvasWidth + "px");
canvas.setCoordinateSpaceWidth(canvasWidth);
canvas.setHeight(canvasHeight + "px");
canvas.setCoordinateSpaceHeight(canvasHeight);
context = canvas.getContext2d();
KeyEventListener=new KeyEventListener();
MouseEventListener=new MouseEventListener();
canvas.addKeyDownHandler(KeyEventListener);
canvas.addKeyUpHandler(KeyEventListener);
canvas.addClickHandler(MouseEventListener);
canvas.addDoubleClickHandler(MouseEventListener);
canvas.setFocus(true);
RootPanel.get( divTagId ).add(canvas);
}

}

howToCreate.client.bgm.BGM.java

package howToCreate.client.bgm;

import com.google.gwt.media.client.Audio;

import howToCreate.client.key.Key;

public class BGM {
private final static long lOnOffInterval=200;
private static Audio BGM;
private static long lTurnOnOffTime;
public static boolean loadAudio()
{
BGM=Audio.createIfSupported();
if(BGM==null)
{
return false;
}
BGM.setSrc("./sound/pastelHouse.mp3");
BGM.setLoop(true);
lTurnOnOffTime=-1-lOnOffInterval;
return true;
}

public static void play()
{
if(BGM.isPaused()==false)
{
return;
}
if(System.currentTimeMillis()<=lTurnOnOffTime+lOnOffInterval)
{
return;
}
if(Key.s==true)
{
BGM.play();
lTurnOnOffTime=System.currentTimeMillis();
}
}

public static void pause()
{
if(BGM.isPaused()==true)
{
return;
}
if(System.currentTimeMillis()<=lTurnOnOffTime+lOnOffInterval)
{
return;
}
if(Key.s==true)
{
BGM.pause();
BGM.setCurrentTime(0.0);
lTurnOnOffTime=System.currentTimeMillis();
}
}

public static boolean isPaused()
{
return BGM.isPaused();
}
}

howToCrate.client.image.ArrowKeyImage.java

package howToCreate.client.image;

import com.google.gwt.canvas.dom.client.Context2d;
import com.google.gwt.dom.client.ImageElement;
import com.google.gwt.user.client.ui.Image;

import howToCreate.client.key.Key;

public class ArrowKeyImage {
private static Image ArrowKeyImage;

public static void loadImage()
{
ArrowKeyImage=new Image("./img/ArrowKey.png");
}

public static void DrawKeys(Context2d context,double dDrawnX,double dDrawnY)
{
if(Key.up==true)
{
drawUpKey(context,dDrawnX+30.0,dDrawnY);
}
if(Key.left==true)
{
drawLeftKey(context,dDrawnX,dDrawnY+30.0);
}
if(Key.down==true)
{
drawDownKey(context,dDrawnX+30.0,dDrawnY+30.0);
}
if(Key.right==true)
{
drawRightKey(context,dDrawnX+60.0,dDrawnY+30.0);
}
}

public static void drawUpKey(Context2d context,double dDrawnX,double dDrawnY)
{
context.drawImage(ImageElement.as(ArrowKeyImage.getElement()), 30.0, 0.0, 30.0, 30.0, dDrawnX, dDrawnY, 30.0, 30.0);
}

public static void drawLeftKey(Context2d context,double dDrawnX,double dDrawnY)
{
context.drawImage(ImageElement.as(ArrowKeyImage.getElement()), 0.0, 30.0, 30.0, 30.0, dDrawnX, dDrawnY, 30.0, 30.0);
}

public static void drawDownKey(Context2d context,double dDrawnX,double dDrawnY)
{
context.drawImage(ImageElement.as(ArrowKeyImage.getElement()), 30.0, 30.0, 30.0, 30.0, dDrawnX, dDrawnY, 30.0, 30.0);
}

public static void drawRightKey(Context2d context,double dDrawnX,double dDrawnY)
{
context.drawImage(ImageElement.as(ArrowKeyImage.getElement()), 60.0, 30.0, 30.0, 30.0, dDrawnX, dDrawnY, 30.0, 30.0);
}
}

howToCreate.client.key.Key.java

package howToCreate.client.key;

public class Key
{
static public boolean left,right,up,down,s;
}

howToCreate.client.key.KeyEventListener.java

package howToCreate.client.key;

import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.event.dom.client.KeyDownEvent;
import com.google.gwt.event.dom.client.KeyDownHandler;
import com.google.gwt.event.dom.client.KeyUpEvent;
import com.google.gwt.event.dom.client.KeyUpHandler;

public class KeyEventListener implements KeyUpHandler,KeyDownHandler{

@Override
public void onKeyDown(KeyDownEvent event)
{
event.preventDefault();
switch(event.getNativeKeyCode())
{
case KeyCodes.KEY_LEFT: Key.left=true; break;
case KeyCodes.KEY_RIGHT: Key.right=true; break;
case KeyCodes.KEY_UP: Key.up=true; break;
case KeyCodes.KEY_DOWN: Key.down=true; break;
case KeyCodes.KEY_S: Key.s=true; break;
}
}

@Override
public void onKeyUp(KeyUpEvent event)
{
event.preventDefault();
switch(event.getNativeKeyCode())
{
case KeyCodes.KEY_LEFT: Key.left=false; break;
case KeyCodes.KEY_RIGHT: Key.right=false; break;
case KeyCodes.KEY_UP: Key.up=false; break;
case KeyCodes.KEY_DOWN: Key.down=false; break;
case KeyCodes.KEY_S: Key.s=false; break;
}
}

}

howToCreate.client.mouse.Mouse.java

package howToCreate.client.mouse;

public class Mouse {
static public boolean click,doubleClick;
}

howToCreate.client.mouse.MouseClickFlagBreaker.java

package howToCreate.client.mouse;

public class MouseClickFlagBreaker {

private static long lInterval=500;

private long lClickedTime;
private long lDoubleClickedTime;

public void setClickedTime()
{
lClickedTime=System.currentTimeMillis();
}

public void setDoubleClickedTime()
{
lDoubleClickedTime=System.currentTimeMillis();
}

public void breakClickFlag()
{
if(Mouse.click==false)
{
return;
}
if(System.currentTimeMillis()>lClickedTime+lInterval)
{
Mouse.click=false;
}
}

public void breakDoubleClickFlag()
{
if(Mouse.doubleClick==false)
{
return;
}
if(System.currentTimeMillis()>lDoubleClickedTime+lInterval)
{
Mouse.doubleClick=false;
}
}
}

howToCreate.client.mouse.MouseEventDrawer.java

package howToCreate.client.mouse;

import com.google.gwt.canvas.dom.client.Context2d;
import com.google.gwt.canvas.dom.client.CssColor;

public class MouseEventDrawer {
public static void Draw(Context2d context,double dDrawnX,double dDrawnY)
{
if(Mouse.click==true)
{
context.setFillStyle(CssColor.make("rgba(237,201,0,1)"));
context.fillRect(dDrawnX, dDrawnY, 30, 60);
}
if(Mouse.doubleClick==true)
{
context.setFillStyle(CssColor.make("rgba(255,0,0,1)"));
context.fillRect(dDrawnX+30, dDrawnY, 30, 60);
}
}
}

howToCreate.client.mouse.MouseEventListener.java

package howToCreate.client.mouse;

import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.DoubleClickEvent;
import com.google.gwt.event.dom.client.DoubleClickHandler;

public class MouseEventListener implements ClickHandler,DoubleClickHandler{

private static final int lFlagHoldTime=200;

private MouseClickFlagBreaker mouseClickFlagBreaker=new MouseClickFlagBreaker();
private long lClickEventTime;
private boolean bDoubleClickedFlag;
private boolean bClickedFlag;

@Override
public void onDoubleClick(DoubleClickEvent event)
{
bDoubleClickedFlag=true;
lClickEventTime=System.currentTimeMillis();
mouseClickFlagBreaker.setDoubleClickedTime();
}

@Override
public void onClick(ClickEvent event)
{
bClickedFlag=true;
lClickEventTime=System.currentTimeMillis();
mouseClickFlagBreaker.setClickedTime();
}

public void buildClickEventFlag()
{
if(System.currentTimeMillis()<=lClickEventTime+lFlagHoldTime)
{
return;
}
if(bDoubleClickedFlag==true)
{
Mouse.doubleClick=true;
bClickedFlag=false;
bDoubleClickedFlag=false;
return;
}
if(bClickedFlag==true)
{
Mouse.click=true;
bClickedFlag=false;
bDoubleClickedFlag=false;
}
}

public void breakDoubleClickFlag()
{
mouseClickFlagBreaker.breakDoubleClickFlag();
}

public void breakClickFlag()
{
mouseClickFlagBreaker.breakClickFlag();
}
}

howToCreate.client.movingRect.XMovingRect.java

package howToCreate.client.movingRect;

import com.google.gwt.canvas.dom.client.Context2d;
import com.google.gwt.canvas.dom.client.CssColor;

import howToCreate.client.HowToCreateAGame;

public class XMovingRect {

private static final long lMoveInterval=10;

private double x;
private double y;
private double width;
private double height;
private CssColor Color;

private long lMovedTime;
private int iMoveCounter;

public XMovingRect(double dDrawnY,double dWidth,double dHeight,CssColor Color)
{
y=dDrawnY;
width=dWidth;
height=dHeight;
this.Color=Color;
lMovedTime=-201;
iMoveCounter=0;
}

public void Draw(Context2d context)
{
if(System.currentTimeMillis()>lMovedTime+lMoveInterval)
{
lMovedTime=System.currentTimeMillis();
iMoveCounter++;
}
x=(HowToCreateAGame.canvasWidth-width)/2+(HowToCreateAGame.canvasWidth-width)/4*Math.sin(iMoveCounter*0.1);
context.setStrokeStyle(Color);
context.strokeRect(x, y, width, height);
}

}

howToCreate.client.polygonDrawer.PolygonDrawer.java

package howToCreate.client.polygonDrawer;

import com.google.gwt.canvas.dom.client.Context2d;
import com.google.gwt.canvas.dom.client.CssColor;

public class PolygonDrawer {
public static void DrawFillPolygon(Context2d context,double[] dVertexX,double[] dVertexY,CssColor Color)
{
context.setFillStyle(Color);
DrawPolygon(context,dVertexX,dVertexY);
context.fill();
}

public static void DrawStrokePolygon(Context2d context,double[] dVertexX,double[] dVertexY,CssColor Color)
{
context.setStrokeStyle(Color);
DrawPolygon(context, dVertexX, dVertexY);
context.stroke();
}

private static void DrawPolygon(Context2d context,double[] dVertexX,double[] dVertexY)
{
context.beginPath();
if(dVertexX.length!=dVertexY.length)
{
return;
}
context.moveTo(dVertexX[0], dVertexY[0]);
for(int i=1;i<dVertexX.length;i++)
{
context.lineTo(dVertexX[i], dVertexY[i]);
}
context.closePath();
}
}



ウェブ全体検索
サイト内検索
関連ページ

知識共有ノート一覧へ


Twitterプロフィールページ
Twitterやってます。お気軽にフォローしてください。


当サイトへのリンクについて
当サイトへリンクする際のお願いを記載しています。バナーもこちらに用意しています。


ホームへ




利用規約 プライバシーポリシー お問い合わせ 当サイトへのリンクについて Twitter
ホームへ