[as3]の最近のブログ記事
例えばイテレータみたいなのを定義するときに
隣から「邪道」「余計にめんどくさい」と一蹴されてしまった。
そもそも、こんなことしてもコード補完は効かないから、 型に * 指定するのと変わらなくなってしまった。
っていうダメダメな話。
public interface Iterator
{
public function next():Object;
}
ってすると、これを実装したクラスを作るときに、 next() が必要なことは明示できるけど
使うときに中身がなんなのかはっきりしなくて、何となく気に入らないので
public interface Iterator
{
public function get next():Function;
}
ってやって、実装したクラスは
public class SpriteIterator implements Iterator
{
public function get next():Function
{
return getNext;
}
private function getNext():Sprite
{
return spriteInstance;
}
}
ってやればいいんじゃないかと、つぶやいたら隣から「邪道」「余計にめんどくさい」と一蹴されてしまった。
そもそも、こんなことしてもコード補完は効かないから、 型に * 指定するのと変わらなくなってしまった。
っていうダメダメな話。
var x:int = 9; var y:int = 3; trace(x + y); // 12 trace(x - y); // 6 trace(x * y); // 27 trace(x / y); // 3 trace(x % y); // 0 trace(x & y); // 1 trace(x | y); // 11 trace(x ^ y); // 10
現在の Progression にはシーンオブジェクトにイベントフローが実装されていますが、
表示オブジェクトとは少し挙動が違っています。(Progression 3.0.7 現在)
useCapture や eventPhase, bubbles あたりの値が有効化されていませんので、これを実装してみました。
変更を加えたファイルを以下に置いておきます。
jp/progression/events/progression_event
jp/progression/events/SceneEvent
jp/progression/scenes/SceneObject
未だ as ファイルだと表示できなくて、拡張子を .txt に変えてますが。
リンク先を trac.progression.jp の方に変更しました。
まとめてダウンロードされる場合はこちら。
eventflow.zip
以下、解説を少し。
出力結果
変更したのは SceneEvent クラスと SceneObject クラスの 2 つ。
まず SceneEvent クラスに以下を追記。
dispatchEvent() にイベントオブジェクトを渡したときに target に値が設定されていると、 dispatchEvent 内で Event.clone() が呼び出されるので、 super.target が null のときは null を返すようにしています。
もし $target が設定されていて super.target も設定されている場合は $target の値を、 $target が null でも super.target が設定されている場合は super.target の値を返します。
これは、 dispatchEvent() の内部以外での clone() メソッドの呼び出しへの配慮でもあります。
次はちょっと長いですが、 SceneObject に追加した内容。
具体的には、 addEventListener() と removeEventListener() で useCapture の値で 登録するオブジェクトを分けています。これはイベント呼び出し時にイベントフェーズによる振り分けができないためです。
dispatchEvent() 内は、親シーンへ遡ってイベントを呼び出すように変更しています。
更に、 _registerScene() と _unregisterScene() にも少し修正を加えます。
_registerScene の変更箇所
_unregisterScene の変更箇所
また、イベントフローのための親シーンの参照を登録・解除しています。
あとは、 dispatchEvent に渡されるイベントオブジェクトの bubbles の値を true に変更しました。
bubbles が false に設定された場合はイベントフローを発生させないようにしているためです。
以上で、表示オブジェクトのようなイベントフローの実装ができます。
ただ、 EventIntegrator での実装を、あまり理解せずに手を加えているので、どこかに不都合はあるかもしれないです。
すこし使い倒してみる必要はあるかと思います。
useCapture や eventPhase, bubbles あたりの値が有効化されていませんので、これを実装してみました。
変更を加えたファイルを以下に置いておきます。
jp/progression/events/progression_event
jp/progression/events/SceneEvent
jp/progression/scenes/SceneObject
リンク先を trac.progression.jp の方に変更しました。
まとめてダウンロードされる場合はこちら。
eventflow.zip
以下、解説を少し。
var sceneA:SceneObject = createScene("sceneA");
var sceneB:SceneObject = createScene("sceneB");
var sceneC:SceneObject = createScene("sceneC");
sceneA.addScene(sceneB);
trace("----");
sceneB.addScene(sceneC);
trace("----");
sceneA.removeScene(sceneB);
function createScene(name:String):SceneObject
{
var scene:SceneObject = new SceneObject(name);
scene.addEventListener(SceneEvent.SCENE_ADDED, eventHandler, false);
scene.addEventListener(SceneEvent.SCENE_REMOVED, eventHandler, false);
scene.addEventListener(SceneEvent.SCENE_ADDED, eventHandler, true);
scene.addEventListener(SceneEvent.SCENE_REMOVED, eventHandler, true);
return scene;
}
function eventHandler(event:Event):void
{
trace(event.target.name, event.currentTarget.name, event.type, event.eventPhase);
}
出力結果
sceneB sceneA sceneAdded 1 sceneB sceneB sceneAdded 2 sceneB sceneA sceneAdded 3 ---- sceneC sceneA sceneAdded 1 sceneC sceneB sceneAdded 1 sceneC sceneC sceneAdded 2 sceneC sceneB sceneAdded 3 sceneC sceneA sceneAdded 3 ---- sceneB sceneA sceneRemoved 1 sceneB sceneB sceneRemoved 2 sceneB sceneA sceneRemoved 3
変更したのは SceneEvent クラスと SceneObject クラスの 2 つ。
まず SceneEvent クラスに以下を追記。
override public function get target():Object
{
return super.target
&& progression_event::$target
|| super.target;
}
override public function get currentTarget():Object
{
return super.currentTarget
&& progression_event::$currentTarget
|| super.currentTarget;
}
override public function get eventPhase():uint
{
return progression_event::$eventPhase || super.eventPhase;
}
progression_event var $target:Object = null;
progression_event var $currentTarget:Object = null;
progression_event var $eventPhase:uint = 0;
ここで SceneObject から値に手を加えられるようにするために、
progression_event という namespace ファイルを jp/progression/events/ 内に追加しています。
package jp.progression.events
{
public namespace progression_event
= "http://www.progression.jp/events/progression_event";
}
dispatchEvent() にイベントオブジェクトを渡したときに target に値が設定されていると、 dispatchEvent 内で Event.clone() が呼び出されるので、 super.target が null のときは null を返すようにしています。
もし $target が設定されていて super.target も設定されている場合は $target の値を、 $target が null でも super.target が設定されている場合は super.target の値を返します。
これは、 dispatchEvent() の内部以外での clone() メソッドの呼び出しへの配慮でもあります。
次はちょっと長いですが、 SceneObject に追加した内容。
/**
* イベントフローのための内部参照
*/
private var $parent:SceneObject;
/**
* キャプチャ段階のイベントに登録されたリスナーを保持しておくための EventIntegrator オブジェクト
*
* コンストラクタ内に以下を追記
* $captureEvent = new EventIntegrator(this);
*/
private var $captureEvent:EventIntegrator;
override public function dispatchEvent(event:Event):Boolean
{
if (event is SceneEvent)
{
return $dispatchEvent(event as SceneEvent);
}
return super.dispatchEvent(event);
}
private function $dispatchEvent(event:SceneEvent):Boolean
{
var bool:Boolean = true;
if (event.eventPhase == EventPhase.AT_TARGET)
event.progression_event::$target = this;
event.progression_event::$currentTarget = this;
if (event.bubbles
&& $parent
&& event.eventPhase <= EventPhase.AT_TARGET)
{
var phase1:SceneEvent = event.clone() as SceneEvent;
phase1.progression_event::$eventPhase = EventPhase.CAPTURING_PHASE;
phase1.progression_event::$target
= event.progression_event::$target;
bool = $parent.dispatchEvent(phase1);
}
if (event.eventPhase == EventPhase.CAPTURING_PHASE)
{
bool = $captureEvent.dispatchEvent(event);
}
else
{
bool = super.dispatchEvent(event);
}
if (event.bubbles
&& $parent
&& event.eventPhase >= EventPhase.AT_TARGET)
{
var phase3:SceneEvent = event.clone() as SceneEvent;
phase3.progression_event::$eventPhase = EventPhase.BUBBLING_PHASE;
phase3.progression_event::$target = event.target;
bool = $parent.dispatchEvent(phase3);
}
return bool;
}
override public function addEventListener(
type:String, listener:Function, useCapture:Boolean = false,
priority:int = 0, useWeakReference:Boolean = false):void
{
if (useCapture)
{
$captureEvent.addEventListener(
type, listener, false, priority, useWeakReference);
}
else
{
super.addEventListener(
type, listener, false, priority, useWeakReference);
}
}
override public function removeEventListener(
type:String, listener:Function, useCapture:Boolean = false):void
{
if (useCapture)
{
$captureEvent.removeEventListener(type, listener, false);
}
else
{
super.removeEventListener(type, listener, false);
}
}
override public function hasEventListener(type:String):Boolean
{
return super.hasEventListener(type)
|| $captureEvent.hasEventListener(type);
}
override public function willTrigger(type:String):Boolean
{
var bool:Boolean = hasEventListener(type);
if ($parent) bool = bool || $parent.hasEventListener(type);
return bool;
}
override public function removeAllListeners(
completely:Boolean = false):void
{
super.removeAllListeners(completely);
$captureEvent.removeAllListeners(completely);
}
override public function restoreRemovedListeners():void
{
super.restoreRemovedListeners();
$captureEvent.restoreRemovedListeners();
}
ここでは EventDispatcher のメソッドをそれぞれオーバーライドして処理内容に手を加えています。具体的には、 addEventListener() と removeEventListener() で useCapture の値で 登録するオブジェクトを分けています。これはイベント呼び出し時にイベントフェーズによる振り分けができないためです。
dispatchEvent() 内は、親シーンへ遡ってイベントを呼び出すように変更しています。
更に、 _registerScene() と _unregisterScene() にも少し修正を加えます。
_registerScene の変更箇所
// イベントフロー時の参照先を登録する
scene.$parent = scene._parent;
// イベントリスナーを登録する
//scene.addExclusivelyEventListener( SceneEvent.SCENE_ADDED,
dispatchEvent, false, int.MAX_VALUE, true );
//scene.addExclusivelyEventListener( SceneEvent.SCENE_REMOVED,
dispatchEvent, false, int.MAX_VALUE, true );
//addExclusivelyEventListener( SceneEvent.SCENE_ADDED_TO_ROOT,
scene.dispatchEvent, false, int.MAX_VALUE, true );
//addExclusivelyEventListener( SceneEvent.SCENE_REMOVED_FROM_ROOT,
scene.dispatchEvent, false, int.MAX_VALUE, true );
// イベントを送出する
scene.dispatchEvent( new SceneEvent(
SceneEvent.SCENE_ADDED, true, false, scene ) );
// ルートシーンが存在し、変更されていれば
if ( root && previousRoot != root ) {
// イベントを送出する
scene.dispatchEvent( new SceneEvent(
SceneEvent.SCENE_ADDED_TO_ROOT, true, false, scene ) );
}
_unregisterScene の変更箇所
// イベントを送出する
scene.dispatchEvent( new SceneEvent( SceneEvent.SCENE_REMOVED, true, false, scene ) );
// ルートシーンが存在せず、変更されていれば
if ( !scene._root && previousRoot != scene._root ) {
// イベントを送出する
scene.dispatchEvent( new SceneEvent( SceneEvent.SCENE_REMOVED_FROM_ROOT, true, false, scene ) );
}
// イベントフローのための参照を削除
scene.$parent = null;
// イベントリスナーを解除する
//scene.completelyRemoveEventListener(
SceneEvent.SCENE_ADDED, dispatchEvent );
//scene.completelyRemoveEventListener(
SceneEvent.SCENE_REMOVED, dispatchEvent );
//completelyRemoveEventListener(
SceneEvent.SCENE_ADDED_TO_ROOT, scene.dispatchEvent );
//completelyRemoveEventListener(
SceneEvent.SCENE_REMOVED_FROM_ROOT, scene.dispatchEvent );
追加・削除された子シーンにイベントリスナーの登録を行っていた処理をコメントアウトしています。また、イベントフローのための親シーンの参照を登録・解除しています。
あとは、 dispatchEvent に渡されるイベントオブジェクトの bubbles の値を true に変更しました。
bubbles が false に設定された場合はイベントフローを発生させないようにしているためです。
以上で、表示オブジェクトのようなイベントフローの実装ができます。
ただ、 EventIntegrator での実装を、あまり理解せずに手を加えているので、どこかに不都合はあるかもしれないです。
すこし使い倒してみる必要はあるかと思います。
役に立つかもしれない情報から、全く無意味としか思えない情報まで。
ひとつひとつエントリーするほどのものでもないので、以下にまとめて書く。
ActionScript とは掛け離れた部分で制御されているのかもしれない。
そのままだと、親オブジェクトの変形の影響をもろに受けてしまうので、
一度 removeChild() で引き剥がした方がいい場合もある。時と場合による。
また、対象が stage 上に配置されていない場合、なぜか取得した値がすべて 5 倍になっているので、その場合は割ってやる必要がある。
さらに、AS 側で [Embed] タグを用いて埋め込んだ場合も、ステージに配置したにもかかわらず、次のフレームに移るまでは、同じく値が 5 倍になっており、 これを判定するのは面倒なので、サイズを調べる場合は、とにかく一度 removeChild() するのが無難かもしれない。
object.constructor は Class から生成されたインスタンスへの参照であって関数ではない。
コンストラクタ関数が MethodClosure インスタンスであるため。
MethodClosure については次項。
が、定義されたクラスメソッドのコンストラクタは Function を継承した MethodClosure クラス。
ちなみに関数が MethodClosure か判別する方法は以下。
上記に反して、以下はキャストしてから引数に入れないとエラーが発生する。
以下のコードはエラーが出る例。
ひとつひとつエントリーするほどのものでもないので、以下にまとめて書く。
■設定されたステージサイズを取得する
stage.addEventListener(Event.ACTIVATE, activateHandler);
function activateHandler(event:Event):void
{
event.target.removeEventListener(Event.ACTIVATE, activateHandler);
var defaultWidth:int = root.loaderInfo.width;
var defaultHeight:int = root.loaderInfo.height;
}
これらの値はステージサイズが変更されても変わらない。
ただし、コンテンツの読み込みが完了しないうちに loaderInfo.width 等を呼び出すとエラーが吐き出されるので、使用には注意。
■レイヤーマスク
タイムライン上で設定されたマスクには不審な点がいくつかある。- マスクオブジェクトはマスクを解除しても表示されなくなる。ただし getRect() や getBounds() 等の矩形情報は残ってる。表示だけがなくなる。
- マスクオブジェクトは深度 (index) が、対象オブジェクトより下に変更される。
- 対象オブジェクトのmask, scrollRect プロパティとは無関係
- マスクを解除する場合は "mask = null" ではなく、マスクオブジェクトをremoveChild() でステージ上から削除するか、setChildIndex() 等を用いて深度を変更する。
- 被マスクオブジェクトの mask プロパティにオブジェクトを指定すると、2重でマスクが掛けられる。
- AS から、別のオブジェクトを被マスクオブジェクトに隣接する深度に設定すると、そのオブジェクトもマスクの対象となる。
もし、現在の被マスクオブジェクトの深度が一番上になっている場合、同階層に addChild() で配置されたオブジェクトも、自動的にマスク対象となってしまう。 - マスクオブジェクトの深度を変更すると、マスクは一度解除されるが、マスクオブジェクトの1つ上の深度にオブジェクトを配置すると、マスク対象になる。
- マスクオブジェクトを他のオブジェクト内に移動させた場合、元のマスクは解除されるが、移動先でもマスク効果は有効であるため、移動後も上記のルールが適応される。例えば、
B.addChild( A.getChildByName("maskObject") );とした場合、A でのマスク効果は解除されるが、B 内では有効となる。
ActionScript とは掛け離れた部分で制御されているのかもしれない。
■scrollRect を指定したオブジェクトのサイズを調べる
function getDefaultRect(target:DisplayObject):Rectangle
{
if(target.parent)
{
var path:DisplayObjectContainer = target.parent;
var index:uint = path.getChildIndex(target);
path.removeChild(target);
}
var bounds:Rectangle = target.transform.pixelBounds;
bounds = new Rectangle(
bounds.x * 0.2 - target.x,
bounds.y * 0.2 - target.y,
bounds.width * 0.2,
bounds.height * 0.2);
if(path)
{
path.addChildAt(target, index);
}
return bounds;
}
transform.pixelBounds を使うと元の大きさが取得できるが、そのままだと、親オブジェクトの変形の影響をもろに受けてしまうので、
一度 removeChild() で引き剥がした方がいい場合もある。時と場合による。
また、対象が stage 上に配置されていない場合、なぜか取得した値がすべて 5 倍になっているので、その場合は割ってやる必要がある。
さらに、AS 側で [Embed] タグを用いて埋め込んだ場合も、ステージに配置したにもかかわらず、次のフレームに移るまでは、同じく値が 5 倍になっており、 これを判定するのは面倒なので、サイズを調べる場合は、とにかく一度 removeChild() するのが無難かもしれない。
■クラスは Class クラスのインスタンスオブジェクト
trace(new Sprite() is Sprite); // true trace(Sprite is Class); // true trace(Sprite is Sprite); // false trace(Class is Class); // true定義されたクラスのコンストラクタ関数と constructor は別物。
object.constructor は Class から生成されたインスタンスへの参照であって関数ではない。
trace(new Object().constructor == Object); // true trace(Object is Class); // true trace(Object is Function); // falseコンストラクタ関数を参照する方法は、真っ当な方法では、おそらく以下の方法しかないかと。
class ExampleClass
{
public var constructorMethod:Function;
public function ExampleClass()
{
constructorMethod = arguments.callee;
}
}
ただしコンストラクタ関数からインスタンスを生成することはできない。コンストラクタ関数が MethodClosure インスタンスであるため。
MethodClosure については次項。
■MethodClosure extends Function
Function クラスは final class なので、継承はできない。が、定義されたクラスメソッドのコンストラクタは Function を継承した MethodClosure クラス。
ちなみに関数が MethodClosure か判別する方法は以下。
function isMethodClosure(func:Function):Boolean
{
return func is
Object(arguments.callee).constructor;
}
var test1:Function = function():void {}
function test2():void {}
trace( isMethodClosure(test1) ); // false
trace( isMethodClosure(test2) ); // true
■クラス内での prototype の使用
package
{
public class PrototypeClass
{
public function PrototypeClass()
{
prototype.property = "property of prototype";
}
prototype.property = null;
prototype.methodProperty = function():void
{
trace("call methodProperty");
}
}
}
var instance:PrototypeClass = new PrototypeClass(); Object(instance).methodProperty(); // call methodProperty trace(instance["property"]); // property of prototype使い道が見当たらない。
■マウスイベントをまとめて無効にする
stage.mouseChildren = false;先日Progressionセミナー後の懇親会で教えてもらった。
■Vector の型指定
var vector:Vector.<Object> = new Vector.<Object>(); trace(getQualifiedClassName(vector)); // output : __AS3__.vec::Vector.<Object>
var vector:Vector.<*> = new Vector.<*>(); trace(getQualifiedClassName(vector)); // output : __AS3__.vec::Vector.<*>
var vector:Vector.<*> = new Vector.<Object>(); trace(getQualifiedClassName(vector)); // output : __AS3__.vec::Vector.<Object>
var vector:Vector.<*> = new Vector.<String>(); trace(getQualifiedClassName(vector)); // output : __AS3__.vec::Vector.<String>
var vector:Vector.<*> = new Vector.<int>(); // TypeError: Error #1034: 強制型変換に失敗しました。 Vector.<int>@1683e41 を __AS3__.vec.Vector.<*> に変換できません。上記のように、型の指定には変数等に使用するときと同様に、 * (アスタリスク)を使うことができるが、 なぜか Number, int, uint の数値型を指定した Vector オブジェクトを代入しようとすると失敗する。
trace(new Vector.<Object>() is Vector); // false trace(new Vector.<Object>() is Vector.<Object>); // true trace(new Vector.<Sprite>() is Vector.<Object>); // false trace(new Vector.<Sprite>() is Vector.<*>); // true trace(new Vector.<String>() is Vector.<*>); // true trace(new Vector.<int>() is Vector.<*>); // falseこういうもんなんだろうか。
上記に反して、以下はキャストしてから引数に入れないとエラーが発生する。
以下のコードはエラーが出る例。
var vector:Vector.<Object> = new Vector.<Object>();
test(vector);
function test(vector:Vector.<*>):void {}
var sprites:Vector.<Sprite> = Vector.<Sprite>([new Sprite()]); var display1:Vector.<DisplayObject> = Vector.<DisplayObject>(sprites); var display2:Vector.<DisplayObject> = sprites;上記は display1 ではキャスト成功するが、display2 のようにそのまま代入しようとするとエラーが出る。
■Vector は Array とは違う
Vector は Array と似ているが、Array とは全く別物なので、以下のようなことは出来ない。
function test(...args:Vector.<*>):void {}
Error: ...残りパラメータ定義のキーワードの後に指定したパラメータで使用できるのは、Array データ型のみです。
func.apply(null, Vector.<int>([1,2,3]));TypeError: Error #1116: Function.prototype.apply の 2 番目の引数は配列でなければなりません。
var matrix:Vector.<Number> =
Vector.<Number>(String("10000010000010000010").split(""));
new ColorMatrixFilter(matrix);
Error: 型 __AS3__.vec:Vector.<Number> の値が、関連しない型 Array に暗黙で型変換されています。
■Vectorオブジェクトの動的生成
動的に Vector オブジェクトを作成したい場合、下記のように変数からではエラーが出て失敗する。var theClass:Class = Sprite; var vector:Vector.<theClass> = new Vector.<theClass>();一度文字列にして作成するとうまくいく。
var T:Class = Sprite;
var classPath:String = getQualifiedClassName(T).replace("::", ".");
var theClass:Class = getDefinitionByName("Vector.<" + classPath + ">") as Class;
trace( getQualifiedClassName(new theClass()) );
// output : __AS3__.vec::Vector.<flash.display::Sprite>
小さいと分かり難いんですが、赤線が curveTo で引いた線。青線が lineTo で20分割して描いてる線。
curveTo の分割数は曲線の形によって変化しています。
少ないときで 1 本。多くて 8 本くらいです。
なんだか、やたら長くなってしまった。
最初は曲線の角度から分割しようと思ってたけど、どうやって割り出せばいいかよくわからず、結局こんなことになってしまった。
一応これだと、どんだけひねっても、ほとんどずれないです。
それにしても、もうちょっとやりようはありそうなもんだけども。
とりあえず僕の低い学力では、今はここまで。
ベジェ曲線のそれぞれの計算は以下。
やっぱりソースが長い。
無駄に計算してる感が否めないので、
せめてこの半分くらいにはしたい。
curveTo の分割数は曲線の形によって変化しています。
少ないときで 1 本。多くて 8 本くらいです。
なんだか、やたら長くなってしまった。
最初は曲線の角度から分割しようと思ってたけど、どうやって割り出せばいいかよくわからず、結局こんなことになってしまった。
一応これだと、どんだけひねっても、ほとんどずれないです。
それにしても、もうちょっとやりようはありそうなもんだけども。
とりあえず僕の低い学力では、今はここまで。
function bezierCurve(
ax:Number, ay:Number,
bx:Number, by:Number,
cx:Number, cy:Number,
dx:Number, dy:Number, n:Number, t:Number):void
{
var t1:Number = n + (t - n) * 0.5;
var t2:Number = n + (t - n) * 0.25;
var ax2:Number = cubicBezier(ax, bx, cx, dx, n);
var ay2:Number = cubicBezier(ay, by, cy, dy, n);
var dx2:Number = cubicBezier(ax, bx, cx, dx, t);
var dy2:Number = cubicBezier(ay, by, cy, dy, t);
var px:Number = cubicBezier(ax, bx, cx, dx, t1);
var py:Number = cubicBezier(ay, by, cy, dy, t1);
var rx:Number = getControl(ax2, px, dx2);
var ry:Number = getControl(ay2, py, dy2);
var qx:Number = quadraticBezier(ax2, rx, dx2, 0.25);
var qy:Number = quadraticBezier(ay2, ry, dy2, 0.25);
var px2:Number = cubicBezier(ax, bx, cx, dx, t2);
var py2:Number = cubicBezier(ay, by, cy, dy, t2);
if (checkPosition(qx-px2, qy-py2, 0.5))
{
curveTo(rx, ry, dx2, dy2);
}
else
{
bezierCurve(ax, ay, bx, by, cx, cy, dx, dy, n, t1);
bezierCurve(ax, ay, bx, by, cx, cy, dx, dy, t1, t);
}
}
ベジェ曲線のそれぞれの計算は以下。
//曲線が許容範囲内に収まってるかチェック
function checkPosition(x:Number, y:Number, d:Number):Boolean
{
return (Math.abs(x) < d && Math.abs(y) < d);
}
//2次ベジェ曲線の通過点からコントロールポイントを得る
function getControl(
a:Number, b:Number, c:Number):Number
{
return (b * 4 - a - c) * 0.5;
}
//2次ベジェ曲線の任意の点を取得
function quadraticBezier(
a:Number, b:Number, c:Number, t:Number):Number
{
var s:Number = 1 - t;
return s * s * a + 2 * s * t * b + t * t * c;
}
//3次ベジェ曲線の任意の点を取得
function cubicBezier(
a:Number, b:Number, c:Number, d:Number, t:Number):Number
{
var s:Number = 1 - t;
return s * s * s * a
+ 3 * s * s * t * b
+ 3 * s * t * t * c
+ t * t * t * d;
}
やっぱりソースが長い。
無駄に計算してる感が否めないので、
せめてこの半分くらいにはしたい。
FlashPlayer の 10 が正式リリースされたけども
今のところ、一番厄介なのは、おそらく Vector クラスの使用じゃないかと思うわけです。
バージョン 9 でも AS3 は使っているわけですが、9 には Vector クラスは対応していないので、 10 対応で作成したクラスを 9 でも使用したくなったときに、Vector クラスはかなりネックになってしまうんじゃないかと思うわけです。
クライアントから古いバージョンを指定されることはよくあることだと思います。
で、こんな風にすると、一応 Vector と Array をプレイヤーの状況によって使い分けれるんじゃないかなあ と、思ってみたわけです。
これだと、FlashPlayer のバージョンが 9 の場合は
ただ、これだと変数の型指定は Object になっちゃうので、
実際どれほどの効果があるのかは謎です。
誰か検証してくれないかなあ。
今のところ、一番厄介なのは、おそらく Vector クラスの使用じゃないかと思うわけです。
バージョン 9 でも AS3 は使っているわけですが、9 には Vector クラスは対応していないので、 10 対応で作成したクラスを 9 でも使用したくなったときに、Vector クラスはかなりネックになってしまうんじゃないかと思うわけです。
クライアントから古いバージョンを指定されることはよくあることだと思います。
で、こんな風にすると、一応 Vector と Array をプレイヤーの状況によって使い分けれるんじゃないかなあ と、思ってみたわけです。
function getArrayOrVector(T:Class):Class
{
var theClass:Class;
try
{
var classPath:String = getQualifiedClassName(T);
classPath = classPath.replace("::", ".");
theClass = getDefinitionByName
("Vector.<" + classPath + ">") as Class;
}
catch (error:Error)
{
theClass = Array;
}
return theClass;
}
var theClass:Class = getArrayOrVector(int);
var a:Object = new theClass();
trace(getQualifiedClassName(a));
これだと、FlashPlayer のバージョンが 9 の場合は
Arrayと出力され、バージョンが 10 だと
__AS3__.vec::Vector.<int>と出力されます。
ただ、これだと変数の型指定は Object になっちゃうので、
実際どれほどの効果があるのかは謎です。
誰か検証してくれないかなあ。
まずはデモから。
デモ
Progression ではURLを指定してシーンを変更できるので、 Flash はビデオプレイヤーのみを実装しておいて、 あとは URL でビデオの再生位置や flv のパスを指定してやる。 ってこともありだと思った。
デモの FLV はキューポイントを3箇所設定している。 で、onCuePoint のイベントを受け取ってシーンを変更すれば ブラウザの「戻る」「進む」で再生位置を変更するとかもできる。
何気にタイトルも変えてみたり。
デモでは一応Flash内にボタンを設置しているけど、 これも場合によってはなくてもいい。
今回はキューポイントの指定だけども、URLで 直接時間指定することもできる。
要は SceneObject で URL の変化が受け取れるわけだから
これにあわせていろんなことができますね。って話。
--- 追記
直接 #page3 とかに飛ぶとビデオが読み込めてなくて、
最初から再生になってますが、デモだから。という言い訳。
デモ
Progression ではURLを指定してシーンを変更できるので、 Flash はビデオプレイヤーのみを実装しておいて、 あとは URL でビデオの再生位置や flv のパスを指定してやる。 ってこともありだと思った。
デモの FLV はキューポイントを3箇所設定している。 で、onCuePoint のイベントを受け取ってシーンを変更すれば ブラウザの「戻る」「進む」で再生位置を変更するとかもできる。
何気にタイトルも変えてみたり。
デモでは一応Flash内にボタンを設置しているけど、 これも場合によってはなくてもいい。
今回はキューポイントの指定だけども、URLで 直接時間指定することもできる。
要は SceneObject で URL の変化が受け取れるわけだから
これにあわせていろんなことができますね。って話。
--- 追記
直接 #page3 とかに飛ぶとビデオが読み込めてなくて、
最初から再生になってますが、デモだから。という言い訳。
先日書いた「FlashDevelop用Progressionプロジェクトテンプレート」を、
正式にリリースされた Progression3 にあわせて変更しました。
前回の Beta 2 版のプロジェクトテンプレートを入れてた方は、一度削除してから入れなおしてください。 削除しておかないと余計なファイルが含まれてしまいます。
今回のリリースで(前からあったかもですが)swc が配布されているのでこれを含めたテンプレートとなっています。
135 ActionScript 3 - AS3 Project with Progression3
あと、Air 用のプロジェクトテンプレートも作っておきました。
165 ActionScript 3 - AIR AS3 Projector with Progression3
使い方は前回のままですが
------------------------------------------
上記リンクからダウンロードしたzipを解凍すると、
中に「135 ActionScript 3 - AS3 Project with Progression3」というフォルダが入っているので、これをXPの場合以下のフォルダにコピーすると使えるようになります。
Documents and Settings\[USER]\Local Settings\Application Data\FlashDevelop\Templates\ProjectTemplates
------------------------------------------
です。
以後 Progression がアップデートされた際は lib ディレクトリ内の swc ファイルを置き換えればいいんじゃないかと思います。
どっかで誰かの役に立てば、これ幸い。
前回の Beta 2 版のプロジェクトテンプレートを入れてた方は、一度削除してから入れなおしてください。 削除しておかないと余計なファイルが含まれてしまいます。
今回のリリースで(前からあったかもですが)swc が配布されているのでこれを含めたテンプレートとなっています。
135 ActionScript 3 - AS3 Project with Progression3
あと、Air 用のプロジェクトテンプレートも作っておきました。
165 ActionScript 3 - AIR AS3 Projector with Progression3
使い方は前回のままですが
------------------------------------------
上記リンクからダウンロードしたzipを解凍すると、
中に「135 ActionScript 3 - AS3 Project with Progression3」というフォルダが入っているので、これをXPの場合以下のフォルダにコピーすると使えるようになります。
Documents and Settings\[USER]\Local Settings\Application Data\FlashDevelop\Templates\ProjectTemplates
------------------------------------------
です。
以後 Progression がアップデートされた際は lib ディレクトリ内の swc ファイルを置き換えればいいんじゃないかと思います。
どっかで誰かの役に立てば、これ幸い。
FlashDevelopのみでProgression Frameworkを使ったオーサリングができます。
ActionScript 3 - AS3 Project with Progression3
バージョンはそれぞれ
上記リンクからダウンロードしたzipを解凍すると、
中に「135 ActionScript 3 - AS3 Project with Progression3」というフォルダが入っているので、これをXPの場合以下のフォルダにコピーすると使えるようになります。
Documents and Settings\[USER]\Local Settings\Application Data\FlashDevelop\Templates\ProjectTemplates
Vistaの場合はよくわかりませんが、似たようなところに入れるんじゃないかと。
以前タロタローグさんとこのブログに「Progression Frameworkを、FlashDevelopだけで使ってしまおうと言う挑戦。」という記事で同様のテンプレートが公開されてましたが、「Progression Framework自体が猛スピードで変化してて、テンプレートを置いといても意味なさそうなんで、公開は止めます。」とのことで今は公開されてないです。(かわりに「FlashDevelop用のプロジェクトテンプレートを作る安易な方法」という記事が出てますが。)
実際、明後日にはProgression 3が公開されるってのに、今更なんですが、今後の参考までにプロジェクトテンプレート用のテンプレート的な気持ちでアップしときます。
あと、fl パッケージはたぶん公開しちゃいけないんだろうから(ライセンス調べろって話ですが)下記の3つだけ仮でクラスファイル入れてあります。中身は定義だけで実装されてないです。
Flash CS3 持ってる人は置き換えればDoTweenコマンドとかも使えるようになります。
ActionScript 3 - AS3 Project with Progression3
バージョンはそれぞれ
- FlashDevelop 3 beta 8
- Progression Framework 3 beta 2
上記リンクからダウンロードしたzipを解凍すると、
中に「135 ActionScript 3 - AS3 Project with Progression3」というフォルダが入っているので、これをXPの場合以下のフォルダにコピーすると使えるようになります。
Documents and Settings\[USER]\Local Settings\Application Data\FlashDevelop\Templates\ProjectTemplates
Vistaの場合はよくわかりませんが、似たようなところに入れるんじゃないかと。
以前タロタローグさんとこのブログに「Progression Frameworkを、FlashDevelopだけで使ってしまおうと言う挑戦。」という記事で同様のテンプレートが公開されてましたが、「Progression Framework自体が猛スピードで変化してて、テンプレートを置いといても意味なさそうなんで、公開は止めます。」とのことで今は公開されてないです。(かわりに「FlashDevelop用のプロジェクトテンプレートを作る安易な方法」という記事が出てますが。)
実際、明後日にはProgression 3が公開されるってのに、今更なんですが、今後の参考までにプロジェクトテンプレート用のテンプレート的な気持ちでアップしときます。
あと、fl パッケージはたぶん公開しちゃいけないんだろうから(ライセンス調べろって話ですが)下記の3つだけ仮でクラスファイル入れてあります。中身は定義だけで実装されてないです。
Flash CS3 持ってる人は置き換えればDoTweenコマンドとかも使えるようになります。
- fl.transitions.Tween
- fl.transitions.TweenEvent
- fl.transitions.easing.Regular
何かと AS3 の Proxy クラスは使えないという声をよく目にします。
特に目立つのが他のクラスを継承できないこと。
例えば DisplayObject の Proxy を作ったとしても、元がProxyクラスなので addChild() が使えないとか。たぶんそんなの。
今回はその辺について、ちょっと思ったことを休み前にまとめとこうかと。
Progression使いたいけど、自前で拡張したクラスも組み合わせて、 なんか有効にいろんなライブラリに手を出せないかな。と思ったのが きっかけでして、そこで一番に思い当たるのはやっぱりProxyクラスだったわけです。
で最初はProxy使った方法をいろいろごにょごにょと考えてたんですけど、 結局 addChild() や removeChild() で指定するためには、どうしても DisplayObject を 参照しなきゃいけなくなって、それが参照できる時点でProxyの役目は破綻してしまうんです。
例えば上記の場合、親のDisplayObjectにProxyクラスが内包するDisplayObjectを displayというプロパティ経由で入れ子にしようとしています。
でも、これをしてしまうと、以降、親DisplayObjectからは本来隠蔽しておきたいはずの 子DisplayObjectを参照できるようになってしまい、 Proxyを通さずに直接参照・変更ができてしまいます。
そもそも Proxy は dynamic ステートメントを付けておかないと 効果を発揮しません。
つまり、頑張って DisplayObjectProxy とか EventProxy とかを作っても、 それを継承して様々なクラスに派生させていくような作り方だと、 全部ダイナミッククラスになってしまって はちゃめちゃになってしまいます。
もしこういう使い方をしたいならば、 素直にDisplayObject のサブクラスを作って、中身をすべて 内包する他のオブジェクトに委譲したほうがいいかもしれません。
ただ、DisplayObject で Proxy クラスを作成した場合でも、 結局のところ addChild には若干問題が残ります。
Sprite や MovieClip なら、委譲先のオブジェクトを DisplayObjectContainer から作成した Proxy 用クラスのコンテナ内に追加しておけば addChild() 時に画面上に現れますが、TextField や Bitmap は親オブジェクトになれないので、これらを継承したProxyクラスは addChild しても画面に表示されません。
あくまで追加されるのは Proxy クラスだからです。
addChild はまだマシな方で、ADDEDイベント発生時に内部処理で置き換えが可能ですが、 removeChild 時にはどう頑張ってもクライアント側で一工夫しないといけなくなります。
結局いろいろ試した結果、僕は addChild の問題は放棄しました。
Proxyを末端の子オブジェクトとして捉える限り、この問題は解決しないように思えたからです。
ProxyならProxyらしくコンテナ側に回してやればいいんじゃないかと。
で、以下はDisplayNodeというProxyクラスを継承したサブクラスを作ってみた場合の一例です。
出力結果
何をやってるかというと、
child2 は delete の実行によって、内部で removeChild してます。
このクラスの中身も一応参考までにアップしておきます。
こういう使い方もあるってことで。
DisplayNode
DisplayObjectを継承したProxyを作りたいときも、なんとなくそれっぽいものまでは作る事ができます。
要はダイナミッククラスな訳ですから、以下のような処理で各メソッドの呼び出し時の処理に手を加えることも。
テキストフォーマットを吐き出すSpriteとか。
ただし、getter と setter は動的に追加できないから、やっぱり完全なProxyクラスの代用は難しいと思う。
上の例の場合はプロパティへのアクセス用メソッドを用意していて、text("hoge") みたいにして使う感じ。
TestProxy
結局のところ TestProxy みたいに無尽蔵にメソッドを追加しちゃうようなものは
実際にはあんまり使いどころがない気がする。コードヒントとか出ないし。
現在 DisplayObject を継承した Proxy 用クラスで、比較的使用頻度の高いのは以下のようなクラス。
こういうクラスを予め用意しておくと、結構役に立ってくれたりします。
SpriteProxy
このクラスはこのままインスタンスを作ってもまったく意味のないクラスなので継承しないと使えません。
これって Proxy じゃなくて Decorator な気もするけど、
その辺そんなに詳しいわけでもないので気にしない。
今回は DisplayObject と一緒に使う事に絞ってみたけど、 Proxy クラス自体はアクセス方法がかなり自由なのでいろんな使い方ができると思います。
中でも MXML みたいな使い方は得意なんじゃないかと。
あんまり多用すると後が怖いですけども。
それにしても、とりとめのない文章だなあ。
特に目立つのが他のクラスを継承できないこと。
例えば DisplayObject の Proxy を作ったとしても、元がProxyクラスなので addChild() が使えないとか。たぶんそんなの。
今回はその辺について、ちょっと思ったことを休み前にまとめとこうかと。
Progression使いたいけど、自前で拡張したクラスも組み合わせて、 なんか有効にいろんなライブラリに手を出せないかな。と思ったのが きっかけでして、そこで一番に思い当たるのはやっぱりProxyクラスだったわけです。
で最初はProxy使った方法をいろいろごにょごにょと考えてたんですけど、 結局 addChild() や removeChild() で指定するためには、どうしても DisplayObject を 参照しなきゃいけなくなって、それが参照できる時点でProxyの役目は破綻してしまうんです。
var parentObject:DisplayObjectContainer var childObject:Proxy parentObject.addChild(childObject.display);
例えば上記の場合、親のDisplayObjectにProxyクラスが内包するDisplayObjectを displayというプロパティ経由で入れ子にしようとしています。
でも、これをしてしまうと、以降、親DisplayObjectからは本来隠蔽しておきたいはずの 子DisplayObjectを参照できるようになってしまい、 Proxyを通さずに直接参照・変更ができてしまいます。
そもそも Proxy は dynamic ステートメントを付けておかないと 効果を発揮しません。
つまり、頑張って DisplayObjectProxy とか EventProxy とかを作っても、 それを継承して様々なクラスに派生させていくような作り方だと、 全部ダイナミッククラスになってしまって はちゃめちゃになってしまいます。
もしこういう使い方をしたいならば、 素直にDisplayObject のサブクラスを作って、中身をすべて 内包する他のオブジェクトに委譲したほうがいいかもしれません。
ただ、DisplayObject で Proxy クラスを作成した場合でも、 結局のところ addChild には若干問題が残ります。
Sprite や MovieClip なら、委譲先のオブジェクトを DisplayObjectContainer から作成した Proxy 用クラスのコンテナ内に追加しておけば addChild() 時に画面上に現れますが、TextField や Bitmap は親オブジェクトになれないので、これらを継承したProxyクラスは addChild しても画面に表示されません。
あくまで追加されるのは Proxy クラスだからです。
addChild はまだマシな方で、ADDEDイベント発生時に内部処理で置き換えが可能ですが、 removeChild 時にはどう頑張ってもクライアント側で一工夫しないといけなくなります。
結局いろいろ試した結果、僕は addChild の問題は放棄しました。
Proxyを末端の子オブジェクトとして捉える限り、この問題は解決しないように思えたからです。
ProxyならProxyらしくコンテナ側に回してやればいいんじゃないかと。
で、以下はDisplayNodeというProxyクラスを継承したサブクラスを作ってみた場合の一例です。
var rootNode:DisplayNode = new DisplayNode(new Sprite()); rootNode.child1 = new Sprite(); rootNode.child1.grandchild1 = new Shape(); rootNode.child1.grandchild2 = new Shape(); rootNode.child1.grandchild3 = new Sprite(); rootNode.child1.grandchild3.addChild(new Bitmap()); rootNode.child2 = new Sprite(); rootNode.child2.dummy = new Sprite(); rootNode.child3 = new Sprite(); delete rootNode.child2; trace(rootNode);
出力結果
instance148 [object Sprite]
child1 [object Sprite]
grandchild1 [object Shape]
grandchild2 [object Shape]
grandchild3 [object Sprite]
instance153 [object Bitmap]
child3 [object Sprite]
何をやってるかというと、
private var _target:DisplayObjectContainer;
override flash_proxy function getProperty(name:*):*
{
var child:DisplayObject = _target.getChildByName(name);
if (child is DisplayObjectContainer)
return new DisplayNode(child as DisplayObjectContainer);
return child;
}
override flash_proxy function setProperty(name:*, value:*):void
{
if (value is DisplayObject)
{
value.name = name;
_target.addChild(value);
}
}
setProperty と getProperty を使って表示オブジェクトを
参照名で addChild していく。ってもの。child2 は delete の実行によって、内部で removeChild してます。
このクラスの中身も一応参考までにアップしておきます。
こういう使い方もあるってことで。
DisplayNode
DisplayObjectを継承したProxyを作りたいときも、なんとなくそれっぽいものまでは作る事ができます。
要はダイナミッククラスな訳ですから、以下のような処理で各メソッドの呼び出し時の処理に手を加えることも。
package
{
import flash.display.DisplayObject;
import flash.display.Sprite;
import flash.utils.describeType;
dynamic public class TestProxy extends Sprite
{
public function TestProxy(target:DisplayObject)
{
setProxyMethods(target);
}
public function setProxyMethods(target:DisplayObject):void
{
var data:XML = describeType(target);
var n:uint, i:uint, name:String;
var methods:XMLList = data.elements("method");
n = methods.length();
for (i = 0; i < n; i++ )
{
name = methods[i].@name;
if (!this.hasOwnProperty(name))
{
this[name] = function():*
{
var func:Function = target[arguments.callee.methodName];
return func.apply(target, arguments);
}
this[name].methodName = name;
}
}
var accessor:XMLList = data.elements("accessor");
n = accessor.length();
for (i = 0; i < n; i++ )
{
name = accessor[i].@name;
if (!this.hasOwnProperty(name))
{
this[name] = function():*
{
if (arguments.length == 1)
return target[arguments.callee.propName] = arguments[0];
return target[arguments.callee.propName];
}
this[name].propName = name;
}
}
}
}// end of class
}// end of package
var proxy:TestProxy = new TestProxy(new TextField());
proxy.appendText("hoge");
trace(proxy.text()); // 出力 : hoge
trace(proxy.getTextFormat()); // 出力 : [object TextFormat]
テキストフォーマットを吐き出すSpriteとか。
ただし、getter と setter は動的に追加できないから、やっぱり完全なProxyクラスの代用は難しいと思う。
上の例の場合はプロパティへのアクセス用メソッドを用意していて、text("hoge") みたいにして使う感じ。
TestProxy
結局のところ TestProxy みたいに無尽蔵にメソッドを追加しちゃうようなものは
実際にはあんまり使いどころがない気がする。コードヒントとか出ないし。
現在 DisplayObject を継承した Proxy 用クラスで、比較的使用頻度の高いのは以下のようなクラス。
こういうクラスを予め用意しておくと、結構役に立ってくれたりします。
SpriteProxy
このクラスはこのままインスタンスを作ってもまったく意味のないクラスなので継承しないと使えません。
これって Proxy じゃなくて Decorator な気もするけど、
その辺そんなに詳しいわけでもないので気にしない。
今回は DisplayObject と一緒に使う事に絞ってみたけど、 Proxy クラス自体はアクセス方法がかなり自由なのでいろんな使い方ができると思います。
中でも MXML みたいな使い方は得意なんじゃないかと。
あんまり多用すると後が怖いですけども。
それにしても、とりとめのない文章だなあ。
一部更新、追加があったのでdocsも更新しときました。
といっても、このライブラリ使ってんの僕だけだったりして。
Utils
http://www.libspark.org/browser/as3/Utils/src/org/libspark/utils
asdocs
http://www.libspark.org/htdocs/as3/utils/
ダウンロード
http://www.libspark.org/svn/as3/Utils/docs/utils_docs.zip
といっても、このライブラリ使ってんの僕だけだったりして。
Utils
http://www.libspark.org/browser/as3/Utils/src/org/libspark/utils
asdocs
http://www.libspark.org/htdocs/as3/utils/
ダウンロード
http://www.libspark.org/svn/as3/Utils/docs/utils_docs.zip
是非挑戦してみてください。
先攻・後攻は「SETTING」ボタンから変更できます。
ルール
FlashDeverop + FlexSDK で作ってます。
Flash IDE は今回使ってません。
FlexSDK だと、書き出しが早くてすばらしいですね。
先攻・後攻は「SETTING」ボタンから変更できます。
ルール
- 同じ行なら何個でも取れます。
- 間を飛ばして取ることはできません。
- 最後の1つになったら負け
FlashDeverop + FlexSDK で作ってます。
Flash IDE は今回使ってません。
FlexSDK だと、書き出しが早くてすばらしいですね。
AS3 で FLV のメタデータの取り方が分からずに、かなり無駄に時間を費やしてしまった。。。
FLV のメタデータは NetStream から onMetaData を使って取得できるんだけども、
この onMetaData の使い方がさっぱり分からなかったので忘れないようにメモ。
経過があまりにもアホくさすぎて絶対忘れないけども。
ビデオメタデータの使用
NetStreamオブジェクトの client プロパティを指定して、
指定したオブジェクトに onMetaData メソッドを定義しておくと使える。
この client プロパティの存在に気づかずに四苦八苦してた自分が情けない。
挙句の果てに、ここ見て自前で ByteArray からメタデータ取ろうとしてしまった。
ByteArray から取得した FLV の値を出力した一例。
アホですよ。まったく。
ここまでやって、ちゃんと取得できることに気づいた。
そもそも何で AS3 なのに NetStream はこんな実装なんだ。
腹立たしい。アホか!
一応以下にソース残しときます。
結果的に中途半端なものになってますが、
なんか消してしまうのも勿体ない気がしてしまって。(費やした時間が。)
FLV のメタデータは NetStream から onMetaData を使って取得できるんだけども、
この onMetaData の使い方がさっぱり分からなかったので忘れないようにメモ。
経過があまりにもアホくさすぎて絶対忘れないけども。
ビデオメタデータの使用
NetStreamオブジェクトの client プロパティを指定して、
指定したオブジェクトに onMetaData メソッドを定義しておくと使える。
この client プロパティの存在に気づかずに四苦八苦してた自分が情けない。
挙句の果てに、ここ見て自前で ByteArray からメタデータ取ろうとしてしまった。
ByteArray から取得した FLV の値を出力した一例。
// FLV Header Signature = FLV Version = 1 Flags = 5 Offset = 9 // FLV Stream PreviousTagSize = 0 // FLV Tag Type = 18 BodyLength = 224 Timestamp = 0 TimestampExtended = 0 StreamId = 0 // MetaData videodatarate = 1000 audiocodecid = 2 canSeekToEnd = true duration = 8.866 audiodatarate = 128 framerate = 30 width = 450 videocodecid = 4 height = 400 audiodelay = 0.038
アホですよ。まったく。
ここまでやって、ちゃんと取得できることに気づいた。
そもそも何で AS3 なのに NetStream はこんな実装なんだ。
腹立たしい。アホか!
一応以下にソース残しときます。
結果的に中途半端なものになってますが、
なんか消してしまうのも勿体ない気がしてしまって。(費やした時間が。)
public function readFLV(data:ByteArray):void
{
// FLV Header
var Signature:String = data.readUTFBytes(3);
var Version:uint = uint8(data);
var Flags:uint = uint8(data);
var Offset:uint = uint32(data);
// FLV Stream
var PreviousTagSize:uint = uint32(data);
// FLV Tag
var Type:uint = uint8(data);
var BodyLength:uint = uint24(data);
var Timestamp:uint = uint24(data);
var TimestampExtended:uint = uint8(data);
var StreamId:uint = uint24(data);
var Body:ByteArray = new ByteArray();
data.readBytes(Body, 0, BodyLength);
// Body(FLV Tag Type = 0x12 : AMF)
var a:uint = uint8(Body);// ???
var b:uint = uint8(Body);// ???
// MetaData
readString(Body);
var metadata:Object = readMixedArray(Body);
trace(Signature, Version, Flags, Offset);
trace(PreviousTagSize);
trace(Type, BodyLength, Timestamp, TimestampExtended, StreamId);
// MetaData の中身
for (var val:String in metadata) {
trace(val + "=" + metadata[val]);
}
}
public function uint8(data:ByteArray):uint { return data.readByte(); }
public function uint16(data:ByteArray):uint { return data.readUnsignedShort(); }
public function uint24(data:ByteArray):uint { return data[data.position++]<<16 | data[data.position++]<<8 | data[data.position++]; }
public function uint32(data:ByteArray):uint { return data.readUnsignedInt(); }
public function readNumber(data:ByteArray):Number
{
return data.readDouble();
}
public function readBoolean(data:ByteArray):Boolean
{
return data.readByte() == 0x01;
}
public function readString(data:ByteArray):String
{
var len:uint = data.readByte();
return data.readUTFBytes(len);
}
public function readObject(data:ByteArray):Object
{
return null;
}
public function readMixedArray(data:ByteArray):Object
{
data.readByte();
var length:uint = data.readInt();
var res:Object = { };
var value:*;
var name:String;
var type:uint;
var len:uint = data.length;
for (;; ) {
var separator:uint = data.readByte();
name = readString(data);
type = data.readByte();
if (type == 0x09) break;
switch(type) {
case 0x00 : { value = readNumber(data); break; } // Object
case 0x01 : { value = readBoolean(data); break; } // Object
case 0x02 : { value = readString(data); break; } // Object
case 0x03 : { value = readObject(data); break; } // Object
//case 0x04 : { break; } // Movie Clip
case 0x05 : { value = null; break; } // Object
case 0x06 : { value = undefined; break; } // Object
//case 0x07 : { break; } // Reference
case 0x08 : { value = readMixedArray(data); break; } // MixedArray
case 0x0a : { value = readArray(data); break; } // Array
case 0x0b : { value = readDate(data); break; } // Date
}
res[name] = value;
}
return res;
}
public function readArray(data:ByteArray):Array
{
return null;
}
public function readDate(data:ByteArray):String
{
return null;
}
Spark Project の Utils ライブラリの ASDoc を書き出してアップしてみました。
HTMLでアップするとファイルが多いのでZIPに圧縮してます。
あとちょろちょろとクラスファイルを足してます。
何か無駄なところも多々あると思います。
実装が微妙なところも結構あります。EventUtil とか。
Utils
http://www.libspark.org/browser/as3/Utils/src/org/libspark/utils
utils_docs.zip
http://www.libspark.org/svn/as3/Utils/docs/utils_docs.zip
とりあえず、今こんなのが入ってるってのが把握できればと思います。
HTMLでアップするとファイルが多いのでZIPに圧縮してます。
あとちょろちょろとクラスファイルを足してます。
何か無駄なところも多々あると思います。
実装が微妙なところも結構あります。EventUtil とか。
Utils
http://www.libspark.org/browser/as3/Utils/src/org/libspark/utils
utils_docs.zip
http://www.libspark.org/svn/as3/Utils/docs/utils_docs.zip
とりあえず、今こんなのが入ってるってのが把握できればと思います。
今までまったく気づかなかったことなんですが
上記のようにイベントハンドラの中に「arguments」が入ってると、引数は別に定義されてなくてもいいみたい。
これは引数の数は関係ないみたいだけども、「...rest」が入ってると「arguments」が使えなくなるのでダメっぽい。
まあ、そもそも「...rest」入れてる時点で引数の数はあんまり関係なくなるけども。
じゃあ、普通に関数でも使えるかと思って試してみた。
で少し呼び出し方を変えてみる。
ようはイベントの通知には apply が使われてるってことか。
もしかして dispatchEvent もこの要領で「dispatchEvent.apply」とかすれば引数勝手に増やせるかも?
とか思ったけど、やっぱりそれはダメでした。
で、結局のところ「だから何なんだ」って話しで、
特に何も得することもないんですが。
通常、外部からの呼び出しの時は引数無しだったり、個定数の引数を渡さないといけなくしておいて
内部処理のみ、内緒でこっそり引数渡してやりたい時なんかは使えるかもしれないですね。
再起処理の時のフラグとか。
addEventListener("enterFrame", handler);
function handler():void {}
ってすると
ArgumentError: Error #1063: _fla::MainTimeline/handler() の引数の数が一致していません。0 が必要ですが、1 が指定されました。ってエラーが出て怒られるんだけども、
addEventListener("enterFrame", handler);
function handler():void { arguments; }
上記の場合はそのまま何事もなく実行される。上記のようにイベントハンドラの中に「arguments」が入ってると、引数は別に定義されてなくてもいいみたい。
これは引数の数は関係ないみたいだけども、「...rest」が入ってると「arguments」が使えなくなるのでダメっぽい。
まあ、そもそも「...rest」入れてる時点で引数の数はあんまり関係なくなるけども。
じゃあ、普通に関数でも使えるかと思って試してみた。
function test():void {arguments;}
test(1, 2, 3);
普通に怒られた。
引数の数が正しくありません。0個以下であることが必要です。って。
で少し呼び出し方を変えてみる。
function test():void {arguments;}
test.apply(null, [1, 2, 3]);
と、今度はすんなり通った。ようはイベントの通知には apply が使われてるってことか。
もしかして dispatchEvent もこの要領で「dispatchEvent.apply」とかすれば引数勝手に増やせるかも?
とか思ったけど、やっぱりそれはダメでした。
で、結局のところ「だから何なんだ」って話しで、
特に何も得することもないんですが。
通常、外部からの呼び出しの時は引数無しだったり、個定数の引数を渡さないといけなくしておいて
内部処理のみ、内緒でこっそり引数渡してやりたい時なんかは使えるかもしれないですね。
再起処理の時のフラグとか。
FlashPlayer10が世間を賑わせてますが、僕はまだインストールしてないです。
3Dに対応したけど、世の中のFlashコンテンツが3Dだらけになったら嫌だなあ。とかぼやきつつ
もうしばらく静観してようかなと思ってます。
いろんな人が新機能についてノウハウを記事にされるのを読んでからにしようかと。
Flexも持ってないし。
まったく人任せな。
ところで、Vectorクラスなんですけども、
これって中身を型指定できるってことなんですが
Vectorクラス自体の実装を一見しても、Array を継承してるようでもないので、
もしかしてArrayとはまったく別物なのかな? と。
個人的な要望としては
もしかして
さすがに
でも、やっぱり Vector が Array として認識できないと何か嫌。
認識できなくていいなら、Proxy使ってこんな実装でいいんじゃないかと。
これなら今のままでも使えるし。
まあ、Arrayより高速に処理できるってことはいいんだろうけど。
ついでに、上記の「こんな実装」を使った場合は以下のような感じ。
ArrayOf
3Dに対応したけど、世の中のFlashコンテンツが3Dだらけになったら嫌だなあ。とかぼやきつつ
もうしばらく静観してようかなと思ってます。
いろんな人が新機能についてノウハウを記事にされるのを読んでからにしようかと。
Flexも持ってないし。
まったく人任せな。
ところで、Vectorクラスなんですけども、
これって中身を型指定できるってことなんですが
var a:Array = new Vector.<String>();みたいなことって出来るんでしょうか。
Vectorクラス自体の実装を一見しても、Array を継承してるようでもないので、
もしかしてArrayとはまったく別物なのかな? と。
個人的な要望としては
var a:String[] = ["a","b","c"];みたいな方が都合良さげな気がするんですが。
もしかして
var v:Vector.<String> = ["a","b","c"];とかもできないんだろうか。
さすがに
function test( arg:Vector.<String> ): Vector.<String>
{
return arg;
}
ってことは出来るんだろうけど。でも、やっぱり Vector が Array として認識できないと何か嫌。
認識できなくていいなら、Proxy使ってこんな実装でいいんじゃないかと。
これなら今のままでも使えるし。
まあ、Arrayより高速に処理できるってことはいいんだろうけど。
ついでに、上記の「こんな実装」を使った場合は以下のような感じ。
ArrayOf
import flash.geom.Point;
var points:ArrayOf = new ArrayOf(Point);
points[0] = new Point(1,1);
points[1] = new Point(2,2);
points.push( new Point(3,3), new Point(4,4) );
trace(points); //(x=1, y=1),(x=2, y=2),(x=3, y=3),(x=4, y=4)
var a:Array = points.toArray();
trace(a); //(x=1, y=1),(x=2, y=2),(x=3, y=3),(x=4, y=4)
var points2:ArrayOf = new ArrayOf(Point);
points2.push(new Point(5,5), new Point(6,6));
var points3:ArrayOf = points.concat(points2);
trace( points3 ); //(x=1, y=1),(x=2, y=2),(x=3, y=3),(x=4, y=4),(x=5, y=5),(x=6, y=6)
trace(points3[4]); // (x=5, y=5)
trace(points3.shift()); // (x=1, y=1)
trace(points3.pop()); // (x=6, y=6)
trace(points3.splice(0,2)); // (x=2, y=2),(x=3, y=3)
test(points); // ok
points[0] = 1; // ArgumentError: Point と関連しない型の値は格納できません。
function test(arg:ArrayOf):void
{
if( arg.type != Point ) throw new ArgumentError("エラー");
trace("ok");
}
Spark Project の Utils ライブラリにコミットしました。
http://www.libspark.org/browser/as3/Utils/src/org/libspark/utils
至らない点は多々あると思うので、使い勝手のいいように変更してもらえればいいと思います。
もちろん何か言っていただければ可能な限り対応します。
コミットしたのは以下のファイル。
ArrayUtil(追記)
BitmapUtil
ColorUtil
DateUtil(追記)
MathUtil(追記)
StringUtil(追記)
(追記)は元々あったものに手を加えました。
いろんなライブラリを使ってると「ArrayUtil」とか「ColorUtil」とか
同じ名前のクラスが 3 つも 4 つもある状態になってしまったりするので
(as3corelibとか、popforgeとか、progressionとか。とかとか。)
そういうのんに含まれてるものも、こっちに書き写してしまえば
使うとき楽でいいんだけどなあ。と思う今日この頃です。
(写したら同じのが増えちゃうんですけども。。。)
ちなみに
BeInteractive!さんが「「これコミットして大丈夫なのかな...」と考える前にコミットして欲しい」って記事を
書いてくれていて、これは小心者の僕としてはコミットへの後押しになりました。
記事を書いておられるのがコメントもらった次の日だっていうのに、気づいたのが今日という僕ですが。
こっそりと感謝です。
追記::(2008.05.16)
GeomUtil
ObjectUtil
上記2点追加しました。
http://www.libspark.org/browser/as3/Utils/src/org/libspark/utils
至らない点は多々あると思うので、使い勝手のいいように変更してもらえればいいと思います。
もちろん何か言っていただければ可能な限り対応します。
コミットしたのは以下のファイル。
ArrayUtil(追記)
BitmapUtil
ColorUtil
DateUtil(追記)
MathUtil(追記)
StringUtil(追記)
(追記)は元々あったものに手を加えました。
いろんなライブラリを使ってると「ArrayUtil」とか「ColorUtil」とか
同じ名前のクラスが 3 つも 4 つもある状態になってしまったりするので
(as3corelibとか、popforgeとか、progressionとか。とかとか。)
そういうのんに含まれてるものも、こっちに書き写してしまえば
使うとき楽でいいんだけどなあ。と思う今日この頃です。
(写したら同じのが増えちゃうんですけども。。。)
ちなみに
BeInteractive!さんが「「これコミットして大丈夫なのかな...」と考える前にコミットして欲しい」って記事を
書いてくれていて、これは小心者の僕としてはコミットへの後押しになりました。
記事を書いておられるのがコメントもらった次の日だっていうのに、気づいたのが今日という僕ですが。
こっそりと感謝です。
追記::(2008.05.16)
GeomUtil
ObjectUtil
上記2点追加しました。
メンバーに対してイベント通知を促すクラスです。
ソースファイル
例えば Shapeオブジェクトにマウスイベントくっつけたり、
Timerオブジェクトに enterFrame イベントくっつけたりするのに使います。
まず通知元オブジェクトを登録した EventMediator オブジェクトを作成します。
作成した EventMediator オブジェクトに通知メンバーを登録します。
通知元オブジェクトがイベントを通知(dispatchEvent の実行)すると、
メンバーの各オブジェクトも同じイベントを通知するようになります。
TimerクラスやURLLoaderクラスは、もともとenterFrameイベントを持っていませんが
登録しておくと、これらのオブジェクトがenterFrameイベントを通知するようになります。
一応 fla ファイルも置いときます。
demo.fla
demo2.fla
ソースファイル
例えば Shapeオブジェクトにマウスイベントくっつけたり、
Timerオブジェクトに enterFrame イベントくっつけたりするのに使います。
まず通知元オブジェクトを登録した EventMediator オブジェクトを作成します。
作成した EventMediator オブジェクトに通知メンバーを登録します。
通知元オブジェクトがイベントを通知(dispatchEvent の実行)すると、
メンバーの各オブジェクトも同じイベントを通知するようになります。
import flash.events.Event;
import flash.net.URLLoader;
import flash.utils.Timer;
import flash.display.Sprite;
import com.seyself.events.EventMediator;
var madiator:EventMediator = new EventMediator( new Sprite() );
var urlLoader:URLLoader = new URLLoader();
var timer:Timer = new Timer(1);
madiator.addEventListener( "enterFrame" , null );
madiator.member = [urlLoader,timer];
urlLoader.addEventListener( "enterFrame" , enterFrameHandler );
timer.addEventListener( "enterFrame" , enterFrameHandler );
function enterFrameHandler( event:Event ):void
{
trace(event.target);
event.target.removeEventListener( "enterFrame" , arguments.callee );
//madiator.removeEventListener( "enterFrame", enterFrameHandler );
}
上記のコードの場合、出力結果は以下のようになります。
[object URLLoader] [object Timer]
TimerクラスやURLLoaderクラスは、もともとenterFrameイベントを持っていませんが
登録しておくと、これらのオブジェクトがenterFrameイベントを通知するようになります。
一応 fla ファイルも置いときます。
demo.fla
demo2.fla
AS3向けのフレームワーク「Progression」がなんだか便利みたいなので、自分も慣れておこうと思った。
とにかく手探りなので、まずは Wiki のチュートリアルを、と思ったら、チュートリアルもまだほとんど準備中だった。
なもんで、とりあえずリファレンスとソースを行き来しながら作ってみた。
作ってみたやつ
一応ソースファイルも一式アップしときます。
恥ずかしいソースファイルのダウンロード
まず組み立て方がよくわからなくて困った。たったこれだけのコンテンツなのに、クラスファイルが錯綜してます。
何気に、右クリックの「新規ウィンドウで開く」がすごいと思った。
Event系のクラスの toString の実装が直接文字列を返すようになってるのは何でなんだろう。
あと、イベントの順番のメモ
とにかく手探りなので、まずは Wiki のチュートリアルを、と思ったら、チュートリアルもまだほとんど準備中だった。
なもんで、とりあえずリファレンスとソースを行き来しながら作ってみた。
作ってみたやつ
一応ソースファイルも一式アップしときます。
恥ずかしいソースファイルのダウンロード
まず組み立て方がよくわからなくて困った。たったこれだけのコンテンツなのに、クラスファイルが錯綜してます。
何気に、右クリックの「新規ウィンドウで開く」がすごいと思った。
Event系のクラスの toString の実装が直接文字列を返すようになってるのは何でなんだろう。
あと、イベントの順番のメモ
sceneStateChange → load → init goto → unload → sceneStateChange
[as3][習作] Box2Dを使った四則演算電卓
Box2Dを使った習作。
数値の入力は画面をドラッグ。
数値の入力は画面をドラッグ。
今日は、前回のエントリー(PV3D と Box2D を組み合わせて使ってみた)で作った迷路FLASHの作り方について簡単に解説します。

