ゲーム開発を容易にするクラス集合(game.jar)の紹介
例として、『TestServerGame01.jar』の作品を作ってみます
『TestServerGame01.bat』の名前のバッチファイルを、メモ帳で作ります。
その内容を以下に示します。(これは、作業をプログラムしたものです。)
set JAVAHOME=c:\j2sdk1.4.2_02 set SRC=TestServerGame01 :LOOP %SystemRoot%\system32\notepad.exe .\%SRC%.java "%JAVAHOME%\bin\javac" -target 1.1 -d . -classpath "%JAVAHOME%\;.\game.jar;." .\%SRC%.java IF ERRORLEVEL 1 GOTO LOOP %SystemRoot%\system32\notepad.exe .\%SRC%.txt "%JAVAHOME%\bin\jar" cmvf .\%SRC%.txt .\%SRC%.jar .\%SRC%.class .\%SRC%.txt pause "%JAVAHOME%\bin\java" -classpath "%JAVAHOME%\;.\game.jar;." -jar .\%SRC%.jar
作品の名前が『TestServerGame01』になっています。必要に応じて変更ください。
Javaの開発ツールのパスである『c:\j2sdk1.4.2_02』も、インストールの環境にあわせてください。
この処理概要は、ソースファイルを編集後、コンパイルして、マニフェストを編集し、それらをまとめています。
まず、適当なフォルダを作成します。この中に次のものを用意します。
(1)上記で作成したバッチファイル『TestServerGame01.bat』をこのフォルダ中に配置します
(2)紹介しているゲーム用クラス集合ファイル『game.jar』をこのフォルダ中に配置します。 (これは、右クリックでダウンロードください)
(3)このフォルダ中に、新しい『contents』と言うフォルダを作成に、その中にデームで使う素材(画像ファイル)などを用意します。 ここでは、次のファイルを入れて置きましょう。
←backsea.gif ←bird00.gif ←balloon00.gif『TestServerGame01.bat』をダブルクリックなどで実行させます。
すると、作品の名前『TestServerGame01.java』のメモ帳による編集状態になります。
この内容が、Java言語のソースファイルで、ここで作品のプログラムをすることになります。 以下に簡単な例を示します。
// AnimeServerFrameを利用してプログラミングします。 // AnimeServerFrameには、描画全体を管理しているcanvasStage、操作対象を管理するpointListが用意されています。 import java.awt.*; import anime.*; public class TestServerGame01 extends AnimeServerFrame{ AnimeImage obj2; //操作対象2を管理用 public void initStage(){ Image img[]; //読み取った外部画像ファイルの管理用 img = AnimeImage.getBitmaps("../contents/bird00.gif",canvasStage);//外部画像ファイル読み取り obj2 = new AnimeImage(canvasStage); //管理対象2の生成 obj2.changeBmps(img); //管理対象2に画像を設定 obj2.setLocation(350,270); //管理対象2を(350,270)の座標へ配置 obj2.setAnimation(100,0,50); //管理対象2を(100,0)の座標へ50ステップで移動するように登録 pointList.append(obj2); //管理対象2を登録 } public static void main(String a[]){ TestServerGame01 frame = new TestServerGame01();//作品を生成 frame.show(); //作品を見えるようにする。 } }
メモ帳による編集を終えて、保存し終了すると、javacによるコンパイル(翻訳)が行われます。 しかし『エラー』があると、再び『TestServerGame01.java』の編集に戻ります。
『エラー』がなければ、コンパイル(翻訳)が終わって『TestServerGame01.class』ファイルが出来上がります。 次にマニフェストファイル『TestServerGame01.txt』の編集状態になります。
これは、何を利用して、どこから始めるかなどを指示するファイルで次のように記述します。
Main-Class: TestServerGame01 Class-Path: TestServerGame01 game.jar .
『TestServerGame01』が作品名なので、『TestServerGame01.bat』を変更した時は、 その作品名と合わせる必要があります。
メモ帳による編集を終えて、保存し終了すると、jarによって『TestServerGame01.class』と 『TestServerGame01.txt』が圧縮して結合され、『TestServerGame01.jar』が作成されます。
以上まで正しくできていれば、javaによってその仮想コンピュータが起動して作品が実行されます。
終了後、『TestServerGame01.jar』をダブルクリックなどで実行できるようになっています。
なお、『TestServerGame01.jar』を移動する場合『game.jar』も一緒に移動しなければなりません。
以上のように作成した作品は、ネットワークサーバー(配信)の機能を持っており、クライアントで接続して画像を共有できます
実は、『game.jar』がクライアントになっています。『game.jar』もダブルクリックなどで実行させます。
つまり、作品『TestServerGame01.jar』とクライアント『game.jar』を2つ実行しておきます。
ネットワーク(TCP/IPプロトコル)を介するので、ネットで繋がっていれば、 それぞれの実行を異なるマシンで実行させても構いません。
そして、作品のタイトルバーの3つのドットをふくめた番号を クライアントに入力して接続ボタンをクリックします。(以下にイメージを示します)
クライントに入力した3つのドットをむくめた番号は、ネットワーク内でマシンを識別するためのもので、
IPアドレスと呼ばれています。
なお、クライアントは複数実行させても構いません。クライアント接続時は、
全てサーバーの画面と同じものが見えるはずです。これは、サーバーから同じ情報がクライアントへネットワークを
介して配信されるからです。
画像を増やして、動きを複雑にしていきます。
画像を増やしたり、背景画像や、他の画像を追加する。 それには、操作対象であるAnimeImageを、必要な数だけ増やして、追加すればよいことになります。
以下のプログラムでbacksea.gifの背景、balloon00.gifの風船を用意し、bird00.gifもをもう一つ追加しています。
import java.awt.*; import anime.*; //【0】 public class TestServerGame01 extends AnimeServerFrame{ AnimeImage back; //背景画像用 AnimeImage obj1; //操作対象1を管理用 AnimeImage obj2; AnimeImage obj3; //操作対象3を管理用 public void initStage(){ //【1】 Image img[]; //読み取った外部画像ファイルの管理用 img = AnimeImage.getBitmaps("../contents/backsea.gif",canvasStage);//外部画像ファイル読み取り back = new AnimeImage(canvasStage); //背景の管理対象を生成 back.changeBmps(img); //管理対象に背景画像を設定 back.setLocation(0,-500); //管理対象を(0,-500)の座標へ配置 back.setAnimation(0,0,100); //管理対象を(0,0)の座標へ100ステップで移動するように登録 pointList.append(back); //管理対象を登録 img = AnimeImage.getBitmaps("../contents/bird00.gif",canvasStage); obj1 = new AnimeImage(canvasStage); obj1.changeBmps(img); obj1.setLocation(0,250); obj1.setAnimation(100,0,50); pointList.append(obj1); //管理対象1を登録 obj2 = new AnimeImage(canvasStage); //管理対象2の生成 obj2.changeBmps(img); //管理対象2に前で使った同じ画像を設定 obj2.setLocation(350,270); //管理対象2を(350,270)の座標へ配置 obj2.setAnimation(100,-20,50); //管理対象2を(100,0)の座標へ50ステップで移動するように登録 pointList.append(obj2); //管理対象2を登録 img = AnimeImage.getBitmaps("../contents/balloon00.gif",canvasStage);//外部画像ファイル読み取り obj3 = new AnimeImage(canvasStage); obj3.changeBmps(img); obj3.setLocation(obj2.x + 20, obj2.y);//管理対象2より右に20画素ずれた位置に配置 pointList.append(obj3); //管理対象3を登録(後からの登録なので、管理対象2の上に重なる) } public static void main(String a[]){ TestServerGame01 frame = new TestServerGame01();//作品を生成 frame.show(); //作品を見えるようにする。 } //【2】 }
上記で、bird00.gifを増やす時、画像ファイル読み取り処理は、一回だけ行えば良いことに注目ください。
AnimeImage型である管理対象の変数には、.x や .yの指定で、その記憶内容を利用した演算をしています。
この x や y はフィールドと呼ばれます。
AnimeImageの型である管理対象は、pointList.appendで、登録することにより、画面にでるようになります。
この登録の順番が描画の順番になっています。
append()以外にもchangeBmps() setLocation() など、最後に();と括弧がついて、実行させているものはメソッドと呼ばれています。
これらメソッドは、登録機能、画像変更機能、画像位置変更機能、と言うように機能に名前を付けてたものと言えます。
これらは、既に用意されているものものでしたが、自作することもできます。
【1】にあるinitStageもメソッドで、使う素材を登録する機能の意味付けでこのプログラム内で作成したメソッドになります。
メソッドの作成範囲は、{ から始まり }で終わります。
メソッドには、始めから機能が決まっていて、指定の名前で作らなければ意味をなさないものと、
そうでなく自由な名前で自由な機能で作れるものがあります。
initStepは前者のメソッドと言うことになります。同様に描画サイクルごとに呼び出されるstepメソッドがあります。
initStepメソッドがプログラム実行開始時に一回だけ実行する機能であるのに対して、
stepメソッドは、特定の時間(約50ミリ秒)ごとに実行する機能です。
よって、ここに全体の動きをプログラミングできます。
ここではobj3をランダムに動かすプログラムを記述してみます。
具体的には、前述のプログラムの//【2】の次行に以下の記述を追加します。
public void step(){ int x = (int)(Math.random() * 400);//0から400までランダムな値をxに設定 int y = (int)(Math.random() * 300); obj3.setLocation(x,y); }
実験すると分かりますが、乱数で横400、縦300までの位置へobj3を配置し直しているのですが、 各移動が、瞬間移動になっています。
この移動をスムーズに行わせるには、setAnimationmで移動の登録を行う必要があります。 そして、この移動が終了した時に次の移動を登録すればよいのです。 この移動が終了したかのチェックにはisActionを使います。
具体的には、前述のstepメソッドを以下のように変更します。
public void step(){ if(obj3.isAction() == false){//移動中でないかをチェック int x = (int)(Math.random() * 400);//0から400までランダムな値をxに設定 int y = (int)(Math.random() * 300); int n = (int)(Math.random() * 20); obj3.setAnimation(x,y,n);//(x,y)の位置へnステップで移動するように登録 } }
左マウスボタンを押した位置へobj1の管理対象を移動させ、
右マウスボタンを押した位置へobj2の管理対象を移動させるstepメソッドを示します。
public void step(){ if(obj3.isAction() == false){//移動中でないかをチェック int x = (int)(Math.random() * 400);//0から400までランダムな値をxに設定 int y = (int)(Math.random() * 300); int n = (int)(Math.random() * 20); obj3.setAnimation(x,y,n);//(x,y)の位置へnステップで移動するように登録 } if(pressed == true && (pressed_M & MouseEvent.BUTTON1_MASK) != 0){//左ボタンを押した? pressed = false; obj1.setAnimation(pressed_X, pressed_Y, 20);//ボタンを押した位置へ移動 } if(pressed == true && (pressed_M & MouseEvent.BUTTON3_MASK) != 0){//右ボタンを押した? pressed = false; obj2.setAnimation(pressed_X, pressed_Y, 20);//ボタンを押した位置へ移動 } }
マウスボタンを押した時に、内部のpressedにtrueが設定されます。それをチェックしてプログラムを書けばよいことになります。 pressedが、trueであることをチェックした後は、pressedをfalseに戻す必要があります。
なお、ここでは、java.awt.eventのMouseEventクラスを利用して、左右のボタン判定を行っています。
この判定に使う情報は、ビット判定でマスクに必要な表現(MouseEvent.BUTTON1_MASK)を利用します。
このようなものを使う場合は、MouseEventを直接に使えるように、
プログラム先頭(【0】の位置)に次を記述を追加する必要があります。
import java.awt.event.*;
つまり、画像をうまくクリックできたら画像を消す。そして、消えている画像をクリックできら出現させるよう変更します。
これは、あるクリックなどの座標(px,py)が、管理対象obj3の描画する長方形内にあるか?を調べればよいことになります。
それには、if( obj3.contains( px, py ) ) { の if文で調べることになります。
次にobj3が見える状態か?、見えない状態かにあるかは visibleのフィールドを調べれは分かります。
これが、trueなら見えており、これがfalseなら見えない状態になっています。
また、このフィールドは値を設定することもでき、falseを=で設定すると見えなくなり、
trueを設定すると見えるようになります。そのためのstepメソッド部分は次のようになります。
public void step(){ if(obj3.isAction() == false){//移動中でないかをチェック int x = (int)(Math.random() * 400);//0から400までランダムな値をxに設定 int y = (int)(Math.random() * 300); int n = (int)(Math.random() * 20); obj3.setAnimation(x,y,n);//(x,y)の位置へnステップで移動するように登録 } if(pressed == true){//マウスの左右ボタンどちらかが押された? pressed = false; if(obj3.contains(pressed_X, pressed_Y)){//マウスを押した位置が、obj3の画像を管理する長方形の中か? if(obj3.visible == true){ //obj3が見えているか? obj3.visible = false; //見えない状態に設定する。 } else { obj3.visible = true; } } } }
obj3は見えない状態でも移動するので、上手くクリックするのはむずかしかく、ゲーム性が作れるのではないでしょうか。
今度は、obj3の画像を10個を作って管理してみます。
このような場合は、配列を作って繰り返しを使います。
まず、操作対象を並べて10個用意する場合、次のようになり、それぞれを要素と言います。
AnimeImage obj3[] = new AnimeImage[10];
つまり配列とは、同じ名前(この場合はobj3)で複数の情報(この場合は10個)を管理するために用いられるもので、 番号指定する変数と言うようなものです。 配列の全ての要素を順番に繰り返しで行わせる記述は次のようになります。
for(int i = 0; i < obj3.length; i++){ //obj3の数だけ繰り返して、その数だけ作る obj3[i]に対する処理 }//繰り返し範囲の終了
これを利用したプログラムを以下に紹介します。以下の赤の部分が変更箇所です。
import java.awt.*; import anime.*; public class TestServerGame01 extends AnimeServerFrame{ AnimeImage back; //背景画像用 AnimeImage obj1; //操作対象1を管理用 AnimeImage obj2; AnimeImage obj3[] = new AnimeImage[10]; //操作対象を10個用意する。 (配列と呼ばれる) public void initStage(){ //【1】 Image img[]; //読み取った外部画像ファイルの管理用 img = AnimeImage.getBitmaps("../contents/backsea.gif",canvasStage);//外部画像ファイル読み取り back = new AnimeImage(canvasStage); //背景の管理対象を生成 back.changeBmps(img); //管理対象に背景画像を設定 back.setLocation(0,-500); //管理対象を(0,-500)の座標へ配置 back.setAnimation(0,0,100); //管理対象を(0,0)の座標へ100ステップで移動するように登録 pointList.append(back); //管理対象を登録 img = AnimeImage.getBitmaps("../contents/bird00.gif",canvasStage); obj1 = new AnimeImage(canvasStage); obj1.changeBmps(img); obj1.setLocation(0,250); obj1.setAnimation(100,0,50); pointList.append(obj1); //管理対象1を登録 obj2 = new AnimeImage(canvasStage); //管理対象2の生成 obj2.changeBmps(img); //管理対象2に前で使った同じ画像を設定 obj2.setLocation(350,270); //管理対象2を(350,270)の座標へ配置 obj2.setAnimation(100,-20,50); //管理対象2を(100,0)の座標へ50ステップで移動するように登録 pointList.append(obj2); //管理対象2を登録 img = AnimeImage.getBitmaps("../contents/balloon00.gif",canvasStage);//外部画像ファイル読み取り for(int i = 0; i < obj3.length; i++){ //obj3の数だけ繰り返して、その数だけ作る obj3[i] = new AnimeImage(canvasStage); obj3[i].changeBmps(img); pointList.append(obj3[i]); //管理対象を登録 }//繰り返し範囲の終了 } public static void main(String a[]){ TestServerGame01 frame = new TestServerGame01();//作品を生成 frame.show(); //作品を見えるようにする。 } public void step(){ //移動が終了したものを、再び移動させる。 for(int i = 0; i < obj3.length; i++){ //obj3の数だけ繰り返し if(obj3[i].isAction() == false){//移動中でないかをチェック int x = (int)(Math.random() * 400);//0から400までランダムな値をxに設定 int y = (int)(Math.random() * 300); int n = (int)(Math.random() * 20); obj3[i].setAnimation(x,y,n);//(x,y)の位置へnステップで移動するように登録 } }//繰り返し範囲の終了 if(pressed == true){//マウスの左右ボタンどちらかが押された? pressed = false; for(int i = 0; i < obj3.length; i++){ //obj3の数だけ繰り返し if(obj3[i].contains(pressed_X, pressed_Y)){//マウスを押した位置が、obj3の画像を管理する長方形の中か? if(obj3[i].visible == true){//obj3が見えているか? obj3[i].visible = false;//見えない状態に設定する。 } else { obj3[i].visible = true; } } } } } }
stepでは、止まっているもの再び動かす処理と、マウスボタンを押した時に行う処理を、異なる繰り返しで行っています。
これは、上から下に移動させ、下の見えない範囲まで移動したら、それを上に戻すことで見せかけ上、 何個も無限に降ってくる騙しの手法で実現できます。
まず、生成の初期位置を、上の見えない範囲に配置します。それはobs3の要素生成の繰り返しを次のように変更します。
for(int i = 0; i < obj3.length; i++){ //obj3の数だけ繰り返して、その数だけ作る obj3[i] = new AnimeImage(canvasStage); obj3[i].changeBmps(img); pointList.append(obj3[i]); //管理対象を登録 obj3[i].setLocation(200,-500); //上部の見えない位置に配置 }//繰り返し範囲の終了
そして、一定時間ごとに実行させるsetpメソッドの、止まったら再び移動登録していた個所を、 次のように『止まっているobj3の要素の移動目標』を下の見えない範囲に設定する繰り返しへと変更します。
//移動が終了したものを、再び移動させる。 for(int i = 0; i < obj3.length; i++){ //obj3の数だけ繰り返し if(obj3[i].isAction() == false){//移動中でないかをチェック int x = (int)(Math.random() * 600) -100;//-100から500までランダムな値をxに設定 int y = 500;//下の見えない所を移動目標にする。 int n = (int)(Math.random() * 150) + 50;//50から200までランダムな値をxに設定 obj3[i].setLocation(200,-500); //上部の見えない位置に配置 obj3[i].setAnimation(x,y,n);//(x,y)の位置へnステップで移動するように登録 obj3[i].visible = true; } }//繰り返し範囲の終了
こんどは、obj1を配列にして利用してみます。そこで、次のようにobj1を配列に変更します。 なお、obj2も同じ画像なので、obj2を使っている行は削除します。
AnimeImage obj1[] = new AnimeImage[10];//操作対象を10個用意する。
次にinitStageメソッド内の生成は、obj3と同じように繰り返しで行います。 次のように変更すればよいでしょう。
img = AnimeImage.getBitmaps("../contents/bird00.gif",canvasStage); for(int i = 0; i < obj1.length; i++){ //obj3の数だけ繰り返して、その数だけ作る obj1[i] = new AnimeImage(canvasStage); obj1[i].changeBmps(img); obj1[i].setLocation(0,-200); //見えない位置に配置する。 pointList.append(obj1[i]); //管理対象を登録 }
そしてマウスを押したら、上記で生成しておいたobj1の要素のどれかを、マウス水平座標でで画面の下にsetLocationで配置し、 setAnimationで移動を登録すればよいわけです。 さて、ここでobj1の要素のどれに対してこの処理を行えばよいのでしょうか? それは、停止しているobj1の要素なので、配列から停止状態になっている要素を探し、 それから、見つかった要素に対してこの処理を行えばよいのです。
具体的には、これまでobj3の要素をクリックしたら可視状態を切り替えていた部分の代わりに、 以下の処理を記述すればよいことになります。
if(pressed == true){//マウスの左右ボタンどちらかが押された? pressed = false; //動いていないものををobj1から探します。 int idx; //見つかったら、その番号がidxに記憶されて繰り返しが止まります。 for(idx = 0; idx < obj1.length && obj1[idx].isAction() == true; idx++) { } if(idx < obj1.length){ //見つかったら //マウスを押した水平位置で、下の見えない所に配置 obj1[idx].setLocation(pressed_X,320); obj1[idx].setAnimation(pressed_X,-100,10);//真上に移動させる登録 obj1[idx].visible = true; obj1[idx].idxBmp = 0; } }
以上でobj3の画像(風船)が上から降りてきて、クリックした位置からobj1の画像(鳥)が、 上に飛んで行くプログラムになっています。 そして、obj1、obj3ともに移動して止まる位置を画面の外側にして、再利用される形態になっています。 このプログラム部分はstepメソッド内に、次のような追加を行うことで実現できます。
for(int i = 0; i < obj3.length; i++){ //obj3の数だけ繰り返し if(obj3[i].isAction() == true){//移動中のものだけ、衝突判定する。 AnimeImage obj = null; do { obj = (AnimeImage) pointList.searchIntersect(obj, obj3[i] , 0, 0); // obj3[i] と交差しているobj1の画像で、移動中ならbreakで繰り返しを止める。 if(obj != null && obj.isAction() == true && obj.bmps == obj1[0].bmps) break; } while(obj != null); if(obj != null){ //衝突オブジェクトが見つかって、objに記憶されている。 obj3[i].visible = false; //見えなくする。 obj3[i].setAnimation(0,0,0); //停止 obj.visible = false; obj.setAnimation(0,0,0);//停止 } } }
pointList.searchIntersect(obj, obj3[i] , 0, 0);で、pointListで管理される画像でobjの前に登録される画像から、 obj3[i]の画像と交差するものを登録の逆順に探して、見つかったAnimeImageを取得しています。 もし見つからない(交差つまり衝突している画像がない場合は、nullがobjに設定されます。)
そのためには、過程を表現するたくさんの画像を必要とします。 実はAnimeImageに複数の画像が管理できるように作られています。 それには、画像読み込みで指定するファイル名にシーケンス番号(00,01,・・)を除いて指定する形態になります。
img = AnimeImage.getBitmaps("../contents/bird.gif",canvasStage);
img = AnimeImage.getBitmaps("../contents/balloon.gif",canvasStage);
と変更すればよいのですが、これだけだと存在する複数のシーケンス画像が自動的に切り替わる状態になります。 これは、AnimeImageのidxModeフィールドで管理され、生成時はLOOP_MODEになっているからです。 この設定をPROGRAM_MODEに変更することで自動的な画像の切り替えは行われなくなります。 それには生成の繰り返しで次を記述を追加すれば止まります。
obj1[i].idxMode = AnimeImage.PROGRAM_MODE;
obj3[i].idxMode = AnimeImage.PROGRAM_MODE;
画像は、AnimeImage のbmpsフィールドの配列の要素に記憶され、 idxBmpフィールドの番号の画像が現在の表示対象になっていいます。よってこれをプログラムで制御すればよいことになります。
具体的には前回変更の衝突したら消す部分を次のように変更します。
for(int i = 0; i < obj1.length; i++){ //obj1の数だけ繰り返し if(obj1[i].idxBmp != 0){ obj1[i].idxBmp++; if(obj1[i].idxBmp >= obj1[i].bmps.length){ obj1[i].idxBmp = 0; obj1[i].visible = false;//見えなくする。 obj1[i].setAnimation(0,0,0);//停止 } } } for(int i = 0; i < obj3.length; i++){ //obj3の数だけ繰り返し if(obj3[i].idxBmp != 0){ obj3[i].idxBmp++; if(obj3[i].idxBmp >= obj3[i].bmps.length){ obj3[i].idxBmp = 0; obj3[i].visible = false;//見えなくする。 obj3[i].setAnimation(0,0,0);//停止 } } } for(int i = 0; i < obj3.length; i++){ //obj3の数だけ繰り返し if(obj3[i].isAction() == true && obj3[i].idxBmp == 0){//移動中のものだけ、衝突判定する。 AnimeImage obj = null; do { obj = (AnimeImage) pointList.searchIntersect(obj, obj3[i] , 0, 0); // obj3[i] と交差しているobj1の画像で、移動中ならbreakで繰り返しを止める。 if(obj != null && obj.isAction() == true && obj.bmps == obj1[0].bmps && obj1.idxBmp == 0) break; } while(obj != null); if(obj != null){ //衝突オブジェクトが見つかって、objに記憶されている。 obj.idxBmp = 1; obj3[i].idxBmp = 1; break; } } }
SEO | [PR] 爆速!無料ブログ 無料ホームページ開設 無料ライブ放送 | ||