スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

--.--.-- | | スポンサー広告

ポテンシャル関数を使ってマウスカーソル追尾

ボールがマウスカーソルを追尾します。ただし、近づきすぎると逆に遠ざかろうとします。




























Adobe Flash Player を取得






いわゆるマウスストーカーなのですが、ボールとマウスカーソルの間に働く力をレナードジョーンズポテンシャル関数を使って求めて、それによってボールを動かしています。

ボールにかかる力 = - a / d^m + b / d^n

といったカンジの式でa, b, m, nは定数、dはマウスカーソルとボールの距離です。
力が正なら引力、負なら斥力になります。

サンプルでは、フレーム毎にボールの速度ベクトルに上記の式で求めた力を加算してボールの次の位置を決定しています。

サンプルのActionScriptは以下です。第一フレームのアクションとして記述しています。

import flash.display.Sprite;
import flash.events.Event;

// パラメータ
var a:Number = 300000;
var b:Number = 10000;
var m:Number = 3;
var n:Number = 2;
// 最大速度
var vMax:Number = 10;
// 速度の減衰率
var vRatio:Number = 0.995;
// 物体の半径
var r:Number = 10;

// 速度ベクトルのx成分
var vx:Number = 0.0;
// 速度ベクトルのy成分
var vy:Number = 0.0;

// 物体を生成する
var obj:Sprite = new Sprite();
obj.graphics.lineStyle(0, 0x0000FF);
obj.graphics.beginFill(0x9999FF);
obj.graphics.drawCircle(0, 0, r);
obj.graphics.endFill();
obj.x = Math.random() * stage.stageWidth;
obj.y = Math.random() * stage.stageHeight;
obj.addEventListener(Event.ENTER_FRAME,
function (e:Event):void {
// マウスカーソルと物体との距離を算出する
var dx:Number = mouseX - obj.x;
var dy:Number = mouseY - obj.y;
var d:Number = Math.sqrt(dx * dx + dy * dy);
// マウスカーソルに触れていたら移動しない
if (d < r) {
vx = 0;
vy = 0;
return;
}
// ポテンシャル関数でかかっている力を算出する
var p:Number = - a / Math.pow(d, m) + b / Math.pow(d, n);
var px:Number = p * dx / d;
var py:Number = p * dy / d;
// 速度を減衰させる
vx *= vRatio;
vy *= vRatio;
// 速度ベクトルに力を加算する
vx += px;
vy += py;
// 速度を最大値以内にする
var v:Number = Math.sqrt(vx * vx + vy * vy);
var vr:Number = 0;
if (v > vMax) {
vr = vMax / v;
vx *= vr;
vy *= vr;
}
// 速度ベクトルにしたがって位置を更新する
obj.x += vx;
obj.y += vy;
// ステージの端で跳ね返る
if (obj.x < 0) {
obj.x = -obj.x;
vx *= -1;
} else if (obj.x > stage.stageWidth) {
obj.x = stage.stageWidth * 2 - obj.x;
vx *= -1;
}
if (obj.y < 0) {
obj.y = - obj.y;
vy *= -1;
} else if (obj.y > stage.stageHeight) {
obj.y = stage.stageHeight * 2 - obj.y;
vy *= -1;
}
});
addChild(obj);


最初に各種パラメータを定義しています。a, b, m, nは上の方に書いてる式で使います。

vMaxはボールの最大速度です。1フレームでの最大移動距離で、あまり速く動かれるとワケがわからないので制限を加えています。

vRatioはフレーム毎の速度の減衰です。抵抗的なモノです。

ボールのx方向成分の速度、y方向成分の速度をvx, vyに保持するようにします。
前フレームでの速度+今フレームでかかった力による速度。というカンジで速度を変化させます。

objがマウスカーソルを追尾するボールです。Spriteに円を描いています。
objにEvent.ENTER_FRAMEイベントハンドラをつけて、この中で移動処理をしています。

フレーム毎の移動処理は、マウスカーソルとボールの距離からボールにかかる力をポテンシャル関数で求めて、x方向成分y方向成分それぞれをvx, vyに加算。
vx, vyをx, yに加算して移動。という流れになっています。