迷路を生成するためにMazeクラスを作成
Maze.as
まず迷路を生成するクラスを作ります。 このクラスはプロパティ内に"1"と"0"の配列で迷路の情報を作成します。 画像は出力結果です。 このクラスは「C言語による最新アルゴリズム事典」を参考にさせてもらいました。

PV3DとMazeを組み合わせる
Cubic.as
先ほど作成したMazeクラスで生成した迷路の情報を元に、PV3DのCubeを配置します。
このCubic.asはPV3Dのサンプルについている「Cube」を元に編集したものです。
あとでボールを転がすので、試しにココでSphereクラスも使ってみます。
サイコロ柄なのは特に意味は無いです。
初めて使うので「MaterialsList」の関係とかを見るために6面を違う柄にしたかっただけです。
今回裏面は最終的に見えなくするので、無駄な処理を省くために接続面と裏面を消しています。

Box2DとMazeを組み合わせる
Ball.as
Cubeと同様に作成した迷路情報を元に四角と丸を配置します。
PV3Dが「x,y,z」の3軸なのに対して、Box2Dは「x,y」の2軸なので、z軸に対する重力は今回無視して
マウスの方向(x,y)に重力の座標を変更しています。

上記2つをさらに組み合わせる
Document.as
先ほど作成した Ball クラスと Cubic クラスを組み合わせます。
このときBallクラスで使用したDisplayObject(Sprite)はもういらないので削除します。
Box2Dは位置情報を持っているので、この情報を PV3D に反映させます。
あとはマウス座標に合わせて程よく傾ければ、傾いてる方向に転がってるように見えます。
全ソースファイルのダウンロードはこちら。
20080208_Maze.zip

