連載:世界のWebサービス
第10回 モバイルWebアプリケーション
3.サンプル肉食動物
田口 景介
2001/12/01
|
|
最後に筆者が試作した肉食動物を紹介しておこう。
1: using System;
2: using System.Drawing;
3: using System.Collections;
4: using System.Reflection;
5: using System.IO;
6:
7: [assembly: OrganismClass("KSK")]
8: [assembly: AuthorInformation("keisuke", "mail@tmepuri.org")]
9: // 動物のサイズは小さめに
10: [MatureSize(30)]
11: // trueならば肉食動物
12: [CarnivoreAttribute(true)]
13:
14: // 動物の外見はサソリを選択
15: [AnimalSkin(AnimalSkinFamilyEnum.Scorpion)]
16: // 全体図に表示される色(未実装?)
17: [MarkingColor(KnownColor.Yellow)]
18:
19: // 移動距離が長くなることを想定して体力は多めに
20: [MaximumEnergyPoints(10)]
21: // 襲われる可能性は無視してゆっくり食べる
22: [EatingSpeedPoints(0)]
23: // 手早くしとめないと逃げられることがあるので
24: // 攻撃力は高めに
25: [AttackDamagePoints(40)]
26: // 防御力0。やられる前にやれ
27: [DefendDamagePoints(0)]
28: // フィールドが混み合っていれば、簡単に獲物を追い
29: // つめられるので、移動速度は低めに
30: [MaximumSpeedPoints(20)]
31: // 偽装能力0。丸見え
32: [CamouflagePoints(0)]
33: // 視界が狭いとあてずっぽうで獲物を探さなければ
34: // ならないので、少し多めに
35: [EyesightPoints(30)]
36:
37: // 草食動物と肉食動物はどちらもAnimalクラスの
38: // サブクラスとして実装する
39: public class KSK : Animal {
40: AnimalState targetAnimal = null;
41: int wanderingCount = 0;
42: Vector wanderingVector = null;
43:
44: //イベントハンドラの登録
45: protected override void Initialize() {
46: Idle += new IdleEventHandler(IdleEvent);
47: MoveCompleted += new
48: MoveCompletedEventHandler(MoveCompletedEvent);
49: }
50:
51: // Idleイベントハンドラ。定期的に呼び出される
52: void IdleEvent(object sender, IdleEventArgs e) {
53: try {
54: // 可能ならば出産する。出産する条件は成人して
55: // いること、体力が十分あること、以前出産して
56: // から十分時間が経過していること
57: if (CanReproduce) {
58: BeginReproduction(null);
59: WriteTrace("Reproduction");
60: }
61: // 行動中ならば何もしない
62: if (IsAttacking || IsEating || IsMoving
63:
|| State.EnergyState > EnergyState.Normal) {
64: return;
65: }
66: // 獲物がいなければ探す。すでにターゲットを見つ
67: // けていれば、最新の情報(座標など)を取得する
68: if (targetAnimal == null) {
69: targetAnimal
70: = FindNewTarget(false,
double.MaxValue);
71: } else {
72: targetAnimal
73: = (AnimalState)
LookFor(targetAnimal);
74: }
75: // ターゲットがいれば攻撃するか、食べるか、隣接する
76: // まで移動する。いなければ、ランダムに移動する
77: if (targetAnimal != null ) {
78: if (!AttackOrEat())
{
79: if (!IsMoving)
MoveToTarget();
80: }
81: } else {
82: if (!IsMoving) BeginWandering();
83: }
84: } catch(Exception ex) {
85: WriteTrace(ex.ToString());
86: }
87: }
88:
89: // ターゲットに関する最新の情報を取得する
90: void RefreshTarget() {
91: if ( targetAnimal == null ) return;
92: targetAnimal = (AnimalState) LookFor(targetAnimal);
93: }
94:
95: // 視界内でもっとも近くにいる獲物をターゲットにする
96: AnimalState FindNewTarget(bool mySpecies, double min) {
97: ArrayList foundAnimals = Scan();
98:
99: double minDistance = min;
100: AnimalState possibleTarget = null;
101: int count = 0;
102:
103: if (foundAnimals.Count > 0) {
104: foreach (OrganismState organismState
in foundAnimals) {
105: if (organismState is AnimalState
&&
106: IsMySpecies(organismState)
== mySpecies) {
107: count++;
108: if (DistanceTo(organismState)
< minDistance) {
109: possibleTarget
= (AnimalState) organismState;
110: minDistance
= DistanceTo(organismState);
111: }
112: }
113: }
114: }
115: // トレースウィンドウにデバッグメッセージを出力する
116: WriteTrace("FindNewTarget: " + count.ToString());
117: return possibleTarget;
118: }
119:
120: // 指定した動物のそばへ移動する
121: void MoveToTarget() {
122: if ( targetAnimal == null ) {
123: return;
124: }
125: // 残り体力を考慮して移動速度を決める
126: int possibleSpeed = Species.MaximumSpeed;
127: while(possibleSpeed > 2 &&
128: State.EnergyRequiredToMove(possibleSpeed,
possibleSpeed)
129: > (State.StoredEnergy /
2)) {
130: possibleSpeed /= 2;
131: }
132: if (possibleSpeed < 2)
133: possibleSpeed = 2;
134:
135: BeginMoving(new
136: MovementVector(targetAnimal.Position,
possibleSpeed));
137: }
138:
139: // MoveCompletedイベント。移動終了すると呼び出される。
140: void MoveCompletedEvent(object sender, MoveCompletedEventArgs
e) {
141: WriteTrace("Move Completed");
142:
143: try {
144: RefreshTarget();
145:
146: // 障害物にあたって止まったのか、目的地に到着したのか
147: if (e.Reason == ReasonForStop.Blocked)
{
148: if (targetAnimal != null)
{
149: if (!AttackOrEat())
{
150: MoveToAvoid(e);
151: }
152: } else {
153: wanderingCount
= 0;
154: BeginWandering();
155: }
156: } else {
157: if (targetAnimal != null)
{
158: if (!AttackOrEat())
{
159: MoveToTarget();
160: }
161: } else {
162: targetAnimal
= FindNewTarget(false, double.MaxValue);
163: if (targetAnimal
== null) {
164: BeginWandering();
165: } else {
166: MoveToTarget();
167: }
168: }
169: }
170: }
171: catch (Exception ex) {
172: WriteTrace(ex.ToString());
173: }
174: }
175:
176: // 近くに獲物がいないときは、ランダムに移動する
177: void BeginWandering() {
178: if (wanderingCount < 1 || wanderingVector == null)
{
179: WriteTrace("New Direction");
180: wanderingCount = 10;
181: wanderingVector = new Vector(
182: 20 * (OrganismRandom.Next(0,
3) - 1),
183: 20 * (OrganismRandom.Next(0,
3) - 1));
184: } else {
185: wanderingCount--;
186: }
187: Point newDest = Vector.Add(Position, wanderingVector);
188: BeginMoving(new MovementVector(newDest, 3));
189: }
190:
191: // 近くに獲物がいれば、生きていれば攻撃し、
192: // 死んでいれば食べる
193: bool AttackOrEat() {
194: bool action = false;
195: if (!IsEating && !IsAttacking ) {
196: if (targetAnimal.IsAlive
197: &&
WithinAttackingRange(targetAnimal)) {
198: action = true;
199: if (State.EnergyState <
EnergyState.Normal) {
200: WriteTrace("Attack");
201: BeginAttacking(targetAnimal);
202: }
203: } else if (!targetAnimal.IsAlive
204:
&& WithinEatingRange(targetAnimal)
205:
&& State.EnergyState < EnergyState.Full) {
206: WriteTrace("Eat");
207: BeginEating(targetAnimal);
208: action = true;
209: }
210: }
211: return action;
212: }
213:
214: // 障害物を回避するアルゴリズム。いろいろ試したあげく
215: // ランダムに移動して距離を置くシンプルな解法で様子見
216: void MoveToAvoid(MoveCompletedEventArgs e) {
217: try {
218: WriteTrace("Avoid");
219: targetAnimal = null;
220: wanderingCount = 2;
221: BeginWandering();
222: }
223: catch (Exception ex) {
224: WriteTrace(ex.ToString());
225: }
226: }
227:
228: // Terrariumを保存、再開するときに呼び出されるメソッド
229: // 保存すべき状態情報がなければ、空でかまわない
230: public override void SerializeAnimal(MemoryStream m) {
231: }
232:
233: public override void DeserializeAnimal(MemoryStream m) {
234: }
235: }
|
|
筆者が試作した肉食動物のサンプル・プログラム |
|
この肉食動物は次の行動パターンに従って活動するようになっている。
・Idleイベント
可能ならば出産
ターゲットに向けて移動中
可能ならば攻撃または捕食
ターゲットに隣接していなければ継続して移動
ターゲットを定めていない
索敵、見つからなければランダムに移動(新規方向または継続)
・MoveCompletedイベント
何かにぶつかって移動終了
ターゲットに向けて移動中
可能ならば攻撃、または捕食
障害物ならば迂回
ターゲットを定めていない
ランダムに移動(新規方向)
目的地に到着
ターゲットに向けて移動中
可能ならば攻撃、または捕食
隣接していなければ(逃げられたら)、継続して移動
ターゲットを定めていない
索敵、見つからなければランダムに移動(新規方向または継続) |
|
肉食動物の行動パターン |
ほとんど獲物を探して食べるだけの頭の悪い動物だが、それすらも苦労するのが肉食動物の宿命だ。植物を枯らさないように1カ所で食べ続ければ基本的には問題ない草食動物に比べて、肉食動物は次から次へと獲物を探して動き回らなければならないので、体力の消耗が激しくなりがちだ。体力を消耗すれば餓死するばかりか、子供を産むこともできなくなるため、種族全体の危機を招いてしまう。Terrariumの動物はすべて単性生殖が可能ではあるが、それでも肉食動物が一生に2回出産するのは至難の業といえそうだ。できのいい動物ができたら、ぜひEcoSystemへチャレンジしてみてほしい。
更新履歴 |
【2002/04/23】 Terrariumおよび.NET Frameworkの正式版リリースに伴い、記事中のプログラムを一部修正しました。 |
Insider.NET 記事ランキング
本日
月間