スポンサーサイト

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

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

多脚歩行する

多脚歩行。といっても、こんな歩き方をする生き物はいないだろうと思いますケド。のサンプルです。胴体の部分をマウスでつまんで動かせマス。





各脚は3つの楕円スプライトを含むスプライトです。コイツラを根元を基点とするフォワードキネマティクスで動かしています。AS3.0でのフォワードキネマティクスの実装についてはFoundation Actionscript 3.0 Animation:Making Things Move!(和訳:ActionScript 3.0 アニメーション)に詳しい解説が載っています。

このサンプルでは脚一本につき横長の楕円形のインスタンスを3個配置して、インスタンスの基準点(左より)を支点、別に指定する長さ分だけ右側の座標をピボットとしています。根元側のピボット座標に先端側の支点を重ねて、根元側の角度を基準とした角度を先端側に設定することで、節が接続されているように見えるという寸法デス。

で、今回はこの脚部分をクラスにしてみました。節となるインスタンスや節の長さ等はこのクラスのコンストラクタのパラメータとして指定しています。

package {
import flash.display.Sprite;
/**
* 脚クラス。
*/
public class Leg extends Sprite {
/** 節の配列 */
private var _segArray:Array;
/** x方向の速度ベクトル */
private var _vx:Number = 0;
/** y方向の速度ベクトル */
private var _vy:Number = 0;
/**
* コンストラクタ。
* @param propArray 節の配列
*/
public function Leg(segArray:Array) {
_segArray = segArray;
for (var i:uint = 0, iEnd:uint = _segArray.length; i < iEnd; i++) {
var seg:Object = _segArray[i];
var sprite:Sprite = seg.sprite;
addChild(sprite);
}
// 節を接続
join(0);
}
/**
* 節を接続します。
* @param cycle 歩行サイクル
*/
private function join(cycle:Number):void {
var jx:Number = 0;
var jy:Number = 0;
var rot:Number = 0;
for (var i:uint = 0, iEnd:uint = _segArray.length; i < iEnd; i++) {
var seg:Object = _segArray[i];
// 一つ前のピボットに支点をジョイント
seg.sprite.x = jx;
seg.sprite.y = jy;
// 節を回転
seg.sprite.rotation = seg.base
+ Math.sin(cycle + seg.offset) * seg.range
+ rot;
// 次の節用にピボット座標と曲げ角度を記憶
var pivot:Object = getPivot(seg);
jx += pivot.x;
jy += pivot.y;
rot += seg.sprite.rotation;
}
}
/**
* 節のピボット座標を戻します。
* @param seg 節オブジェクト
* @return ピボット座標をプロパティに持つオブジェクト
*/
private function getPivot(seg:Object):Object {
var sprite:Sprite = seg.sprite;
var angle:Number = sprite.rotation * Math.PI / 180;
return {x:Math.cos(angle) * seg.length, y:Math.sin(angle) * seg.length};
}
/**
* 脚の歩行サイクルを設定します。
* @param cycle 歩行サイクル
*/
public function setCycle(cycle:Number):void {
var old:Object = getTip();
join(cycle);
var tip:Object = getTip();
// 速度ベクトルを更新
_vx = tip.x - old.x;
_vy = tip.y - old.y;
}
/**
* 脚の先端の座標を戻します。
* @return 先端座標をプロパティに持つオブジェクト
*/
private function getTip():Object {
return getPivot(_segArray[_segArray.length - 1]);
}
/**
* x方向の速度ベクトルを戻します。
* @return x方向の速度ベクトル
*/
public function get vx():Number {
return _vx;
}
/**
* y方向の速度ベクトルを戻します。
* @return y方向の速度ベクトル
*/
public function get vy():Number {
return _vy;
}
}
}
コンストラクタのパラメータは、節の情報を持つオブジェクトの配列デス。なので、このサンプルでは、配列の要素数は3デス。渡される節の情報はこんなカンジです。
  • length:節の長さ
  • sprite:節をあらわす表示オブジェクト(サンプルでは青い楕円)
  • base:基準角度。ココで指定した角度を中心に往復運動します
  • range:可動範囲。脚が動く角度の範囲デス
  • offset:角度のオフセット値。指定角度に対するずれ
LegクラスはSpriteを継承したクラスで、パラメータとして渡された節の表示オブジェクトをaddChildして内包するようになってます。

