ActionScriptで弾力シート

ActionScriptで、伸び縮みするシートを作ってみました。弾性体の物理シミュレーションで使われてる手法をなるべく簡単に。バネ運動、ドラッグ、など(非力なマシンの方、お気をつけて)。

弾力のあるシート

適当に球をドラッグして遊んでください。

バネ運動

ザクッと言って、バネ運動の力は、「基点との距離に比例」します。ここではいろいろ端折ってますので、正確なところは物理の教科書を読んでください。ke'ik jo':k skri'ptさんのエントリ(ActionScript3/Flash9)もとても参考になります。

下の球をドラッグして離すと、基点にボヨンと戻るようになっています。

球にリンクしたActionScriptクラスは、以下のとおりです。
戻る方向は、三角関数を使って計算しています。


 class Spring extends MovieClip{
  var center_x = 100;//基点
  var center_y = 100;//基点
  var SPRING_CONST = 1.7;
  var onDrag = false;
  function onLoad(){
   this._x = center_x;
   this._y = center_y;
  }
  function onPress(){ //ドラッグ開始
    onDrag = true;
    this.startDrag();
  }
  function onRelease(){ //ドラッグ終了
    onDrag = false;
    this.stopDrag();
  }
  function onEnterFrame(){
   if( onDrag ) return; //ドラッグ中は無効

   var rx = center_x - this._x;
   var ry = center_y - this._y;
   var radian = Math.atan2( ry, rx ) ;

   //力は、基点との距離に比例
   var N = Math.sqrt(Math.pow(rx,2)+Math.pow(ry,2));

   //戻る方向を、X軸、Y軸に按分する。
   var sx = N * Math.cos(radian);
   var sy = N * Math.sin(radian);

   this._x += SPRING_CONST*(sx);
   this._y += SPRING_CONST*(sy);
  }
 }

バネの固定点を複数にする

ここまでは、基点は1つでしたが、タマが4本のバネにつながっていることを考えてみます。次のFlashは、4つのバネの固定点(黒タマ)のバランスがとれるところに、ボヨンするはずです。固定点を移動させて確かめてみてください。

とはいっても、むずかしく考える必要はなく、それぞれの固定点との関係を別々に計算して、最後に足し算すればOKです。修正したSpringクラスは、次のようになっています。


class Spring extends MovieClip{

  var relatives:Array; //固定点のオブジェクト
  var relative_size = 0;

  var Sxs:Array;
  var Sys:Array;

  function Spring(){
    relatives = new Array();
    Sxs = new Array();
    Sys = new Array();
  }

  //バネの固定点を追加する。
  function addRelative(relative){
    this.relatives.push(relative);
    relative_size++;
  }
    :
  function onEnterFrame(){
    :
    for( var i = 0; i < relative_size; i++ ){ //別々に計算して、
      var rx = this.relatives[i]._x - this._x;
      var ry = this.relatives[i]._y - this._y;
      var radian = Math.atan2( ry , rx ) ;
      var N = Math.sqrt( Math.pow(rx,2)+Math.pow(ry,2) );
      this.Sxs[i] = N * Math.cos(radian);
      this.Sys[i] = N * Math.sin(radian);
    }
    //足し算する。
    this._x += SPRING_CONST*this.sumArray(this.Sxs)/this.relative_size;
    this._y += SPRING_CONST*this.sumArray(this.Sys)/this.relative_size;
  }
 }

格子状につなぐ

そして、タマがいくら増えても、関係は変わりません。ひとつのタマにかかる力は、上と同じ4方向だけ。

ですので、Springクラスは変更せずに、タマを生成して、格子状に編みこむコードを書けば、冒頭のようになります。ここでもオブジェクト指向は、威力を発揮しまくりですね。もっと格子を細かくしていけば、トランポリンみたいな感じになると思います。

くどいようですが、正確な物理は端折ってるので、あしからず。

付録-格子を編むコード

var oset = 10;//全体配置のオフセット
var x_int = 25;//横方向の間隔
var y_int = 25;//縦方向の間隔
var x_max = 10;//横に並べる個数
var y_max = 10;//縦に並べる個数

var ballCount = x_max*y_max; //タマの個数

//タマと固定点のオブジェクトの格納領域
var balls:Array = new Array();
var fixTs:Array = new Array();
var fixLs:Array = new Array();
var fixRs:Array = new Array();
var fixBs:Array = new Array();

//シート周辺の固定点を配置する
for( var i =0; i < x_max; i++ ){
  fixTs.push(_root.attachMovie( 'fix', 'fixT'+i,
     _root.getNextHighestDepth(),
    { _x: (i+1) * x_int+oset, _y: oset } ) );
  fixBs.push(_root.attachMovie( 'fix', 'fixB'+i,
     _root.getNextHighestDepth(),
     { _x: (i+1) * x_int+oset, _y: (y_int)*(y_max+1)+oset } ) );
  fixLs.push(_root.attachMovie( 'fix', 'fixL'+i,
     _root.getNextHighestDepth(),
     { _x: oset, _y: (i+1)*y_int+oset } ) );
  fixRs.push(_root.attachMovie( 'fix', 'fixR'+i,
     _root.getNextHighestDepth(),
     { _x: (x_int)*(x_max+1)+oset, _y: (i+1)*y_int+oset } ) );
}

//タマをたくさん生成する。
//力が均衡するように自動配置されるので、座標指定はいりません。
for( var i = 0; i < ballCount; i++ ){
  balls.push( _root.attachMovie( 'ball' , 'ball' + i , _root.getNextHighestDepth(), { } ) );
}

//タマに、それぞれの固定点(4つずつ)を設定する。
var row = 0;
var col = 0;

for( var i = 0; i < ballCount; i++ ){
  //上端なら
  if( row == 0 ){
    balls[i].addRelative( fixTs[ col ] );
  }else{
    balls[i].addRelative( balls[ i - x_max ] );
  }
  //下端なら
  if( row == y_max - 1 ){
    balls[i].addRelative( fixBs[ col ] );
  }else{
    balls[i].addRelative( balls[ i + x_max ] );
  }
  //左端なら
  if( col == 0 ){
    balls[i].addRelative( fixLs[row] );
  }else{
    balls[i].addRelative( balls[i-1] );
  }
  //右端なら
  if( col == x_max-1 ){
    balls[i].addRelative( fixRs[row] );
    row++;
    col = 0;
  }else{
    balls[i].addRelative( balls[i+1] );
    col++;
  }
}

コメント

コメントしてください

closed.