迷路を生成するためにMazeクラスを作成
Maze.as
まず迷路を生成するクラスを作ります。 このクラスはプロパティ内に"1"と"0"の配列で迷路の情報を作成します。 画像は出力結果です。 このクラスは「C言語による最新アルゴリズム事典」を参考にさせてもらいました。

PV3DとMazeを組み合わせる
Cubic.as
先ほど作成したMazeクラスで生成した迷路の情報を元に、PV3DのCubeを配置します。
このCubic.asはPV3Dのサンプルについている「Cube」を元に編集したものです。
あとでボールを転がすので、試しにココでSphereクラスも使ってみます。
サイコロ柄なのは特に意味は無いです。
初めて使うので「MaterialsList」の関係とかを見るために6面を違う柄にしたかっただけです。
今回裏面は最終的に見えなくするので、無駄な処理を省くために接続面と裏面を消しています。

Box2DとMazeを組み合わせる
Ball.as
Cubeと同様に作成した迷路情報を元に四角と丸を配置します。
PV3Dが「x,y,z」の3軸なのに対して、Box2Dは「x,y」の2軸なので、z軸に対する重力は今回無視して
マウスの方向(x,y)に重力の座標を変更しています。
m_world.m_gravity.x = mouseX - 640 / 2; m_world.m_gravity.y = mouseY - 360 / 2;このBallクラスもBox2Dについているサンプル「HelloWorld」をもとにしています。

