3.1 戦闘制御の概要
戦闘を制御するプログラムを作成します。プログラムを設計する時、対象とする事象の働きを箇条書きにすると必要な動作が見えてきます。処理の手順を箇条書きにすると言った方が良いでしょうか。難しく考える事は有りません。対象とする事象の働きを最小単位に分解しているだけです。「箇条書きにする」といったのは、ある処理の複数の働きが混じらないようにする為です。では、ロールプレイングゲームの戦闘制御を考えていきましょう。対象とする事象は、戦闘の制御です。ゲームのプログラムなどでは、ルールを箇条書きにするとよいでしょう。
戦闘の働き(ルール)
敵のHPが0になったら勝ち
自分のHPが0になったら負け
行動は、戦う・特技・道具・逃げるから選択できる
戦う場合は通常攻撃を行う
特技の場合は特技を使用する
道具の場合は道具を使用する
逃げるの場合は逃げる処理を行う
特技は、HPの回復と攻撃の種類がある
特技は、MPを消費する
特技は、消費MP分のMPがないと使用できない
道具は、HPの回復と攻撃の種類がある
道具は、もっていないと使えない
逃げる場合は、必ずしも逃げれるとは限らない
戦闘に勝つと敵がアイテムを落とす場合がある
戦闘に勝つと経験値がもらえる
戦闘に勝つとお金がもらえる
戦闘に勝って経験値が一定の値まで上がるとレベルが上がる
働き(ルール)の一覧は、こんな感じでしょう。次に戦闘に求められる処理の一覧を示します。働き(ルール)をもう少し大きい単位にまとめたものです。この一覧がメソッドの候補になります。
処理一覧
戦闘画面の描画処理
通常攻撃の攻撃判定
特技の効果判定
道具の効果判定
逃げるの判定
行動後のダメージ判定(モンスターの攻撃)
戦闘終了後の勝利・負けの判定
負けの場合は、初期画面(ゲームオーバー)
経験値の加算処理
レベルアップの判定
お金の加算
戦闘に勝った場合のアイテムの敵モンスターがアイテムを落としたかの判定
持ち物が一杯だった場合のアイテムの処理(持ち物を捨てる・アイテムをあきらめる)
これらの要件を満たすようにプログラムを作成していきましょう。まずは、基本データを定義しましょう。戦闘画面では、主人公とモンスターが戦闘を行います。そして、道具も利用します。主人公・モンスター・道具のデータが必要です。場合によっては、魔法などの特技も必要になります。
では、ユーザー定義のデータとしてゲームの主人公を表すクラス・モンスタークラス・道具クラスを作成します。それぞれ、Hero・Monster・Itemクラスとします。ここで、当マニュアルの原則を確認します。クラスは、アプリケーションのサイズ上、メソッドを作りたくありません。データ型として利用するクラスは、Heroクラスを除いて単純な構成にします。また、クラスにメソッドを定義するとその分インスタンスのサイズが増えます。PCと比べてヒープ領域が少ない携帯端末では、データ型として利用するクラスのメソッドの定義は最小限に抑えた方が良いでしょう。
以降、Hero・Monster・Itemクラスのデータ構成を説明しますが完璧に理解する必要はありません。戦闘制御でクラスを利用しますので、そこで定義しているデータの意味が分かるはずです。取りあえず「こんなデータが定義されているんだ」と確認するだけで構いません。
プロジェクトは、「AppBattleCnt」というプロジェクトを作成してそのsrcフォルダにソースファイルを保存します。また、以下のイメージをresフォルダに保存してください。
イメージファイル
![]()
3.2 Itemクラス
最初にItemクラスを定義します。アイテムは、名前があります。特徴を示す説明が有ります。武器・道具・回復・攻撃などの種類があります。そのアイテムを装備・使用した時の効果があります。売る場合は、金額も必要ですね。これらのことを考えるとItemクラスは以下のようなデータ型になります。
Item.java 道具を表すクラス
| Item.java |
| public class Item{ String name;//アイテム名 10文字 String exposition;//アイテム説名 10文字 int kind; //アイテム種類(1.武器、2.防具、3.回復、4.攻撃、5.キーアイテム) int eftpnt; //効果のポイント(攻撃力・防御力・回復ポイント) int price; //金額 public void setData(String sdata){ sdata=sdata.trim(); name=sdata.substring(0,sdata.indexOf(',')); sdata=sdata.substring(sdata.indexOf(',')+1); exposition=sdata.substring(0,sdata.indexOf(',')); sdata=sdata.substring(sdata.indexOf(',')+1); kind = Cmn.cla(sdata.substring(0,1)); eftpnt = Cmn.cla(sdata.substring(1,4)); price = Cmn.cla(sdata.substring(4)); } } |
フィールドにデータを代入・参照するには、フィールドに直接アクセスして行います。メソッドは介しません。是は、前にも述べたようにインスタンスのサイズを小さくする為の対策です。くれぐれも言いますが、フィールドはユーザーに直接操作させるべきではありません。
メソッドは、setDataメソッドが定義されています。当マニュアルでは、データをインターネット上のファイルから取得します。アイテムの情報もインターネットのファイルから取得します。このデータファイルはテキストファイルを利用します。つまり、テキストファイルの文字列を分析してデータを作り上げるのです。データファイルの操作は、後の章で行います。ここでは、アイテム情報のデータ構成を説明します。アイテム情報は、以下のような構成になっています。
アイテム情報の構成 薬草のデータ
薬草,傷を回復,30100010
大まかな構成は、名前・説明・詳細データを半角のカンマ(,)で区切っています。最後の数値の文字列が種類・効果・金額の情報になります。setDataメソッドでは、この情報を分解して各フィールドにデータを設定します。メソッド内の処理を説明しましょう。
sdata= sdata.trim();
最初にtrimメソッドを利用して文字列にスペースなどが含まれていたら削除します。データ作成の時に、うっかりスペースが入っていたようなミスに対処する為です。次に、名前のデータを抜き出してnameフィールドに代入します。
name=sdata.substring(0,sdata.indexOf(','));
文字を切り出すsubstringメソッドに、インデックス0から「,」までを指定しています。「薬草」のデータを例にとりましょう。この処理で、取り出される文字列は「薬草」です。
切り出す文字
薬草,傷を回復,30100010
名前の次は、説明に設定する文字列を取り出します。まずは、必要の無くなった「名前」のデータをなくします。
sdata=sdata.substring(sdata.indexOf(',')+1).trim();
substringメソッドに、最初にあらわれるカンマのインデックスに+1した値を渡します。これで、アイテムの文字列データは、「傷を回復,30100010」の文字列になります。データはsdataに代入しています。
分解した文字
傷を回復,30100010
文字列の加工が済んだら「名前」のデータを取り出した時と同じように処理を行います。先頭のインデックス(0)からコロンの最初にあらわれるインデックスを指定してsubstringメソッドで切り出します。
exposition=sdata.substring(0,sdata.indexOf(','));
切り出す文字
傷を回復,30100010
expositionフィールドにデータを設定したら不要になった「説明」の情報を削除します。
sdata=sdata.substring(sdata.indexOf(',')+1);
これで、「説明」のデータ処理がなくなりますからsdataには「30100010」といった数値フィールドのデータが代入されています。
分解した文字
「30100010」
数値部のデータ構成は、最初のインデックス(0)が種類、1〜3のインデックスが効果、4〜7のインデックスが金額です。
数値部のデータ構成

データの構成に従って、数値部のデータをsubstringメソッドで抜き出します。substringメソッドは、最後のインデックスの値から-1したインデックスまで切り出します。
kind = Cmn.cla(sdata.substring(0,1));
eftpnt = Cmn.cla(sdata.substring(1,4));
price = Cmn.cla(sdata.substring(4));
この処理で、Cmnクラスのclaメソッドを利用しています。これは、文字列を数値に変換するメソッドです。「010」と言う文字列なら数値の「10」に変換します。戻り値は、intデータです。
メソッド cla
int cla(文字列)
このclaメソッドの説明は、Hero・Monster・Itemクラスの説明が終わったら行います。今は、数値を表す文字列を渡すintデータに変換してくれるメソッドと覚えておいて下さい。
各フィールドの働きを軽く確認しましょう。nameは名前です。薬草や花火など、アイテムを表す名前をつけます。kindは、道具の属性を表します。道具は、装備する武器・防具・回復アイテム・攻撃アイテム・キーアイテムがあります。eftpntは、その道具のもつ効果力です。例えば攻撃アイテムでeftpntが100の場合、道具を使用した時、100のダメージを与えられます。priceはアイテムの売値です。
3.3 Monsterクラス
モンスタークラスを定義します。モンスターは、戦闘画面でイメージが表示されます。名前も必要です。アイテムを落とすのでアイテム情報も必要です。主人公を攻撃する時の攻撃力、攻撃を受けた時の防御力、倒された時に与える経験値・お金、アイテムを落とす確率。後、「逃走」を選択した時にレベルが必要です。自分よりレベルの高いモンスターからは逃げにくくします。これらのことを考えるとMonsterクラスは以下のようなデータ型になります。
Monster.java モンスターを表すクラス
| Monster.java |
| import com.nttdocomo.ui.Image; public class Monster{ Image image;//イメージのインデックス int dip;//アイテムを落とす確率 String name;//キャラクター名 String itmStr;//アイテム情報の文字列を格納 int hp;//HP int mhp;//マックスHP int attack;//攻撃力 int defense;//防御力 int maney;//お金 int expoint;//経験値 int lve;//レベル public Monster(){ image=null; } //モンスターのステータスを設定 public void setData(String sdata){ sdata=sdata.trim(); //名前の切り出し name=sdata.substring(0,sdata.indexOf('(')); //アイテム情報の保存 itmStr=sdata.substring(sdata.indexOf(',')+1,sdata.indexOf(')')); //各ステータス部の切り出し sdata=sdata.substring(sdata.indexOf('(')+1,sdata.indexOf(',')); hp = Cmn.cla(sdata.substring(0,3)); mhp = hp; attack = Cmn.cla(sdata.substring(3,6)); defense = Cmn.cla(sdata.substring(6,9)); maney = Cmn.cla(sdata.substring(9,12)); lve = Cmn.cla(sdata.substring(12,15)); expoint = Cmn.cla(sdata.substring(15,19)); dip = Cmn.cla(sdata.substring(19,20)); } } |
注意点として、itmStrフィールドにはItem情報を文字列で格納します。当マニュアルでは、モンスターが特殊攻撃を行うことは考慮に入れてありません。アイテムの情報は、倒された時に落とすアイテムとしての役割だけです。その為、文字列のままの情報を持っています。
Monsterクラスもデータを文字列で取得します。Itemクラスを同様にsetDataメソッドで文字列データを分析して各フィールドにデータを設定します。Monster情報のデータは、以下のような構成になっています。
Monster情報の構成 ツライムのデータ
ツライム(01000200201000500101,薬草,傷を回復,30100010)
大まかな構成は、名前・ステータス・アイテム情報です。Monster情報はアイテムの情報も含みますから見やすくする為に「()」(括弧)を利用しています。最初の文字列がモンスターの名前、括弧の中がモンスターのステータス情報とアイテム情報です。
Monster情報のデータ構成
![]()
setDataメソッド内の処理を説明しましょう。trimでスペースを削除するのはItemと同じです。次に、名前のデータを抜き出してnameフィールドに代入します。
name=sdata.substring(0,sdata.indexOf(','));
文字を切り出すsubstringメソッドに、インデックス0から「(」(半角括弧)までを指定しています。「ツライム」のデータを例にとりましょう。この処理で、取り出される文字列は「ツライム」です。
切り出す文字
ツライム(01000200201000500101,薬草,傷を回復,30100010)
モンスター名を切り出したらアイテム情報を切り出します。最初に出現するコロン(,)のインデックスから「)」(半角括弧)までを指定します。是で、アイテム情報を切り出す事が出来ます。切り出した文字列は、itmStrフィールドに格納します。
itmStr=sdata.substring(sdata.indexOf(',')+1,sdata.indexOf(')'));
切り出す文字
ツライム(01000200201000500101,薬草,傷を回復,30100010)
アイテム情報が切り出せたらモンスター名とアイテム情報のデータは不要です。ステータス情報だけを切り出してsdataに代入します。ステータス情報は、substringメソッドに開始位置を「(」(半角括弧)が出現するインデックスに1を加算した値を指定します。終了位置は、最初に出現するコロン(,)のインデックスを指定すれば切り出せます。
sdata=sdata.substring(sdata.indexOf('(')+1,sdata.indexOf(','));
切り出す文字
ツライム(01000200201000500101,薬草,傷を回復,30100010)
これで、sdataには「01000200201000500101」といったステータス情報(数値フィールドのデータ)が代入されています。ステータス情報は、インデックスの0〜2がHP(hp)、3〜5が攻撃力(attack)、6〜8が防御力(defense)、9〜11がお金(maney)、12〜14がレベル(lve)、15〜18が経験値(expoint)、19がアイテムを落とす確率(dip)です。
ステータス情報(数値フィールドのデータ)部のデータ構成

データの構成に従って、数値部のデータを抜き出して数値に変換します。substringメソッドは、切り出すインデックス-1の文字列を切り出します。切り出す最後のインデックスは、データ構成のインデックス+1になっています。
hp = Cmn.cla(sdata.substring(0,3));
mhp = hp;
attack = Cmn.cla(sdata.substring(3,6));
defense = Cmn.cla(sdata.substring(6,9));
maney = Cmn.cla(sdata.substring(9,12));
lve = Cmn.cla(sdata.substring(12,15));
expoint = Cmn.cla(sdata.substring(15,19));
dip = Cmn.cla(sdata.substring(19,20));
確認して欲しいところとして、mhpにhpを代入しています。mhpには、最大HPが代入されます。これは、hpは戦闘によって減算されていきます。0になれば、モンスターは倒されます。フィールドの値が変わっていますから戦闘終了後にhpを元に戻します。この時、利用するのがmhpフィールドの値です。これは、戦闘制御の後半で役割がわかります。
3.4 Heroクラス データ構成
最後に、主人公を表すHero クラスを定義します。Heroクラスは戦闘で利用しますが、Monsterクラスと違って特技や道具が使えます。戦闘に利用しますから名前・HP・攻撃力・防御力は必要です。特技を利用する為のMP、所持しているお金、レベルアップの目安の経験値、現在のレベル、次のレベルまでの経験値、特技の利用可能レベル、所持アイテム、覚える特技と言ったデータが必要になります。
また、戦闘以外でも利用されます。例えば、武器や防具の装備などです。その為、振る舞いとしてレベルアップの処理、アイテムの取得、アイテムを利用した時のアイテム削除、武器や防具の装備といったメソッドが必要です。これらのことを考えるとHeroクラスは以下のようなデータ型になります。
Hero.java 主人公を表すクラス
| Hero.java |
| import com.nttdocomo.ui.Image; public class Hero{ Image[] image=new Image[4]; String name;//キャラクター名 String exposition;//アイテム説明 10文字 int hp;//HP int mhp;//マックスHP int mp;//MP int mmp;//マックスMP int attack;//攻撃力 int defense;//防御力 int maney;//お金 int expoint;//経験値 int lve;//レベル int nlve;//次のレベル int slv;//特技の使用可能レベル //通常アイテム Item[] myItem = new Item[8]; //特技(アイテムで代用) Item[] special = new Item[6]; //各特技の使用可能レベル int sul[]={0,5,7,8,10,15}; public Hero(){ int i; int skd[]={3,4,4,4,4,4};//特技の種類の種類 int ump[]={3,7,9,12,15,18};//使用MP int sep[]={70,5,6,7,10,100};//特技の威力 String[] snam={"回復","火魔法","水魔法","稲妻魔法","氷魔法","究極魔法"}; String[] expo={"スッキリHPを回復","燃えさかる炎で攻撃","なぜか沸く水で攻撃","静電気で稲妻を発生?","親父ギャグで凍らせる","究極の魔法"}; //特技データの初期化 for(i=0;i<=5;i++){ special[i] = new Item(); special[i].name=snam[i]; special[i].exposition=expo[i]; special[i].kind=skd[i]; special[i].eftpnt=sep[i]; special[i].price=ump[i]; } //アイテムデータの初期化 for(i=0;i<=7;i++){ myItem[i] = new Item(); myItem[i].name =""; myItem[i].exposition=""; myItem[i].kind=0; myItem[i].eftpnt=0; myItem[i].price=0; } } //レヴェルアップ処理 public void upLev(){ if(lve>=99) return; mhp+=10;//HPの加算 mmp+=10;//MPの加算 attack+=10;//攻撃力の加算 defense+=10;//防御力の加算 nlve=nlve*3;//次のレベルの値を算出 lve+=1;//現在のレベルを加算 //使用できる特技が増えるかの判定 for(int i=0;i<=5;i++){ if(lve==sul[i]) slv++; } } //アイテムの取得処理 //引き数 アイテム情報(文字列) public boolean setItem(String sitm){ int sti; if((sti=enpItem())>=0){ myItem[sti].setData(sitm); return true; } //戦闘メッセージ中に描画するカーソルの座標 int mxp=Cmn.dmx+Cmn.cx; int mxy=Cmn.dmy+Cmn.fnthgt+Cmn.cy; while(true){ //アイテム満杯のメッセージの描画 Cmn.battleMsg("アイテムが一杯です。何か捨てますか?"); //選択を描画 Cmn.battleMsg(" あきらめる| 捨てる"); //選択制御 if(Cmn.chsCnt(mxp+5,mxy,0,2)==0){ Cmn.battleMsg(sitm.substring(0,sitm.indexOf(','))+"をあきらめた"); return false; } //サブ一覧(道具一覧)を表示 if((sti=Cmn.subCnt(myItem,6))>5) continue; //捨てるアイテムの確認メッセージ Cmn.battleMsg(myItem[sti].name+"を捨てますか?"); Cmn.battleMsg(" はい| いいえ"); //0なら持ち物を捨てる、1(いいえなら、最初に戻る) if(Cmn.chsCnt(mxp+5,mxy,0,2)==0){ Cmn.battleMsg(myItem[sti].name+"を捨てた"); myItem[sti].setData(sitm); return true; } else{ continue; } } } //アイテムに空が無い場合は、-1を返す public int enpItem(){ //アイテムのインデックス int eni=0; //空きアイテムのインデックスを検索 while(eni<=6){ //アイテムのインデックスが6以上なら-1を返す。 if(eni>=6) return -1; //種類が0のアイテムがあったらインデックスを返す if(myItem[eni].kind == 0) return eni; //インデックスの更新 eni++; } return eni; } //アイテムの削除 //引き数 削除アイテムのインデックス public void lostItem(int lno){ myItem[lno].name = "";//アイテム名の初期化 myItem[lno].exposition="";//説明の初期化 myItem[lno].kind = 0;//アイテム種類の初期化 myItem[lno].eftpnt = 0;//効果のポイントの初期化 myItem[lno].price = 0;//金額の初期化 } //アイテムの装備 //装備アイテムのインデックス public void equipsItem(int ino){ //武器・防具のインデックス int ind; //種類が1の場合は「武器」2は「防具」 if(myItem[ino].kind ==1){ //インデックス6に登録する ind = 6; } else if(myItem[ino].kind ==2){ //インデックス7に登録する ind = 7; } else{//武器防具以外の種類は装備できない Cmn.battleMsg("装備できない"); return; } //アイテムを入れ替える為のワークインスタンス Item workItem = new Item(); //現在の装備アイテムをワークインスタンスに一時退避 workItem.name = myItem[ind].name;//アイテム名 workItem.exposition=myItem[ind].exposition;//説明 workItem.kind = myItem[ind].kind;//アイテム種類 workItem.eftpnt = myItem[ind].eftpnt;//効果のポイント workItem.price = myItem[ind].price;//金額 //武器・防具のインデックスに格納 myItem[ind].name = myItem[ino].name;//アイテム名 myItem[ind].exposition=myItem[ino].exposition;//説明 myItem[ind].kind = myItem[ino].kind;//アイテム種類 myItem[ind].eftpnt = myItem[ino].eftpnt;//効果のポイント myItem[ind].price = myItem[ino].price;//金額 //送られてきたデータに入れ替えたデータを格納 myItem[ino].name = workItem.name;//アイテム名 myItem[ino].exposition= workItem.exposition;//説明 myItem[ino].kind = workItem.kind;//アイテム種類 myItem[ino].eftpnt = workItem.eftpnt;//効果のポイント myItem[ino].price = workItem.price;//金額 } } |
フィールドは、Monsterクラスと重複しているフィールドの働きは予想がつくでしょう。ここでは、想像しづらいフィールドの説明を行います。Imageクラスのフィールドは、マップの移動で利用するイメージです。戦闘では利用しません。
Heroクラスは、アイテムと特技を利用する為にその働きを実現するデータフィールドが定義しています。Itemクラスの配列myItemフィールドは、アイテムのデータを格納します。要素数は8です。
//通常アイテム
Item[] myItem = new Item[8];
myItemフィールドの要素は8ですが、通常のアイテムを保持するインデックスは0〜5の6つです。6と7は、装備アイテムのデータに利用します。6が装備武器のデータで、7が装備防具のデータを格納します。例えば、装備する武器を変えたければ要素6(myItem[6])に武器に属するアイテムクラスを設定します。
特技のデータ(special)もItemクラスの配列を利用しています。特技には、名前、説明、種類、効果ポイントとアイテムと重複するデータが有ります。priceは、特技に存在しませんがこの値は消費MPのデータを格納するフィールドとして利用します。
//特技(アイテムで代用)
Item[] special = new Item[6];
Itemは、ゲームイメージと結びつきやすいように「Item」という名前にしました。しかし、実際は「使う物」と言った意味合いが強くなります。priceも「消費する値」と言った意味合いです。
//各特技の使用可能レベル
int sul[]={0,5,7,8,10,15};
最後のフィールドのsulは「各特技の使用可能レベル」を格納します。格納データは、特技が利用できるレベルです。レベルアップの時に参照して特技使用レベルのフィールド(slv)を加算します。
3.5 Heroクラス コンストラクタ
Heroクラスのコンストラクタを説明をします。コンスタラクタでは、アイテムデータを格納するmyItemフィールドと特技のデータを格納するspecialフィールドの初期化を行っています。myItemフィールドの初期化は単純です。文字列データのフィールドには、「""」で初期化します。数値フィールドは、0で初期化します。少し面倒なのは、特技のデータを格納するspecialフィールドです。最初に各フィールドのデータを配列で定義しています。
int skd[]={3,4,4,4,4,4};//特技の種類の種類
int ump[]={3,7,9,12,15,18};//使用MP
int sep[]={70,5,6,7,10,100};//特技の威力
String[] snam={"回復","火魔法","水魔法","稲妻魔法","氷魔法","究極魔法"};
String[] expo={"スッキリHPを回復","燃えさかる炎で攻撃","なぜか沸く水で攻撃","静電気で稲妻を発生?","親父ギャグで凍らせる","究極の魔法"};
各データのskd配列は、特技の種類を格納します。回復する特技か、攻撃を行う特技かの種類です。Itemクラスのkindフィールドに代入するデータです。ump配列は、特技を利用した時の消費MPのデータです。Itemクラスのpriceフィールドに代入します。sep配列は、特技の効果ポイントです。Itemクラスのeftpntフィールドに代入します。snam・expoの内容は、想像できますね。Itemクラスのname・expositionフィールドに代入するデータです。後は、これらのデータを対応するspecialフィールドの要素に代入します。
//特技データの初期化
for(i=0;i<=5;i++){
special[i] = new Item();
special[i].name=snam[i];
special[i].exposition=expo[i];
special[i].kind=skd[i];
special[i].eftpnt=sep[i];
special[i].price=ump[i];
}
ここで、注意です。最初に「special[i] = new Item();」と各要素にnew演算子を利用してItemクラスのインスタンスを生成しています。フィールドで定義した「Item[] myItem = new Item[8];」という命令では、myItemという名前のItemクラスの要素を8つ持った配列を宣言しているに過ぎません。各要素には、インスタンスが存在しないのです。配列でクラスを持つ時、各要素のインスタンス生成をうっかり忘れる事があります。良くあるミスなので注意しましょう。配列にクラスを利用する時は、配列の要素の確定とインスタンスの生成でnew演算子を利用します。
念のため、myItemフィールドの初期化を軽く説明します。myItemフィールドの初期化もループで各要素にインスタンスを生成します。Itemクラスの要素の初期化はlostItemメソッドで行っています。このメソッドは、引き数で渡されたインデックスに対応するmyItemフィールドの要素を初期化します。初期化内容は、Stringクラスの場合は「""」で数値データは0で初期化します。lostItemメソッドは、簡単な処理なので内容を確認してみてください。
3.6 Heroクラス レベルアップ処理
Heroクラスの通常メソッドの説明をします。ここでは、レベルアップの処理を行うupLevメソッドについて説明します。メソッドの内容を確認しましょう。
//レヴェルアップ処理
public void upLev(){
if(lve>=99) return;
mhp+=10;//HPの加算
mmp+=10;//MPの加算
attack+=10;//攻撃力の加算
defense+=10;//防御力の加算
nlve= nlve*3;//次のレベルの値を算出
lve+=1;//現在のレベルを加算
//使用できる特技が増えるかの判定
for(int i=0;i<=5;i++){
if(lve==sul[i]) slv++;
}
}
レベルは99を最大値にします。99を超えていると処理は行われません。upLevメソッドを実行すると最大HP、最大MP、攻撃力、防御力が+10されます。ランダムに加算する値をけると能力の上昇に変化がつきます。次のレベル(nlve)には、経験値expointに現在の経験値を5で除算した値を設定します。ここまでの値は、自分の好みによって変えてください。モンスターの強さや貰える経験値などゲーム全体のバランスによって変えて構いません。次に現在のレベルと1加算します。レベルアップですから当たり前ですね。最後に、現在のレベルと「各特技の使用可能レベル」と格納した配列sulフィールドを突合せします。配列sulフィールドに、現在のレベルと同じ値があった場合は、「特技の使用可能レベル」を格納したslvフィールドの値を1加算して利用できる特技を増やします。
3.7 Heroクラス アイテムの取得処理
アイテムの取得には、setItem・enpItemメソッドを利用します。アイテムは、myItemフィールドの0〜5の要素に格納します。モンスターがアイテムをとしたり宝箱からアイテムを拾ったりしてアイテムを取得した場合、空きのある(データの無い)要素を探してデータをセットします。このデータを格納できる要素を探すのがenpItemメソッドです。メソッドの内容を確認しましょう。
//アイテムに空が無い場合は、-1を返す
public int enpItem(){
int i;
for(i=0;i<=5;i++) if(myItem[i].kind
== 0) return i;
return -1;
}
}
処理は簡単です。フィールドmyItemの要素0〜5までループで参照します。その中で、myItem
(Itemクラス)のkindフィールドを0と比較します。kindが「0」の場合は、空データとなります。そして、kindが0の場合はそのインデックス(i)を返します。kindが0の要素が無い場合は、ループを終了後「-1」を返します。これは、アイテムを取得する時、必ずアイテム欄が開いているとは限りません。空のアイテム欄が無い事を通知する為の処理です。
このプログラムでは、kindに0のデータは有りません。その為、kindが0の場合は空データとして扱っています。コンストラクタでもkindは0で初期化しています。ただ、好みによってnameフィールドをequalsで比較しても良いでしょう。茶目っ気をもって、名前の無いアイテムを存在させる時は利用できませんが・・・。
例 equals利用
for(i=0;i<=5;i++) if(myItem[i].name. equals(""))
return i;
アイテムの取得で、実際にデータの設定やアイテム欄に空がない時の処理を行っているのはsetItemメソッドです。機能は、enpItemメソッドでmyItemのデータが空の要素を取得してアイテムを設定します。アイテムが一杯だった場合は、何か捨てるか確認して、「アイテムを捨てる」もしくは「あきらめる」といった処理を行います。メソッドの内容を確認しましょう。
//アイテムの取得処理
//引き数 アイテム情報(文字列)
public boolean setItem(String sitm){
int sti;
if((sti=enpItem())>=0){
myItem[sti].setData(sitm);
return true;
}
//戦闘メッセージ中に描画するカーソルの座標
int mxp=Cmn.dmx+Cmn.cx;
int mxy=Cmn.dmy+Cmn.fnthgt+Cmn.cy;
while(true){
//アイテム満杯のメッセージの描画
Cmn.battleMsg("アイテムが一杯です。何か捨てますか?");
//選択を描画
Cmn.battleMsg(" あきらめる| 捨てる");
//選択制御
if(Cmn.chsCnt(mxp+5,mxy,0,2)==0){
Cmn.battleMsg(sitm.substring(0,sitm.indexOf(','))+"をあきらめた");
return false;
}
//サブ一覧(道具一覧)を表示
if((sti=Cmn.subCnt(myItem,6))>5)
continue;
//捨てるアイテムの確認メッセージ
Cmn.battleMsg(myItem[sti].name+"を捨てますか?");
Cmn.battleMsg(" はい| いいえ");
//0なら持ち物を捨てる、1(いいえなら、最初に戻る)
if(Cmn.chsCnt(mxp+5,mxy,0,2)==0){
Cmn.battleMsg(myItem[sti].name+"を捨てた");
myItem[sti].setData(sitm);
return true;
}
else{
continue;
}
}
}
setItemの引き数(sitm)は、アイテム情報の文字列です。戻り値は、アイテムを「取得した」・「あきらめた」の判定する為のフラグです。を受け取ります。アイテムを取得した場合は、trueを返します。アイテムが一杯で取得しなかった場合はfalseを返します。
メソッドでは、最初にenpItemメソッドを実行して空の要素を探します。enpItemメソッドの戻り値が0以上ならItemクラスのsetDataメソッドにアイテム情報(sitm)を渡して実行します。後は、tureを返して終了します。
if((sti=enpItem())>=0){
myItem[sti].setData(sitm);
return true;
}
enpItemメソッドの戻り値が0以下(-1)の場合は、データが空の要素が無いので現在のアイテムを「捨てる」・「あきらめる」の判定をユーザーに問い合わせます。
//戦闘メッセージ中に描画するカーソルの座標
int mxp=Cmn.dmx+Cmn.cx;
int mxy=Cmn.dmy+Cmn.fnthgt+Cmn.cy;
「捨てる」・「あきらめる」の判定には、選択が必要になります。カーソルを描画して選択を行いますからメッセージに描画するカーソルの座標を設定します。次のwhileの中が選択処理です。
//アイテム満杯のメッセージの描画
Cmn.battleMsg("アイテムが一杯です。何か捨てますか?");
//選択を描画
Cmn.battleMsg(" あきらめる| 捨てる");
//選択制御
if(Cmn.chsCnt(mxp+5,mxy,0,2)==0){
Cmn.battleMsg(sitm.substring(0,sitm.indexOf(','))+"をあきらめた");
return false;
}
「アイテムが一杯」の旨のメッセージを描画して、選択肢を描画します。この時の選択肢は、「あきらめる」・「捨てる」の二つです。カーソルの描画用に、最初に全角スペースを入れています。選択肢の描画が出来たらCmnクラスのchsCntメソッドで選択の制御を行います。戻り値が0の場合は「あきらめる」、1の場合は「捨てる」が選択されたことになります。戻り値が0の場合は、アイテムをあきらめた旨のメッセージを描画してfalseを返します。戻り値が、0以外の場合は「捨てる」が選択されたことになります。捨てるアイテムを選んでもらわなければなりません。
//サブ一覧(道具一覧)を表示
if((sti=Cmn.subCnt(myItem,6))>5) continue;
CmnクラスのsubCntメソッドでアイテム一覧を表示して捨てるアイテムを選択してもらいます。アイテム一覧で「やめる」を選んだ場合は、ループの最初に制御を戻します。
//捨てるアイテムの確認メッセージ
Cmn.battleMsg(myItem[sti].name+"を捨てますか?");
Cmn.battleMsg(" はい| いいえ");
//0なら持ち物を捨てる、1(いいえなら、最初に戻る)
if(Cmn.chsCnt(mxp+5,mxy,0,2)==0){
Cmn.battleMsg(myItem[sti].name+"を捨てた");
myItem[sti].setData(sitm);
return true;
}
else{
continue;
}
捨てるアイテムが決まったら、再度、そのアイテムを捨ててよいか確認します。ここも、CmnクラスのchsCntメソッドで選択の制御を行います。戻り値が0の場合は、「はい」が選択されたことになりますからアイテムを捨てる胸のメッセージを描画して選択したmyItemの要素にアイテム情報を渡してデータを書き換えます。後は、trueを返して終わりです。もし、「いいえ」が選択された場合は繰り返し処理の最初に戻ります。
3.8 Heroクラス 武器・防具の装備
武器・防具の装備には、equipsItem メソッドを利用します。引き数で渡されたインデックスのmyItemの要素を種類によってmyItemの要素6・要素7に設定します。処理は単純で、現在の要素6(もしくは要素7)と引き数で指定された要素のデータを入れ替えているだけです。メソッドの内容を確認します。
//アイテムの装備
//装備アイテムのインデックス
public void equipsItem(int ino){
//武器・防具のインデックス
int ind;
//種類が1の場合は「武器」2は「防具」
if(myItem[ino].kind ==1){
//インデックス6に登録する
ind = 6;
}
else if(myItem[ino].kind ==2){
//インデックス7に登録する
ind = 7;
}
else{//武器防具以外の種類は装備できない
Cmn.battleMsg("装備できない");
return;
}
//アイテムを入れ替える為のワークインスタンス
Item workItem = new Item();
//現在の装備アイテムをワークインスタンスに一時退避
workItem.name = myItem[ind].name;//アイテム名
workItem.exposition=myItem[ind].exposition;//説明
workItem.kind = myItem[ind].kind;//アイテム種類
workItem.eftpnt = myItem[ind].eftpnt;//効果のポイント
workItem.price = myItem[ind].price;//金額
//武器・防具のインデックスに格納
myItem[ind].name = myItem[ino].name;//アイテム名
myItem[ind].exposition=myItem[ino].exposition;//説明
myItem[ind].kind = myItem[ino].kind;//アイテム種類
myItem[ind].eftpnt = myItem[ino].eftpnt;//効果のポイント
myItem[ind].price = myItem[ino].price;//金額
//送られてきたデータに入れ替えたデータを格納
myItem[ino].name = workItem.name;//アイテム名
myItem[ino].exposition= workItem.exposition;//説明
myItem[ino].kind = workItem.kind;//アイテム種類
myItem[ino].eftpnt = workItem.eftpnt;//効果のポイント
myItem[ino].price = workItem.price;//金額
}
最初に、防具か武器かの判別を行います。武器ならインデックス6の要素にデータを代入して、防具ならインデックス7の要素にデータを代入しなければなりません。どちらの要素に代入するかは、変数indにインデックスを格納します。もし、引き数で渡されたインデックスに対応するmyItemのデータが武器・防具以外の場合はメッセージを描画して処理を終了します。
//武器・防具のインデックス
int ind;
//種類が1の場合は「武器」2は「防具」
if(myItem[ino].kind ==1){
//インデックス6に登録する
ind = 6;
}
else if(myItem[ino].kind ==2){
//インデックス7に登録する
ind = 7;
}
else{//武器防具以外の種類は装備できない
Cmn.battleMsg("装備できない");
return;
}
武器・防具の判別が出来たら現在装備しているデータを一時退避します。退避用のItemクラスのインスタンスを生成してフィールドのデータを格納します。
//アイテムを入れ替える為のワークインスタンス
Item workItem = new Item();
//現在の装備アイテムをワークインスタンスに一時退避
workItem.name = myItem[ind].name;//アイテム名
workItem.exposition=myItem[ind].exposition;//説明
workItem.kind = myItem[ind].kind;//アイテム種類
workItem.eftpnt = myItem[ind].eftpnt;//効果のポイント
workItem.price = myItem[ind].price;//金額
現在のデータを退避したら引き数で渡されたインデックスのデータを判別した要素(武器・防具)に代入します。これで、指定された要素のアイテムが装備されました。
//武器・防具のインデックスに格納
myItem[ind].name = myItem[ino].name;//アイテム名
myItem[ind].exposition=myItem[ino].exposition;//説明
myItem[ind].kind = myItem[ino].kind;//アイテム種類
myItem[ind].eftpnt = myItem[ino].eftpnt;//効果のポイント
myItem[ind].price = myItem[ino].price;//金額
最後に、退避していたデータを引き数で指定されたインデックスの要素(装備したデータ)に代入します。これで、データの入れ替えが終了しました。
//送られてきたデータに入れ替えたデータを格納
myItem[ino].name = workItem.name; //アイテム名
myItem[ino].exposition= workItem.exposition;//説明
myItem[ino].kind = workItem.kind; //アイテム種類
myItem[ino].eftpnt = workItem.eftpnt;//効果のポイント
myItem[ino].price = workItem.price;//金額
以上がHeroクラスのメソッドです。これらのメソッドをHeroクラスに定義するか悩みました。繰り返しますが、携帯端末のヒープ領域は小さいのでインスタンスが大きくなる事は避けたいのです。製作者が最初に作成したプログラムでは、これらのメソッドも外部のクラスで操作しました。今回は、作り変えがしやすいようにHeroクラスに定義してみました。
3.9 AppBattleCnt ver1
基本データの作成も出来ました。どんな働きになるかサンプルプログラムを作成します。Hero・Monster・Itemクラスのソースファイルを「src」フォルダに保存します。次に、以下のソースファイルを作成して「src」フォルダに保存してください。Cmnクラスに、subCntメソッドの修正と追加メソッドがあります。変更、追加のメソッドに関しては太字で強調しています。初心者の方は、subCntメソッドの修正を修正点を見つけて修正を行うより、サンプルのように書き換えた方が良いかもしれません。
| Cmn.java |
| import com.nttdocomo.ui.*; public class Cmn{ ・・・・・省略・・・・・ //************************画面表示関係 メソッドデータ群 開始******************** ・・・・・省略・・・・・ //サブ選択の制御 //引数 Itemクラス(配列)、表示する項目のインデックス public static int subCnt(Item[] sub,int ce){ //エラーチェック if(sub.length<6 | ce>6) return -1; int ch=0; int xp=dsx; int yp=dsy+12; //道具で使用 インデックスを減算しながら次の使える道具のインデックスを探す //最初の要素も空白なら「やめる」に移動して処理を終える if(sub[ch].name.trim().equals("")){ while(sub[ch].name.trim().equals("")){ ch++; if(ch<0){ ch=6; break; } } } //表示する選択肢の数を0起算に変換 ce--; //グラフィックのロック g.lock(); //サブ選択ウィンドウの描画 drawSel(); //選択内容の描画 g.setColor(claWht); for(int i=0;i<=ce;i++) g.drawString(sub[i].name,xp+12,yp+(i*fnthgt)); g.drawString("やめる",xp+12,yp+(6*fnthgt)); //説明部の描画 if(ch<6) drawSub(sub[ch].name.trim(),sub[ch].exposition.trim()); else drawSub("",""); //選択カーソルの描画位置に更新 xp+=3; //選択カーソルの描画 csrCnt(xp,yp,ch,7); //グラフィックのロック解除 g.unlock(true); while(true){ switch(gKey()){ case Display.KEY_UP: ch--; if(ch<0){ ch=6; break; } else if(ch>ce){ ch=ce; break; } //道具で使用 インデックスを減算しながら次の使える道具のインデックスを探す //最初の要素も空白なら「やめる」に移動して処理を終える if(sub[ch].name.trim().equals("")){ while(sub[ch].name.trim().equals("")){ ch--; if(ch<0){ ch=6; break; } } } break; case Display.KEY_DOWN: ch++; //if(ch==6) break; if(ch>6){ ch=0; } else if(ch>ce){ ch=6; break; } //道具で使用 インデックスを加算しながら次の使える道具のインデックスを探す。 //「やめる」インデックスになったら加算処理を止める if(sub[ch].name.trim().equals("")){ while(sub[ch].name.trim().equals("")){ ch++; if(ch==6) break; } } break; case Display.KEY_SELECT: return ch; default: continue; } //グラフィックのロック g.lock(); //選択カーソルの描画 csrCnt(xp,yp,ch,7); //説明部の描画 if(ch<6) drawSub(sub[ch].name.trim(),sub[ch].exposition.trim()); else drawSub("",""); //グラフィックのロック解除 g.unlock(true); } } //---------------------------主人公ステータスの表示 開始----------------------------- public static void drawStatus(Hero shr){ int xp=dsx; int yp=dsy+12; //グラフィックのロック g.lock(); //ステータスエリアの描画 drawSel(); //グラフィックのロック g.setColor(claWht); //ステータスの描画 g.drawString("名前:"+shr.name,xp,yp+(0*fnthgt)); g.drawString("HP:"+Integer.toString(shr.hp),xp,yp+(1*fnthgt)); g.drawString("MP:"+Integer.toString(shr.mp),xp,yp+(2*fnthgt)); g.drawString("最大HP:"+Integer.toString(shr.mhp),xp,yp+(3*fnthgt)); g.drawString("最大MP:"+Integer.toString(shr.mmp),xp,yp+(4*fnthgt)); g.drawString("攻撃力:"+Integer.toString(shr.attack+shr.myItem[6].eftpnt),xp,yp+(5*fnthgt)); g.drawString("防御力:"+Integer.toString(shr.defense+shr.myItem[7].eftpnt),xp,yp+(6*fnthgt)); drawSub("武器:"+shr.myItem[6].name,"防具:"+shr.myItem[7].name); //グラフィックのロック解除 g.unlock(true); //ページ送りの待機 while(true) if(gKey()==Display.KEY_SELECT) break; //グラフィックのロック g.lock(); //ステータスエリアの描画 drawSel(); //グラフィックのロック g.setColor(claWht); //ステータスの描画 Cmn.g.drawString("レベル:"+Integer.toString(shr.lve),xp,yp+(0*fnthgt)); Cmn.g.drawString("次のレベル:"+Integer.toString(shr.nlve),xp,yp+(1*fnthgt)); Cmn.g.drawString("お金:"+Integer.toString(shr.maney),xp,yp+(2*fnthgt)); //グラフィックのロック解除 g.unlock(true); //ページ送りの待機 while(true) if(gKey()==Display.KEY_SELECT) break; } //---------------------------主人公ステータスの表示 終了----------------------------- //************************画面表示関係 メソッドデータ群 終了******************** //********************************************************************************** //* 共通処理 開始 * //********************************************************************************** ・・・・・省略・・・・・ //---------------------------文字列数値変換 開始------------------------------------- public static int cla(String str){ str=str.trim(); try{ if(str.indexOf('*')!=-1){ return Integer.parseInt(str.substring(0,str.indexOf('*')))*Integer.parseInt(str.substring(str.indexOf('*')+1)); } else if(str.indexOf('/')!=-1){ return Integer.parseInt(str.substring(0,str.indexOf('/')))/Integer.parseInt(str.substring(str.indexOf('/')+1)); } else if(str.indexOf('+')!=-1){ return Integer.parseInt(str.substring(0,str.indexOf('+')))+Integer.parseInt(str.substring(str.indexOf('+')+1)); } else if(str.indexOf('-')!=-1){ return Integer.parseInt(str.substring(0,str.indexOf('-')))-Integer.parseInt(str.substring(str.indexOf('-')+1)); } else{ return Integer.parseInt(str); } } catch (Exception e) { return 0; } } //---------------------------文字列数値変換 終了------------------------------------- //********************************************************************************** //* 共通処理 終了 * //********************************************************************************** } |
| MainCav.java |
| import com.nttdocomo.ui.*; public class MainCav extends Canvas{ //コンストラクタ public MainCav(){ //共通処理にデータを設定 Cmn.InitCmn(this); } //描画 public void paint(Graphics g) {} //プログラムの開始 public void stApp (){ String strTest; int i; Image resImage; MediaImage Mimage; //メディアイメージインターフェース型の変数宣言 //イメージの読み込み try{ Mimage = MediaManager.getImage("resource:///enemy1.gif"); Mimage.use(); resImage = Mimage.getImage(); }catch(Exception e){ System.out.println(e); System.out.println("例外が発生しました。処理を終了します。"); return; } Hero thisHero = new Hero(); Monster dbMonster = new Monster(); //主人公情報の設定 thisHero.name="権兵衛太郎大輔"; thisHero.hp=15; //HP thisHero.mhp=15; //マックスHP thisHero.mp=20; //MP thisHero.mmp=20; //マックスMP thisHero.attack=5; //攻撃力 thisHero.defense=15;//防御力 thisHero.maney=150; //お金 thisHero.expoint=0;//経験値 thisHero.lve=1; thisHero.nlve=10; thisHero.slv=3; for(i=0;i<=5;i++) thisHero.myItem[i].setData("薬草,傷を回復,30100010"); thisHero.myItem[3].setData("燃える草,燃えやすい草,40060010"); thisHero.myItem[4].setData("デバッグ剣,デバッグ用の剣,10200010"); thisHero.myItem[5].setData("デバッグ防具,デバッグ用の防具,20100010"); thisHero.myItem[6].setData("木の棒,落ちていた木の棒,10020010"); thisHero.myItem[7].setData("ただの服,普段着,20020010"); //モンスター情報の設定 dbMonster.setData("ツライム(01000200201000500101,薬草,傷を回復,30100010)"); dbMonster.image=resImage; while(true){ switch(Cmn.gKey()){ case 1: //デバッグ用 ヒーロー情報の表示 Cmn.drawStatus(thisHero); Cmn.cleFd(); //デバッグ用 ヒーロー所持アイテムの表示 Cmn.subCnt(thisHero.myItem,6); for(int tstin=0;tstin<=7;tstin++){ strTest="ヒーローアイテム情報"+Integer.toString(tstin)+"|名前:"+thisHero.myItem[tstin].name+"|アイテムの種類:"+Integer.toString(thisHero.myItem[tstin].kind)+"|効果のポイント:"+Integer.toString(thisHero.myItem[tstin].eftpnt)+"|金額:"+Integer.toString(thisHero.myItem[tstin].price); //Cmn.msgWrite(strTest,9,0,-2,118,0,0); Cmn.msgWrite(strTest,0,0,130,9,0,0); Cmn.tSlp(1000); Cmn.cleFd(); } //デバッグ用 モンスター情報の表示 strTest="モンスター情報|モンスター名:"+dbMonster.name+"|アイテムを落とす確率:"+Integer.toString(dbMonster.dip)+"|HP:"+Integer.toString(dbMonster.hp)+"|マックスHP:"+Integer.toString(dbMonster.mhp)+"|攻撃力:"+Integer.toString(dbMonster.attack)+"|防御力:"+Integer.toString(dbMonster.defense)+"|お金:"+Integer.toString(dbMonster.maney)+"|経験値:"+Integer.toString(dbMonster.expoint)+"|レベル:"+Integer.toString(dbMonster.lve)+"|アイテム情報:"+dbMonster.itmStr; Cmn.msgWrite(strTest,0,0,130,9,0,1); Cmn.cleFd(); continue; case 2: i=Cmn.subCnt(thisHero.myItem,6); thisHero.equipsItem(i); Cmn.drawStatus(thisHero); Cmn.cleFd(); continue; case 3: thisHero.setItem("薬草,傷を回復,30100010"); Cmn.drawStatus(thisHero); Cmn.cleFd(); continue; default: return; } } } } |
| App.java |
| import com.nttdocomo.ui.*; public class App extends IApplication { //アプリの開始 public void start(){ MainCav myObj=new MainCav(); Display.setCurrent(myObj); myObj.stApp(); } } |
srcの保存ファイル

Cmnクラスに追加したメソッドは、cla・drawStatusメソッドです。修正したメソッドは、subCnt(Item[] sub,int ce)メソッドです。subCntメソッドは、引き数が文字配列からItemクラスの配列に変わっています。その為、選択肢の表示文字がアイテムクラスのnameフィールドを参照するように変わっています。また、説明部の描画にはname・expositionフィールドの値を渡しています。
claメソッドは文字列の数値変換、drawStatusメソッドはキャラクター情報の描画です。
cla:文字列をint型に変換
drawStatus:主人公のステータスの描画
claメソッドから確認してみましょう。文字列から数値に変換するにはIntegerクラスのparseIntメソッドで変換が可能です。このメソッドもparseIntメソッドを利用しています。
public static int cla(String str){
str=str.trim();
try{
if(str.indexOf('*')!=-1){
return Integer.parseInt(str.substring(0,str.indexOf('*')))*Integer.parseInt(str.substring(str.indexOf('*')+1));
}
else if(str.indexOf('/')!=-1){
return Integer.parseInt(str.substring(0,str.indexOf('/')))/Integer.parseInt(str.substring(str.indexOf('/')+1));
}
else if(str.indexOf('+')!=-1){
return Integer.parseInt(str.substring(0,str.indexOf('+')))+Integer.parseInt(str.substring(str.indexOf('+')+1));
}
else if(str.indexOf('-')!=-1){
return Integer.parseInt(str.substring(0,str.indexOf('-')))-Integer.parseInt(str.substring(str.indexOf('-')+1));
}
else{
return Integer.parseInt(str);
}
} catch (Exception e) {
return 0;
}
}
メソッドでは、分岐処理が行われています。このメソッドは、文字列を数値変換します。そして、当メソッドは文字列に1つの算術演算子の記号を含む事が出来ます。分岐は、「*/+-」の算術演算子の記号が入っていないか判定する為の処理です。具体的には、「010*5」と言った文字列が渡されたとします。すると「*」を区切り文字として「010」と「5」に分解します。そして、「010」と「5」をparseIntメソッドで数値変換して乗算します。
除算を行う分岐のreturn命令を見てみましょう。以下のように長い命令になっています。
return Integer.parseInt(str.substring(0,str.indexOf('*')))*Integer.parseInt(str.substring(str.indexOf('*')+1));
一気に読み解く必要はありません。この命令をparseIntメソッドの範囲で分けてみましょう。以下の3つに分けられます。「*」は乗算の記号です。
処理単位
Integer.parseInt(str.substring(0,str.indexOf('*')))
*
Integer.parseInt(str.substring(str.indexOf('*')+1));
最初のparseIntメソッドの引き数には、「str.substring(0,str.indexOf('*'))」と文字列の最初のインデックスから「*」が出現するインデックスまでを切り出しています。「010*5」を例にすると「010」が切り出せます。是をparseIntメソッドで数値変換すると「10」というintデータが返って来ます。
次のparseIntメソッドの引き数には、「str.substring(str.indexOf('*')+1)」と「*」が出現するインデックスに1加算したインデックス以降の文字列を切り出しています。「010*5」を例にすると「5」が切り出せます。是をparseIntメソッドで数値変換すると「5」というintデータが返って来ます。
これらの値で「*」をはさんでいますからreturnでは、「10*5」の値が戻り値として返る事になります。処理を一気に書いているので見難いかもしれませんが理解できたでしょうか?。
claメソッドでは、例外処理として「数値変換できない文字列が入っていた場合」や「算術演算子が複数利用されていた場合」などを想定します。その場合は0を返します。
次にdrawStatusメソッドですが、このメソッドは主人公のステータスを描画するだけです。難しい処理は行っていません。主人公のステータスは、drawSelで表示エリアを描画します。サブ選択ウィンドウを利用しているのです。この描画の仕方は、好みによります。製作者は、サブ選択ウィンドウを利用しました。主人公のデータが描画できるならばこのメソッドの限りでは有りません。このメソッドは、処理も座標と文字列描画だけなので詳しい説明は省略させて頂きます。
MainCavクラスで、Heroクラス・Monsteerクラスの生成を行っています。Monsterクラスのイメージはリソースから取得しています。フィールドの設定では、基本データで説明した構成の文字列を各クラスのインスタンスに渡しています。ボタンを押すと、各データが表示されます。ボタンの処理は、以下の通りです。
1:Heroのステータス、Heroの所持アイテム情報、Monsterの情報
2:アイテムの装備
3:アイテムの収得
MainCavクラスの内容は、テスト用なので詳しく考える事はありません。各基本データに対応する文字列を渡すとフィールドが設定されている事を確認してください。後、Heroクラスの装備とアイテムの収得の処理を確認してください。ちゃんと動きますね。
3.10 AppBattleCnt ver2 概要
基本データが出来れば、後は戦闘の制御だけです。具体的な戦闘制御を行う前に、大まかな戦闘制御を行います。戦闘制御は、選択肢に合わせて処理を分岐するだけです。戦闘の制御を行うBattCntクラスを作成します。以下のソースファイルを作成して「src」フォルダに保存します。MainCavクラス・Cmnクラスにも変更があります。MainCavクラスは戦闘制御のテスト用に書き換えています。Cmnクラスにはフィールド・メソッドの追加、InitCmnメソッド(初期化処理)の修正があります。追加した部分は、太文字で強調してあります。
また、リソースのイメージが増えますので以下のイメージファイルを「res」フォルダに保存してください。小さいのと色が色に近いので水らいかもしれません。表題の下にあります。
追加リソース(下にあります)
![]()
| Cmn.java |
| import com.nttdocomo.ui.*; import java.util.*; public class Cmn{ //********************************************************************************** //* 画面表示関係 開始 * //********************************************************************************** //************************画面表示関係 フィールドデータ群 開始******************** ・・・・・省略・・・・・ //ダメージの画像 private static Image dmgimg; //************************画面表示関係 フィールドデータ群 終了******************** //****************************初期処理メソッド************************************** //初期処理 public static void InitCmn(Canvas maincvs,Image dmg){ c=maincvs; g=c.getGraphics(); g.setFont(font); //画面の描画座標の設定 int i; if((i=c.getWidth())>130){ cx=(i-130)/2; //戦闘画面の描画位置 bcx=cx+5; //bcx=(i-120)/2; } if((i=c.getHeight())>130){ cy=(i-130)/2; //戦闘画面の描画位置 bcy=cy+5; //bcy=(i-120)/2; } //戦闘画面の行動選択ウィンドウの描画位置 dcx=bcx+1; dcy=bcy+31; //戦闘画面の選択ウィンドウの描画位置 dsx=bcx; dsy=bcy; //戦闘画面のメッセージウィンドウの描画位置 dmx=bcx-cx+1; dmy=bcy-cy+89; //戦闘画面のHP/MPウィンドウの描画位置(上段への描画) hcx=bcx+1; hcy=bcy; //ダメージイメージの格納 dmgimg=dmg; } //********************************************************************************** //************************画面表示関係 メソッドデータ群 開始******************** ・・・・・省略・・・・・ //モンスターの表示 //引数 グラフィッククラス、描画座標 public static void drawMonster(Image sImage){ if(sImage==null) return; int xp=dcx+cWdh+((120-cWdh-sImage.getWidth())/2); int yp=bcy+((120-(fnthgt*2)-sImage.getHeight())/2); g.drawImage(sImage,xp,yp); } //モンスター攻撃時の物理攻撃ダメージ描画 //引数 public static void drawAttack(){ int xp=dcx+cWdh+((120-cWdh-dmgimg.getWidth())/2); int yp=bcy+((120-(fnthgt*2)-dmgimg.getHeight())/2); g.drawImage(dmgimg,xp,yp); tSlp(200); g.setColor(claBlk); g.fillRect(xp,yp,dmgimg.getWidth(),dmgimg.getHeight()); } //モンスター攻撃時の魔法攻撃ダメージ描画 //引数 public static void drawAttack(Image mimge){ //塗りつぶしエリア int iwdh=mimge.getWidth(); int ihgt=mimge.getHeight(); //塗りつぶし開始座標 int xp=dcx+cWdh+((120-cWdh-mimge.getWidth())/2); int yp=bcy+((120-(fnthgt*2)-mimge.getHeight())/2); //塗りつぶす色 g.setColor(claBlk); for(int i=1;i<=2;i++){ //drawMonster(mimge,6,0) g.drawImage(mimge,xp-(3*i),yp); tSlp(200); g.fillRect(xp-(3*i),yp,iwdh,ihgt); g.drawImage(mimge,xp+(3*i),yp); tSlp(200); g.fillRect(xp+(3*i),yp,iwdh,ihgt); } g.fillRect(xp,yp,iwdh,ihgt); g.drawImage(mimge,xp,yp); } //************************画面表示関係 メソッドデータ群 終了******************** //********************************************************************************** //* 画面表示関係 終了 * //********************************************************************************** ・・・・・省略・・・・・ } |
| BattCnt.java |
| import com.nttdocomo.ui.*; public class BattCnt{ private Hero mhr; private Monster mms; private int btslp=1000; //選択肢の配列 private String[] cstr={"戦う","特技","道具","逃走"}; //戦闘の制御 0:戦闘勝利、1:戦闘負け(ゲームオーバー) public int CntBattle(Hero sHero,Monster sMons){ //戦闘結果のインデックス 0:戦闘勝利、1:戦闘負け(ゲームオーバー) int jd=0; //HeroクラスとMonsterクラスのインスタンスを設定 mhr=sHero; mms=sMons; //行動選択のメッセージ String msg="どうする"; //メイン・サブの選択位置 int rw=0; //行動選択のインデックス int mc=0; //サブ選択のインデックス int sc=0; //行動選択ウィンドウのカーソル座標 int cxp=Cmn.dcx+3; int cyp=Cmn.dcy+13; //戦闘エリアの描画 cbCnv(mms.name+"が現れた"); //スリープ処理 Cmn.tSlp(1000); while(true){ //戦闘エリアの描画 cbCnv(msg); //選択ウィンドウの描画 switch (mc=Cmn.chsCnt(cxp,cyp,mc,4)){ case 0://戦闘 Cmn.drawAttack(); break; case 1://特技選択 if((sc=Cmn.subCnt(mhr.special,mhr.slv))>5) break; cbCnv(msg); Cmn.drawAttack(mms.image); mc=0; break; case 2://アイテム選択 if((sc=Cmn.subCnt(mhr.myItem,5))>5) break; cbCnv(msg); mc=0; break; case 3://逃げる選択 if(Cmn.rdInt(0)==0){ Cmn.battleMsg("逃げるッス!!"); } else{ Cmn.battleMsg("回り込まれた!!"); } mc=0; break; } } } //戦闘画面の描画 private void cbCnv(String msg){ //グラフィックのロック Cmn.g.lock(); Cmn.cleBf(); Cmn.drawChs(cstr); Cmn.drawMonster(mms.image); Cmn.battleMsg(msg); //ロック解除 Cmn.g.unlock(true); } } |
| MainCav.java |
| import com.nttdocomo.ui.*; public class MainCav extends Canvas{ //コンストラクタ public MainCav(){ Image img=new Image; MediaImage Mimage;//メディアイメージインターフェース型の変数宣言 //イメージの読み込み try{ Mimage = MediaManager.getImage("resource:///attack.gif"); Mimage.use(); img = Mimage.getImage(); }catch(Exception e){ System.out.println(e); System.out.println("例外が発生しました。処理を終了します。"); return; } //共通処理にデータを設定 Cmn.InitCmn(this,img); } //描画 public void paint(Graphics g) {} //プログラムの開始 public void stApp (){ int i; Image resImage; MediaImage Mimage;//メディアイメージインターフェース型の変数宣言 //イメージの読み込み try{ Mimage = MediaManager.getImage("resource:///enemy1.gif"); Mimage.use(); resImage = Mimage.getImage(); }catch(Exception e){ System.out.println(e); System.out.println("例外が発生しました。処理を終了します。"); return; } Hero thisHero = new Hero(); Monster dbMonster = new Monster(); //主人公情報の設定 thisHero.name="権兵衛太郎大輔"; thisHero.hp=15; //HP thisHero.mhp=15; //マックスHP thisHero.mp=20; //MP thisHero.mmp=20; //マックスMP thisHero.attack=5; //攻撃力 thisHero.defense=15;//防御力 thisHero.maney=150; //お金 thisHero.expoint=0;//経験値 thisHero.lve=1; thisHero.nlve=10; thisHero.slv=3; for(i=0;i<=5;i++) thisHero.myItem[i].setData("薬草,傷を回復,30100010"); thisHero.myItem[3].setData("燃える草,燃えやすい草,40060010"); thisHero.myItem[4].setData("デバッグ剣,デバッグ用の剣,10200010"); thisHero.myItem[5].setData("デバッグ防具,デバッグ用の防具,20100010"); thisHero.myItem[6].setData("木の棒,落ちていた木の棒,10020010"); thisHero.myItem[7].setData("ただの服,普段着,20020010"); //モンスター情報の設定 dbMonster.setData("ツライム(01000200201000500101,薬草,傷を回復,30100010)"); dbMonster.image=resImage; BattCnt myBattle=new BattCnt(); while(true){ switch(Cmn.gKey()){ case 1: myBattle.CntBattle(thisHero,dbMonster); continue; default: return; } } } } |
最初にCmnクラスの追加フィールド・メソッドの説明をします。追加されたフィールドは、Imageクラスのdmgimgフィールドです。モンスターへの攻撃のイメージを格納します。Imageを利用しますからjava.utilパッケージをインポートします。
import java.util.*;
//ダメージの画像
private static Image dmgimg;
dmgimgフィールドの追加に伴って、コンストラクタも修正してあります。引き数にdmgimgフィールド用のImageクラス(dmg)が追加されました。見落とさないようにしてください。
//初期処理
public static void InitCmn(Canvas maincvs,Image
dmg){
省略
//ダメージイメージの格納
dmgimg=dmg;
}
次に、追加されたメソッドはdrawMonster・drawAttack・drawAttack(引き数あり)のメソッドです。メソッドの内容は、以下の通りです。
drawMonster:モンスターイメージの描画
drawAttack:モンスターへの特技(道具)攻撃の効果を描画
drawAttack(引き数あり):モンスターへの通常攻撃の効果を描画
描画処理なので詳しい説明は、省きます。drawMonsterメソッドでは、行動選択ウィンドウと画面右端の中心にイメージが描画されるように座標を修正しています。drawAttackメソッドでは、画面にダメージのイメージ(dmgimgフィールド)を行動選択ウィンドウと画面右端の中心に描画します。drawAttack(引き数あり)メソッドは、モンスターイメージを引き数に渡します。渡されたイメージを左右に揺れる様に描画します。
次に、MainCavクラスの処理を説明します。コンストラクタでモンスターダメージのイメージをリソースから取得してCmnクラスのInitCmnメソッドに渡します。InitCmnメソッドの引き数が増えたのは、前述したとおりです。
プログラムの開始となるstAppメソッドでは、HeroクラスとMonsterクラスのインスタンスを生成しています。Monsterクラスが生成されるまでは、前回のサンプルと同じです。基本データの生成の後、戦闘制御を行うBattCntクラスのインスタンスを生成しています。
BattCnt myBattle=new BattCnt(thisHero,dbMonster);
BattCntクラスのコンストラクタには、HeroクラスとMonsterクラスを引き数として渡します。主人公とモンスターが戦うのですから戦う者同士の情報が必要ですね。
while(true){
switch(Cmn.gKey()){
case 1:
myBattle.CntBattle(thisHero,dbMonster);
continue;
default:
return;
}
}
次に、ループでBattCntクラスのCntBattleメソッドを実行します。CntBattleメソッドが戦闘を制御する為のメソッドです。戦闘が終了するまで制御が戻る事はありません。戻り値は、戦闘結果を表す値です(0:戦闘勝利、1:戦闘負け)。
CntBattleメソッド
int(戦闘結果)CntBattle(Heroクラス,Monsterクラス)
では、BattCntクラスの内容を確認しましょう。ソースの内容は、以下の通りです。
import com.nttdocomo.ui.*;
public class BattCnt{
private Hero mhr;
private Monster mms;
private int btslp=1000;
//選択肢の配列
private String[] cstr={"戦う","特技","道具","逃走"};
//戦闘の制御 0:戦闘勝利、1:戦闘負け(ゲームオーバー)
public int CntBattle(){
//戦闘結果のインデックス 0:戦闘勝利、1:戦闘負け(ゲームオーバー)
int jd=0;
//HeroクラスとMonsterクラスのインスタンスを設定
mhr=sHero;
mms=sMons;
//行動選択のメッセージ
String msg="どうする";
//メイン・サブの選択位置
int rw=0;
//行動選択のインデックス
int mc=0;
//サブ選択のインデックス
int sc=0;
//行動選択ウィンドウのカーソル座標
int cxp=Cmn.dcx+3;
int cyp=Cmn.dcy+13;
//戦闘エリアの描画
cbCnv(mms.name+"が現れた");
//スリープ処理
Cmn.tSlp(1000);
while(true){
//戦闘エリアの描画
cbCnv(msg);
//選択ウィンドウの描画
switch (mc=Cmn.chsCnt(cxp,cyp,mc,4)){
case
0://戦闘
Cmn.drawAttack();
break;
case
1://特技選択
if((sc=Cmn.subCnt(mhr.special,mhr.slv))>5)
break;
cbCnv(msg);
Cmn.drawAttack(mms.image);
mc=0;
break;
case
2://アイテム選択
if((sc=Cmn.subCnt(mhr.myItem,5))>5)
break;
cbCnv(msg);
mc=0;
break;
case
3://逃げる選択
if(Cmn.rdInt(0)==0){
Cmn.battleMsg("逃げるッス!!");
}
else{
Cmn.battleMsg("回り込まれた!!");
}
mc=0;
break;
}
}
}
//戦闘画面の描画
private void cbCnv(String msg){
//グラフィックのロック
Cmn.g.lock();
Cmn.cleBf();
Cmn.drawChs(cstr);
Cmn.drawMonster(mms.image);
Cmn.battleMsg(msg);
//ロック解除
Cmn.g.unlock(true);
}
}
フィールドには、Hero・Monsterクラスの参照が定義されています。戦闘を行うデータです。btslpフィールドは、スリープ時間を格納します。攻撃やメッセージの待機時間です。戦闘を早く進めたい場合は、待機時間を調整してください。あまりは少なくするとメッセージが読めなかったなどのトラブルに見舞われます。配列のフィールドは、描画する選択肢です。
private Hero mhr;
private Monster mms;
private int btslp=1000;
//選択肢の配列
private String[] cstr={"戦う","特技","道具","逃走"};
戦闘制御の本題に入る前にcbCnvメソッドを確認してください。内容は、以下の通りです。
//戦闘画面の描画
private void cbCnv(String msg){
//グラフィックのロック
Cmn.g.lock();
Cmn.cleBf();
Cmn.drawHpmp(mhr.hp,mhr.mp);
Cmn.drawChs(cstr);
Cmn.drawMonster(mms.image);
Cmn.battleMsg(msg);
//ロック解除
Cmn.g.unlock(true);
}
cbCnvメソッドは、戦闘画面を描画します。まず、戦闘画面のエリアを塗りつぶします。次に、HP/MPの描画、行動選択ウィンドウの描画、モンスターイメージの描画、戦闘メッセージの描画を行います。描画の際は、Graphicsクラスのlockメソッドで描画をロックします。そして、各ウィンドウの描画が終了したらロックを解除して一気に表示します。これで、画面のちらつきを防ぐ事が出来ます。引き数のString(msg)クラスは、メッセージウィンドウに描画する文字を渡します。最初は、モンスターの名前を渡します。次で、いよいよ、戦闘制御の本体であるCntBattleメソッドを説明します。
3.11 AppBattleCnt ver2 CntBattleメソッド
戦闘の制御を行うCntBattleメソッドの内容を以下に示します。このメソッドが、戦闘制御の開始となるメソッドです。戦闘が終了するまで制御が終わる事はありません。
//戦闘の制御 0:戦闘勝利、1:戦闘負け(ゲームオーバー)
public int CntBattle(){
//戦闘結果のインデックス 0:戦闘勝利、1:戦闘負け(ゲームオーバー)
int jd=0;
//HeroクラスとMonsterクラスのインスタンスを設定
mhr=sHero;
mms=sMons;
//行動選択のメッセージ
String msg="どうする";
//メイン・サブの選択位置
int rw=0;
//行動選択のインデックス
int mc=0;
//サブ選択のインデックス
int sc=0;
//行動選択ウィンドウのカーソル座標
int cxp=Cmn.dcx+3;
int cyp=Cmn.dcy+13;
//戦闘エリアの描画
cbCnv(mms.name+"が現れた");
//スリープ処理
Cmn.tSlp(1000);
while(true){
//戦闘エリアの描画
cbCnv(msg);
//選択ウィンドウの描画
switch (mc=Cmn.chsCnt(cxp,cyp,mc,4)){
case
0://戦闘
Cmn.drawAttack();
break;
case
1://特技選択
if((sc=Cmn.subCnt(mhr.special,mhr.slv))>5)
break;
cbCnv(msg);
Cmn.drawAttack(mms.image);
mc=0;
break;
case
2://アイテム選択
if((sc=Cmn.subCnt(mhr.myItem,5))>5)
break;
cbCnv(msg);
mc=0;
break;
case
3://逃げる選択
if(Cmn.rdInt(0)==0){
Cmn.battleMsg("逃げるッス!!");
}
else{
Cmn.battleMsg("回り込まれた!!");
}
mc=0;
break;
}
}
}
CntBattleメソッドが戦闘状態を制御します。今回は、具体的な処理は記述していません。骨組みとなる処理を記述しています。本来は、行動選択の選択肢によって対応する処理を行うメソッドを呼び出すことになります。このメソッドの戻り値は、0:戦闘勝利、1:戦闘負け(ゲームオーバー)となっています。
//戦闘結果のインデックス 0:戦闘勝利、1:戦闘負け(ゲームオーバー)
int jd=0;
//HeroクラスとMonsterクラスのインスタンスを設定
mhr=sHero;
mms=sMons;
//行動選択のメッセージ
String msg="どうする";
//行動選択のインデックス
int mc=0;
//サブ選択のインデックス
int sc=0;
最初に、制御に利用するデータの設定と変数の宣言を行っています。戦闘に利用するHeroクラスとMonsterクラスのインスタンスを設定します。変数jdは戦闘結果を格納します。変数msgは、戦闘の選択時のメッセージです。問題は、mc,scの変数です。mcには、行動選択の選択肢のインデックス。scは、サブ選択肢のインデックスを格納します。
戦闘画面では、行動選択ウィンドウで特技や道具を選択するとサブ選択ウィンドウに制御が切り替わります。例えば、特技を選んだとしましょう。操作では、行動選択ウィンドウの「特技」にカーソルをカーソルを当ててセレクトボタンを押します。この時、mcには「特技」を表す数値「1」が代入されます。次に、サブ選択画面が表示されて特技一覧が表示されます。ここで、なにかしら特技を選択した場合にmcの値は必要ありません。しかし、「やめる」と選んだ時、mcに行動選択の値を代入しておかないと選択カーソルが「戦闘」を描画する事になります。行動選択で「特技」を選んで特技一覧を表示して、キャンセルして行動選択に戻ったら「特技」カーソルが当たってないと変な気がしますよね。
//行動選択ウィンドウのカーソル座標
int cxp=Cmn.dcx+3;
int cyp=Cmn.dcy+Cmn.fnthgt;
変数cxp,cypには、行動選択のカーソルを描画する座標を設定します。カーソルで選択の制御を行うCmnクラスのchsCntメソッドに渡す座標です。
//戦闘エリアの描画
cbCnv(mms.name+"が現れた");
//スリープ処理
Cmn.tSlp(1000);
基本データの設定が出来たら戦闘画面を描画して、1秒待機します。この時、画面ではHP/MP・行動選択ウィンドウ・メッセージウィンドウ(「〜が現れた」と表示)・モンスターイメージが描画されています。
戦闘の行動選択を制御するのは、次のループ処理です。ループの最初では、cbCnvメソッドで戦闘画面の描画を行っています。メッセージウィンドウには「どうする?」と表示して選択を行うように伝えます。ただ、メッセージは好みの問題なので好きな言葉を利用して構いません。変える場合は、変数msgに代入する文字列を変えます。
while(true){
//戦闘エリアの描画
cbCnv(msg);
次のswicht文が行動選択によって処理を変える分岐です。chsCntは、カーソルを描画してくれます。デフォルトのカーソルの表示位置にはmcを設定します。最初のmcの値は0ですから「戦闘」にカーソルが当たって描画されます。また、chsCntで選択肢が選択されると変数mcに選択された選択肢に対応する値が代入されます。現在の選択肢の格納です。
//選択ウィンドウの描画
switch (mc=Cmn.chsCnt(cxp,cyp,mc,4)){
chsCntで選択された選択肢に対応する値が分かると後は、caseで処理を変えるだけです。0が通常攻撃、1が特技利用、2が道具利用、3が逃走です。以降、このケースに具体的な戦闘の処理を記述すれば完成です。今回は、行動選択の制御機能の仕組みで終わります。
ここで、特技選択・道具選択の処理を確認しましょう。特技選択・道具選択ともにsubCntメソッドを利用します。ここでは、特技選択を例に取ります。
case 1://特技選択
if((sc=Cmn.subCnt(mhr.special,mhr.slv))>5)
break;
cbCnv(msg);
Cmn.drawAttack(mms.image);
mc=0;
break;
最初に、subCntメソッドでサブ選択の制御を行います。サブ選択ウィンドウから選択肢が選択されると選択肢に対応する数値が返ります。5以下ならば、何らかの特技が選択されたわけですから問題はありません。CbCnvメソッドで戦闘画面を再描画します。ここでは、記述してありませんが戦闘処理を行ってmcを0にしてswitch文を抜けます。これで、ループの最初に戻って、次のchsCntメソッドの実行では「戦闘」が選択された状態で行動選択ウィンドウが描画されます。
しかし、サブ選択画面の選択肢が5より大きい場合は処理が違ってきます。5より多き場合は「やめる」が選択されていますからそのままswitch文を抜けます。mcの値は、初期化しません。mcには、特技に対応する値「1」が設定されたままです。ループでswitch文の先頭に処理が戻ります。「switch
(mc=Cmn.chsCnt(cxp,cyp,mc,4))」の処理です。mcが、1ですから特技にカーソルが当たった状態で行動選択ウィンドウが表示されます。次の項では、いよいよ具体的な戦闘の処理を作成していきます。
3.12 AppBattleCnt ver3 概要
戦闘処理を完成させます。その前に、戦闘の処理を確認しておきましょう。モンスターを攻撃すると、モンスターにダメージを与えます。モンスターを倒した場合は、お金と経験値、場合によってはアイテムが取得できます。モンスターを攻撃して、倒せないと反撃にあいます。反撃されて主人公のHPが0になったら処理は終了です(ゲームオーバー)。アイテムや特技を利用した時の仕組みも同じです。回復アイテムを利用した場合は、HPを回復後モンスターに攻撃されます。「逃走」の場合は、失敗した場合にモンスターの攻撃にあいます。処理毎の手順を単純に示すと以下の図のようになります。
| 戦闘(主人公勝利) | 戦闘(モンスターを倒せなかった場合) |
![]() |
![]() |
| アイテム・特技利用(主人公勝利) | アイテム・特技利用(モンスターを倒せなかった場合) |
![]() |
![]() |
| 逃走処理 | |
![]() |
では、具体的な処理を作成します。BattCntクラスの内容を以下のように変えてソースファイルを保存します。以下のソースファイルを作成して「src」フォルダに保存します。CntBattleメソッドの戦闘の分岐に具体的な処理を行う命令が記述されているのと、メソッドが追加されています。修正・追加した部分は、太文字で強調してあります。また、MainCavクラスもテスト用に変更してあります。
| BattCnt.java |
| import com.nttdocomo.ui.*; public class BattCnt{ private Hero mhr; private Monster mms; private int btslp=1000; //選択肢の配列 private String[] cstr={"戦う","特技","道具","逃走"}; //戦闘の制御 0:戦闘勝利、1:戦闘負け(ゲームオーバー) public int CntBattle(Hero sHero,Monster sMons){ //戦闘結果のインデックス 0:戦闘勝利、1:戦闘負け(ゲームオーバー) int jd=0; //HeroクラスとMonsterクラスのインスタンスを設定 mhr=sHero; mms=sMons; //選択時のメッセージ String msg="どうする"; //行動選択のインデックス int mc=0; //サブ選択のインデックス int sc=0; //行動選択ウィンドウのカーソル座標 int cxp=Cmn.dcx+3; int cyp=Cmn.dcy+Cmn.fnthgt; //戦闘エリアの描画 cbCnv(mms.name+"が現れた"); //スリープ処理 Cmn.tSlp(1000); while(true){ //戦闘エリアの描画 cbCnv(msg); //選択ウィンドウの描画 switch (mc=Cmn.chsCnt(cxp,cyp,mc,4)){ case 0://戦闘 if((jd=jadBattle(0))<2) return jd; break; case 1://特技選択 if((sc=Cmn.subCnt(mhr.special,mhr.slv))>5) break; cbCnv(msg); if((jd=useSb(sc,0))<2) return jd; mc=0; break; case 2://アイテム選択 if((sc=Cmn.subCnt(mhr.myItem,6))>5) break; cbCnv(msg); if((jd=useSb(sc,1))<2) return jd; mc=0; break; case 3://逃げる選択 if((jd=jdEsc())<2) return jd; mc=0; break; } } } //戦闘画面の描画 private void cbCnv(String msg){ //グラフィックのロック Cmn.g.lock(); Cmn.cleBf(); Cmn.drawHpmp(mhr.hp,mhr.mp); Cmn.drawChs(cstr); Cmn.drawMonster(mms.image); Cmn.battleMsg(msg); //ロック解除 Cmn.g.unlock(true); } //戦闘を実行 戦闘勝利:0、負け:1、戦闘を継続:2 //引き数 与えるダメージの値 public int jadBattle(int a){ //与えるダメージの値を格納 int dm; //0の場合は、通常攻撃のダメージなので計算を行う if(a<=0){ dm=((mhr.attack+mhr.myItem[6].eftpnt)-mms.defense); if(dm<0) dm=0; Cmn.battleMsg(mhr.name+"の攻撃"); Cmn.drawAttack(); Cmn.drawMonster(mms.image); } else{//道具を利用した場合のダメージ dm=a; Cmn.drawAttack(mms.image); } //ダメージを与えたメッセージの描画 Cmn.battleMsg(mms.name+"に|"+Integer.toString(dm)+"のダメージを与えた"); //モンスターの残りHPの計算 mms.hp=mms.hp-dm; //モンスターのHPが0以上の場合は、モンスターの攻撃後メソッド終了 if(mms.hp>0) return mAttack(); //モンスターを倒した場合の処理(モンスターのHPが0以下) //モンスターのヒットポイントを元に戻す mms.hp=mms.mhp; //モンスター退治のメッセージを描画 Cmn.battleMsg(mms.name+"を退治した|"+Integer.toString(mms.expoint)+"の経験値を得た"); //モンスターの所持するお金を主人公に加算 mhr.maney+=mms.maney; //所持金が最高額を超えたら修正 if(mhr.maney>9999) mhr.maney=9999; //経験値の加算ループ for(int i=1;i<=mms.expoint;i++){ //主人公の経験値が設定可能レベルと超えていた場合は処理しない if(mhr.expoint>9999) mhr.expoint=9999; //経験値を加算 mhr.expoint+=1; //レベルアップの判定 if(mhr.expoint>=mhr.nlve){ //レベルアップのメッセージ描画 Cmn.battleMsg(mhr.name+"はレベルが上がった"); //レベルアップ処理 mhr.upLev(); } } //アイテム取得の判定 jdDitem(); return 0; } //アイテムを落としたか判定 private void jdDitem(){ if(mms.dip<=1 || Cmn.rdInt(mms.dip)==0){ Cmn.battleMsg("モンスターは、"+mms.itmStr.substring(0,mms.itmStr.indexOf(','))+"を落とした"); mhr.setItem(mms.itmStr); } } //モンスターの攻撃判定 //(主人公が)負け:1、戦闘を継続:2 public int mAttack(){ //主人公に与えるダメージを算出 int dm=(mms.attack-(mhr.defense+mhr.myItem[7].eftpnt)); //攻撃のメッセージ Cmn.battleMsg(mms.name+"の攻撃"); //ダメージの描画 Cmn.drawDamagi(); //スリープ処理 Cmn.tSlp(300); //HP/MPの描画 Cmn.drawHpmp(mhr.hp,mhr.mp); //ダメージが無い場合、メソッド終了 if(dm<=0){ //攻撃の無効のメッセージを描画 Cmn.battleMsg("痛くなかった!!"); //戦闘継続の値(2)を返してメソッド終了 return 2; } //ダメージがある場合、ダメージのメッセージ描画 Cmn.battleMsg(Integer.toString(dm)+"のダメージを受けた"); //主人公の残りHPを算出 if((mhr.hp=mhr.hp-dm)<0) mhr.hp=0; //HP/MPの再描画 Cmn.drawHpmp(mhr.hp,mhr.mp); //主人公のヒットポイントが残っていれば、戦闘継続の値(2)を返してメソッド終了 if(mhr.hp>0) return 2; //主人公のHPが残っていない場合は、メッセージ描画後 //負けの値(3)を返してメソッド終了 Cmn.battleMsg("がはっ・・・|やられたっス・・・"); //モンスターのヒットポイントを元に戻す mms.hp=mms.mhp; return 1; } //戻り値は攻撃時と同じ。ただし、特技・道具が使えなかった場合は3を返す //引き数 利用する特技・道具のインデックス、種類 //fが1ならアイテム、2なら特技 private int useSb(int a,int f){ //kが種類を格納、pが効果の値を格納 int k,p; //表示メッセージを格納 String tmsg; if(f==0){//「特技」の処理 //残りのMPが消費MPを下回る場合は、終了 if(mhr.special[a].price>mhr.mp){ Cmn.battleMsg("MPが足りないっス"); return 3; } //特技名・種類・効果のポイントを格納 tmsg=mhr.special[a].name; k=mhr.special[a].kind; p=mhr.special[a].eftpnt; } else{//「道具」の処理 //アイテム名・種類・効果のポイントを格納 tmsg=mhr.myItem[a].name; k=mhr.myItem[a].kind; p=mhr.myItem[a].eftpnt; } //道具・特技を使った旨のメッセージを作成 tmsg+="を使った|"; switch (k){ case 3://回復 //HPが満杯の場合、 if(mhr.mhp==mhr.hp){ Cmn.battleMsg("回復の必要は無いっス"); return 3; } //HPを回復、回復後のHPが最大HPを越えたら修正 if((mhr.hp+=p)>mhr.mhp) mhr.hp=mhr.mhp; //道具。特技の利用メッセージを描画 Cmn.battleMsg(tmsg); //効果のメッセージを描画 Cmn.battleMsg(Integer.toString(p)+"回復した"); //消費の処理 if(f==0){//特技を使った場合 //MPの減算 mhr.mp-=mhr.special[a].price; } else{//道具を利用した場合 //利用アイテムの削除 mhr.lostItem(a); } //HP/MPの再描画 Cmn.drawHpmp(mhr.hp,mhr.mp); return mAttack(); case 4://攻撃 Cmn.battleMsg(tmsg); if(f==0){ //MPの減算 mhr.mp-=mhr.special[a].price; //HP/MPの再描画 Cmn.drawHpmp(mhr.hp,mhr.mp); } else{ //利用アイテムの削除 mhr.lostItem(a); } return jadBattle(p); default: Cmn.battleMsg("戦闘では、使えないっス"); return 3; } } //逃げ切れたかの判定 private int jdEsc(){ if(mms.lve-mhr.lve<=1 || Cmn.rdInt(mms.lve-mhr.lve)==0){ Cmn.battleMsg("逃げるっス!"); mms.hp=mms.mhp; return 0; } Cmn.battleMsg("まわりこまれた!"); return mAttack(); } } |
| MainCav.java |
| import com.nttdocomo.ui.*; public class MainCav extends Canvas{ //コンストラクタ public MainCav(){ Image img; MediaImage Mimage; //メディアイメージインターフェース型の変数宣言 //イメージの読み込み try{ Mimage = MediaManager.getImage("resource:///attack.gif"); Mimage.use(); img = Mimage.getImage(); }catch(Exception e){ System.out.println(e); System.out.println("例外が発生しました。処理を終了します。"); return; } //共通処理にデータを設定 Cmn.InitCmn(this,img); } //描画 public void paint(Graphics g) {} //プログラムの開始 public void stApp (){ String strTest; int i; Image resImage; MediaImage Mimage; //メディアイメージインターフェース型の変数宣言 //イメージの読み込み try{ Mimage = MediaManager.getImage("resource:///enemy1.gif"); Mimage.use(); resImage = Mimage.getImage(); }catch(Exception e){ System.out.println(e); System.out.println("例外が発生しました。処理を終了します。"); return; } Hero thisHero = new Hero(); Monster dbMonster = new Monster(); //主人公情報の設定 thisHero.name="権兵衛太郎大輔"; thisHero.hp=15; //HP thisHero.mhp=15; //マックスHP thisHero.mp=20; //MP thisHero.mmp=20; //マックスMP thisHero.attack=5; //攻撃力 thisHero.defense=15;//防御力 thisHero.maney=150; //お金 thisHero.expoint=0;//経験値 thisHero.lve=1; thisHero.nlve=10; thisHero.slv=3; for(i=0;i<=5;i++) thisHero.myItem[i].setData("薬草,傷を回復,30100010"); thisHero.myItem[4].setData("燃える草,燃えやすい草,40060010"); thisHero.myItem[3].setData("デバッグ剣,デバッグ用の剣,10200010"); thisHero.myItem[6].setData("木の棒,落ちていた木の棒,10020010"); thisHero.myItem[7].setData("ただの服,普段着,20020010"); //モンスター情報の設定 dbMonster.setData("ツライム(01000200201000500101,デバッグ防具,デバッグ用の防具,20100010)"); dbMonster.image=resImage; BattCnt myBattle=new BattCnt(); while(true){ switch(Cmn.gKey()){ case 1: myBattle.CntBattle(thisHero,dbMonster); Cmn.cleFd(); continue; case 2: i=Cmn.subCnt(thisHero.myItem,6); thisHero.equipsItem(i); Cmn.drawStatus(thisHero); Cmn.cleFd(); continue; case 3: Cmn.drawStatus(thisHero); Cmn.cleFd(); continue; default: return; } } } } |
追加したメソッドの大まかな機能は以下のようになります。これらのメソッドを利用して戦闘処理がおこなわれます。
jadBattle:モンスターの攻撃処理
jdDitem:アイテム拾得の処理
mAttack:モンスター攻撃
useSb:アイテム利用の処理
jdEsc:逃走の判定
次から戦闘処理のケースに分けてメソッドの内容を確認していきます。
3.13 AppBattleCnt ver3 通常攻撃(モンスターの反撃無し)
通常攻撃の処理を説明します。通常攻撃は、行動選択の制御のcase
0になります。処理は、jadBattleメソッドを呼び出しているだけです。戻り値が2より小さい場合はreturnでメソッドを終了しています。
case 0://戦闘
if((jd=jadBattle(0))<2) return jd;
break;
jadBattleメソッドの戻り値は、戦闘の結果を返します。0が勝利、1が負け、2が戦闘の継続です。つまり、2より小さければ戦闘処理を終了します。CntBattleの戻り値に、jadBattleメソッドの戻り値を返せばゲームを継続させるか、ゲームオーバーの処理をするか利用する側にも判定が出来ます。jadBattleメソッドの中には、モンスターを倒せなかった場合のモンスターの反撃を処理するmAttackメソッド、モンスターを倒した場合のアイテム拾得処理のjdDitemメソッドを内部で利用します。
jadBattleメソッド
int(戦闘結果) jadBattle(通常攻撃呼び出し、特技での呼び出しフラグ)
jadBattleメソッドの処理内容を確認しましょう。
//戦闘を実行 戦闘勝利:0、負け:1、戦闘を継続:2
//引き数 与えるダメージの値
public int jadBattle(int a){
//与えるダメージの値を格納
int dm;
//0の場合は、通常攻撃のダメージなので計算を行う
if(a<=0){
dm=((mhr.attack+mhr.myItem[6].eftpnt)-mms.defense);
if(dm<0)
dm=0;
Cmn.battleMsg(mhr.name+"の攻撃");
Cmn.drawAttack();
Cmn.drawMonster(mms.image);
}
else{//道具を利用した場合のダメージ
dm=a;
Cmn.drawAttack(mms.image);
}
//ダメージを与えたメッセージの描画
Cmn.battleMsg(mms.name+"に|"+Integer.toString(dm)+"のダメージを与えた");
//モンスターの残りHPの計算
mms.hp=mms.hp-dm;
//モンスターのHPが0以上の場合は、モンスターの攻撃後メソッド終了
if(mms.hp>0) return
mAttack();
//モンスターを倒した場合の処理(モンスターのHPが0以下)
//モンスターのヒットポイントを元に戻す
mms.hp=mms.mhp;
//モンスター退治のメッセージを描画
Cmn.battleMsg(mms.name+"を退治した|"+Integer.toString(mms.expoint)+"の経験値を得た");
//モンスターの所持するお金を主人公に加算
mhr.maney+=mms.maney;
//所持金が最高額を超えたら修正
if(mhr.maney>9999)
mhr.maney=9999;
//経験値の加算ループ
for(int i=1;i<=mms.expoint;i++){
//主人公の経験値が設定可能レベルと超えていた場合は処理しない
if(mhr.expoint>9999)
mhr.expoint=9999;
//経験値を加算
mhr.expoint+=1;
//レベルアップの判定
if(mhr.expoint>=mhr.nlve){
//レベルアップのメッセージ描画
Cmn.battleMsg(mhr.name+"はレベルが上がった");
//レベルアップ処理
mhr.upLev();
}
}
jadBattleメソッドは、戦闘以外にも攻撃用の特技や道具を利用した場合も利用します。引き数は、通常攻撃で呼び出されたか、特技・道具で呼び出されたかの判定に利用します。引き数の値が0の場合は通常攻撃扱いになります。
public int jadBattle(int a){
モンスターに与えるダメージは、dm変数に格納します。
//与えるダメージの値を格納
int dm;
変数の宣言の後は、モンスターに与えるダメージの算出を行います。通常攻撃の場合は、引き数aが0以下の処理の場合です。
if(a<=0){
dm=((mhr.attack+mhr.myItem[6].eftpnt)-mms.defense);
if(dm<0) dm=0;
Cmn.battleMsg(mhr.name+"の攻撃");
Cmn.drawAttack();
Cmn.drawMonster(mms.image);
}
ダメージには、Heroクラスのattackフィールドの値と装備武器の効果のポイントを加算します。この値が攻撃力になります。算出した攻撃力からモンスターの防御力(defense)を減算した値が与えるダメージになります。
攻撃力
計算式:(攻撃力+武器の威力)-モンスターの防御力
具体的な命令:mhr.attack+mhr.myItem[6].eftpnt)-mms.defense
ダメージは、場合によって0以下になる可能性があります。0以下の場合は、与えるダメージの値を0にします。後は、攻撃する旨のメッセージ描画・ダメージの描画・モンスターイメージの再描画を行います。
elseの特技で呼び出された処理は単純で引き数aの値をdm変数に代入するだけです。後は、特技のダメージを描画します。
else{//道具を利用した場合のダメージ
dm=a;
Cmn.drawAttack(mms.image);
}
ダメージの算出とダメージ効果の描画が終わると、ダメージを与えた旨のメッセージを描画します。そして、モンスターのHPをダメージ(dm)の値分減算します。
//ダメージを与えたメッセージの描画
Cmn.battleMsg(mms.name+"に|"+Integer.toString(dm)+"のダメージを与えた");
//モンスターの残りHPの計算
mms.hp=mms.hp-dm;
ここまでが、攻撃の処理です。攻撃後、モンスタークラスのHPを調べます。0以下になれば、モンスターを倒した事になります。
//モンスターのHPが0以上の場合は、モンスターの攻撃後メソッド終了
if(mms.hp>0) return mAttack();
モンスタークラスのHPが0より大きい場合は、モンスターの攻撃mAttackメソッドの処理になりますが今回はモンスターを倒した場合の処理です。モンスターのHPは、0以下なのでmAttackメソッドは実行されません。次の処理に移ります。
mms.hp=mms.mhp;
//モンスター退治のメッセージを描画
Cmn.battleMsg(mms.name+"を退治した|"+Integer.toString(mms.expoint)+"の経験値を得た");
//モンスターの所持するお金を主人公に加算
mhr.maney+=mms.maney;
//所持金が最高額を超えたら修正
if(mhr.maney>9999) mhr.maney=9999;
モンスターを倒したらモンスタークラスのHPを元に戻します。次に遭遇する時にHPが0のままではまずい事になります。次に、モンスターを倒した旨のメッセージを描画します。そして、モンスターの持っているお金をHeroクラスに加算します。但し、所持できるお金は9999までですので9999を超えた場合は9999を代入します。次に、経験値の加算を行います。
//経験値の加算ループ
for(int i=1;i<=mms.expoint;i++){
//主人公の経験値が設定可能レベルと超えていた場合は処理しない
if(mhr.expoint>9999) mhr.expoint=9999;
//経験値を加算
mhr.expoint+=1;
//レベルアップの判定
if(mhr.expoint>=mhr.nlve){
//レベルアップのメッセージ描画
Cmn.battleMsg(mhr.name+"はレベルが上がった");
//レベルアップ処理
mhr.upLev();
}
}
次に経験値の取得ですが、ループの処理で1づつ加算していきます。ループの中では、経験値が次のレベルの経験値になるとレベルアップ処理を行っています(mhr.expoint>=mhr.nlve)。レベルアップの処理は、HeroクラスのupLevメソッドを利用します。なぜ、ループで1づつ加算しているのかと言うと高レベルの敵を倒してレベルが2段階など複数レベルアップがある場合を考慮しての処理です。
レベルアップの処理が終わると、アイテム取得の判定を行って0を返してメソッドを終了します。0は、モンスターを倒した場合の戻り値です。
//アイテム取得の判定
jdDitem();
return 0;
ここで、jdDitemメソッドを内容を確認しましょう。処理は、単純です。
//アイテムを落としたか判定
private void jdDitem(){
if(mms.dip<=1 || Cmn.rdInt(mms.dip)==0){
Cmn.battleMsg("モンスターは、"+mms.itmStr.substring(0,mms.itmStr.indexOf(','))+"を落とした");
mhr.setItem(mms.itmStr);
}
}
モンスターのdipが1の場合は、必ずアイテムを落とす扱いになります。「||」で比較すると左辺の式がtrueの場合は、右辺は演算される事無くtrueの評価になります。dipが1で無い場合は、乱数を求めるメソッドで乱数を取得します。乱数が0の場合はアイテムを落とした事になります。つまり、この式ではdipの値が大きいほどアイテムが拾得できる確立が低くなります。アイテムを取得できたらHeroクラスのsetItemメソッドにアイテム情報を渡して処理を任せます。以上が、モンスターを倒せた場合の処理です。処理的には難しくないでしょう。メッセージやダメージの描画が入るのでややこしく感じるかもしれません。
3.14 AppBattleCnt ver3 通常攻撃(モンスターの反撃有り)
戦闘でモンスターの反撃があった場合の処理を説明します。jadBattleメソッドの処理でモンスターのHPが残っていた場合の処理です。
jadBattleメソッドの制御
if(mms.hp>0) return mAttack();
モンスターのHPが残っていた場合は、mAttackメソッドが実行されます。
mAttackメソッド
int(戦闘結果) mAttack(引き数 なし)
mAttackメソッドの内容を確認しましょう。
//モンスターの攻撃判定
//(主人公が)負け:1、戦闘を継続:2
public int mAttack(){
//主人公に与えるダメージを算出
int dm=(mms.attack-(mhr.defense+mhr.myItem[7].eftpnt));
//攻撃のメッセージ
Cmn.battleMsg(mms.name+"の攻撃");
//ダメージの描画
Cmn.drawDamagi();
//スリープ処理
Cmn.tSlp(300);
//HP/MPの描画
Cmn.drawHpmp(mhr.hp,mhr.mp);
//ダメージが無い場合、メソッド終了
if(dm<=0){
//攻撃の無効のメッセージを描画
Cmn.battleMsg("痛くなかった!!");
//戦闘継続の値(2)を返してメソッド終了
return 2;
}
//ダメージがある場合、ダメージのメッセージ描画
Cmn.battleMsg(Integer.toString(dm)+"のダメージを受けた");
//主人公の残りHPを算出
if((mhr.hp=mhr.hp-dm)<0)
mhr.hp=0;
//HP/MPの再描画
Cmn.drawHpmp(mhr.hp,mhr.mp);
//主人公のヒットポイントが残っていれば、戦闘継続の値(2)を返してメソッド終了
if(mhr.hp>0) return
2;
//主人公のHPが残っていない場合は、メッセージ描画後
//負けの値(3)を返してメソッド終了
Cmn.battleMsg("がはっ・・・|やられたっス・・・");
//モンスターのヒットポイントを元に戻す
mms.hp=mms.mhp;
return 1;
}
mAttackメソッドの戻り値は、1:主人公がやられた・2戦闘の継続です。
public int mAttack(){
最初に主人公に与えるダメージを算出します。変数dmに、モンスター主人公に与えるダメージを格納します。
//主人公に与えるダメージを算出
int dm=(mms.attack-(mhr.defense+mhr.myItem[7].eftpnt));
計算式:モンスターの攻撃力-(防御力+防具の効果)モンスターの防御力
ダメージが算出できたら、描画処理を行います。
//攻撃のメッセージ
Cmn.battleMsg(mms.name+"の攻撃");
//ダメージの描画
Cmn.drawDamagi();
//スリープ処理
Cmn.tSlp(300);
//HP/MPの描画
Cmn.drawHpmp(mhr.hp,mhr.mp);
描画処理は、モンスターが攻撃する旨のメッセージ・主人公のダメージ・HP/MPの描画です。特に問題は無いでしょう。次に、分岐処理があります。
//ダメージが無い場合、メソッド終了
if(dm<=0){
//攻撃の無効のメッセージを描画
Cmn.battleMsg("痛くなかった!!");
//戦闘継続の値(2)を返してメソッド終了
return 2;
}
モンスターのダメージも0以下の場合があります。弱いモンスターに攻撃を受けてもダメージは受けません。その場合の処理としては、dmが0以下の場合はメッセージを描画して2(戦闘継続)を返してメソッドを終了します。jadBattleメソッドでは、returnにmAttack
メソッドを指定していますからjadBattleメソッドの戻り値も2となります。
//ダメージがある場合、ダメージのメッセージ描画
Cmn.battleMsg(Integer.toString(dm)+"のダメージを受けた");
//主人公の残りHPを算出
if((mhr.hp=mhr.hp-dm)<0) mhr.hp=0;
//HP/MPの再描画
Cmn.drawHpmp(mhr.hp,mhr.mp);
dmが、0以上の場合は主人公はダメージを受けます。Heroクラスのhpをdmで減算します。もし、0以下になったらhpに0を代入します。これは、HP/MPの描画に-(マイナス)の数値が表示させないようにする為の処理です。処理が終わると、HP/MPを描画します。
//主人公のヒットポイントが残っていれば、戦闘継続の値(2)を返してメソッド終了
if(mhr.hp>0) return 2;
主人公のHPが0より大きい場合は、2(戦闘の継続)を返してメソッドを終了します。0以下の場合は、次の処理に移ります。
//負けの値(3)を返してメソッド終了
Cmn.battleMsg("がはっ・・・|やられたっス・・・");
//モンスターのヒットポイントを元に戻す
mms.hp=mms.mhp;
return 1;
主人公のHPが0以下の場合は、主人公がやられた場合です。メッセージを描画します。モンスターのHPも初期状態に戻します。跡は、1(主人公がやられた)を返してメソッドを終了します。注意するところは、モンスターのHPの初期化を忘れないようにしてください。以上が、モンスターの攻撃処理です。
3.15 AppBattleCnt ver3 特技・道具の利用
戦闘で特技・道具を利用した場合の処理を説明します。特技・道具を利用した後は、モンスターを攻撃・モンスターの攻撃が発生します。つまり、アイテムを利用した後、jadBattleメソッドを呼び出します。回復の場合は、回復処理の後、mAttackメソッドを呼び出します。処理的には、特技の場合はMPを減算して道具の場合は利用したアイテムを削除します。回復・攻撃の種類のよる制御や戦闘で使えない種類の制御などが発生しますが難しい処理はしていません。
CntBattleメソッドの選択肢の制御で、特技・道具を利用した処理を行っている制御はcase
1とcase 2の処理です。基本的な内容は変わりません。subCntメソッドで利用する特技・道具のインデックスを取得してuseSbメソッドに渡します。後は、useSbの戻り値を判定するだけです。
case 1://特技選択
if((sc=Cmn.subCnt(mhr.special,mhr.slv))>5)
break;
cbCnv(msg);
if((jd=useSb(sc,0))<2) return jd;
mc=0;
break;
case 2://アイテム選択
if((sc=Cmn.subCnt(mhr.myItem,6))>5)
break;
cbCnv(msg);
if((jd=useSb(sc,1))<2) return jd;
mc=0;
break;
CntBattleメソッドの選択肢の制御から分かるように、実際に特技・道具の制御を行うのはuseSbメソッドです。
useSbメソッド
int(戦闘の結果) useSb (選択肢のインデックス,特技・道具の判別フラグ)
useSbメソッドの処理の内容を確認しましょう。
//戻り値は攻撃時と同じ。ただし、特技・道具が使えなかった場合は3を返す
//引き数 利用する特技・道具のインデックス、種類
//fが1ならアイテム、2なら特技
private int useSb(int a,int f){
//kが種類を格納、pが効果の値を格納
int k,p;
//表示メッセージを格納
String tmsg;
if(f==0){//「特技」の処理
//残りのMPが消費MPを下回る場合は、終了
if(mhr.special[a].price>mhr.mp){
Cmn.battleMsg("MPが足りないっス");
return
3;
}
//特技名・種類・効果のポイントを格納
tmsg=mhr.special[a].name;
k=mhr.special[a].kind;
p=mhr.special[a].eftpnt;
}
else{//「道具」の処理
//アイテム名・種類・効果のポイントを格納
tmsg=mhr.myItem[a].name;
k=mhr.myItem[a].kind;
p=mhr.myItem[a].eftpnt;
}
//道具・特技を使った旨のメッセージを作成
tmsg+="を使った|";
switch (k){
case 3://回復
//HPが満杯の場合、
if(mhr.mhp==mhr.hp){
Cmn.battleMsg("回復の必要は無いっス");
return
3;
}
//HPを回復、回復後のHPが最大HPを越えたら修正
if((mhr.hp+=p)>mhr.mhp)
mhr.hp=mhr.mhp;
//道具。特技の利用メッセージを描画
Cmn.battleMsg(tmsg);
//効果のメッセージを描画
Cmn.battleMsg(Integer.toString(p)+"回復した");
//消費の処理
if(f==0){//特技を使った場合
//MPの減算
mhr.mp-=mhr.special[a].price;
}
else{//道具を利用した場合
//利用アイテムの削除
mhr.lostItem(a);
}
//HP/MPの再描画
Cmn.drawHpmp(mhr.hp,mhr.mp);
return
mAttack();
case 4://攻撃
Cmn.battleMsg(tmsg);
if(f==0){
//MPの減算
mhr.mp-=mhr.special[a].price;
//HP/MPの再描画
Cmn.drawHpmp(mhr.hp,mhr.mp);
}
else{
//利用アイテムの削除
mhr.lostItem(a);
}
return
jadBattle(p);
default:
Cmn.battleMsg("戦闘では、使えないっス");
return
3;
}
}
useSbメソッドの戻り値は、0:戦闘勝利、1:戦闘負け(ゲームオーバー)、2:戦闘の継続です。内部で、jadBattleメソッド・mAttackメソッドを利用しますからこれらのメソッドに対応した戻り値になります。
private int useSb(int a,int f){
引き数には、利用する特技・道具のインデックス、特技・道具の判別フラグを指定します。判別フラグは2が特技で1が道具利用です。
//kが種類を格納、pが効果の値を格納
int k,p;
//表示メッセージを格納
String tmsg;
内部変数には、特技・アイテムの種類と効果を格納する変数、メッセージを格納する変数を宣言します。変数宣言後の分岐処理が特技・道具の違いによる制御です。
if(f==0){//「特技」の処理
//残りのMPが消費MPを下回る場合は、終了
if(mhr.special[a].price>mhr.mp){
Cmn.battleMsg("MPが足りないっス");
return 3;
}
//特技名・種類・効果のポイントを格納
tmsg=mhr.special[a].name;
k=mhr.special[a].kind;
p=mhr.special[a].eftpnt;
}
else{//「道具」の処理
//アイテム名・種類・効果のポイントを格納
tmsg=mhr.myItem[a].name;
k=mhr.myItem[a].kind;
p=mhr.myItem[a].eftpnt;
}
処理していることは同じです。アイテム名(特技名)・種類・効果の値を対応する変数に代入しています。ただ、特技だけは、残りのMPが特技に必要な消費MPより少ない場合は利用できませんのでその制御を行います。
//道具・特技を使った旨のメッセージを作成
tmsg+="を使った|";
特技・道具のデータを格納したら特技・道具を利用した旨のメッセージを作成します。特に問題は無いでしょう。次のswitch文が種類による処理の制御です。
switch (k){
種類には、回復・攻撃があります。回復では、回復処理後、モンスターの攻撃。攻撃では、モンスターの攻撃と処理に違いがあります。そこで、種類によって処理を分岐しています。制御は、if文で構いません。switch文を利用しているのは、今後、状態回復のアイテムなどアイテムの種類を増やしても良いよう考えた為です。では、回復のケース(case
3:)から見ていきましょう。
//HPが満杯の場合、
if(mhr.mhp==mhr.hp){
Cmn.battleMsg("回復の必要は無いっス");
return 3;
}
回復処理では、HPが満タンなのに操作ミスで回復処理を行ってしまうことが考えられます。この様な操作は、極めて頻度の多い操作ミスです。その為、HPが満タンにも関わらず回復した場合はメッセージを描画して3(戦闘継続)と返してメソッドを終了します。
//HPを回復、回復後のHPが最大HPを越えたら修正
if((mhr.hp+=p)>mhr.mhp) mhr.hp=mhr.mhp;
//道具。特技の利用メッセージを描画
Cmn.battleMsg(tmsg);
//効果のメッセージを描画
Cmn.battleMsg(Integer.toString(p)+"回復した");
HPの回復は、現在のHPに効果のポイント(p)を加算します。この時、加算後のHPが最大HPの値を超えてしまう場合があります。この時は、HPに最大HP(mhp)を代入して修正します。後は、メッセージの描画です。
//消費の処理
if(f==0){//特技を使った場合
//MPの減算
mhr.mp-=mhr.special[a].price;
}
else{//道具を利用した場合
//利用アイテムの削除
mhr.lostItem(a);
}
メッセージの描画後には、特技の場合はMPの減算、道具の場合はアイテムの削除を行います。最初に説明したように消費MPは、priceフィールドを利用します。
//HP/MPの再描画
Cmn.drawHpmp(mhr.hp,mhr.mp);
return mAttack();
後は、HP/MPの値が変わるのでHP/MPを再描画します。そして、モンスターの攻撃に制御を移します。モンスターの攻撃処理は、mAttackメソッドを呼び出すだけです。
次に、攻撃のケース(case 4:)を見ていきましょう。回復に比べると処理は簡単です。
case 4://攻撃
Cmn.battleMsg(tmsg);
if(f==0){
//MPの減算
mhr.mp-=mhr.special[a].price;
//HP/MPの再描画
Cmn.drawHpmp(mhr.hp,mhr.mp);
}
else{
//利用アイテムの削除
mhr.lostItem(a);
}
return jadBattle(p);
攻撃の処理は、説明するまでも無いでしょう。アイテムを利用した旨のメッセージを描画して、特技・道具によってMPの減算・アイテムの削除を行います。。特技を利用した場合のみMPの消費が発生しますからHP/MPの再描画を行います。そして、jadBattleメソッドに効果のポイント(p)を渡して呼び出します。引き数が、0以上の場合は、特技・アイテム処理としてjadBattleメソッドで処理してくれます。是だけです。
3.16 AppBattleCnt ver3 逃走の処理
「逃走」を選択した場合の処理を説明します。今までの処理に比べると簡単です。CntBattleメソッドの選択肢の制御では、case
3の処理です。
case 3://逃げる選択
if((jd=jdEsc())<2) return jd;
mc=0;
break;
「逃走」の処理は、jdEscメソッドで行います。
jdEscメソッド
int(戦闘結果) jdEsc(引き数 なし)
jdEscメソッドの内容を確認しましょう。
//逃げ切れたかの判定
private int jdEsc(){
if(mms.lve-mhr.lve<=1 || Cmn.rdInt(mms.lve-mhr.lve)==0){
Cmn.battleMsg("逃げるっス!");
mms.hp=mms.mhp;
return 0;
}
Cmn.battleMsg("まわりこまれた!");
return mAttack();
}
jdEscメソッドの戻り値は、0:逃走成功(戦闘勝利)、1:戦闘負け(ゲームオーバー)、2:戦闘の継続です。内部で、mAttackメソッドを利用しますからこれらのメソッドに対応した戻り値になります。
「逃走」の成否の判定は以下の式になります。モンスターのレベルから主人公のレベルを減算した値が1以下なら無条件で逃げられます。違う場合は、モンスターのレベルから主人公のレベルを減算した値をCmnクラスのrdIntメソッドに渡して乱数を取得します。乱数が、0なら逃走成功です。つまり、主人公のレベルがモンスターのレベルに近いほど逃げやすくなります。
if(mms.lve-mhr.lve<=1 || Cmn.rdInt(mms.lve-mhr.lve)==0)
乱数の判定で、逃走が成功した場合はモンスターのHPを元に戻します。元に戻さないと次に合う時にHPが少ないモンスターに遭遇してしまいます。その後、0を返して終了します。もし、逃走が失敗した場合は、mAttackメソッドを呼び出してモンスターに攻撃されます。
以上が戦闘の制御です。仕組みを考えていけば難しいところはありません。画面の描画など好みで変えて構いません。コンストラクタとCntBattle()メソッドのデータ構成が同じならエラーにはならないでしょう。プログラムの内容的には、お手本になるとは言い難い内容です。分かりやするくるためにStringクラスに文字を連結しましたがStringBufferを利用した方が効率的です。必要な処理の処理の流れを参考にして自分で作り変えるのも良いでしょう。注意点としては、アプリのサイズには気を使うようにしてください。目標サイズとしては、12k以下が理想です。