MAGI JAVA -Make a game in Java-
JAVA Game作成講座 013回
オブジェに接触→イベント起動


kouza013 jarファイル



[今回追加したもの]
・EventExecutionクラスを実践仕様に改造

描画関連をよくわからないまま使っているので、
クラスまたいで描画させるだけでエラー連発。
ほとんど丸コピーで作ってるのに何故同じことができない?
というトラブルがあったので異様に時間を食った。
何とか予定の動作を実行できたのでやっと公開できる。

ただし、今まで以上に説明が難しい…。
プログラム本体の解説よりも先に、
「イベントシステム」の説明をする。

「イベントシステム」って何?
EventExcecution(eveExe イベント実行をそのまま英訳しただけ)クラスの
内部ループで行う処理。
RPGツクールの動作を自分なりに解釈して組み込んだものだ。
今回作ったイベントは一つだけで、それを例に説明する。
ほんとは次作のツールの作り方なんて覚えなくてもいいんだが…。
慣れて来たらアレンジしてください。

[eventNumber==1の設定データ]
dataBlock[0]={1,0};
dataBlock[1]={999,0};

phase=0からスタートする。
まずdataBlockのphase番目を読み込む。1,0が取り出される。
最初の要素はevExe内で実行する処理の番号になる。
1番は会話イベント。999はイベント終了イベント。
1,0の二個目の要素、0は会話の中身を設定する番号。
0ならテスト用の会話が読み込まれる。

これらの実際の会話データは本来テキストデータで外部に作るべきだが、
めんどくさいのでソースコードに直接書き込む。
読込用の専用クラス、talkDataクラスを作り、
そこに全会話パターンをデータとして打ち込む。

で、1の会話イベントを終えたらphaseが+1される。
すると、999,0を読み込む。
999はイベント終了イベントなので、eveExeのループを抜け、
再びマップ移動モードに戻る。

仮に、
dataBlock[0]={1,0};
dataBlock[1]={1,1};
dataBlock[2]={999,0};

こういう風にイベントデータを設定すると、
会話0を起こした後に会話1が始まり、
ev999を実行して終了する。

phaseをスキップしたり戻したりする命令を作成すれば、
イベント内でループさせたり分岐イベントを作れる。

そんな感じの処理がイベントシステム。
それほど大層な物ではないが、イベントの追加が楽で、
ブロックの様に組み替えることで多彩なイベントを作れるのが強み。
今は直接コードに入力しているが、
「イベントエディター」のようなもので
データ入力する方法にいずれ変更する。


[まずイベントの呼び出し]
抜粋しながら説明する。
全部コピペするのは無理なので、流れだけざっと書いておく。
ARPanelからイベントを起動するのだが、この際使うのは接触判定。
//hitSideが0以外なら接触したとみなす。
[ObjHitCheckの一部]
if (oxy[2]!=0 && waitCount<=0) {

int evNumber=cObj.eventNumber;
if(evNumber>=1) {
eventDataSet(i,evNumber);
cObj.shiftChange(oxy[2]);
i=999;
}


}

追加したのは
eventDataSet(i,evNumber);

渡したデータは、objのナンバーと、そのオブジェから作動するイベントのナンバー。


private void eventDataSet(int touchObjNumber, int evNum) {

//触ったオブジェのナンバーと、実行するイベントのナンバーをキープする。
//イベント終了後そのオブジェを消すときに使うし、
//実行するイベントのナンバーはすぐ次のステップで使う。
waitCount=10;
gData.keepNumber[0]=touchObjNumber;
gData.keepNumber[1]=evNum;
evExe.dataSet(gData,0);
//ゲームモードをイベントモードに切り替える。
gData.gameMode=1;
gameMode=1;
System.out.println("<--arPanel eventdataset eventスタート--> evum"+ gData.keepNumber[1]);
}



gData.keepNumberという変数に、触ったオブジェのナンバー、
作動させるイベントのナンバーを記憶させる。
それをgDataごとイベントクラスにパスする。
gameMode=1にすることで、run処理のループを移動ではなくイベント駆動に切り替える。
以降gameModeが0に戻るまでマップ上のプレイヤー、オブジェに干渉できない。
EventExecutionクラスに移る。まずはDataSet関数。