ただし、マウスカーソルがボールに触れている状態ではボールは停止。速度はvMaxで定義した制限以内に抑える。ステージの端にいったら跳ね返る。
といった制限を加えています。すぐにどっかに飛んでって見失うので。

スポンサーサイト

テーマ:Flash - ジャンル:コンピュータ

2010.10.29 | | Comments(2) | Trackback(0) | Flash CS5

直線ぽくフォーカスを移動する

フォーカスをタイル上で直線ぽく移動させます。




























Adobe Flash Player を取得






ステージ上で、タイルをクリックすると、クリックした位置にフォーカスが移動します。
フォーカスは2つあって、は単純な移動、は移動開始位置からクリック位置まで引いた直線に近い経路をたどります。

は、列/行それぞれクリック位置に近づくように1タイルずつ移動します。
結果として、行か列が等しくなるまでナナメに進んで、あとは横or縦移動します。

は、元いた位置からクリック位置までの直線に沿うようにタイル上を移動します。

今回のサンプルも、第一フレームのアクションとして記述しています。

// 行数
var rowCnt:uint = 20;
// 列数
var colCnt:uint = 30;
// タイルのサイズ
var tileSize:Number = 16;
// タイルを生成する
setupTile();
// 目標列
var targetCol:uint = 0;
// 目標行
var targetRow:uint = 0;
// 素朴な移動をするフォーカスを生成する
var simpleFocus:Sprite = new Sprite();
simpleFocus.graphics.lineStyle(0, 0x0000FF);
simpleFocus.graphics.drawCircle(tileSize / 2, tileSize / 2, tileSize / 2);
simpleFocus.addEventListener(Event.ENTER_FRAME,
function (e:Event):void {
// 現在の列インデックスと行インデックスを取得する
var col:uint = getColIndex(simpleFocus.x);
var row:uint = getRowIndex(simpleFocus.y);
// 目標地点であったら何もしない
if (col == targetCol && row == targetRow) {
return;
}
// 列を目標地点に近づける
if (col > targetCol) {
simpleFocus.x = getX(col - 1);
} else if (col < targetCol) {
simpleFocus.x = getX(col + 1);
}
// 行を目標地点に近づける
if (row > targetRow) {
simpleFocus.y = getY(row - 1);
} else if (row < targetRow) {
simpleFocus.y = getY(row + 1);
}
});
addChild(simpleFocus);
// ブレゼンハムのアルゴリズムで移動するフォーカスを生成する
var bresenhamPath:Array = [];
var bresenhamIdx:uint = 0;
var bresenhamFocus:Sprite = new Sprite();
bresenhamFocus.graphics.lineStyle(0, 0xFF0000);
bresenhamFocus.graphics.drawCircle(tileSize / 2, tileSize / 2, tileSize / 2);
bresenhamFocus.addEventListener(Event.ENTER_FRAME,
function (e:Event):void {
// 移動先の経路がなければ何もしない
if (bresenhamIdx >= bresenhamPath.length) {
return;
}
// 移動する
var nextObj:Object = bresenhamPath[bresenhamIdx];
bresenhamFocus.x = getX(nextObj.col);
bresenhamFocus.y = getY(nextObj.row);
// 経路を勧める
bresenhamIdx++;
});
addChild(bresenhamFocus);

// ブレゼンハムのアルゴリズムで経路を算出する
function bresenham(startCol:uint, startRow:uint):Array {
// 結果を保持する配列を準備する
var ary:Array = [];
// 列の距離、行の距離を算出する
var dCol:int = Math.abs(targetCol - startCol);
var dRow:int = Math.abs(targetRow - startRow);
// 列、行の移動方向を決定する
var stepCol:int = targetCol - startCol > 0 ? 1 : -1;
var stepRow:int = targetRow - startRow > 0 ? 1 : -1;
// 列と行のどちらの移動距離が多いかによって場合分けする
var e:Number;
var col:uint = startCol;
var row:uint = startRow;
var dCol2:int = dCol * 2;
var dRow2:int = dRow * 2;
if (dCol >= dRow) {
// 列方向が遠い場合は、列移動しながら必要に応じて行を移動する
e = dCol;
// 目的地まで進む
while (col != targetCol) {
col += stepCol;
e += dRow2;
if (e >= dCol2) {
e -= dCol2;
row += stepRow;
}
ary.push({col:col, row:row});
}
} else {
// 行方向が遠い場合は、行移動しながら必要に応じて列を移動する
e = dRow;
// 目的地まで進む
while (row != targetRow) {
row += stepRow;
e += dCol2;
if (e >= dRow2) {
e -= dRow2;
col += stepCol;
}
ary.push({col:col, row:row});
}
}
return ary;
}

