dynamicクラスとsealedクラス

2011.02.02kickbaseActionScript3.0


今回はダイナミックプロパティについて解説します。ダイナミックプロパティとは何か?ダイナミックプロパティを設定可能なクラスを作るには?というところをお話しします。特にAS2をバリバリ書いていた方や、JSに精通している方には当然の性質だと思いますが、AS3から始めた方には理解に苦しむところもあると思いますので、基本的なところを見ていきましょう。

00-01 : MovieClipの場合
参考書籍や諸先輩のコードを見ていると、下記のようなものを見かけることがあると思います。MovieClipには元々speed というプロパティはありませんが、宣言することなく唐突に出てきて代入をすることが可能です。もちろんエラーも出ません。

var mc:MovieClip = new MovieClip();
mc.speed = 100;

00-02 : Spriteの場合
それでは上記と同じことをSpriteでやってみるとどうなるでしょうか。見事にコンパイルエラーが出ますね。

var sp:Sprite = new Sprite();
sp.speed = 100;//そんなプロパティはないよというエラーがでます

このようにインスタンスに対して動的にプロパティ、メソッドを追加することが出来るクラスのことをdynamicクラスと呼び、インスタンスに対してプロパティやメソッドの動的追加ができないクラスのことをsealedクラスと呼びます。上記の例ではMovieClipがdynamicクラスで、Spriteがsealedクラスとなるわけです。

AS2ではこのような区別がなく、全てのオブジェクトに対して動的にプロパティ、メソッドを追加することができます(つまり全てdynamicクラス扱いという訳です)。AS3では通常のクラスはsealed属性であり、dynamicクラスの方が特殊な扱いとなります。

※dynamic属性は非常に便利な性質ですが、多用しすぎないようにしましょう。sealed属性に比べ処理速度が遅くなる点や、コードアシストも効かないなどデメリットもあります。

01 : 動的にプロパティ、メソッドを追加する方法について
ここではdynamicクラスの代表選手であるObjectクラスとMovieClipクラスを例に、動的にプロパティ、メソッドを追加する方法について解説します。といってもObjectクラスを普段使用している方にはご存知の内容だと思います。ドットシンタックスで追加する方法と、連想配列で追加する方法があるということです。(ここでは追加されたプロパティ、メソッドの確認をfor~inステートメントで行っています)

Main

package 
{
	import flash.display.MovieClip;
	import flash.display.Sprite;
	
	/**
	 * ...
	 * @author kickbase
	 */
	public class Main extends Sprite 
	{
		public function Main():void 
		{
			/*
			 * Object:ドットシンタックスでプロパティを追加
			*/
			var obj1:Object = { };
			obj1.prop1 = "文字列";
			obj1.prop2 = 50;
			obj1.prop3 = new Sprite();
			
			for (var key:String in obj1) 
			{
				trace(key + " : " + obj1[key]);
			}
			
			trace("---");
			
			/*
			 * Object:連想配列でプロパティを追加
			*/
			var obj2:Object = {
				sky:"空",
				ocean:"海",
				ground:"大地"
			}
			
			for (key in obj2) 
			{
				trace(key + " : " + obj2[key]);
			}
			
			trace("---");
			
			/*
			 * MovieClip:ドットシンタックスと連想配列でプロパティを追加
			*/
			var mc:MovieClip = new MovieClip();
			mc.speedX = 100;//ドットシンタックス
			mc["speedY"] = 50;//連想配列
			
			for (key in mc) 
			{
				trace(key + " : " + mc[key]);
			}
			
			trace("---");
			
			/*
			 * プロパティだけではなくメソッドも動的に追加可能
			*/
			mc.func = function():void { trace(this + " : 動的に追加されたメソッド"); };
			mc.func();//インスタンス経由で実行
		}
	}
}

02-01 : ダイナミックプロパティの使用例
20個の円が配置されており、ステージをクリックする度にランダムに移動します。 この時円は下記項目を個々に保持しているところがポイントとなります。

▼friction(摩擦係数)
▼targetX(目的地のX座標)
▼targetY(目的地のY座標)

上記ダイナミックプロパティをインスタンスに対して個々に設定することで、全ての円が独立した目的地、摩擦係数を持つことができます。

Main

