RPG01

MAGI JAVA -Make a game in Java-
JAVA Game作成講座 016回
アイテムを使えるようにする


kouza016 jarファイル



[今回追加したもの]

・ステータスウィンドウの「アイテム使用」を実用化した
・アイテムを三つ配置した 武器、薬草、毒草(!)
・アイテム入手イベントを追加した

アイテム入手と使用までをセットで作った。
アイテムリストの表示は少し癖があるが、
このやり方ならいくらでもアイテムを追加できる。

アイテム使用テストのため、
回復アイテムとダメージ受けるアイテムを作った。
アイテムは無限に入手できるようにしてあるが、
本来は入手後画面から消滅させ、
取得フラグを立て、二度と出ないようにする。

では、実際に追加した部分を見ていこう。
まずはアイテム取得イベントから。

[EventExecutionクラス]
public void itemGetEvent(int dt1,int dt2) {

ItemData idata=new ItemData();
//dt[1]がカテゴリー。1武器 2防具 3補助 4アイテム 5お金 6EXP? 50でセットするとランダム作成になる。
if (dt1>=1 && dt1<=4) {
idata=idSet.dataSet_easy(dt1, dt2);
keepItemName=idata.name;
}
if (dt1==5 || dt1==6) {

} else {
//作ったデータをアイテムリストに反映させる。
int itemMax=199;
//カテゴリー4のアイテムは1枠に複数所持できる。まずはそのチェック。
int sameItemNumber=9999;
boolean same=false;
//消耗アイテムの場合のみ、1枠に複数個まとめられる。FFのポーションと同じ。
if (idata.category==4) {
sameItemNumber=idSet.sameCheck(gData.siData,idata.itemNumber);
if (sameItemNumber<=itemMax) {
gData.siData[sameItemNumber].quantity++;
same=true;
}
}
//カテゴリー4ではないorすでに一枠に20以上持っていた場合、空欄チェックに入る。
if (same==false) {
//まずはアイテムリストの空きチェック。そこに新しく手に入れたアイテムを挿入する。
int blankNumber=idSet.blankCheck(gData.siData);
if (blankNumber<=itemMax) {
gData.siData[blankNumber]=idata;
gData.siData[blankNumber].quantity=1;
}
}
}


phasePlus++;
}

カテゴリー1-4は武器、アイテムなど。
5-6はお金、経験値なので入手してすぐに数値化される。
今回は武器と消耗アイテムを作る。
このイベントを起動するときに、dt1,dt2をセットしている。

[武器入手オブジェのセット]
obj=new ObjectBase();
obj=odtSet.dataSetEasy(10*32,6*32,0,1,3,1,1);
obj.setMapSize(mapSize[0]*32, mapSize[1]*32);
objs.add(obj);

obj=odtSet.dataSetEasy(10*32,6*32,0,1,3,1,1);
後ろ3ケタがイベントナンバー、dt1,dt2に該当する。
dt1=1 は武器。その中のdt2=1 ナンバー1をセットしているわけ。

obj=odtSet.dataSetEasy(14*32,6*32,0,2,3,4,1);
薬草入手イベントはこう。カテゴリー4の1番目をセットしている。

消耗アイテムは1枠に20個までまとめられる。
20個越えたら持てないわけではなく、また別の枠を使うだけ。
アイテム総数として200枠用意してあるので、消耗品のみで埋めると4000個は持てる。
その他の装備は1個につき1枠使うため、
空いてる場所を探し、そこにアイテムデータをコピーして入手する。
消耗品の場合は同じアイテムを持っていたらquantityを増やす。
装備と扱いが少し違うのがポイント。

では、ItemDataSetクラスを見ていく。

