forked from: IK Bone Sample
forked from IK Bone Sample (diff: 643)
IK の学習用にコメントを付けて見るバージョン 理解をしやすくするために出来るだけソースを短くしてみます。 後で読む。http://gamehell.g.hatena.ne.jp/kenmo/20080123/1201102997 こっそり開発している、3DライブラリにIKを実装しようと思ったのだけど、 いきなり3Dで考えると訳わからなくなってきたので、ちょっと試しに2Dでプロトタイプを作成。 先の事考えて、余分なクラスとか入ってますが気にしないでください。 Joint部分の、抵抗値や、稼動範囲値などは、まだ未実装 次Verでは、自作Meshやシェイプに関連付けられるようにする予定 @author narutohyper メインのスプライトクラス
ActionScript3 source code
/**
* Copyright h_sakurai ( http://wonderfl.net/user/h_sakurai )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/uyjh
*/
// forked from narutohyper's IK Bone Sample
package
{
import flash.display.Sprite;
/**
* IK の学習用にコメントを付けて見るバージョン
* 理解をしやすくするために出来るだけソースを短くしてみます。
* 後で読む。http://gamehell.g.hatena.ne.jp/kenmo/20080123/1201102997
* こっそり開発している、3DライブラリにIKを実装しようと思ったのだけど、
* いきなり3Dで考えると訳わからなくなってきたので、ちょっと試しに2Dでプロトタイプを作成。
*
* 先の事考えて、余分なクラスとか入ってますが気にしないでください。
*
* Joint部分の、抵抗値や、稼動範囲値などは、まだ未実装
* 次Verでは、自作Meshやシェイプに関連付けられるようにする予定
*
* @author narutohyper
*/
[SWF(width = 465, height = 465, frameRate = 60)]
/**
* メインのスプライトクラス
*/
public class Main extends Sprite {
/**
* IKのアーマチュア略してikaっと
*/
private var ika:IKArmature2d;
public function Main() {
//アーマチュアの作成
ika = new IKArmature2d();
//ボーンの作成
ika.setBone(new IKBone2d('test1', 100, 0));
ika.setBone(new IKBone2d('test2', 50, 70));
ika.setBone(new IKBone2d('test3', 150, 170));
ika.joint('root', 'test1');
ika.joint('test1', 'test2');
ika.joint('test2', 'test3');
this.addChild(ika);
ika.x = 465/2;// 画面の中心に置く
ika.y = 465/2;
}
}
}
import flash.display.Shape;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.geom.ColorTransform;
import flash.geom.Matrix3D;
import flash.geom.Point;
import flash.geom.Vector3D;
/**
* IKアーマチュア
*/
class IKArmature2d extends Sprite {
/**
* ルートのジョイント
*/
private var rootJoint:IKJoint2d = new IKJoint2d()
/**
* ボーンの集合
*/
private var bones:Object = {};
/**
* コンストラクタ
*/
public function IKArmature2d () {
// ルートジョイントを追加
this.addChild(rootJoint)
}
/**
* ボーン設定
*/
public function setBone(value:IKBone2d):void {
// ボーンIDで登録
bones[value.id]=value;
this.addChild(value);
}
/**
* ジョイントの設定
*/
public function joint($boneA:String = null, $boneB:String = null):void {
if ($boneA) {
if ($boneA == 'root') {
if (bones[$boneB]) {
bones[$boneB].jointRoot(rootJoint);
} else {
trace('Error:boneが存在しません。', $boneB + '=', bones[$boneB]);
}
} else if (bones[$boneB] && bones[$boneA]) {
bones[$boneB].setParentBone(bones[$boneA]);
bones[$boneA].setChildBone(bones[$boneB]);
} else {
trace('Error:boneが存在しません。', $boneA + '=', bones[$boneA], $boneA + '=', bones[$boneB]);
}
}
}
}
/**
* IKのボーン
*/
class IKBone2d extends Sprite {
private var joint : IKJoint2d = new IKJoint2d();// ジョイント
private var _parentBones : Array = [];// 親のボーン配列
private var _childBones : Array = [];// 子のボーン配列
private var _length : Number;// ボーンの長さ
private var _bone:Shape= new Shape();// ボーンのシェイプ
public var id:String;// 名前
private var _rootFlag:Boolean = false; // ルートかどうかフラグ
private var _clickPoint:Point = new Point();// クリックした位置保存用
/**
* コンストラクタ
*/
public function IKBone2d ($id:String = 'null', $length:Number=100, $rotation:Number=0) {
id = $id;// 名前保存
addChild(_bone);// ボーン追加
addChild(joint);// ジョイント追加
length = $length;// 長さ設定
rotationZ = $rotation;// 回転角設定
// マウスイベント設定
addEventListener(MouseEvent.MOUSE_OVER, onMouseOver);
addEventListener(MouseEvent.MOUSE_OUT, onMouseOut);
addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
}
/**
* マウスオーバー時処理
*/
private function onMouseOver(event:MouseEvent):void {
changeColor(0x3333ff);// 色を変える
}
/**
* マウスアウト時処理
*/
private function onMouseOut(event:MouseEvent):void {
changeColor();// 色を戻す。
}
/**
* マウス押下時処理
*/
private function onMouseDown(event:MouseEvent):void {
_clickPoint = new Point(mouseX, mouseY);// クリック位置保存
// マウスイベントを削除
removeEventListener(MouseEvent.MOUSE_OUT, onMouseOut);
removeEventListener(MouseEvent.MOUSE_OVER, onMouseOver);
removeEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
// マウスアップと移動イベントを登録
stage.addEventListener(MouseEvent.MOUSE_UP, onMouseUp);
stage.addEventListener(MouseEvent.MOUSE_MOVE, onMouseMove);
}
/**
* マウスアップ時処理
*/
private function onMouseUp(event:MouseEvent):void {
_clickPoint = new Point(0, 0);// クリック位置リセット
if (event.target != this) {// イベントのターゲットが自分でなければ色を元に戻す。
changeColor();
}
// イベントを登録
addEventListener(MouseEvent.MOUSE_OVER, onMouseOver);
addEventListener(MouseEvent.MOUSE_OUT, onMouseOut);
addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
// マウス移動イベントを削除
stage.removeEventListener(MouseEvent.MOUSE_MOVE, onMouseMove);
}
/**
* マウス移動時処理
*/
private function onMouseMove($color:uint = 0x006600):void {
// 移動処理実行
move(joint,// IKBone2dの位置
new Point(parent.mouseX, parent.mouseY), // IKArmature2dのマウス位置
new Point(x, y),// IKBone2dの位置
null,
null,
'mouseMove')
//親と子両方に伝達
for each (var bone:IKBone2d in _childBones) {
bone.parentMove(this, new Point(joint.v3d.x + x, joint.v3d.y + y))
}
for each (bone in _parentBones) {
bone.childMove(this, new Point(x, y))
}
}
/**
* 親から子へ動かす
*/
public function parentMove($target:IKBone2d,$pt:Point):void {
move(null, $pt, new Point(joint.v3d.x + x, joint.v3d.y + y),$target,'parent')
//親から回ってきたら、子に伝達
for each (var bone:IKBone2d in _childBones) {
bone.parentMove(this,new Point(joint.v3d.x+x, joint.v3d.y+y));
}
}
/**
* 子から親を動かす
*/
public function childMove($target:IKBone2d, $pt:Point):void {
move(joint, $pt, new Point(x, y), $target);
//子から回ってきたら、親に伝達
for each (var bone:IKBone2d in _parentBones) {
bone.childMove(this,new Point(x, y))
}
}
/**
* 移動処理
* $j ジョイント
* $p1 親のアーマチュアの位置
* $p2 ボーンの位置
* $mode mouseMove or parent
*/
private function move($j:IKJoint2d, $p1:Point, $p2:Point, $target:IKBone2d = null ,
$type:String = null,$mode:String = null):void {
var dx:Number = $p1.x-$p2.x;// ボーン座標とアーマチュアのマウス位置の差分
var dy:Number = $p1.y-$p2.y;
// 角度取得
var angle:Number = Math.atan2( dy, dx );
if (!$type) {
rotationZ = angle * 180 / Math.PI + 90;
} else {
rotationZ = angle * 180 / Math.PI - 90;
}
//もし、jointが固定点(rootJoint)に繋がっていたら、動かさない
if (!_rootFlag) {
if ($mode) {// モード指定時の処理(マウスの移動あるいは、親から子を動かす場合)
// クリックのy座標入りのベクタ
var v3d:Vector3D = new Vector3D(0 ,_clickPoint.y, 0);
// マトリックス生成
var mtx:Matrix3D = new Matrix3D();
// 回転する
mtx.prependRotation(rotationZ, Vector3D.Z_AXIS);
v3d = mtx.transformVector(v3d);
// クリック位置からxは回転した座標を引いたものにする。
x = $p1.x - v3d.x
y = $p1.y - v3d.y;
} else if ($j) {// ジョイントが指定されている場合
x = $p1.x - $j.v3d.x;// ジョイント分を引く
y = $p1.y - $j.v3d.y;
} else {
x = $p1.x;// それ以外のときは、位置をそのままに
y = $p1.y;
}
} else if ($target) {// ルートでターゲット指定されている場合
$target.parentMove(null,new Point(joint.v3d.x+x, joint.v3d.y+y));
}
}
/**
* 色変更
*/
private function changeColor($color:uint = 0x006600):void {
var tc:ColorTransform = new ColorTransform();
tc.color = $color;
transform.colorTransform = tc;
}
/**
* ボーンの長さ設定
*/
public function set length(value:Number):void {
drawBone(value);
_length = value;
joint.y = -value;
joint.v3d = new Vector3D(0, -_length, 0);
var mtx:Matrix3D = new Matrix3D();
mtx.prependRotation(rotationZ, Vector3D.Z_AXIS);
joint.v3d = mtx.transformVector(joint.v3d);
}
public function get length():Number {
return _length;
}
//ボーンの回転
//単にrotationでもいいのだが、3D化に備えあえてrotationZ&Vector3D
override public function set rotationZ(value:Number):void {
super.rotationZ = value;
joint.v3d = new Vector3D(0,-_length, 0);
var mtx:Matrix3D = new Matrix3D();
mtx.prependRotation(value, Vector3D.Z_AXIS);
joint.v3d = mtx.transformVector(joint.v3d);
}
override public function get rotationZ():Number {
return super.rotationZ;
}
//JointBone(始点Jointを親Boneのjointに結合する)
public function jointBone(value:IKBone2d):void {
x = value.joint.v3d.x + value.x;
y = value.joint.v3d.y + value.y;
}
//JointRoot(始点JointをrootJointに結合する)
public function jointRoot(value:IKJoint2d):void {
_rootFlag = true;
x = value.v3d.x;
y = value.v3d.y;
}
//親Boneのセット
public function setParentBone(value:IKBone2d):void {
_parentBones.push(value);
jointBone(value);
}
//子Boneのセット
public function setChildBone(value:IKBone2d):void {
_childBones.push(value);
}
private function drawBone(length:Number=100,$color:uint=0x006600):void {
//bone画像の生成
_bone.graphics.clear();
_bone.graphics.beginFill($color, 1);
_bone.graphics.moveTo(0, 0);
_bone.graphics.lineTo(5, -7);
_bone.graphics.lineTo(length - 5, -2);
_bone.graphics.lineTo(length, -2);
_bone.graphics.lineTo(length - 5, 2);
_bone.graphics.lineTo(5, 7);
_bone.graphics.endFill();
_bone.rotation = -90;
}
}
/**
* IKのジョイント
*/
class IKJoint2d extends Sprite {
public var v3d:Vector3D = new Vector3D(0, 0, 0);
public function IKJoint2d() {
graphics.lineStyle(3, 0x006600,1);
graphics.drawCircle(0, 0, 3);
}
}