前回 EntityPlayer
を継承して痛い目に合ったので、今回からは村人ベースで進めていこうと思います。機能はたくさん必要になると思いますが、仕方ないですね。
1. EntityVillager クラスを見てみる
まず目についたのは村人の行動パターンメソッド、すなわちAIにあたる部分ですね。
protected void initEntityAI() { this.tasks.addTask(0, new EntityAISwimming(this)); this.tasks.addTask(1, new EntityAIAvoidEntity(this, EntityZombie.class, 8.0F, 0.6D, 0.6D)); this.tasks.addTask(1, new EntityAIAvoidEntity(this, EntityEvoker.class, 12.0F, 0.8D, 0.8D)); this.tasks.addTask(1, new EntityAIAvoidEntity(this, EntityVindicator.class, 8.0F, 0.8D, 0.8D)); this.tasks.addTask(1, new EntityAIAvoidEntity(this, EntityVex.class, 8.0F, 0.6D, 0.6D)); this.tasks.addTask(1, new EntityAITradePlayer(this)); this.tasks.addTask(1, new EntityAILookAtTradePlayer(this)); this.tasks.addTask(2, new EntityAIMoveIndoors(this)); this.tasks.addTask(3, new EntityAIRestrictOpenDoor(this)); this.tasks.addTask(4, new EntityAIOpenDoor(this, true)); this.tasks.addTask(5, new EntityAIMoveTowardsRestriction(this, 0.6D)); this.tasks.addTask(6, new EntityAIVillagerMate(this)); this.tasks.addTask(7, new EntityAIFollowGolem(this)); this.tasks.addTask(9, new EntityAIWatchClosest2(this, EntityPlayer.class, 3.0F, 1.0F)); this.tasks.addTask(9, new EntityAIVillagerInteract(this)); this.tasks.addTask(9, new EntityAIWanderAvoidWater(this, 0.6D)); this.tasks.addTask(10, new EntityAIWatchClosest(this, EntityLiving.class, 8.0F)); }
行動はタスク tasks
で管理されているようです。そのタスクを addTask
で追加することでやれることを増やすのでしょうか。引数にはプライオリティ(優先度)と行動内容を入れるようです。
2. AIタスクをいじってみる
タスクを1つにしてみます。まずはドアを開けることだけをさせて、優先度を 0 の一番高く設定します。私の予想が正しければ、ドアを設置するとドアの開け閉めを繰り返すのではと思います。
public class HumanUnitBase extends EntityVillager { public HumanUnitBase(World worldIn) { super(worldIn, 0); } @Override protected void initEntityAI() { this.tasks.addTask(0, new EntityAIOpenDoor(this, true)); } }
実際にやってみた。
まず、ドアを開けてくれないどころか振り向いてもくれません。悲しいですw そもそも、なぜドアを開けてくれないのかが謎ですね。もしかしたら動作させるためのタスクが必要なのかもしれません。
ということで、動作に関するタスク?を追記します。動作っぽいものを入れてみるとどうなるでしょうか。
@Override protected void initEntityAI() { this.tasks.addTask(0, new EntityAIOpenDoor(this, true)); this.tasks.addTask(2, new EntityAIMoveIndoors(this)); this.tasks.addTask(5, new EntityAIMoveTowardsRestriction(this, 0.6D)); }
やっぱり動かないですね。もしかして村判定されないと動かないかと思いましたがどうやら違うようです。仕方ないので、元に戻してコメントアウトしながら確認していきます。
ちなみに、もともとタスクとしてあった EntityAISwimming
をなしにした状態で水に入水させると動かないまま沈んでいきました。溺死です。
2.1 EntityAITradePlayer メソッドを無効化
私の予想ではアイテム取引ができなくなると思います。
普通に取引できるじゃあねぇかw 何を制御しているのでしょうかね...?
2.2 EntityAILookAtTradePlayer を無効化
これはプレイヤーのほうを見なくなると思います。
普通に見てくるじゃねーかw
と思いましたが、取引するときに振り向くかどうかでした。このあと普通の村人と比べてみたら、違いがありました。こちらを見ていない状態で取引すると、取引後にこちらを向いています。
2.3 EntityAIMoveIndoors メソッドを無効化
あれですかね。ドアを開けて家に入る処理ですかね。EntityAIOpenDoor
もあるのですが、これは逆に家から出るときに使うと予想しました。
夜になっても家に入らなかったのでそのようです。これは夜になったら家に入る というロジックですね。
2.4 EntityAIOpenDoor メソッドを無効化
これは家から出るかどうかの処理っぽいですね。
違いました!まさかの ドアを開閉するためだけのロジックでした!
EntityAIMoveIndoors
をもとにもどして夜にしたところ、家のドアに群がっているようでしたがドアを開けることはありませんでした。昼夜にかかわらずドアの開閉をさせるにはこのメソッドが必要らしいです。
2.5 EntityAIRestrictOpenDoor メソッドの無効化
上記の2つだけでなくもう一つドアのメソッドがありました。これは何ですかね。Restrictって直訳だと制限ですが...
どうやら夜の間の行動を制限するようです。無効化する前は、夜になると家に入った後一切出てきませんでした。しかし、無効化すると頻繁に出たり入ったりを繰り返します。
EntityAIMoveIndoors
メソッドによって夜間は家に戻るようなロジックにしておき、EntityAIRestrictOpenDoor
によって夜間の行動を制限するようなロジックなのでしょう。これらは相互作用してこそ意味がありそうです。
人間ユニットにはそのような制限はかけなくてもいいかなとは考えていますが、この辺は色々やってみないと分かりませんね。
2.6 EntityAIMoveTowardsRestriction メソッドの無効化
まぁ Move と書いてあれば動きだとおもうのですが...。英語からして何かに対しての動きを制限しているようですね。
普通に動いているので何を処理するメソッドなのか分かりません。
2.7 EntityAIVillagerMate メソッドの無効化
これは子供を産むか否かの機能ですが、今は必要ないですね。
よく分からなかったです。目を合わせているだけなのか喋っているのかよく分かりませんでした。ただ、話すというタスクは人間ユニットにも必要な動作だと思うので、残しておこうと思います。
2.8 EntityAIWatchClosest メソッドの無効化
これと同じく EntityAIWatchClosest2
もありました。ただ、どっちがどっちかは良く分かりませんが、恐らく視線を合わせてくれるかどうかの機能ですね。
引数を見ればわかるのですが EntityAIWatchClosest2
の引数には EntityPlayer.class
が代入されています。EntityAIWatchClosest
の引数には EntityLiving.class
が代入されています。優先順位は 2 のほうが高いことから、プレイヤーに対して視線を合わせることを優先しているようです。後者はクラス名からしてすべての生物に対して視線を合わせるようですね。
はい。振り向いてくれなくなりました。
2.9 EntityAIAvoidEntity メソッドについて
これはこちらに記事がありました。
どうやら引数に代入した Entity から逃げるようです。逆に襲うようなメソッドもあるようなので、試してみたいと思います。
3. プレイヤーから逃げるユニットをつくる
EntityAIAvoidEntity
メソッドを用いてプレイヤーから逃げるようなユニットを作ってみます。
次のような1行を追記してみました。
this.tasks.addTask(1, new EntityAIAvoidEntity(this, EntityPlayer.class, 10.0F, 1.0D, 1.5D));
引数 | 代入値 |
---|---|
第1引数 | this 修飾子 |
第2引数 | 逃げる対象となる Entity のクラス |
第3引数 | 対象との間をとる距離 |
第4引数 | 対象から十分に離れている際の逃げる速度倍率 |
第5引数 | 対象に近いときの逃げる速度倍率 |
結果、プレイヤーから逃げるような Enitiy に仕上がりました。
4. プレイヤーを襲うユニットをつくりたかった
EntityAIAttackOnCollide
メソッドを実装すると追いかけまわしてくるそうですw
しかし、バージョン 1.12.2 では使用できませんでした。代わりになりそうなものを探してみたら EntityAIAttackMelee
を見つけたので、これを実装してみます。 が、これだけでは意味がなかったです。他にも2つのメソッドを追記しています。
先ほどの逃げるメソッドは消しました。(共存させるとどうなるか面白そうですが...)
this.tasks.addTask(3, new EntityAIAttackMelee(this, 1.0D, false)); this.targetTasks.addTask(1, new EntityAINearestAttackableTarget(this, EntityPlayer.class, true)); //攻撃されなくても追跡される this.targetTasks.addTask(2, new EntityAIHurtByTarget(this, false, new Class[0])); //攻撃されると追跡される
こんな感じで囲まれますが、攻撃はされないみたいですね...w
人間ユニットに道具を使わせてみたいですね。