// タイルを生成する
function setupTile() {
for (var rowIdx:int = 0; rowIdx < rowCnt; rowIdx++) {
for (var colIdx:int = 0; colIdx < colCnt; colIdx++) {
var tile:Sprite = new Sprite();
tile.graphics.moveTo(0, 0);
tile.graphics.beginFill(0xCCCCCC);
tile.graphics.drawRect(0, 0, tileSize, tileSize);
tile.graphics.endFill();
tile.graphics.beginFill(0x00CC00);
tile.graphics.drawRect(1, 1, tileSize - 2, tileSize - 2);
tile.graphics.endFill();
tile.x = colIdx * tileSize;
tile.y = rowIdx * tileSize;
tile.addEventListener(MouseEvent.CLICK,
function (e:Event):void {
var clickCol = getColIndex(mouseX);
var clickRow = getRowIndex(mouseY);
// 目的タイルが変わらなければ何もしない
if (clickRow == targetRow && clickCol == targetCol) {
return;
}
targetCol = getColIndex(mouseX);
targetRow = getRowIndex(mouseY);
// 経路を求める
bresenhamPath = bresenham(getColIndex(bresenhamFocus.x), getRowIndex(bresenhamFocus.y));
bresenhamIdx = 0;
});
addChild(tile);
}
}
}

// 列インデックスを戻します。
function getColIndex(x:Number):uint {
return x / tileSize;
}
// 行インデックスを戻します。
function getRowIndex(y:Number):uint {
return y / tileSize;
}
// x座標を戻します。
function getX(col:uint):Number {
return col * tileSize;
}
// y座標を戻します。
function getY(row:uint):Number {
return row * tileSize;
}


最初にタイルを生成して並べています。
setupTile()メソッドが実際にタイルを生成している部分で、規定の数だけSpriteオブジェクトを作っています。

それぞれのタイルはマウスクリックイベントリスナを登録してあってクリックしたら自身が目的地となるようにtargetCol, targetRowを更新しています。
また、の移動用に経路を生成しています。

simpleFocusはで表現されるフォーカスです。
コイツは、単純に現在位置と目的位置から移動先を決定しています。

bresenhamFocusはで表現されるフォーカスで、bresenhamPath配列で示される経路どおりに移動します。

bresenhamPath配列はどうしてるのかというと、タイルをクリックしたときにbresenham(uint, uint)メソッドが実行され、そこで生成されます。
メソッドパラメータは移動の開始位置(列、行)です。

bresenham(uint, uint)メソッドは、ブレゼンハムのアルゴリズムという直線を描画するためのアルゴリズムを利用して経路を生成しています。
行方向と列方向の移動量のどちらが多いかで場合分けしていますが、内容は基本的には同じです。
たとえば列方向の移動が多い場合は、列を1ずつ移動しながら直線の傾きに相当(2 × dx倍)する分変数を加算し、変数が1マス相当(2 × dx倍)に達したら行を1つ移動する。というカンジです。たぶん。
こうやって取得した座標を配列にセットしておいて、実際の移動は配列の中身を順次使用します。

テーマ:Flash - ジャンル:コンピュータ

2010.10.13 | | Comments(0) | Trackback(0) | Flash CS5

«  | HOME |  »

プロフィール

HundredthMonkey

Author:HundredthMonkey
プログラマ。

ブロとも申請フォーム

この人とブロともになる

メールフォーム

名前:
メール:
件名:
本文:

ブログ内検索


上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。