[EventExecutionクラス]
public void dataSet(GameData gdt,int callMode) {

firstDataInit();
gData=gdt;
keepNumber=gData.keepNumber; //触ったオブジェのナンバー、イベントのナンバーをgDataからそのまま抜く。
//gData.touchObjNumber=999;
int[] someData=new int[10];
for (int i=0;i<=5;i++) {
someData[i]=0;
}

int evnum=keepNumber[1];
if (evnum==1) {
datablock[0][0]=1;
datablock[0][1]=someData[0]; //オブジェから会話データを取り出す。
datablock[1][0]=999;
}


}

keepNumberからeventNumberを取り出し、それを元にイベントを読み込む。
それがdatablockになる。
このdatablockが最重要処理。
最初に書いたphaseで処理が移り変わるやつ。
今回は0と1しかないので、0を実行したらすぐに1の999処理を読み込んで終了する。
datablockをセットすればここでの処理は終了。


public void run(){

time++;

if(time % 2 == 0){
if (waitCount>=1) waitCount--;
//マップ移動モード
gData.pPlatform.keyCheck(keyPressTbl);
if (gameMode==0) {
gData.pPlatform.move(gData);
gData.cameraSet(1);
objsMove();//オブジェ移動
//ブロック衝突チェック
blockHitCheck();
//オブジェ接触チェック
objHitCheck();
}
//イベント駆動モード イベントが終わるまでマップ移動モードに戻らない
if (gameMode==1) {
evExe.eventRoop();
if (evExe.eventEndFlag) {
//System.out.print("event終了----");
evExe.eventEndFlag=false;
gData.gameMode=0;
gameMode=0;
}
}
}else{
repaint();
}


} // end run

[再びARPanel 次はrun関数]
丸ごと書いたが、gameModeによるループの切り替えを追加している。
gameMode==1 の間は、evExe.eventRoop(keyPressTbl);を回し続ける。
その内部で999イベントを通過すると、eventEndFlagがtrueになり、
通常移動モードに戻る。

keyPressTblを渡しているのは、キーコード取得をARPanelでやっているから。
evExe内でもキーの読み取り処理が作れるんだろうけど、
やり方が分からないので既存の物を流用する。
そういう修正は後でもできるので、分かってることをそのまま使う。

では、またEventExecutionクラスに移り、eventRoop処理。


[EventExecutionクラス eventRoop関数]
//ARPanelから実行する処理
public void eventRoop () {

keyPressTbl=kpt;
rpTime++;
if (rpTime % 2 == 1) {
//メイン処理
if (count==0) eventSet();
//System.out.print("----eventRoop----");
//キーチェック
if (keyInputMode==1) {
//gData.pPlatform.mapCursorKeyCheck(keyPressTbl);
} else {
gData.pPlatform.eventKeyCheck();
}
eventStart();
}
if (rpTime>=100) rpTime=0;


}

if (count==0) eventSet();
この処理は一度だけ回す。
イベントを実行するための初期設定を行う。

eventStart();
これがメイン処理。
ひたすらこれを実行し続ける。

gData.pPlatform.eventKeyCheck();
キーの取得はpPlatformから流用している。
専用のクラスを作っても良いのだが、せっかくなのであるものを使いまわす。
専用の関数を使っているので、マップ移動時と動作が少し異なる。
この処理の説明は省略する…。


[eventSet,eventStart関数]
public void eventSet() {

//今のフェーズのデータを切り出す。
int[] data=datablock[phase];
//一度だけ通る初期設定
switch (data[0]) {
case 1: //(001)会話イベント
talkEventSet(data[1],0);
break;
case 999:
eventEnd();
break;
default:
phasePlus++;
break;
}
count++;
System.out.println("phase="+phase+", phasePlus="+phasePlus+" , data0="+data[0]);


}

dataという変数にdatablockを代入しているのは、
視覚的な分かりやすさを優先するため。
data[0]にはイベントナンバー、data[1]にはsomedata[0]が入る。
本来、イベントにもsomedataを入力しておくのだが、今回は0で通した。

public void eventStart() {

time++;
//実行する
if (eventMode==1) talkEventStart();

if (phasePlus>=1) {
phase+=phasePlus;
phasePlus=0;
count=0;
eventMode=0; //モードはフェーズ切り替え時に一度リセットされる。
time=0;
}
if (time>=200) {
time=0;
}


}

イベントは一個しか作っていないので、ひたすらtalkEventStartをループする。
999のeventEndにeventStart処理はない。セットした時点で終了してしまうので。

では、会話イベントをまとめてコピペする。