[ItemdataSetクラス]
//カテゴリー、ナンバーを受けとり、それに対応したアイテムを戻す。基本のアイテム作成方法。
public ItemData dataSet_easy(int dt1,int dt2) {

init();
ItemData idata=new ItemData();
//System.out.println("dt1, dt2="+dt1+","+dt2);
switch (dt1) {
case 1: //手装備
idata=weaponSet(dt2);
//System.out.println("dt1, dt2="+dt1+","+dt2);
break;
case 4:
idata=itemSet(dt2);
break;
}
idata.category=dt1;
idata.itemNumber=dt2;

return idata;


}

武器と道具のセットしか作っていない。
他のナンバーを入力しても空白のアイテムを作って返すだけになる。
カテゴリーを元に別々の関数でアイテムをセットする。
まずは武器をセットするweaponSet関数。

//category 1 武器データのセット
public ItemData weaponSet(int num) {

ItemData idata=new ItemData();
String[] itemName= {"ダミー近","剣","槍","斧","弓","手甲","盾"};

int[] shape= {0,1,2,3,4,5,6}; //装備の形状
idata.name=itemName[num];
int[] price= {0,50,60,80,30,50};
int[][] ist= {
{0,0,0,0,0,0,0,0,0,0,0},//0
{0,0,0,6,2,1,0,0,0,0,0},//1
{0,0,0,5,3,2,0,0,0,0,0},//2
{0,0,0,8,1,0,0,0,0,0,0},//3
{0,0,0,4,3,2,1,0,0,0,0},//4
{0,0,0,2,8,0,2,0,0,0,0}//5
};
//上記の変数を各ステータスに割り振っていく。vst0-2 fst3-6 mst7-10の順に入っている。
for (int i=0;i<=2;i++) {
idata.vst[i]=ist[num][i];
}
for (int i=0;i<=3;i++) {
idata.fst[i]=ist[num][i+3];
idata.mst[i]=ist[num][i+7];
}
idata.shape=shape[num];
int[] range= {1,2,3,2,1,20,1}; //装備のレンジ 弓は実質レンジ無限。どこにいても当てられる。
idata.range=1; //shapeで切り替える。
idata.price=price[num];
return idata;


}

{0,0,0,6,2,1,0,0,0,0,0},
というのは、ステータスを一括入力している。
vst0-2,fst3-6,mst7-10 という感じで、まとめて打ち込んでいる。
その後の処理はそれを各ステータスにばらして代入しているだけ。
武器レンジも一応セットしているが、実際にレンジを活用するかどうか未定。

priceは価値。店で買う時の値段がこれになる。
全て完成したらidataをreturnする。

防具も装飾品も作る要領は同じ。
ステータスと装備する部位を決める。shapeが無い事が違う。
次は消耗品アイテムの設定。

//category 4 消耗品のデータセット
public ItemData itemSet(int num) {

ItemData idata=new ItemData();
//int[] eff= {ef0,ef1,ef2,ef3};
String[] itemName= {"ダミー","薬草","毒草","治療薬"};
int[] itemPrice= {0,10,5,20};
idata.name=itemName[num];
//効率悪いけど手入力してテストする。
if (num==1) {
idata.type=1;
idata.power=10;
}
if (num==2) {
idata.type=1;
idata.power=-10;
}
if (num==3) {
idata.type=4;
idata.power=10;
}
idata.price=itemPrice[num];
return idata;


}

num=1は薬草のデータ。type=1は回復アイテム。
powerが実際の効力=この場合は回復量 になる。
num=2は毒草。powerがマイナス値で設定してある。
使うと薬草と反対にHPが減る。

type=1 HP変動 2=MP変動 3=KP変動 4=HP,MP,KP変動 と決めている。
エリクサー的な物を作りたければ、type=3,power=99999とかにするという事。

num=3はステータス異常回復アイテム。何も設定してないから効果ないけど。

では、StatusWindowクラスで実際にこれらのアイテムを使う処理を見ていく。

