Flashの学習(12) オブジェクト指向で考えるActionScript

[しゃべり担当] なんだかさ、この本やってると ActionScriptというより、JavaScriptの知識が増える感じさ。


[合いの手担当] JavaScriptActionScriptは文法的には同じなんでしょ?


[しゃべり担当] ActionScript1.0はWebブラウザに搭載されてるのと同じ、ECMAScript 3 (ECMA-262 Edition 3)の文法なんだ。

ところがActionScript2.0/3.0はWebブラウザに搭載されているのと異なる、ECMAScript 4 になってしまったので、ActionScriptJavaScriptは離れていきつつある。詳しくは 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 というのがクラス定義なのかな? さっきはこれ使ってなかったけど。


初めてのJavaScript

初めてのJavaScript