上記2つをさらに組み合わせる
Document.as
先ほど作成した Ball クラスと Cubic クラスを組み合わせます。
このときBallクラスで使用したDisplayObject(Sprite)はもういらないので削除します。
Box2Dは位置情報を持っているので、この情報を PV3D に反映させます。
あとはマウス座標に合わせて程よく傾ければ、傾いてる方向に転がってるように見えます。
全ソースファイルのダウンロードはこちら。
20080208_Maze.zip
巷でウワサの(?) Papervision3D と Box2DFlashAS3 を試しに使ってみた。
どちらも使うのは今回が初めてなので、ちょいちょいいろんなブログを徘徊しつつ、
探り探りですが、簡単な迷路を作って、ボールを転がす。みたいなのを作ってみました。
最初に PV3D でオブジェクトの書き出しをするのにちょっと待たないといけないです。
3秒~10秒ほどかな?(マシンにもよるのだろうけども)
ちょっと重い気がするので別ページに置いておきます。

PV3D & Box3D :: MAZE
今回触ってみて困った点は、
2つのライブラリを触ってみた感想は
PV3Dはさすが人気があるだけのことはあるなあと思いました。
ドキュメントもあるし、みんなブログに説明書いてくれてるし、で
これぐらいの物を作るぐらいならあんまり困らなかった。
一方Box2Dの方は、(たぶん)C++からの移植みたいだけども、
まだ移植しきれていないのか、バージョンアップによる変更のせいなのか
クラスによっては変数が定義されてないものを参照してたり、パッケージ名が
違ってたりしたのが気になった。
ソースとか迷路の詳細とかは、また今度。
どちらも使うのは今回が初めてなので、ちょいちょいいろんなブログを徘徊しつつ、
探り探りですが、簡単な迷路を作って、ボールを転がす。みたいなのを作ってみました。
最初に PV3D でオブジェクトの書き出しをするのにちょっと待たないといけないです。
3秒~10秒ほどかな?(マシンにもよるのだろうけども)
ちょっと重い気がするので別ページに置いておきます。