[StatusWindowクラス ]
public void eventStart(GameData gdt) {

int cursorPlus=0;
int pushKey=0;
if (gData.pPlatform.keyPush[4]) {
gData.pPlatform.keyPush[4]=false;
pushKey=1;
}
if (gData.pPlatform.keyPush[5]) {
gData.pPlatform.keyPush[5]=false;
pushKey=2;
}
if (gData.pPlatform.keyPush[0]) {
gData.pPlatform.keyPush[0]=false;
cursorPos[mode]--;
}
if (gData.pPlatform.keyPush[1]) {
gData.pPlatform.keyPush[1]=false;
cursorPos[mode]++;
}
if (gData.pPlatform.keyPush[2]) {
gData.pPlatform.keyPush[2]=false;
cursorPos[mode]-=20;
}
if (gData.pPlatform.keyPush[3]) {
gData.pPlatform.keyPush[3]=false;
cursorPos[mode]+=20;
}
if (cursorPos[mode] > cursorMax[mode]) {
cursorPos[mode]=0;
//if (mode==20) pagePlus++;
}
if (cursorPos[mode] < 0) {
cursorPos[mode]=cursorMax[mode];
//if (mode==20) pagePlus--;
}

↑ここから上はいじってないはず。

switch (mode) {
case 1://総合メニュー
if (pushKey==1) mainMenuMode0();
if (pushKey==2) stWinEnd=true;
break;
case 2: //ステータスを見るキャラを選択するモード
if (pushKey==1) {
if (unitLiveCheck(cursorPos[mode]) ) {
selectUnitNumber=cursorPos[mode];
modeChange=3;
}
}
if (pushKey==2) modeChange=1;
break;
case 3:
if (pushKey==2) modeChange=2;
break;
case 4: //item Use Mode
if (pushKey==1) {
ItemData cItem=gData.siData[cursorPos[mode]];
if (cItem.category==4 && (cItem.type>=1 && cItem.type<=5) ) {
healPlusInit();
if (cItem.type==1 || cItem.type==4) healPlus[0]=cItem.power;
if (cItem.type==2 || cItem.type==4) healPlus[1]=cItem.power;
if (cItem.type==3 || cItem.type==4) healPlus[2]=cItem.power;
if (cItem.type==5) healPlus[3]=cItem.power;
selectItemNumber=cursorPos[mode];
modeChange=5; //アイテム使用する相手を決めるモードに移る。使用できるアイテム以外無視する
}
}
if (pushKey==2) modeChange=1;
break;
case 5://アイテムを使う対象を決める
if (pushKey==1) {
UnitData cUnit=gData.uData[cursorPos[mode]];
if (cUnit.live) {
healPlus[4]=cursorPos[mode];
heal();
modeChange=6;
}
}
if (pushKey==2) modeChange=1;
break;
case 6://アイテム使用を実行中
itemUsing();
break;
case 7:
if (pushKey==2) modeChange=1;
break;
}
if (timer[1]>=1 && timer[0] if (modeChange>=1) {
mode=modeChange;
modeChange=0;
count=0;
timerSet(0);
}


}

追加したのはcase 4 5 6 7
7は次回の装備で使う。今回は入り口しか作ってない。

case 4:
使うアイテムを選ぶ。
if (cItem.category==4 && (cItem.type>=1 && cItem.type<=5) ) {

カテゴリー4以外のアイテムは使えないので無効。
さらに、ステータス変動系のアイテムも移動中は使えない。

type=1 - 4 HP,MP,KPを変動させるアイテム
type=5 ステータス異常の回復アイテム
このタイプしか移動中の使用は無効化する…という式。
決定したときに、ある程度のデータをhealPlus変数に格納している。
基本的にアイテムはHPならHPだけ、MPならMPだけ、という風に作用するので、
typeに応じて格納場所を変えている。
例外としてtype4の場合は三種全てのステータスを変動させる。

条件を満たして決定したらcase 5に移る。

case 5:
アイテムを使う対象を選ぶ。
if (cUnit.live) {

healPlus[4]=cursorPos[mode];
heal();
modeChange=6;


}

前提としてアイテムを使えるのはlive==tureのキャラのみ。
キャラ作成しない場所を選んでも無効。
healPlus[4]に使用対象のナンバーをキープする。
カーソルの位置から割り出すので、
決定したときのカーソル位置が0なら0番目、1なら1番目のキャラが対象になる。

heal(); 関数でアイテム使用の実行をしている。
次の項目で中身を見ていく。

最後にmodeChange=6; をやっている。
これは、「HPが回復した!」「MPが回復した!」
などの案内メッセージを表示するだけのモード。
操作することなく一定時間が過ぎると解除される。
ビジュアル的な処理なので、
本来はここ(mode=6)を経由しなくてもアイテムは使用できている。

public void heal() {

UnitData cUnit=gData.uData[healPlus[4]];
//アイテムの数を減らす
gData.siData[selectItemNumber].quantity--;
if (gData.siData[selectItemNumber].quantity<=0) gData.siData[selectItemNumber].init();
for (int i=0;i<=2;i++) {
cUnit.vst_total[i]+=healPlus[i];
if (cUnit.vst_total[i]>cUnit.vst_max_total[i]) {
cUnit.vst_total[i]=cUnit.vst_max_total[i];
}
}


}

数々の条件を潜り抜けているので、ここに来る時点で
そのアイテムは使えるアイテムしかない。
その前提で処理は進む。

UnitData cUnit=gData.uData[healPlus[4]];
まず使用対象のユニットを抜き出す。
healPlus[4]には前項でカーソル位置から割り出してキープした
使用対象のナンバーが入っている。

gData.siData[selectItemNumber].quantity--;
if (gData.siData[selectItemNumber].quantity<=0) gData.siData[selectItemNumber].init();
まとめ登録されているアイテムの個数を減らす。
その時クアンティティ(数量)が0になったら、
その枠のアイテムを使い切ったとみなし、アイテムを初期化する。

後の処理はhealPlusの数値分vstを増やす(or減らす)だけ。
vst_totalがvst_max_totalを越える事が無いようリミッターをかけている。

では最後にcase 6: itemUsing();と、paint処理を見ていく。
それほど複雑な事やってないんだけど。

[StatusWindowクラス paint関数]
if (mode>=4 && mode<=6) {

gSet.rectSet2(g,177,100+10,450,480,0,200);
int nx=210;
int ny=140;
int nys=ny;
int page=cursorPos[4]/40;
int kz=cursorPos[4]-page*40;
for (int i=0;i<=39;i++) {
ItemData cItem=gData.siData[i+page*40];
String word=cItem.name;
if (cItem.category==4) word+=" ("+cItem.quantity+")";
if (cItem.category==0) word="――――――――";
gSet.wordDisp_short2(g,word,nx,ny,0);
ny+=22;
if (i==19) {
nx+=200;
ny=nys;
}
}
int px=0;
if (kz >=20) px=200;
int cy=cursorPos[4]%20;
gSet.wordDisp_short2(g,"→",cursorX[4]+px,cursorY[4]+cy*22,2);
gSet.wordDisp_short2(g,"No."+cursorPos[4],450,580,0);
gSet.wordDisp_short2(g,"Page "+page+"/ 4",530,580,0);


}
if (mode==5 || mode==6) {
cSelWinDraw(mode);
}
if (mode==6) {
healResultDisp(mode); //回復魔法でも使えるので関数にする
}

多分前回の時はやってないと思うが、アイテム表示の場所をページ単位で区切っている。
一画面に200アイテム一気に表示できないこともないが、視認性が悪く見苦しい。
というわけで40アイテムで1ページと仮定してある。5ページが最終という事。
50でもいいんだが、画面サイズがタテ600なので50/2=25を二列で、
22*25=550とギリギリまで使ってしまう。
40なら20を二列で440と少し余裕ができる。そういう理由。

現在のページ数は
int page=cursorPos[4]/40;
で求められる。

int kz=cursorPos[4]-page*40;
このkzというのは、ページ数に対応したカーソル表示位置。
0-39の間に収まるようになっている。
カーソル位置を元に機械的に描画するとカーソルが画面をはみ出してしまう。
そこで、この表示形式にあった場所にカーソルを出すための変数になる。

ItemData cItem=gData.siData[i+page*40];
String word=cItem.name;
if (cItem.category==4) word+=" ("+cItem.quantity+")";
if (cItem.category==0) word="――――――――";

ItemData cItem=gData.siData[i+page*40];
当然そのページ内のアイテムだけを表示するので、そこだけ抜き取る。
これを40回行う。

if (cItem.category==4) word+=" ("+cItem.quantity+")";
一枠に複数キープできる消耗品は、アイテム名+枠内の数量も同時に描画しておく。
if (cItem.category==0) word="――――――――";
カテゴリー0のアイテムは存在しないとみなし、棒線一本だけ描画する。

gSet.wordDisp_short2(g,word,nx,ny,0);
ny+=22;
if (i==19) {
nx+=200;
ny=nys;
}

アイテム名を書き終わったらyを一個ずらす。
0-19の終端に達したらx座標を20-39を描く位置ににリセットする。

int px=0;
if (kz >=20) px=200;
カーソル位置が20-39の箇所にある場合は、→表示位置を右にずらす。

int cy=cursorPos[4]%20;
カーソル表示のy座標を決める。0-19ならそのまま。
20-39の場合は0-19が返り値になる。

gSet.wordDisp_short2(g,"→",cursorX[4]+px,cursorY[4]+cy*22,2);
gSet.wordDisp_short2(g,"No."+cursorPos[4],450,580,0);
gSet.wordDisp_short2(g,"Page "+page+"/ 4",530,580,0);
選択しているアイテムナンバーと、現在のページ数を表示する。
アイテムナンバーはプレイヤーにあまり関係ない。
プログラマ的に便利なので一緒に出している。

if (mode==5 || mode==6) {
cSelWinDraw(mode);
}
mode=5はアイテム使用対象を選んでいるモード。
ステータス表示の時のキャラ選択を使いまわしている。
このように各所で使うものは関数かしておくとあとでリサイクル出来て便利。

if (mode==6) {
healResultDisp(mode); //回復魔法でも使えるので関数にする
}
回復効果を見た目で分かるように描画している。
この処理も後に回復魔法で使いまわすので関数化してある。

[healResultDisp関数]
private void healResultDisp(int mode) {

int stx=600;
int sty=240;
gSet.rectSet2(g,stx-24,sty-24,144,100,0,0);
String[] moji= {"HP","MP","KP"};
gSet.wordDisp_short2(g,charaName[healPlus[4]],stx+10,sty,0);
for (int i=0;i<=2;i++) {
if (healPlus[i]!=0) {
if (healPlus[i]>=1) gSet.wordDisp_short2(g,moji[i]+" +"+healPlus[i],stx+10,sty+22+i*22,0);
if (healPlus[i]<=-1) gSet.wordDisp_short2(g,moji[i]+" "+healPlus[i],stx+10,sty+22+i*22,0);
}
}


}

長々と書いてきたがこれで今回はラスト。
HP,MP,KPの回復量をhealPlusの値を元に描画している。

if (healPlus[i]>=1) gSet.wordDisp_short2(g,moji[i]+" +"+healPlus[i],stx+10,sty+22+i*22,0);
if (healPlus[i]<=-1) gSet.wordDisp_short2(g,moji[i]+" "+healPlus[i],stx+10,sty+22+i*22,0);

効力がプラスの時は頭に+をつけ、マイナスの時は何もつけない。それだけの処理。

今回は以上で終わり。
次はいよいよ武具を装備できるようにする。
今回より面倒なことが多く、やや複雑な処理になる予定。
それが出来たら戦闘…と行きたいところだが、
その前に場所移動イベントを作るかもしれない。



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