今日は普通に「x += 10; y += 30;」っていう世界から一歩進んでみます。
面白い割に認知度が低く、日本語の文章が少ないので書いてみます。
今回は移動と描画のためにp5.jsというライブラリを使っていますが、Unityやd3.jsやCanvas、enchant.jsなど何でも大丈夫です。
##応用性が高いモーションシステム、Steering Behaviorsについて
もともと、この「Steering Behaviors」というシステムはCraig W. Reynoldsさんという方が考えたもので、「実例で学ぶゲームAIプログラミング」や「Nature of Code」といったゲーム系、ビジュアルプログラミング系の書籍で紹介されている手法です。
本家サイトはこちら http://www.red3d.com/cwr/steer/
これは、座標の計算にベクトルを用いることによって独自に確立された動きのプログラミングシステムです。
Steeringとは車のステアリングと同じです。つまり舵を切るという点に着目しています。車はいきなり舵を180度後ろにきることはできませんよね?ではどうやってプログラミングしたらいいだろうか?そんな感じです。
舵をきる力を弱めると旋回力が弱くなり、強めると旋回力が強くなりすぐ移動できます。こういった観点でリアルな動きを作っていきます。
こうするとホーミングミサイルのホーミングの動きや、車や船の動き、ルンバのような簡単なロボットの動き(ルンバは曲線を描かないので例は適切じゃないけどw)や虫のような単純な動きであればシミュレートすることができてしまうかもしれません。つまり応用がきくということです。
##追いかける流れ
これは全て下のコードのthis.seek()という関数の説明です。
ちなみにここでいうベクトルって何?かというと、プログラミング的に言えばxとyをプロパティに持つ、ただのオブジェクトです。
そういう意味では座標も、速度も加速度も全部ベクトルです。
以下にフローを示しますが、
まぁ、パッと見意味分からないと思いますのでコードと照合しながら読んでいったほうがいいかもしれません。
1 . まず希望する方向(ベクトル)を出す。出し方は「現在位置 - 目標位置」
2 . 希望する方向のベクトルを正規化する
3 . 希望ベクトルの長さを最大まで伸ばす
4 . ステアリングのベクトルを出す。出し方は「希望ベクトル - 現在のベクトル」
5 . ステアリングベクトルの大きさを限る(limit()関数)
##おすすめ動画、書籍
英語ですが動画がありますので見て理解したほうが早いかもしれません。
https://vimeo.com/channels/natureofcode/63089177
あとは深く知りたいなら「実例で学ぶゲームAIプログラミング」や「Nature of Code」を読みましょう。ただ残念ながら、前者はC++で後者はJava(Processing)の本です。
##コード
なにはともあれコードを公開します。p5.js特有のAPIも紛れてますが、Canvasとかでも表現できるものです。(長くはなりますが)
簡単に前置きしておくと、setup()は最初に呼ばれる関数で、draw()が毎フレーム呼ばれる関数です。
jsdoit : http://jsdo.it/hp0me_/vKq7
var obj,randomObj;
//最大スピード
var maxspeed = 7;
//旋回力 大きいほど舵を切りやすい
var maxsteer = 0.3;
function setup() {
createCanvas(600,400);
smooth();
obj = new MovingObj(100,100);
randomObj = new RandomMovingObj(300,300);
}
function draw() {
background(205);
//これはブロック崩しのボールのように動く
randomObj.move();
randomObj.draw();
//ボールを追いかける(追跡 = seek)
obj.seek(randomObj.pos);
obj.move();
obj.draw();
}
function mousePressed() {
var wind = createVector(-12.4,0);
obj.applyForce(wind);
}
//Steering Behavior(操舵行動)を扱うクラス
var MovingObj = function(x,y){
this.pos = createVector(x,y); //位置
this.vel = createVector(0,0); //速度
this.acc = createVector(0,0); //加速度
this.move = function(){
this.vel.add(this.acc);//速度に加速度をプラス
this.pos.add(this.vel);//位置に速度をプラス
this.acc.mult(0);
};
//操舵行動 追跡(seek)
this.seek = function(target){
//希望ベクトル = 目標位置 - 現在位置
var desired = p5.Vector.sub(target,this.pos);
//希望ベクトルをノーマライズ
desired.normalize();
//希望ベクトルのマグニチュードをmaxに
desired.setMag(maxspeed);
//ステアリングベクトル(速度) = 希望ベクトル - 現在のベクトル(速度)
var steer = p5.Vector.sub(desired,this.vel);
//速度を限る
steer.limit(maxsteer);
//適用
this.applyForce(steer);
};
this.applyForce = function(force){
this.acc.add(force);
};
this.draw = function(){
ellipse(this.pos.x,this.pos.y,30,30);
};
};
//ここからはランダムに動くオブジェクトなので蛇足
var RandomMovingObj = function(x,y){
this.pos = createVector(x,y);
this.vel = createVector(5,5);
this.move = function(){
this.pos.add(this.vel);
if(this.pos.x < 0 || this.pos.x > 600){
this.vel.x *= -1;
}
if(this.pos.y < 0 || this.pos.y > 400){
this.vel.y *= -1;
}
};
this.draw = function(){
ellipse(this.pos.x,this.pos.y,10,10);
};
}
##追記
これをArduinoとかで動かしてみたいですよね・・・w
ハードウェア分野で応用できたらすごいです。