Flashの学習(12) オブジェクト指向で考えるActionScript
なんだかさ、この本やってると ActionScriptというより、JavaScriptの知識が増える感じさ。
JavaScriptとActionScriptは文法的には同じなんでしょ?
ActionScript1.0はWebブラウザに搭載されてるのと同じ、ECMAScript 3 (ECMA-262 Edition 3)の文法なんだ。
ところがActionScript2.0/3.0はWebブラウザに搭載されているのと異なる、ECMAScript 4 になってしまったので、ActionScriptとJavaScriptは離れていきつつある。詳しくは google:ActionScript ecma 3.0
まあそういう意味でも、ActionScript1.0学ぶのはムダではない。
第6章 オブジェクトを拡張する
すでにあるStringやArrayオブジェクト(クラス)に、メソッドを追加できるぞ。
それでは、クラスにメソッドを追加することで、21個の数字をシャッフルしてみよう。
//シャッフル Array.prototype.shuffle = function(){ var i = this.length; while(i){ var j = Math.floor(Math.random()*i); i--; var t = this[i]; this[i] = this[j]; this[j] = t; } } //数字の頭に 0 や空白などを付加し、文字列として返す Number.prototype.padding = function(pad_str, pad_length){ var s = ''; //string for(var i=0;i<pad_length;i++){ s += pad_str; } s += this.toString(); return s.substr(s.length-pad_length, s.length); } //21個の配列を作成。まだ並べ替えてない var ar = new Array(); for(var i=0; i<=20; i++){ ar[i] = i.padding('0',3); } //並べ替え前の結果を見る for(var i=0; i<=20; i++){ trace(i.padding('0',2) +":"+ar[i]); } ar.shuffle(); //並べ替えた後の結果を見る for(var i=0; i<=20; i++){ trace(i.padding('0',2) +":"+ar[i]); }
これを動作させると、もともとこんな配列が
00:000 01:001 02:002 03:003 04:004 05:005 06:006 07:007 08:008 09:009 10:010 11:011 12:012 13:013 14:014 15:015 16:016 17:017 18:018 19:019 20:020
以下のようにシャッフルされるのだ。
00:004 01:001 02:020 03:019 04:018 05:008 06:014 07:007 08:015 09:002 10:017 11:011 12:010 13:000 14:012 15:009 16:016 17:003 18:006 19:013 20:005
これはどういう仕組み?
シャッフルの動作を見ると……
- i = this.length = 21 になる。
- Math.random()*i は 0.0〜20.99の範囲のランダムな数(小数あり)を生成する。ここでは 5.43 とする。
- Math.floor()で小数点切捨て。0〜20の整数になる。ここでは j = 5 とする。
- 変数 t (temporaryの略)に、一時的に ar[20]の内容つまり 20 を入れる。
- ar[20]とar[5]の内容を置き換える。 → ar[20]=5、 ar[5]=20
- これを iが0になるまで実行する。つまり配列のおしりar[20]から処理して、ar[0]まで処理する
この方法だと、並べ替えによる偏りも ないらしい。
http://www.fumiononaka.com/TechNotes/Flash/FN0212003.html
■次、新規クラス定義。
クリックしているとき、オブジェクトが螺旋の動きをするぞ。
http://sites.google.com/site/itouhiro/2009/20090628oop_as06_04.swf
//クラス定義 function CirclePlot(ox,oy){ // origin x x = ox; y = oy; } //クラス名とプロパティの定義、兼 コンストラクタ定義 constractor CirclePlot.prototype.getAngleAsRad = function(dx,dy){ //destination x return Math.atan2(dy-y, dx-x); } //メソッド定義 CirclePlot.prototype.getDistance = function(dx,dy){ var tmp = Math.pow(dx-x,2) + Math.pow(dy-y,2); return Math.sqrt(tmp); } CirclePlot.prototype.getPoint = function(dist,rad){ // distance, radian, destination x var dx = x + dist*Math.cos(rad); var dy = y + dist*Math.sin(rad); return {x:dx, y:dy}; } // main this.onMouseDown = function(){ this.onEnterFrame = this.goRound; } this.onMouseUp = function(){ delete this.onEnterFrame; } function goRound(){ var myobj = new CirclePlot(_parent._xmouse, _parent._ymouse); dx = (_parent._xmouse - _x) / 100; //delta x dy = (_parent._ymouse - _y) / 100; //trace("_parent,_xmouse="+ _parent._xmouse +", _parent._ymouse="+ _parent._ymouse); var rad = myobj.getAngleAsRad(_x+dx,_y+dy); var dist = myobj.getDistance(_x+dx,_y+dy); var newLoc = myobj.getPoint(dist,rad+0.1); //new location _x = newLoc.x; _y = newLoc.y; }
このクラスはどこで使ってるんだ?
座標の計算に使ってる。
1フレームごとに、CirclePlotクラスのインスタンスを生成して、マウス座標と現在のムービークリップオブジェクト(キリン)との座標から、角度(rad)と距離(dist)を求めて、最終的に新しいムービークリップオブジェクト座標を計算する。
■次、継承なんだが……この本、サンプル紹介の途中でいきなり終わってるんだよ。サンプル動かすまで説明してほしいよ。
内容も、シンボルを回転させるために回転させる基準点を別に持たせようとしてるんだけど、シンボルの原点を中心軸にしてしまえばいいんじゃないのか? 何がしたいのか、よく分からないぞ。
たぶん、回転軸をシンボル原点とは別に持たせたかったんじゃないのか、と思って、こんなのを作ってみた。
マウスをクリックしたときと、スペースバー押したときで、回転軸が変わるぞ。
https://sites.google.com/site/itouhiro/2009/20090628oop_as06_05_7.swf
手書きのキーボードイラストに味があるような。
マウスとキーボードは、二つ合わせて1つのシンボルなんだ。コンポーネント定義というので特殊な変数定義をしたけど、これもこのサンプルでは必要性がよく分からなかった。
ソースコードはこんなのだ。 シンボル'mousekey'の1フレーム目に書く。
#initclip function myClass(){ this.fstx = _x; //first x this.fsty = _y; } myClass.prototype = new MovieClip(); myClass.prototype.rotateAsRad = function(rad, inputdevice){ var deg = rad * 180 / Math.PI; if(inputdevice == "kbd"){ var ax = this.kbd_cx*Math.cos(rad) - this.kbd_cy*Math.sin(rad); //angle x var ay = this.kbd_cx*Math.sin(rad) + this.kbd_cy*Math.cos(rad); this._x = this.fstx + this.kbd_cx - ax; this._y = this.fsty + this.kbd_cy - ay; } this._rotation = deg; } myClass.prototype.getNewOrigin = function(ox,oy,rad){ //original x var kbd_dist = Math.sqrt(Math.pow(this.kbd_cx,2) + Math.pow(this.kbd_cy,2)); var kbd_rotate = this.getAngleAsRad(0,0, this.kbd_cx,this.kbd_cy) / 1.6; var kbd_center_x = ox + kbd_dist*Math.cos(rad+kbd_rotate); var kbd_center_y = oy + kbd_dist*Math.sin(rad+kbd_rotate); var rotate_center_x = kbd_center_x-this.kbd_cx; var rotate_center_y = kbd_center_y-this.kbd_cy; return {x:rotate_center_x, y:rotate_center_y}; } myClass.prototype.getAngleAsRad = function(sx,sy, dx,dy){ //source x, destination x return Math.atan2(dy-sy, dx-sx); } Object.registerClass("mousekey_link", MyClass); delete myClass; #endinitclip // main var rad = 0.0; var isMouseDown = false; var kbd_cont = false; //var isKbdDown = false; //this.onKeyDown = function(){ // isKbdDown = true; //} //this.onKeyUp = function(){ // isKbdDown = false; //} this.onMouseDown = function(){ isMouseDown = true; } this.onMouseUp = function(){ isMouseDown = false; } this.onEnterFrame = function(){ if(isMouseDown){ this.rotateAsRad(rad,"mouse"); rad += 0.1; } // if(isKbdDown){ if(Key.isDown(Key.SPACE)){ if(! kbd_cont){ kbd_cont =true; var tobj = this.getNewOrigin(_x,_y,rad); //temp object this.fstx = tobj.x; this.fsty = tobj.y; } this.rotateAsRad(rad,"kbd"); rad += 0.1; }else{ kbd_cont = false; } }
イベントハンドラのonKeyDownがなぜか使えない。
調べたら、フォーカスをどうのこうの……と書いてあったけど http://www.fumiononaka.com/TechNotes/Flash/FN0305001.html
フォーカスしたはずなのに使えない。しかたないので毎フレーム Key.isDown() で調べてる。→ Key.addListener(this); 使えばよかったようだ。
キーボード中心に回すための getNewOriginメソッドは動くけど、ちょっと不明な点もある。
クラスの定義をMovieClipのInstanceロード前に実行したいけど、
ステージにすでにMovieClipのInstanceを置いているとき、「#initclip 〜 #endinitclip」を使えばステージ表示前にスクリプトを実行できる。
この#initの中では、thisつけるのとつけないので結果が違ってしまうので注意。thisつけないと、クラスのインスタンスではなく、_root (_level0)がthisになってしまうようだぞ。
「myClass.prototype = new MovieClip();」て MovieClipオブジェクス(クラス)を継承している。
Object.registerClass というのがクラス定義なのかな? さっきはこれ使ってなかったけど。