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

forked from: IK実験

IK実験・アルゴリズム修正版
クリックで追加。
カーソルキー左右で回転レート調整、上下で繰り返し回数変更
前回のグラディウスの触手風のアルゴリズムから、もうちょっと
まともなIKっぽいやり方に変えてみた。
毎回初期状態にリセットしてから、根元から目標に向かって
最大レートまで回転させるのを繰り返す。繰り返し回数を増やすと
精度が上がる。
動きの面白さは減ったけど、このアルゴリズムならクォータニオンを
使えば3次元でも関節計算用のIKとしてそのまま使えそう。
Get Adobe Flash player
by Nao_u 09 May 2009
// forked from Nao_u's IK実験
// 
// IK実験・アルゴリズム修正版
// 
// クリックで追加。
// カーソルキー左右で回転レート調整、上下で繰り返し回数変更
//
//
// 前回のグラディウスの触手風のアルゴリズムから、もうちょっと
// まともなIKっぽいやり方に変えてみた。
//
// 毎回初期状態にリセットしてから、根元から目標に向かって
// 最大レートまで回転させるのを繰り返す。繰り返し回数を増やすと
// 精度が上がる。
//
// 動きの面白さは減ったけど、このアルゴリズムならクォータニオンを
// 使えば3次元でも関節計算用のIKとしてそのまま使えそう。
//  
package {     
    import flash.display.Sprite;     
    import flash.events.*;     
    [SWF(width="465", height="465", backgroundColor="0xFFFFFF", frameRate="60")]      
    public class FlashTest extends Sprite {     
        public function FlashTest() {     
            Main = this;     
            initialize();     
            stage.addEventListener(Event.ENTER_FRAME,update);      
            stage.addEventListener(KeyboardEvent.KEY_DOWN, keyCheckDown);       
            stage.addEventListener(MouseEvent.MOUSE_DOWN,  onClick);       
        }     
    }     
}             

import flash.display.Sprite;     
import flash.text.*; 
import flash.events.*;     
import flash.geom.*;  
import flash.ui.Keyboard;       
var SCREEN_W:Number = 465, SCREEN_H:Number = 465;    
var Main:Sprite;     
var Text:TextField    
var RepNum:int = 10;
var RotRate:Number = 0.1;

var IkAry:Array = new Array;

function initialize():void{     
    Text = new TextField();     
    Text.text = "";   
    Text.autoSize = "left";   
    Main.addChild(Text);      

    IkAry.push( new IkBase(100, 150, 14 ) );
}     

function onClick(event:MouseEvent):void{ 
    IkAry.push( new IkBase(Main.stage.mouseX, Main.stage.mouseY,  8+Math.random()*5 ) );

    if( IkAry.length > 6 ){  
        IkAry.splice( 0, 1 );
    }
} 

function keyCheckDown(event:KeyboardEvent):void {       
    switch (event.keyCode){       
        case Keyboard.UP:    RepNum++; break;     
        case Keyboard.DOWN:  RepNum--; break; 
        case Keyboard.LEFT:  RotRate-=0.005; break;     
        case Keyboard.RIGHT: RotRate+=0.005; break; 
    }      
    if( RepNum < 1 ) RepNum = 1; 
}  


function update(e :Event):void{     
    graphicClear();   

    Text.text = "回転レート:"+(RotRate).toFixed(3)+"  繰り返し回数:"+RepNum;   

    var ik:IkBase;
    for each( ik in IkAry ) ik.resetNodeRot();
    for( var i:int=0; i<RepNum; i++ ) {
        for each( ik in IkAry ) ik.update();
    }
    for each( ik in IkAry ) ik.draw();
}  

class Node{
    public var x:Number;
    public var y:Number;
    public var Rot:Number = 0;
    public var WorldRot:Number = 0;
    public function Node( ){
        
    }
}

class IkBase{
    public var Length:Number = 25.0;
    public var Base:Point = new Point(0,0);
    public var Target:Point = new Point(0,0);
    public var Nodes:Vector.<Node> = new Vector.<Node>; 

    public function IkBase( x:Number, y:Number, num:int ){
        Base.x = x;
        Base.y = y;
        for( var i:int=0; i<num; i++ ) Nodes[i] = new Node();          
        calcNodePos();
    }

    // 内積
    public function dot( p0:Point, p1:Point ):Number{
        return p0.x*p1.x + p0.y*p1.y
    }

    // 値のクランプ
    public function clamp( x:Number, min:Number, max:Number ):Number{ 
        if( x < min ) x = min; 
        if( x > max ) x = max; 
        return x; 
    } 