四角い枠だして文字を表示するだけの処理だが、
これだけ面倒な工程を経てやっと実行できる。
//---------------------------------------------------------------------------
// (001) 会話イベント
//---------------------------------------------------------------------------
public void talkEventSet(int dt1,int type) {

eventMode=1;//System.out.println("dt1="+dt1);
talkText=tkData.talkGet(dt1);
nowline=0;
timeEnd=20;
if (type==1) {
talkText[1]+=keepItemName;
}
endline=talkText.length;


} //end talkEvent

tkData.talkGetというのが、会話データを取得する処理。
TalkDataクラスから対応したナンバーの会話を抜き出すだけ。
本来はテキストデータから読んだ方がいいのだが、
ソースコードに直接書いてしまう。
そのクラスの説明は省略。
endlineは全体の行数。


public void talkEventStart() {

if (gData.pPlatform.keyPush[4]) {
gData.pPlatform.keyPush[4]=false;
nowline+=4;
//最終ぺ―ジまで読んで、ページ送りしたら会話イベント終了。
if (nowline>=endline) phasePlus++;
}


}

データセットが終わったらこのtalkEventStartをループし続ける。
keyPush[4]というのはスペースキーに対応させている。
スペースを押すたびにnowlineを+4し、
4行先の文字を読み込む。
4以下ならスペース一回で終了する。
終了したらphasePlusを増やして終了。
phasePlusはあとでphaseに加算される。
ここまでがデータのやり取りのメイン処理。

以下、会話イベントの描画処理になる…。
rectSet、wordSetは今後他のクラスでも同じものを何度か使う。
gameData内に作って、どこからでも使えるようにしておく。
意味も分からず使っている部分が多いため説明は省く。
色々数値や色を変更してみると、
ここがこう変わるのか、と効果を実感できる。

public void talkModePaint() {

for (int i=0;i<=talkText.length-1;i++) {
if (talkText[i]==null) talkText[i]="";
}
int sx=64;
int sy=452;
gData.rectSet(g,sx,sy,672,130,1,200,1);
gData.wordDisp(g,talkText,nowline,endline,sx,sy);
if (time % 20 <=10) {
g.drawString("▼",704, sy+120);
}


}


[GameData内に追加した関数]
//同様に四角も簡素なメソッドで描けるようにする。これも枠と塗りつぶしで二個いるので行を使うのだ。
public void rectSet(Graphics gr,int x,int y,int width,int height,int col,int alpha,int waku) {

Color grn=(new Color (46,139,87,alpha));
Color gray=(new Color (126,126,126,alpha));
Color black=(new Color (0,0,0,alpha));
Color white=(new Color (255,255,255,alpha));
gr.setColor(Color.darkGray); //枠の色
if (waku>=1) {
gr.drawRect(x-1,y-1, width+1, height+1);
}

if (col==0) gr.setColor(grn);
if (col==1) gr.setColor(gray);
if (col==2) gr.setColor(black);
if (col==3) gr.setColor(white);
gr.fillRect(x, y, width, height);


}

public void wordDisp(Graphics gr,String[] allStr,int nline,int endline,int sx,int sy) {

String[] dispStr=new String[5];

for (int i=0;i<=3;i++) {
if ( (nline+i) <= endline-1) {
if (allStr[nline+i] != null) {
dispStr[i]=allStr[nline+i];
//System.out.println(dispStr[i]);
} else {
dispStr[i]="";
}
} else {
dispStr[i]="";
}
}

for (int i=0;i<=3;i++) {
String nowStr=dispStr[i];
Font font2 = new Font("Meiryo UI", Font.PLAIN, 24);
gr.setFont(font2);

gr.setColor(Color.black);
gr.drawString(nowStr, sx+24+1, sy+24+1+i*24);

gr.setColor(Color.white);
gr.drawString(nowStr, sx+24, sy+24+i*24);
}


}


説明不足の気がするが、その内追加する。
プログラム作るのと同じくらい説明文に時間がかかる…。
次は、会話イベントを少し発展させて、会話に分岐を作る。

似た処理を繰り返し作ることで、ここをこう変えるとこうなるのか、
というのが分かってくると思う。
エラーなく動いていればなんでもいいので、
お綺麗なプログラムは出来るようになってからでいい。
まずは回りくどい方法でもいいからとりあえず作ってみること。


・トップページへ戻る
inserted by FC2 system