In case Flash no longer exists; a copy of this site is included in the Flashpoint archive's "ultimate" collection.

Dead Code Preservation :: Archived AS3 works from wonderfl.net

IK Bone Sample

こっそり開発している、3DライブラリにIKを実装しようと思ったのだけど、
いきなり3Dで考えると訳わからなくなってきたので、ちょっと試しに2Dでプロトタイプを作成。

先の事考えて、余分なクラスとか入ってますが気にしないでください。

Joint部分の、抵抗値や、稼動範囲値などは、まだ未実装
次Verでは、自作Meshやシェイプに関連付けられるようにする予定

@author narutohyper
/**
 * Copyright narutohyper ( http://wonderfl.net/user/narutohyper )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/tj5H
 */

package
{
    import flash.display.Sprite;
    
    /**
     * こっそり開発している、3DライブラリにIKを実装しようと思ったのだけど、
     * いきなり3Dで考えると訳わからなくなってきたので、ちょっと試しに2Dでプロトタイプを作成。
     *
     * 先の事考えて、余分なクラスとか入ってますが気にしないでください。
     * 
     * Joint部分の、抵抗値や、稼動範囲値などは、まだ未実装
     * 次Verでは、自作Meshやシェイプに関連付けられるようにする予定
     *
     * @author narutohyper
     */
    [SWF(width = 465, height = 465, frameRate = 60)]
    
    public class Main extends Sprite {
        private var ika:IKArmature2d;

        private var segments:Array;
        private var num:Number = 10;

        public function Main() {
            this.stage.quality = "BEST";

            //アーマチュアの作成
            ika = new IKArmature2d();
            //ボーンの作成
            var test:uint = 0;
            if (test) {
                ika.setBone(new IKBone2d('test1', 100, 0));
                ika.setBone(new IKBone2d('test2', 50, 70));
                ika.joint('root', 'test1');
                ika.joint('test1', 'test2');
            } else {
            
                ika.setBone(new IKBone2d('body', 60, 0));
                
                ika.setBone(new IKBone2d('breast', 50, 0));
                
                ika.setBone(new IKBone2d('waistRight', 50, 140));
                ika.setBone(new IKBone2d('upperFootRight', 80, 180));
                ika.setBone(new IKBone2d('lowerFootRight', 70, 180));
                ika.setBone(new IKBone2d('ankleRight', 30, 120));
                
                ika.setBone(new IKBone2d('shoulderRight',50,100));
                ika.setBone(new IKBone2d('upperArmRight',50,150));
                ika.setBone(new IKBone2d('lowerArmRight',50,170));
                ika.setBone(new IKBone2d('handRight', 20, 180));
                
                ika.setBone(new IKBone2d('waistLeft', 50, -140));
                ika.setBone(new IKBone2d('upperFootLeft', 80, -180));
                ika.setBone(new IKBone2d('lowerFootLeft', 70, -180));
                ika.setBone(new IKBone2d('ankleLeft', 30, -120));
                    
                ika.setBone(new IKBone2d('shoulderLeft',50,-100));
                ika.setBone(new IKBone2d('upperArmLeft',50,-150));
                ika.setBone(new IKBone2d('lowerArmLeft',50,-170));
                ika.setBone(new IKBone2d('handLeft', 20, -180));
                
                ika.setBone(new IKBone2d('neck',20,0));
                ika.setBone(new IKBone2d('head',40,0));

                //ボーンをジョイント              
                ika.joint('root', 'body');
                ika.joint('root', 'waistRight');
                ika.joint('root', 'waistLeft');
                ika.joint('body', 'breast');

                ika.joint('breast', 'neck');
                ika.joint('neck', 'head');
                
                ika.joint('breast', 'shoulderRight');
                ika.joint('breast', 'shoulderLeft');
                ika.joint('shoulderRight','upperArmRight');
                ika.joint('shoulderLeft', 'upperArmLeft');
                ika.joint('upperArmRight','lowerArmRight');
                ika.joint('upperArmLeft','lowerArmLeft');
                ika.joint('lowerArmRight','handRight');
                ika.joint('lowerArmLeft', 'handLeft');
                
                ika.joint('waistRight','upperFootRight');
                ika.joint('waistLeft','upperFootLeft');
                ika.joint('upperFootRight','lowerFootRight');
                ika.joint('upperFootLeft', 'lowerFootLeft');
                ika.joint('lowerFootRight','ankleRight');
                ika.joint('lowerFootLeft','ankleLeft');
            }
            

            
            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;


    
    class IKArmature2d extends Sprite  {
        private var _rootJoint:IKJoint2d;
        private var _bones:Object;
        private var _mesh:Mesh2d;

        
        public function IKArmature2d () {
            bones = new Object;
            rootJoint = new IKJoint2d('root')
            
            this.addChild(rootJoint)
            

        }

        
        public function setBone(value:IKBone2d):void {
            bones[value.id]=value;
            value.mesh = mesh;
            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]);
                }
            } else {

            }
        }
        
        
        
        
        //関連付けられたMesh2d
        public function set mesh(value:Mesh2d):void {
                _mesh = value;
        }
        
        public function get mesh():Mesh2d {
            return _mesh;
        }
        
        
        //基準となるジョイント
        public function set rootJoint(value:IKJoint2d):void {
                _rootJoint = value;
        }
        
        public function get rootJoint():IKJoint2d {
            return _rootJoint;
        }
        
        public function set bones(value:Object):void {
                _bones = value;
        }
        
        public function get bones():Object {
            return _bones;
        }
        
    }
    
    
    
    
    
    class IKBone2d extends Sprite {
        public static const MOVE_BONE:String = 'move_bone';
        
        private var _me:IKBone2d;
        private var _headJoint : IKJoint2d;
        private var _tailJoint : IKJoint2d;

        private var _parentBones : Array;
        private var _childBones : Array;
        
        private var _length : Number;
        private var _vectors : Vector.<Vector2d>;
        private var _mesh:Mesh2d;
        private var _bone:Shape;
        private var _id:String;
        private var _rootFlag:Boolean = false
        
        private var _clickPoint:Point
        
        public function IKBone2d ($id:String = 'null', $length:Number=100, $rotation:Number=0) {
            id = $id;
            _me = this;
            _clickPoint = new Point();
            _parentBones = [];
            _childBones = [];
            _bone = new Shape();
            addChild(_bone);
            headJoint = new IKJoint2d();
            addChild(headJoint);
            tailJoint = new IKJoint2d();
            addChild(tailJoint)
            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(headJoint, new Point(parent.mouseX, parent.mouseY), new Point(x, y),null,null,'mouseMove')

            //親と子両方に伝達
            var i:uint;
            if (_childBones.length) {
                for (i = 0; i < _childBones.length;i++) {
                    _childBones[i].parentMove(this,new Point(tailJoint.jx+x, tailJoint.jy+y))
                }
            }
            if (_parentBones.length) {
                for (i = 0; i < _parentBones.length;i++) {
                    _parentBones[i].childMove(this,new Point(x, y))
                }
            }
        }

        public function parentMove($target:IKBone2d,$pt:Point):void {
            move(null, $pt, new Point(tailJoint.jx+x, tailJoint.jy+y),$target,'parent')
            //親から回ってきたら、子に伝達
            var i:uint;
            if (_childBones.length) {
                for (i = 0; i < _childBones.length;i++) {
                    _childBones[i].parentMove(this,new Point(tailJoint.jx+x, tailJoint.jy+y));
                }
            }
        }
        
        public function childMove($target:IKBone2d, $pt:Point):void {
            move(headJoint, $pt, new Point(x, y),$target)
            //子から回ってきたら、親に伝達
            var i:uint;
            if (_parentBones.length) {
                for (i = 0; i < _parentBones.length;i++) {
                    _parentBones[i].childMove(this,new Point(x, y))
                }
            }

        }
        
        
        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;
            }

            //もし、headerJointが固定点(rootJoint)に繋がっていたら、動かさない
            if (!_rootFlag) {
                if ($mode) {
                    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 = $p1.x - v3d.x
                    y = $p1.y - v3d.y;
                } else if ($j) {
                    x = $p1.x - $j.jx;
                    y = $p1.y - $j.jy;
                } else {
                    x = $p1.x;
                    y = $p1.y;
                }

            } else {
                if ($target) {
                    $target.parentMove(null,new Point(tailJoint.jx+x, tailJoint.jy+y));
                }
            }
            
        }
        
        
        
        
        private function changeColor($color:uint = 0x006600):void {
            var tc:ColorTransform = new ColorTransform()
            tc.color = $color;
            transform.colorTransform = tc;
            
        }

        
        //関連付けられたMesh2d
        public function set mesh(value:Mesh2d):void {
            _mesh = value;
        }
        
        public function get mesh():Mesh2d {
            return _mesh;
        }
                
        
        //ボーンが影響するVector2d
        public function set vectors(value:Vector.<Vector2d>):void {
            _vectors = value;
        }
        
        public function get vectors():Vector.<Vector2d> {
            return _vectors;
        }
        
        //ボーンの長さ
        public function set length(value:Number):void {
            makeBone(value)
            _length = value;
            tailJoint.y = -value;
            
            tailJoint.v3d = new Vector3D(0,-_length, 0);
            var mtx:Matrix3D = new Matrix3D();
            mtx.prependRotation(rotationZ, Vector3D.Z_AXIS);
            tailJoint.v3d = mtx.transformVector(tailJoint.v3d);
            

        }
        
        public function get length():Number {
            return _length;
        }
        
        //ボーンの回転
        //単にrotationでもいいのだが、3D化に備えあえてrotationZ&Vector3D
        override public function set rotationZ(value:Number):void {
            super.rotationZ = value;
            tailJoint.v3d = new Vector3D(0,-_length, 0);
            var mtx:Matrix3D = new Matrix3D();
            mtx.prependRotation(value, Vector3D.Z_AXIS);
            tailJoint.v3d = mtx.transformVector(tailJoint.v3d);
        }
        
        override public function get rotationZ():Number {
            return super.rotationZ;
        }
        

        
        
        
        //始点ジョイント
        public function set headJoint(value:IKJoint2d):void {
            _headJoint = value;
        }
        
        public function get headJoint():IKJoint2d {
            return _headJoint;
        }
        
        //終点ジョイント
        public function set tailJoint(value:IKJoint2d):void {
            _headJoint = value;
        }
        
        public function get tailJoint():IKJoint2d {
            return _headJoint;
        }
        
        //JointBone(始点Jointを親BoneのtailJointに結合する)
        public function jointBone(value:IKBone2d):void {
            x = value.tailJoint.jx;
            y = value.tailJoint.jy;
            if (value){
                x += value.x
                y += value.y
            }
        }

        //JointRoot(始点JointをrootJointに結合する)
        public function jointRoot(value:IKJoint2d):void {
            _rootFlag=true
            x = value.jx;
            y = value.jy;
        }
        

        
        //親Boneのセット
        public function setParentBone(value:IKBone2d):void {
            _parentBones.push(value);
            jointBone(value);
        }
        

        //子Boneのセット
        public function setChildBone(value:IKBone2d):void {
            _childBones.push(value);
        }
        
        
        
        //関連付けられたMesh2d
        public function set id(value:String):void {
            _id = value;
        }
        
        public function get id():String {
            return _id;
        }
        
        
        
        private function makeBone(no:Number=100,$color:uint=0x006600):void {
            //bone画像の生成
            _bone.graphics.clear();
            _bone.graphics.beginFill($color, 1);
            _bone.graphics.moveTo(0, 0);
            if (no > 10) {
                _bone.graphics.lineTo(5,-7);
                _bone.graphics.lineTo(no - 5,-2);
                _bone.graphics.lineTo(no,-2);
                _bone.graphics.lineTo(no - 5,2);
                _bone.graphics.lineTo(5,7);
            } else {
                _bone.graphics.lineTo(no / 2, -7);
                _bone.graphics.lineTo(no - 5,-2);
                _bone.graphics.lineTo(no - 5,2);
                _bone.graphics.lineTo(no / 2, 7);
            }
            _bone.graphics.endFill();
            _bone.rotation=-90

        }
        
        
        
    }
    
    class IKJoint2d extends Sprite  {

        public var v3d:Vector3D;
        
        private var _type:String;
        
        public function IKJoint2d ($type:String = 'default') {
            _type = $type;
            v3d = new Vector3D(0, 0, 0);
            if (_type == 'root') {
                drawRoot();
            } else {
                drawDefault();
            }
        }
        
        private function drawRoot():void {
            graphics.lineStyle(4, 0x006600,1);
            graphics.drawCircle(0, 0, 10);
        }
        
        private function drawDefault():void {
            graphics.lineStyle(3, 0x006600,1);
            graphics.drawCircle(0, 0, 3);
        }
        
        
        //rootJointの場合は、自分自身の位置を返す。
        //defaultJointは、位置、回転変換された座標を返す。
        public function get jx():Number {
            return v3d.x
        }
        
        public function get jy():Number {
            return v3d.y
        }
        
        public function get type():String {
            return _type;
        }
        
    }
    
    
        
    class Object2d {
        public function Object2d () {
            
        }
    
    }
    
    
    class Mesh2d extends Object2d {
        public function Mesh2d () {
            
        }
    }
    

    
    
    class Vector2d {
        private var _x:Number;
        private var _y:Number;
        private var _name:String;

        public function Vector2d ($name:String=null,$x:Number=0,$y:Number=0) {
            name = $name;
            x = $x;
            y = $y;
        }
        
        public function set x(value:Number):void {
                _x = value;
        }
        
        public function get x():Number {
            return _x;
        }
            
        public function set y(value:Number):void {
                _y = value;
        }
        
        public function get y():Number {
            return _y;
        }

        public function set name(value:String):void {
                _name = value;
        }
        
        public function get name():String {
            return _name;
        }
        
    }