PV3D & Box3D :: MAZE
今回触ってみて困った点は、
- 迷路はランダムに自動生成しているが、マス目が少ないとかなり単純なものになってしまい、逆に増やすと、PV3Dがオブジェクトを生成するのに時間がかかってしまう。
- Box2Dで円が2つのオブジェクトに挟まれると、動かなくなってしまう。
2つのライブラリを触ってみた感想は
PV3Dはさすが人気があるだけのことはあるなあと思いました。
ドキュメントもあるし、みんなブログに説明書いてくれてるし、で
これぐらいの物を作るぐらいならあんまり困らなかった。
一方Box2Dの方は、(たぶん)C++からの移植みたいだけども、
まだ移植しきれていないのか、バージョンアップによる変更のせいなのか
クラスによっては変数が定義されてないものを参照してたり、パッケージ名が
違ってたりしたのが気になった。
ソースとか迷路の詳細とかは、また今度。
Sandyがバージョン3になってますね。
もうすでに2ヶ月ほど経ってるけども。
Sandy 3D engine
http://www.flashsandy.org/
DistortImageクラスがいつのまにかなくなってるけど、
これに近いクラスってあるのかな。
もうすでに2ヶ月ほど経ってるけども。
Sandy 3D engine
http://www.flashsandy.org/
DistortImageクラスがいつのまにかなくなってるけど、
これに近いクラスってあるのかな。
あんまり取り沙汰されてない気がするけども、AS3になって複数キーのイベントがきちんと取れるようになったのはすばらしい事だと思う。
import flash.events.KeyboardEvent;
import flash.display.Sprite;
var sprite:Sprite = new Sprite();
this.addChild( sprite );
var pressPattern:Array = new Array(300);
function keyDownHandler( event:KeyboardEvent ):void
{
if( !pressPattern[event.keyCode] ){
trace("DOWN : " + event.keyCode);
pressPattern[event.keyCode] = true;
}
}
function keyUpHandler( event:KeyboardEvent ):void
{
if( pressPattern[event.keyCode] ){
trace("UP : " + event.keyCode);
pressPattern[event.keyCode] = false;
}
}
sprite.addEventListener( KeyboardEvent.KEY_DOWN , keyDownHandler );
sprite.addEventListener( KeyboardEvent.KEY_UP , keyUpHandler );
this.stage.focus = sprite;
出力例
DOWN : 97 DOWN : 98 DOWN : 99 UP : 98 UP : 99 UP : 97ちなみにAS2だとこうなる。
DOWN : 97 DOWN : 98 DOWN : 99 UP : 99
最大値と最小値が決まっていて、その間の値を取るときに
Mathクラスのminとmaxを今まで使っていた。
でも、Mathクラスのmaxとminで比較するより、条件分岐を使った方が倍くらい早かった。
minとmax使った方が書くのが楽チンだから
ついついやってしまうのでメモメモ。
Mathクラスのminとmaxを今まで使っていた。
var res:Number = Math.min( 1 , Math.max( 0 , i ) );こんなふうに。
でも、Mathクラスのmaxとminで比較するより、条件分岐を使った方が倍くらい早かった。
var res:Number = ( i > 1 ) ? 1 : ( i < 0 ) ? 0 : i ;Function呼ばないから当たり前なんだろうけど、
minとmax使った方が書くのが楽チンだから
ついついやってしまうのでメモメモ。
flex_sdk_2のディレクトリの中の以下の場所
もしかしてコンポーネント用意すればCS3でFlexオーサリングもできるのかな?
今度暇が出来たらためしてみよかな。
flex_sdk_2/frameworks/source/mx/ここの「mx」ディレクトリにクラスファイルが入ってるので、ここから使いたいものをコピる。
もしかしてコンポーネント用意すればCS3でFlexオーサリングもできるのかな?
今度暇が出来たらためしてみよかな。
AS2だと BezierSegment クラスは用意されていないけど、CS3の以下のディレクトリ
Adobe Flash CS3\ja\Configuration\ActionScript 3.0\Classes\fl\motion
の中に入ってる BezierSegment.as をコピーして少し書き換えればAS2でも使えます。
・packageの記述を削除
・int を Number に書き換え
・for each を for in に書き換え
・function の引数のデフォルト値を削除
swfファイルがココに置いているので、これをダウンロードして WindowSWF フォルダに入れれば Flash 内で
「ウィンドウ」→「他のパネル」から開けます。
AS3 の Math.max と Math.min ってパラメータ2つじゃないんだ。ってことに今気づいたよ。
trace( Math.max( 10, 20, 30 ) );//出力 30
先のエントリーで書いていたことが出来るか試してみたら、出来た。
そのまんまなんだけど、バイナリ型で読み込んだSWFファイルを
一度 compress を実行した後、配列から1バイトずつ取り出して文字列として結合。
それを SharedObject に保存。
使うときはその逆。びっくりするほど簡単だった。
ByteArrayUtil というクラスとして作成しておいた。
各クラスファイルは以下にあります。
使用している自作クラス
・com.seyself.utils.ByteArrayUtil
・com.seyself.data.BinaryData
内部でもろもろクラスを使用しているので、試してみる場合はココからまるごとSVNで落としてもらった方がいいかも。
ByteArrayUtil クラスの使用例
そのまんまなんだけど、バイナリ型で読み込んだSWFファイルを
一度 compress を実行した後、配列から1バイトずつ取り出して文字列として結合。
それを SharedObject に保存。
使うときはその逆。びっくりするほど簡単だった。
ByteArrayUtil というクラスとして作成しておいた。
各クラスファイルは以下にあります。
使用している自作クラス
・com.seyself.utils.ByteArrayUtil
・com.seyself.data.BinaryData
内部でもろもろクラスを使用しているので、試してみる場合はココからまるごとSVNで落としてもらった方がいいかも。
ByteArrayUtil クラスの使用例
import flash.display.Loader; import flash.utils.ByteArray; import flash.net.SharedObject; import com.seyself.data.BinaryData; import com.seyself.utils.ByteArrayUtil; var so:SharedObject = SharedObject.getLocal( "binary_to_string" ); //SharedObject にデータが保存されている場合 if( so.data.hasOwnProperty("savedata") ){ trace( "--load to local data" ); toStringCallback( so.data.savedata ); } //SharedObject にデータが保存されていない場合 else { trace( "--load to file data" ); var binaries:BinaryData = new BinaryData(); binaries.complete = loadComplete; binaries.load( "example.swf" ); //外部SWFファイルをバイナリ形式で読み込む } //変換中に呼び出される関数です。 var progress = function( parcent:Number ) { //parcent : Number - 現在の変換率が渡されます。 trace("completed convert percentage : "+parcent); } //外部からの読み込みが完了すると呼び出される関数です。 function loadComplete() { ByteArrayUtil.toString( binaries.bytes , toStringCallback, progress ); } //ByteArray から文字列への変換が完了したときに呼び出されます。 function toStringCallback( code:String ):void { so.data.savedata = code; ByteArrayUtil.toBinary( so.data.savedata , toBinaryCallback , progress ); } //文字列から ByteArray への変換が完了したときに呼び出されます。 function toBinaryCallback( binary:ByteArray ) { //Loaderインスタンスに、変換されたByteArrayを読み込ませる。 var loader:Loader = new Loader(); this.addChild( loader ); loader.loadBytes( binary ); }
先のエントリーに引き続き。
ステージ上に配置したムービークリップのインスタンス名には
シンボル名をそのままインスタンス名に使うことが多々ある。
AS3ではリンケージの設定に「クラス」と「基本クラス」があって、
「クラス」の方には1つのクラスにつき、1つのシンボルにしか割り当てられない。
そのため、CustomButtonクラスのようなものを作って、複数のボタンの「クラス」に
同じクラスを割り当てようとすると、このクラスを継承したサブクラスをボタンの数だけ
用意しなければならなくなる。
そうした無駄にクラスが増えてしまうことを回避するために「基本クラス」を使う。
この「基本クラス」を使うときに僕がハマってしまったのが、「基本クラス」は
そのままクラスを割り当てればいいのだけれど、「クラス」の方はデフォルトでは
シンボル名が入るようになっている。
親切なことに、Flashはパブリッシュ時に、存在しないクラスを勝手に生成してくれるから
そのまま気にせず書き出しても普通は問題ない。
で、これをステージ上に配置して、インスタンス名をシンボル名と同じ名前に設定すると
たちまちコンパイルエラーが出てしまう。
しかも、このコンパイルエラーが曲者で、
実際エラーを起こす原因は、クラス名と同じ名前のインスタンスがステージ上に存在するから。
例えば「startButton」というシンボルを作成して、そこに
これをステージ上に配置して、当然のようにインスタンス名を「startButton」にする。
この状態で書き出すと上記のようなコンパイルエラーが発生する。
手っ取り早く回避するには「クラス」の定義名を「 シンボル名 + Class 」とかにすることかなあ。
ステージ上に配置したムービークリップのインスタンス名には
シンボル名をそのままインスタンス名に使うことが多々ある。
AS3ではリンケージの設定に「クラス」と「基本クラス」があって、
「クラス」の方には1つのクラスにつき、1つのシンボルにしか割り当てられない。
そのため、CustomButtonクラスのようなものを作って、複数のボタンの「クラス」に
同じクラスを割り当てようとすると、このクラスを継承したサブクラスをボタンの数だけ
用意しなければならなくなる。
そうした無駄にクラスが増えてしまうことを回避するために「基本クラス」を使う。
この「基本クラス」を使うときに僕がハマってしまったのが、「基本クラス」は
そのままクラスを割り当てればいいのだけれど、「クラス」の方はデフォルトでは
シンボル名が入るようになっている。
親切なことに、Flashはパブリッシュ時に、存在しないクラスを勝手に生成してくれるから
そのまま気にせず書き出しても普通は問題ない。
このクラスの定義がクラスパス内に見つからなかったため、 定義は書き出し時に SWF ファイル内に自動生成されます。って警告されるやつ。
で、これをステージ上に配置して、インスタンス名をシンボル名と同じ名前に設定すると
たちまちコンパイルエラーが出てしまう。
しかも、このコンパイルエラーが曲者で、
1046: 型が見つからないか、コンパイル時定数ではありません。と出力されるが、はじめはこのエラーが何を指しているのかがメチャクチャ分かりにくい。
実際エラーを起こす原因は、クラス名と同じ名前のインスタンスがステージ上に存在するから。
例えば「startButton」というシンボルを作成して、そこに
クラス : startButton 基本クラス : CustomButtonと設定した場合。(「クラス」にはデフォルトで入力されているシンボル名のまま)
これをステージ上に配置して、当然のようにインスタンス名を「startButton」にする。
この状態で書き出すと上記のようなコンパイルエラーが発生する。
手っ取り早く回避するには「クラス」の定義名を「 シンボル名 + Class 」とかにすることかなあ。
[Strict モード] を選択すると、警告がエラーとして報告されます。これは、それらのエラーが存在する場合は、コンパイルに成功しないことを意味しています。
[Warnings モード] を選択すると、ActionScript 2.0 コードを ActionScript 3.0 に更新するときに非互換性を検出するのに役立つ、追加の警告が報告されます。
[Warnings モード] を選択すると、ActionScript 2.0 コードを ActionScript 3.0 に更新するときに非互換性を検出するのに役立つ、追加の警告が報告されます。
AS2の時と同様にオーサリングしてたら、プレビューしたときにコンパイルエラーで
怒られまくったのでメモ。
例えば box というシンボル(ムービークリップ)を作成して、
さらに、その内部にテキストフィールドを配置して、インスタンス名を tf とする。
box内のタイムラインに以下のスクリプトを記述。
次にムービークリップ box のリンケージの設定を
BoxClass の中身は以下の通り。
ステージのインスタンスを自動宣言にはチェックを入れておく。
これで書き出すと以下のコンパイルエラーが出力される。
つづく…
例えば box というシンボル(ムービークリップ)を作成して、
さらに、その内部にテキストフィールドを配置して、インスタンス名を tf とする。
box内のタイムラインに以下のスクリプトを記述。
tf.text = "TextField";これで一度パブリッシュしてみると、うまく書き出される。
次にムービークリップ box のリンケージの設定を
クラス : BoxClass 基本クラス : flash.display.MovieClipと設定する。
BoxClass の中身は以下の通り。
package
{
import flash.display.MovieClip;
dynamic public class BoxClass extends MovieClip
{
public function BoxClass(){}
}
}
ちなみにパブリッシュ設定の ActionScript 3.0 「設定」 内のステージのインスタンスを自動宣言にはチェックを入れておく。
これで書き出すと以下のコンパイルエラーが出力される。
説明:1046: 型が見つからないか、コンパイル時定数ではありません。 : TextField。タイムラインかクラスファイル内に
import flash.text.TextField;が必要とのこと。
つづく…
import fl.controls.TextInput;
import flash.text.TextFormat;
var itext:TextInput = new TextInput();
this.addChild( itext );
var tf:TextFormat = new TextFormat();
tf.color = 0x666666;
tf.size = 14;
tf.font = "Verdana";
itext.setStyle("textFormat", tf);
AS2だと
trace(this);だけで済むのに、AS3でこれをしようとすると
function getGlobalPath( target:DisplayObject ):String
{
if( target!=target.root ){
var _path:String = getGlobalPath( target.parent );
return _path+"."+target.name;
} else {
return "root";
}
}
var res:String = getGlobalPath( this );
trace(res); //出力 : root.instance1.instance2.instance3.instance4.instance5
var mc:Object = this.root;
trace( mc.instance1.instance2.instance3.instance4.instance5==this ); //出力 : true
いちいちこんな事しないといけないのかな?
Motion クラスと Ease クラス
サンプル
サンプルソース
ドキュメント
使用例:
サンプル
サンプルソース
ドキュメント
使用例:
import com.seyself.controls.Motion;
import com.seyself.controls.Ease;
import flash.display.Sprite;
var motion:Motion = new Motion();
var easing:Array = Ease.cubic( "out" , 30 );
for(var i=0;i<10;i++){
creater(i);
}
function creater( i )
{
var sprite:Sprite = new Sprite();
sprite.graphics.beginFill( 0x666666 , 1 );
sprite.graphics.drawRect( 0, 0, 20 , 20 );
sprite.y = i*30+5;
sprite.x = 10;
motion.start( sprite , { x:550 } , easing , i*3 );
this.addChild( sprite );
}
enterFrameイベントを管理するクラス「EnterFrame」
サンプル
サンプルソース
ドキュメント
使用例:
サンプル
サンプルソース
ドキュメント
使用例:
import com.seyself.events.EnterFrame;
import com.seyself.events.EnterFrameEvent;
import flash.display.Sprite;
function emptiedHandler( event:EnterFrameEvent )
{
trace("empty");
}
EnterFrame.addEventListener( "emptied" , emptiedHandler );
function registeredHandler( event:EnterFrameEvent )
{
trace("registered / "+event.currentTarget);
}
EnterFrame.addEventListener( "registered" , registeredHandler );
function removedHandler( event:EnterFrameEvent )
{
trace("removed / "+event.currentTarget);
}
EnterFrame.addEventListener( "removed" , removedHandler );
for(var i=0;i<10;i++){
creater(i);
}
function creater( i )
{
var sprite:Sprite = new Sprite();
sprite.graphics.beginFill( 0x666666 , 1 );
sprite.graphics.drawRect( 0, 0, 20 , 20 );
sprite.y = i*10;
sprite.x = i*4;
var tx = Math.random()*800;
var ty = Math.random()*800;
function enterFrameHandler( event:EnterFrameEvent )
{
sprite.x += (tx-sprite.x)/3;
sprite.y += (ty-sprite.y)/3;
if( event.currentTime==20 ){
event.remove();
}
}
EnterFrame.register( enterFrameHandler , sprite , i*3 );
this.addChild( sprite );
}
AS2でAbstract
AS3でAbstract
class AbstractClass
{
function AbstractClass()
{
if(arguments.caller.prototype instanceof AbstractClass){
trace("create instance");
} else {
throw new Error("ArgumentError: Error #2012: AbstractClass class cannot be instantiated.");
}
}
}
class SubClass extends AbstractClass
{
function SubClass()
{
super();
}
}
AS3でAbstract
package
{
public class AbstractClass
{
function AbstractClass( access:*=null ):void
{
if( access!=this ){
throw new ArgumentError("Error #2012: AbstractClass class cannot be instantiated.");
}
}
}
}
package
{
public class SubClass extends AbstractClass
{
function SubClass( access:*=null ):void
{
super(this);
}
}
}
Singleton メモ
package
{
public class Singleton
{
private static var instance:Singleton;
function Singleton(caller:Function=null):void
{
if( caller != createInstance ) throw new ArgumentError("Cannot user access constructor.");
trace("create new instance.");
}
private static function createInstance():Singleton
{
return new Singleton(arguments.callee);
}
public static function getInstance():Singleton
{
if( instance == null ){
instance = createInstance();
}
return instance;
}
}
}
DisplayObjectを用いるのはいかがなものなんだろう。
AS3ではクラスのコンストラクタ定義に private が使用できないので
普通に作ってると、作ったクラスがすべてインスタンス化できてしまう。
どうしてもインスタンス化されたくないクラスは DisplayObject を継承すれば
一応インスタンス化できなくなるんだけども。
使い方として間違ってる気がする。
しかも Singleton 的なことができない。
インスタンス化そのものができないから。
なんかいい方法がどっかに落っこちてないかなあ。
--追記
package
{
import flash.display.DisplayObject;
public class Abstract extends DisplayObject
{
}
}
AS3ではクラスのコンストラクタ定義に private が使用できないので
普通に作ってると、作ったクラスがすべてインスタンス化できてしまう。
どうしてもインスタンス化されたくないクラスは DisplayObject を継承すれば
一応インスタンス化できなくなるんだけども。
使い方として間違ってる気がする。
しかも Singleton 的なことができない。
インスタンス化そのものができないから。
なんかいい方法がどっかに落っこちてないかなあ。
--追記
function Constructor()
{
throw new ArgumentError ("#xxxx: Constructor Class cannot make a Instance.");
}
インスタンス化させないだけなら、単純にコンストラクタにエラーを返せばいいみたい。
AS3 の apply と call の scope が変更されない。
DocumentRootTimeline :: Frame Action
---- 追記
ちなみにプロパティとして定義された関数には今まで通り適用されるみたい。
DocumentRootTimeline :: Frame Action
まいっちんぐ
---- さらに追記
こうやってもダメだった。
DocumentRootTimeline :: Frame Action
DocumentRootTimeline :: Frame Action
import flash.display.MovieClip;
var mc = new MovieClip();
test.apply( mc , null );
function test()
{
trace(this);// [object MainTimeline]
}
---- 追記
ちなみにプロパティとして定義された関数には今まで通り適用されるみたい。
DocumentRootTimeline :: Frame Action
import flash.display.MovieClip;
var mc:MovieClip = new MovieClip();
var test = function()
{
trace(this);
}
test.apply( mc );// [object MovieClip]
まいっちんぐ
---- さらに追記
こうやってもダメだった。
DocumentRootTimeline :: Frame Action
import flash.display.MovieClip;
var mc:MovieClip = new MovieClip();
function test()
{
trace(this);
}
var tmp = test;
tmp.apply( mc );// [object MainTimeline]
import flash.display.Sprite;
import flash.events.Event;
this.addEventListener( Event.ENTER_FRAME , enterframe1 );
this.addEventListener( Event.ENTER_FRAME , enterframe2 );
this.addEventListener( Event.ENTER_FRAME , enterframe2 );
var obj = { };
obj.clipChild = new Sprite();
var grandChild:Sprite = new Sprite();
obj.clipChild.addEventListener( Event.ENTER_FRAME , enterframe3 );
grandChild.addEventListener( Event.ENTER_FRAME , enterframe4 );
obj.clipChild.addChild( grandChild );
this.addChild( obj.clipChild );
function enterframe1( e:Event )
{
trace("enterframe1");
this.removeEventListener( Event.ENTER_FRAME , enterframe1 );
}
function enterframe2( e:Event )
{
trace("enterframe2");
delete obj.clipChild;
this.removeEventListener( Event.ENTER_FRAME , enterframe2 );
}
function enterframe3( e:Event )
{
trace("enterframe3");
trace( obj.clipChild );
obj.clipChild.removeEventListener( Event.ENTER_FRAME , enterframe3 );
}
function enterframe4( e:Event )
{
trace("enterframe4");
grandChild.removeEventListener( Event.ENTER_FRAME , enterframe4 );
}
出力結果は
enterframe1 enterframe2 enterframe3 undefined TypeError: Error #1010: 条件は未定義であり、プロパティがありません。 at test_fla::MainTimeline/enterframe3() enterframe4 enterframe3 undefined TypeError: Error #1010: 条件は未定義であり、プロパティがありません。 at test_fla::MainTimeline/enterframe3() … …以下メモ:
・this参照は Event でもないし、追加したオブジェクトでもなく Function を定義している場所。
・同じ Function は2重に定義できない。
・違う Function であれば同じイベントを複数追加できる。
・removeEventListener を実行する前に そのリスナーオブジェクトを削除するとイベントが消せなくなる。
・enterFrame の実行される順序は、addした順。
var str = "abcdefghijklmnopqrstuvwxyz";
var obj = {}
for(var i=0;i<10;i++){
var s = str.substr(i,1);
obj[s] = i+"";
}
obj["en"] = "english";
obj["jp"] = "japanese";
delete obj["d"];
for ( var val in obj ){
trace(val);
}
AS2までの for in では
jp en j i h g f e c b aと出力されていた。
一応追加された逆順になっている。
これをAS3で実行すると
h c i jp j e f a en g b
こんな具合。
さっぱりルールが分からない。
ちなみに AS3 で追加された for each を使うとこんな感じ。
7 2 8 japanese 9 4 5 0 english 6 1
バラバラ事件です。
AS2 で使用していたクラスは、これのせいで一部仕様変更しないといけなくなっちゃいました。
先日のエントリーに書いたクラスファイルを下記の場所にアップ。
http://blog.seyself.com/archives/classicEvent.zip
やたらといろいろ定義しないといけないのが面倒なのと、覚えるのも一苦労しそうなので
AS2 ベースのイベント、メソッドを持ったクラスを作成。
たぶんみんなおんなじようなことはやってるだろうから、探せばどっかにあるんだろうけど
まずは AS3 の仕組みを理解するためにも、もんどりうって作ってみる。
以下はその時の経過をメモ。
クラス内に
と警告されてしまう。
onLoad, onRelease, onPress 等も同様。
public ステートメントを付けると更に上書きもできなくなってしまう。
でも onEnterFrame や onRelease は定義した直後から自動的にイベントを受け取ってほしいので
でもこれだと同じく警告が出てしまう。
で、Proxy クラスを用いて onEnterFrame プロパティに
無理やり setter , getter を再定義する方法を試みてみる。
こんな感じに。
でもこれだとどうしても this.onEnterFrame と書くのが難しかった。
いろいろ試行錯誤していると、
getter 無しだと警告が出てなかった。
理由は良く分からない。
でもこれだと onEnterFrame で定義した function を抜き出すことができないので
これだと警告も出ないし、モーマンタイ。
この調子でいろいろイベントを作成していく。
気にくわないのは onSetFocus の getter の名前が
「getSetFocus」になってしまったこと。
get なんだか set なんだか。。。
AS2 ベースのイベント、メソッドを持ったクラスを作成。
たぶんみんなおんなじようなことはやってるだろうから、探せばどっかにあるんだろうけど
まずは AS3 の仕組みを理解するためにも、もんどりうって作ってみる。
以下はその時の経過をメモ。
クラス内に
public function onEnterFrame():void {}
と書くとWarning: 1090: Migration issue: The onEnterFrame is not triggered automatically by Flash Player at run time in ActionScript 3.0. You must first register this handler for the event using addEventListener ( 'enterFrame', callback_handler).
と警告されてしまう。
onLoad, onRelease, onPress 等も同様。
public ステートメントを付けると更に上書きもできなくなってしまう。
public var onEnterFrame:Function;これだと警告は出ない。
でも onEnterFrame や onRelease は定義した直後から自動的にイベントを受け取ってほしいので
public function set onEnterFrame( func:Function ):void
{
__onEnterFrame = func;
this.addEventListener( Event.ENTER_FRAME, __enterFrameCall );
}
public function get onEnterFrame():Function
{
return __onEnterFrame;
}
private function __enterFrameCall( e:Event ):void
{
__onEnterFrame.apply( target , null );
}
private var __onEnterFrame:Function;
と書きたい。でもこれだと同じく警告が出てしまう。
で、Proxy クラスを用いて onEnterFrame プロパティに
無理やり setter , getter を再定義する方法を試みてみる。
override flash_proxy function getProperty( name )
{
return classicEvent['__'+name];
}
override flash_proxy function setProperty( name, func ):void
{
var str = name.toString();
var tmp = str.split('on').join('');
classicEvent['__'+name] = func;
classicEvent['__set'+tmp+'Event']();
}
こんな感じに。
でもこれだとどうしても this.onEnterFrame と書くのが難しかった。
いろいろ試行錯誤していると、
public function set onEnterFrame( func:Function ):void
{
__onEnterFrame = func;
}
private var __onEnterFrame:Function;
getter 無しだと警告が出てなかった。
理由は良く分からない。
でもこれだと onEnterFrame で定義した function を抜き出すことができないので
public function set onEnterFrame( func:Function ):void
{
__onEnterFrame = func;
}
public function getEnterFrame():Function
{
return __onEnterFrame;
}
private var __onEnterFrame:Function;
として、中身の取得も可能にする。これだと警告も出ないし、モーマンタイ。
この調子でいろいろイベントを作成していく。
気にくわないのは onSetFocus の getter の名前が
「getSetFocus」になってしまったこと。
get なんだか set なんだか。。。
昨日、今日と Flash CS3 で AS3 をあれこれ触ってみて、
for(var i=0;i<10;i++){
trace(i);
}
for(var i=0;i<10;i++){
trace(i);
}
これに何でエラーが出るのかまったく分からず四苦八苦してた。
どうも同じスクリプト内で i が2回 var で宣言されてるのがダメらしい。
for(var i=0;i<10;i++){
trace(i);
}
for( i=0;i<10;i++){
trace(i);
}
こうすると無事実行してくれる。