    // 角度を-PIからPIに収める
    public function radAdjust( rad:Number ):Number{
	if( rad < -Math.PI ){
		do{ rad += Math.PI*2; }while( rad < -Math.PI );
	}else if( rad > Math.PI ){
		do{ rad -= Math.PI*2; }while( rad > Math.PI );
	}
	return rad;
    }

    //	角度を加算して-πからπに調整
    public function radAdd( lhs:Number, rhs:Number ):Number{
	return radAdjust( lhs + rhs );
    }

    //	角度の差分を取って-πからπに調整
    public function radSub( lhs:Number, rhs:Number ):Number{
	return radAdjust( lhs - rhs );
    }

    // 角度を制限つきで線形補間
    public function radLerpRange( src0:Number, src1:Number, rate:Number, rotSpd:Number ):Number{
	var add:Number = radSub( src1, src0 ) * rate;
	add = clamp( add, -rotSpd, rotSpd );
	return radAdd( src0, add );
    }


    public function update():void{
        Target.x = Main.stage.mouseX;
        Target.y = Main.stage.mouseY;
        
        var rotMax:Number = 0.001;        

        // 先端ノード
        var sn:Node = Nodes[Nodes.length-1]
       for( var i:int=1; i<=Nodes.length-2; i++ ){
    // for( var i:int=Nodes.length-2; i>=1; i-- ){    // 逆順にすると計算が不安定になりやすい?
            var n:Node = Nodes[i];
            var tv:Point = new Point();
            var sv:Point = new Point();
            var rv:Point = new Point();
            var lv:Point = new Point();
            // 間接から目標に向いたベクトル
              tv.x = Target.x - n.x;
            tv.y = Target.y - n.y;
            
            // 間接から先端に向いたベクトル
              sv.x = sn.x - n.x;
            sv.y = sn.y - n.y;

            if( tv.x == 0 && tv.y == 0 ) tv.y = 1;
            if( sv.x == 0 && sv.y == 0 ) sv.y = 1;

            var angT:Number = Math.atan2( tv.x, tv.y );
            var angS:Number = Math.atan2( sv.x, sv.y );
            var ang:Number = radLerpRange( angS, angT, RotRate, Math.PI );
            n.Rot += ang - angS;
            
        }
        calcNodePos();
    }

    public function calcNodePos():void{
        Nodes[0].x = Base.x;
        Nodes[0].y = Base.y;
        var pn:Node = Nodes[0];
        for( var i:int=1; i<Nodes.length; i++ ){
            var n:Node = Nodes[i];
            n.WorldRot = pn.WorldRot + n.Rot;
            n.x = Length * Math.sin( pn.WorldRot );
            n.y = Length * Math.cos( pn.WorldRot );
            n.x += pn.x;
            n.y += pn.y;
            pn = n;
        }
    }

    public function resetNodeRot():void{
        for( var i:int=0; i<Nodes.length; i++ ){
            var n:Node = Nodes[i];
            n.Rot = 0;
            n.WorldRot = 0;//pn.WorldRot + n.Rot;
        }
    }

    public function draw():void{
        var pn:Node = Nodes[0];
        for( var i:int=1; i<Nodes.length; i++ ){
            var n:Node = Nodes[i];
            drawLine( pn.x, pn.y, n.x, n.y, 7.5, 0x808020 ); 
            pn = n;
        }

        drawCircle( Target.x, Target.y, 12, 0xe04000 );        
        for each( n in Nodes ) drawCircle( n.x, n.y, 8, 0xe0d000 );        
        drawCircle( Base.x, Base.y, 12, 0xb0a000 );        
    }
}


function graphicClear():void{   
    Main.graphics.clear();    
    Main.graphics.lineStyle(1.2,0xb0b040);        
    Main.graphics.moveTo( SCREEN_W/2,          0 );        
    Main.graphics.lineTo( SCREEN_W/2,   SCREEN_H );           
    Main.graphics.moveTo(          0, SCREEN_H/2 );        
    Main.graphics.lineTo(   SCREEN_W, SCREEN_H/2 );           
}   

function drawLine( sx:Number, sy:Number, ex:Number, ey:Number, size:Number, col:int ):void{     
    Main.graphics.lineStyle(size,col);        
    Main.graphics.moveTo( sx, sy );        
    Main.graphics.lineTo( ex, ey );           
} 

function drawCircle( x:Number, y:Number, size:Number, col:int ):void{     
    Main.graphics.lineStyle(1.4,0x000000);        
    Main.graphics.beginFill(col,1);    
    Main.graphics.drawCircle(x,y,size);    
    Main.graphics.endFill();    
}