コンストラクタの最後で実行しているjoinメソッドが各節を接続し、角度を調整する部分デス。メソッドパラメータは、角度デス。コイツを元に各節のrotationプロパティを設定します。

先頭の表示オブジェクトから順に、
  • 手前の表示オブジェクトのピボット座標に支点をあわせる
  • 手前の表示オブジェクトの角度を基準に自身の角度を決定する
というカンジです。節の角度は、基準角度+(指定角度+オフセットの正弦)×可動範囲+手前の節の角度となっています。なので、指定角度はどんどん加算してけば正弦波を基準とした動きになります。

ピボット座標はgetPivotメソッドで、表示オブジェクトの角度とコンストラクタで指定された長さから計算しています。

_vx, _vyは脚の先端部分の速度ベクトルです。setCycleメソッドが実行された際の、先端の表示オブジェクトのピボット座標の変化を設定しています。内部的には特に使用しないのですが、フレームアクションで脚が地面を蹴ったときの処理に使ってマス。

で、サンプルのフレームアクションはこんなカンジです。
// 歩行速度
var speed:Number = 0.05;
// 重力
var gravity:Number = 0.1;
// 初期位置
var sx:Number = 100;
var sy:Number = 100;
// 脚の設定値
var rLeg1PropArray:Array
= [{length:40, sprite:r1seg1_mc, base:-30, range:20, offset:0},
{length:40, sprite:r1seg2_mc, base:70, range:20, offset:-Math.PI / 3},
{length:20, sprite:r1seg3_mc, base:75, range:20, offset:-Math.PI * 2 / 3}];
var rLeg2PropArray:Array
= [{length:40, sprite:r2seg1_mc, base:-30, range:20, offset:Math.PI},
{length:40, sprite:r2seg2_mc, base:70, range:20, offset:Math.PI * 2 / 3},
{length:20, sprite:r2seg3_mc, base:75, range:20, offset:Math.PI / 3}];
var lLeg1PropArray:Array
= [{length:40, sprite:l1seg1_mc, base:210, range:20, offset:Math.PI / 2},
{length:40, sprite:l1seg2_mc, base:-80, range:20, offset:Math.PI * 5 / 6},
{length:20, sprite:l1seg3_mc, base:90, range:20, offset:Math.PI * 7 / 6}];
var lLeg2PropArray:Array
= [{length:40, sprite:l2seg1_mc, base:210, range:20, offset:Math.PI * 3 / 2},
{length:40, sprite:l2seg2_mc, base:-80, range:20, offset:Math.PI * 11 / 6},
{length:20, sprite:l2seg3_mc, base:90, range:20, offset:Math.PI / 6}];
// 歩行体
var crab:Sprite = new Sprite();
crab.x = sx;
crab.y = sy;
addChild(crab);
// 右脚1
var rLeg1:Leg = new Leg(rLeg1PropArray);
crab.addChild(rLeg1);
// 右脚2
var rLeg2:Leg = new Leg(rLeg2PropArray);
crab.addChild(rLeg2);
// 左脚1
var lLeg1:Leg = new Leg(lLeg1PropArray);
crab.addChild(lLeg1);
// 左脚2
var lLeg2:Leg = new Leg(lLeg2PropArray);
crab.addChild(lLeg2);
// 脚の配列
var legArray:Array = [rLeg1, rLeg2, lLeg1, lLeg2];
// 全体の速度ベクトル
var vx:Number = 0;
var vy:Number = 0;
// 胴体
var body:MovieClip = body_mc;
body.x = 0;
body.y = 0;
crab.addChild(body);
// ドラッグ中かどうか
var dragging:Boolean = false;
body.addEventListener(MouseEvent.MOUSE_DOWN,
function (e:Event):void {
vx = 0;
vy = 0;
dragging = true;
stage.addEventListener(MouseEvent.MOUSE_MOVE, doStageMouseMove);
});
stage.addEventListener(MouseEvent.MOUSE_UP,
function (e:Event):void {
dragging = false;
stage.removeEventListener(MouseEvent.MOUSE_MOVE, doStageMouseMove);
});
// マウスカーソル位置への移動をします。
function doStageMouseMove(e:Event):void {
vx = (mouseX - crab.x) * 0.5;
vy = (mouseY - crab.y) * 0.5;
crab.x = mouseX;
crab.y = mouseY;
}
// 歩行サイクル
var cycle:Number = 0;
addEventListener(Event.ENTER_FRAME, doEnterFrame);
function doEnterFrame(e:Event):void {
if (!dragging) {
// 重力
vy += gravity;
// 速度ベクトルによる移動
doVelocity();
}
// 脚角度更新
doCycle();
cycle += speed;
if (!dragging) {
// 床/壁衝突処理
doCollision();
}
}

