第8回.連載でJavaゲーム作っちゃいます弐
Masa: 前回予告の通りに、今回からコンピュータ側の思考ルーチンを考えていきます。といったものの思考ルーチンだけで本になるぐらいボリュームがあるのでどこまでしっかりやれるかは疑問ですが。(^-^;
ほるえ: いきなり、出来ない時の予防線を張るんじゃない。この軟弱もの!!(バシッ)
Masa: いてっ。お前はセイラさんかっ。やってるよ、一応サンプルを作ってみたんだけど思考ルーチンというか、移動の方法が気に入らなくなって、没にした。やっぱ、この辺はコーディングしながら考えるでは、限界ありってことだ。
ほるえ: ふーん。ま、やってるなら良しとしましょ
Masa: (こいつ。後でお仕置きしてやるっ)

「思考について考える」

キャラクタの思考ルーチンとして、こんなルーチンを考えます。

マップ上の目標地点をピックアップします。

1.自分の状態(ヒットポイント,味方,敵キャラとの位置関係)のチェック TIP.救援要請?
 例えば下のような場合、ピクシーが町をとりに行ってもすぐに敵の騎士に攻撃されてしまいます。
こういったときに、Helpを出して他のキャラクタに援護してもらう様に組むことにします。

 ・ヒットポイントが少ない場合は、ヒーラー(ヒットポイントを回復する場所、キャラクタを探しそちらへ移動します。 

 ・味方キャラから突出してしまっている時は、足並みをそろえます。
 (各個撃破されてしまうのを防ぐ)

 ・敵キャラが有利の時は、逃げます。結構、弱気(^-^。

2.周りの様子を確認し、評価値を算定します。但し、そのキャラクタが行おうとしている行動ごとに評価関数は異なります。

・自分がピクシーの場合は占領できる街を探します。
・ドライアートの場合は、回復が必要そうなキャラを探します。
・自分の占領済みの街で、防衛が必要そうな場所を探します。
・’救援要請’がされていたら、そちらの移動も考慮します。

占領用評価関数を用意します。

ここで、ちょっと思いつきで’救援要請’というモードを作ってみます。これを入れることで、少しキャラ間の動きに関連性が出ることを期待しています。

Masa: これが基本的な動きで、プログラムに落としていけばいいのですが、まだこのままでは無理ですね。何が足らないかわかりますか?
ほるえ: 才能と努力(きっぱり)
Masa: そうそう・・・って、じゃねーだろっ。
あーこれだけだと、移動できる場所が複数出てくるだけだから、その中のどこにコマを進めるか決める必要があります。この方法として移動先毎に評価点を持たせてもっとも評価の高いところへ移動する方法をとります。
この評価関数は、上の移動位置の決定とは別に製作しておけば、後で評価方法の変更が容易です。

【移動先毎に、評価点を算定】

移動先の評価
周囲 評価点 距離
敵の城 +200 −5×距離
+100
ドライアート +150
人間キャラ −10
精霊キャラ +10
ドライアートの移動先
ヒットポイント 評価値 距離
Maxの半分 +100 −5×距離
Maxの1/4 +150
・characterクラス内に評価値を出力する関数character[].pointOut()を追加します。

1.移動先が町(未占領,人間の町)の場合
  自分がピクーシーの場合は、街周囲の相手と味方キャラクタによって評価点を算出。必要ならば’救援要請’をします。
2.移動先が’救援要請’場所
  距離とそこまでの間にいる敵の数を評価し、近距離にいるキャラクタは救援に向かいます。
3.移動先がヒーラーの場合
  距離とそこまでの間にいる敵の数を評価し、移動先を決定します。

Masa: で、これを関数に落としていけばOKなんだけど、ここまでに作っている関数と合わせていけば評価関数を減らせるので、これまでのものもまとめていきます。
ほるえ: ふーん。なんか、今回は、まじめに解説してますね。
Masa: あのね。俺はいつもまじめなの。

【ここまでに作って、使えそうなもの】

public int idou(int VP, int HP, int cara, int ido) 座標VP,HPから移動量idoで動ける範囲を配列tansaku[][]に入れる
入力 出力
VP
HP
cara
ido
スタート位置のX軸座標
スタート位置のY軸座標
キャラクタの種類
移動力
tansaku[][0]
tansaku[][1]
tansaku[][2]
tansaku[][3]
tansaku[][4]= cheakpoint(V,H);
tanpc

draw_area_flag
X軸座標
y軸座標
残り移動量
スタート位置からの移動量
そのポイントの情報
配列の数

移動可能エリアの表示フラグ
・町、城等の位置を見つける。
・人間、エルフキャラの場所を探す。
・tansaku[][4]に場所情報が入るので、そこをチェックして移動ポイントとして配列に書き出す処理を別に作る。

TIP.キャラの場所を知る

キャラの位置をただ知るだけならば、character[].getPointX(),getPointY()を作ってありますが、これを使ってしまうと場所に関係なく見つけてしまうので、ちょっと・・・・な感じになってしまうので偵察範囲だけわかるようにします。


public int cheakpoint(int VP,int HP) 座標VP,HPの位置に何があるかを調べて戻り値を返す。
入力 出力
VP
HP
位置のX軸座標
位置のY軸座標

戻り値 あるもの マップ値 キャラ番
0 何もない - -
10 エルフ - 0〜9
20 人間 - 10〜20
1 8 -
2 人間の町 9 -
3 エルフの町 10 -
4 人間の城 11 -
5 エルフの城 12 -

キャラとマップ値が重なる場合は、その和が戻り値になります。
変更
何もない場合の戻り値を0にしました。

・評価値の算定に使います。
・場所の情報と、キャラ情報を同じ配列内にいれ、後の処理を簡単にします。



public void pathserch( int OBV, int OBH,int VP, int HP,int ido) 元の位置(VP,HP)から、目的地(OBV,OBH)までの最短距離を配列path[][]に入れる
入力 出力
VP
HP

OVP
OHP
ido
目標位置のX軸座標
目標位置のY軸座標

スタート位置のX軸座標
スタート位置のY軸座標

移動力
path[][0]
path[][1]
path[][2]
path[][3]
path[][4]
pathhai
draw_path_flag
X軸座標
y軸座標
残り移動量
スタート位置からの移動量
そのポイントの情報
配列の数
移動路の表示フラグ
・目標地点までの移動路がpath[][]に入る ・path[][]配列の順番にido()関数を呼び出し、評価点を算出する関数を作る。


Masa: 結構、この2つの関数が基本で後は、評価関数を作っていくことになります。
ほるえ: 作っていくってことは、まだ作ってないんですね。
Masa: えっ、ぁあ、ほら全体バランスっこことがあるからさ。ちゃんと決めてからやらないと。ほら、品質・品質。
ほるえ: そういう風に品質を盾にするのもどうかと・・・・。

【新たに作る評価関数】

SLGクラスに新設する関数をまとめます。

public int[] cheak_path(int select) 配列path[][]をチェックし、評価値を算出する。
1.idou(path[][0],path[][1],character[].getChr(),character[].getIdo())関数で、path[][]周囲の評価を行う。
2.tansaku[][4]の結果に応じて評価値を算出、tanpcまで実施する。
3.pathhaiまで繰り返す。
入力 出力
select

path[][0]
path[][1]
path[][2]
path[][3]
path[][4]
pathhai
使用する評価関数を選択

X軸座標
y軸座標
残り移動量
スタート位置からの移動量
そのポイントの情報
配列の数

進行ルートの評価値の積算
キャラ移動量1コマについて、−5point

撤退用
戻り値 評価対象
+100 エルフ町
+150 エルフ城
+100 ドライアート
-10 人間キャラ
+10 エルフキャラ

(int tetai=10)
評価対象 変数
エルフ町 elf_city=3
エルフ城 elf_castle=5
ドライアート dryad=6
人間 man=20
エルフ elf=10


占領用
戻り値 評価対象
+100 人間町
+100
+150 人間の城
-10 人間キャラ
+10 エルフキャラ

(int ocup=11)
評価対象 変数
人間町 man_city=2
city=1
人間城 man_castle=3
人間 man=20
エルフ elf=10


攻撃用
戻り値 相手HP
+100 1/2
+150 1/4
-10 人間キャラ
+10 エルフキャラ


(int attack==12)
相手HP 変数
1/2 -
1/4 -
人間 man=20
エルフ elf=10

回復用
戻り値 相手HP
+100 1/2
+150 1/4
+10 エルフキャラ


(int heal==13)
相手HP 変数
1/2 -
1/4 -
エルフ elf=10

・町、城等の位置を見つける。
・人間、エルフキャラの場所を探す。
・この関数を実行するとtansaku[][]配列は破壊されるため、実行はじめにpush終わりにpopをつける。
10:エルフ,20:人間,1:町,2:人間の町,3:エルフの町,4:人間の城,5:エルフの城,6:ドライアート


public int push() 配列tansaku[][]をacu[][]配列にコピーする。
入力 出力
tansaku[][0]
tansaku[][1]
tansaku[][2]
tansaku[][3]
tansaku[][4]
tanpc
X軸座標
y軸座標
残り移動量
スタートからの移動量
そのポイントの情報
配列の数

acu[][0]
acu[][1]
acu[][2]
acu[][3]
acu[][4]
acupc
X軸座標
y軸座標
残り移動量
スタートからの移動量
そのポイントの情報
配列の数

tansaku[][]を保存する
・この関数を実行するとacu[][]配列にtansaku[][]をコピーする。


public int pop() 配列tansaku[][]をacu[][]配列にコピーする。
入力 出力
acu[][0]
acu[][1]
acu[][2]
acu[][3]
acu[][4]
acupc
X軸座標
y軸座標
残り移動量
スタートからの移動量
そのポイントの情報
配列の数

tansaku[][0]
tansaku[][1]
tansaku[][2]
tansaku[][3]
tansaku[][4]
tanpc
X軸座標
y軸座標
残り移動量
スタートからの移動量
そのポイントの情報
配列の数

tansaku[][]を保存する
・この関数を実行するとacu[][]配列にtansaku[][]をコピーする。


public int cheak_he(int VP,int HP) VP,HPにいるキャラクタ番号を返す
入力 出力
VP
HP
X軸座標
y軸座標
number キャラクタ番号を返す。
指定位置にキャラがいない場合は、-1を返す。

tansaku[][4]で検出したキャラのHPなどを調べるために使う。
character[].getPointX(),character[].getPointY()で、VP,HPと等しいところを探す。


public void rest_path()
public void rest_tanpc()
それぞれpath配列とtanpc配列をクリアする。
入力 出力
なし なし
path[][],pathhai
tansaku[][],tanpc
配列クリアをする
tansaku[][]やpath[][]内のサーチするとき、前のデータが残っているとバグるので新設。
それぞれ、pathhai,tanpcまでの配列に0を入れる。

Masa: 以上がメインクラスであるSLGクラス側で処理される関数です。基本的にはすべてこちら側で処理してもいいのですが、せっかくのオブジェクトプログラムなので、そのあたりの利点も享受することにします。
ほるえ: (プログラムをみて)・・・オブジェクトプログラムって、これが?
Masa: (こいつ、絶対泣かす!!)
ほるえ: で、このあとは?
Masa: また、今度ってことで。
今回は、プログラムの更新したけど、あまりにひどくなったので、手直し中です。(品質第一)

Copyright (C) 錬金術師Masa
新規:2005年09月10日,更新:2005年09月18日