package 
{
	import flash.display.MovieClip;
	import flash.display.Sprite;
	import flash.display.StageAlign;
	import flash.display.StageScaleMode;
	import flash.events.Event;
	import flash.events.MouseEvent;
	
	/**
	 * ...
	 * @author kickbase
	 */
	[SWF(width = "640", height = "480", frameRate = "30", backgroundColor = "#ffffff")]
	 
	public class Main extends Sprite 
	{
		private var arr:Array = [];
		public function Main():void 
		{
			if (stage) init();
			else addEventListener(Event.ADDED_TO_STAGE, init);
		}
		
		private function init(e:Event = null):void 
		{
			removeEventListener(Event.ADDED_TO_STAGE, init);
			
			//ステージ設定
			stage.scaleMode = StageScaleMode.NO_SCALE;
			stage.align = StageAlign.TOP_LEFT;
			
			createBalls();
			stage.addEventListener(MouseEvent.CLICK, onClick);
			addEventListener(Event.ENTER_FRAME, loop);
		}
		
		private function createBalls():void
		{
			for (var i: int = 0; i < 20; i++) 
			{  
				var mc:MovieClip = new MovieClip();
				var posX:int = Math.random() * stage.stageWidth;
				var posY:int = Math.random() * stage.stageHeight;
				var color:uint = Math.random() * 0xFFFFFF;
				
				with (mc.graphics)
				{
					beginFill(color);
					drawCircle(0, 0, 20);
					endFill();
				}
				
				mc.friction = Math.random();//プロパティfriction(摩擦係数)を追加しています
				mc.x = mc.targetX = posX;//プロパティtargetX(目的地のX座標)を追加しています
				mc.y = mc.targetY = posY;//プロパティtargetY(目的地のY座標)を追加しています
				
				addChild(mc);
				arr.push(mc);
			}
		}
		
		private function onClick(e:MouseEvent):void 
		{
			for (var i: int = 0; i < arr.length; i++) 
			{  
				var mc:MovieClip = arr[i] as MovieClip;
				
				//個々のMovieClipの目的地(targetX,targetY)を設定しなおします
				mc.targetX = Math.random() * stage.stageWidth;
				mc.targetY = Math.random() * stage.stageHeight;
			}
		}
		
		private function loop(e:Event):void 
		{
			for (var i: int = 0; i < arr.length; i++) 
			{  
				var mc:MovieClip = arr[i] as MovieClip;
				
				//イージングの公式を用いて、各々目的地まで移動します
				mc.x += (mc.targetX  - mc.x) * mc.friction;
				mc.y += (mc.targetY  - mc.y) * mc.friction;
			}
		}		
	}
}

02-02 : 通常の静的なプロパティに置き換える
Spriteを継承したBallクラスをsealed属性で作成します。動的なプロパティ追加はできないため、Ballクラスにプロパティを設定しておきます。この手法が一般的なAS3の作法となります。
※記事が長くなるため本ソースの掲載は割愛させていただきます。(サンプルには全クラスが含まれています)

03 : dynamicクラスの独自実装
最後にdynamicクラスを独自実装する方法を掲載しておきます。方法としては非常に簡単で、クラスを宣言する際dynamic属性を記載するだけです。

Main

package 
{
	import flash.display.Sprite;
	
	/**
	 * ...
	 * @author kickbase
	 */
	public class Main extends Sprite 
	{
		public function Main():void 
		{
			var dynamicObj:DynamicClass = new DynamicClass();
			
			//ダイナミックプロパティに対し、クラスで定義されたプロパティは、固定プロパティと呼ばれます
			trace( "dynamicObj.num : " + dynamicObj.num );

			dynamicObj.prop1 = "動的にプロパティを付与できます";//ダイナミックプロパティ
			dynamicObj.prop2 = new Array(10, 20, 30);//もちろん配列も保持できます

			for (var key:String in dynamicObj)
			{
				dynamicObj[key]
				trace( key + " : " + dynamicObj[key] );
			}
			
			//プロパティだけでなくメソッドも付与可能
			dynamicObj.doFunc = dynamicMethod;
			dynamicObj.doFunc();
		}
		
		private function dynamicMethod():void
		{
			trace("メソッドを付与することも可能です");
		}
	}
}

DynamicClass

package  
{
	/**
	 * ...
	 * @author kickbase
	 */
	public dynamic class DynamicClass
	{
		public var num:int = 100;
	}
}

サンプルのダウンロード

参考サイト

Adobe ActionScript 3.0 * ダイナミッククラス
クラス周りの仕様(ActionScript3) – 自分用メモとか
ダイナミックプロパティ – c9日記 -カタヤマンがプログラマチックに今日もコードアシスト


ページトップへ