// 速度ベクトルによる移動処理を行います。
function doVelocity():void {
crab.x += vx;
crab.y += vy;
}

// 脚角度更新
function doCycle():void {
for each (var leg:Leg in legArray) {
leg.setCycle(cycle);
}
}

// 床/壁衝突処理
function doCollision():void {
// 床/右壁衝突処理
var dx:Number = 0;
var dy:Number = 0;
for each (var leg:Leg in legArray) {
var rect:Rectangle = leg.getBounds(this);
if (rect.left < 0) {
dx = Math.min(dx, rect.left);
vx = 0;
} else if (rect.right > stage.stageWidth) {
dx = Math.max(dx, rect.right - stage.stageWidth);
vx = 0;
}
if (rect.bottom > stage.stageHeight) {
dy = Math.max(dy, rect.bottom - stage.stageHeight);
// 床を蹴ったことによる速度ベクトルの変更
vx = -leg.vx;
vy = -leg.vy;
} else if (rect.top < 0) {
dy = Math.min(dy, rect.top);
vy = 0;
}
}
// 位置調整
crab.x -= dx;
crab.y -= dy;
}
最初のほーで定義してる4つの配列がソレゾレの脚のコンストラクタのパラメータです。length,sprite,base,range,offsetというプロパティを持つオブジェクトの配列になってます。rLeg1PropArray,rLeg2PropArrayが右側の脚用、lLeg1PropArray,lLeg2PropArrayが左側の脚用デス。サンプルではいきなりObjectを作ってますが、節クラスを別途定義しても良かったカモ。

crabスプライトが4本の脚と胴体を内包していて、歩行体全体をあらわしています。脚は先ほどのLegクラスのインスタンスです。胴体はオーサリング環境で作ったインスタンスです。

胴体をあらわすbodyクラスはMOUSE_DOWNイベントリスナを登録していて、コイツとステージに登録したMOUSE_UPイベントリスナでマウスでつまんで動かせるようにしています。

ENTER_FRAMEイベントハンドラでは、
  1. 速度ベクトルによる移動
  2. 脚の角度を更新
  3. 床/壁との衝突処理
をしています。速度ベクトルによる移動はdoVelocityメソッドで処理しています。vx, vyが歩行体の速度ベクトルで、コイツにしたがってcrabの座標を変更しているダケです。

doCycleメソッドが脚の角度の更新デス。cycleが指定角度になります。コレをすべての脚のsetCycleメソッドで指定しています。

doCollisionメソッドが、床/天井/壁との衝突処理デス。天井と壁はぶつかったら突き抜けないようにしてるだけデス。床との衝突は、脚の先端の速度ベクトルの逆ベクトルを歩行体の速度ベクトルとすることで、脚が床を蹴って進むようなカンジにしています。
スポンサーサイト

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

2008.01.05 | | Comments(2) | Trackback(0) | Flash CS3

コメント

はじめまして~

初めましてSouthと申します♪

自分もASを勉強中なんですが
すごいですね!!コレ!!


本物の虫みたいです!!
(^_^; 虫は苦手なんでちょっとアレですが…)

お邪魔しました~♪

2008-01-05 土 22:06:18 | URL | South #VWFaYlLU [ 編集]

コメントありがとうございます。

はじめまして。

フォワードキネマティクスって、単純なルールで動いてるのに実際に見るとソレっぽいのが面白いですよねえ( ´∀`)

ゲーム作ってる方なんですね。ゲームはAS凝ってそうですねー。

2008-01-06 日 03:20:03 | URL | HundredthMonkey #mQop/nM. [ 編集]

コメントの投稿


秘密にする

«  | HOME |  »

プロフィール

HundredthMonkey

Author:HundredthMonkey
プログラマ。

ブロとも申請フォーム

この人とブロともになる

メールフォーム

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

ブログ内検索


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