openFrameworks
flash
ActionScript3
FLASHerDay 11

AS3が便利すぎてAS3ライクにコードが書けるopenFrameworksアドオン作った。

More than 1 year has passed since last update.

この記事は FLASHer Advent Calendar 2016 11日目の記事です。

こんばんわ。selflashと言います。

思い出話


本題に入る前にFlash全盛期の「あの人は今」というキーワードを見かけたので少し思い出話(雑談)を。

当時のフルFlashサイトといえばどんどんリッチWebサイトになればなるほど、次第に
3D的な表現が多用されるようになりましたね。
PaperVision3D, Away3D, Alternativa3Dから始まりついには時代は自作3Dエンジンへ!
と時代は戦国時代へと突入したのですが、、
その時代に1人ずば抜けた伝説的なFlasherがいました。

そうです。 誰もがそこに痺れる憧れる伝説のFlasher城戸さん!!
城戸さんのサイトはいつもどうやって作っているのかわからないようなサイトばかりでした。

エコだ!動物園

バーベイタム選手権(サービス終了)

The Planet Zero

僕がFlasherとしての駆け出しの頃は、毎回城戸さんの新作コンテンツを見る度に心を躍らせ、
その数分後には

「そっか、この人だけ1日が48時間くらいあるんだろうぁ。。(ひとり精神と時の部屋マン)」

と自分に言い聞かせて心を落ち着かせていた思い出があります。(遠い目)

時を同じくして、僕が今の会社に入社したての頃、社内でも自社サイトを3D格闘ゲームのコンテンツへと
リニューアルする為の開発の真っ最中でした。
僕はWeb上のFlashでこんな3D格闘ゲームが作れるのか!!!と度肝を抜かれましたし、
開発が進んでいく様子を見るのが楽しみでもありました。
そのサイトが僕も大好きな

CREATIVE IS ENDLESS BATTLE

です。
このサイトの3D格闘する部分のFlash製の独自3Dエンジンを制作した大先輩が
何日か後にこのFLASHEer Advent Calendarに記事を投稿してくれるらしいです。
超絶クオリティの3Dサイトを生み出してきた城戸さんや僕もお世話になったProgressionフレームワークのniumさん等、フルFlashサイト黄金期を賑わせた有名Flasher達は今どうしているんだろう。。と気になる人達もいると思います。
その中で、当時の貧弱なPCでのWebブラウザ環境で3D格闘ゲームを実現させたFlasherは今どこで何をしているのだろうと。。。
その先輩は今でも僕の隣の席で仕事をばりばりこなしているわけですが、あのサイト以降今日までどんな仕事をしてきたのかの日記でもいいので是非Calendarに投稿してくださいよ!と懇願したら、いやいや引き受けてくれました!
こうした場に出てくる事なんてめったにないツチノコFlasherな方なので僕も今から楽しみにしてます。

本題


さて、ここからが本題なのですが。
僕は5年程前から、WebでのフルFlashコンテンツからインスタレーションと自身
フィールドを変えた来たのですがその際に初めてopenFrameworks(以下 oF)というものを触りました。
AS3からプログラミングを始めた僕にとって、初めてのC++(oF)というのはもの凄く
難しかったし、今でも全然理解ができてませんで、なんとなくでコードを書いている毎日です・・

当時は「Flashのディスプレイオブジェクトツリーはなんて素晴らしいんだろう・・」と
噛み締めながらoF(C++)の勉強をしていました。
oF上で少し複雑な構造の表示物を(UI等)を
作ろうとするとAS3の時のようにいかずかなり手こずります、、
そしてデバッグ用に微調整するUIが欲しくてとうとうこんなものを作りました。

oF上でAS3ライクな記述ができるアドオン「ofxSelflash」

アドオンというよりもはやフレームワークに近いような大きさになってしまったので、
作り始めた当時はFlashFrameworks(仮)という名前でこんな感じにコツコツ作ってました。

TEST01 Multilight - flashFramework for openFrameworks (3年前)
TEST02 Mesh field - flashFramework for openFrameworks (3年前)

過去の実案件でもちょくちょく投入してたり
REAL INVADER developer's view (3年前)
InkShader-1 (2年前)
InkShader-2 (2年前)

いろいろと未実装箇所が多すぎるのとバギー過ぎた為、公開してなかったのですが、
安定して少し落ち着いてきたので思い切ってアドオン化してgithubにて公開してます。

特徴


+ インタラクティブオブジェクトが使える。(AS3でのマウスイベント周りの挙動を再現)
+ ディスプレイオブジェクトツリーが使える。(AS3での表示オブジェクトの重なり順の挙動を再現)

oFは描画は高速なのですが、独自UIを作りたいという場合にこれら2つの機構が必須だった
のですが、やっぱりAS3の挙動を真似るのが早いと思い作りました。
oF上でAS3まがいを実装してて思った事は、まぁこれは確かにAS3って重くなるよね・・って感じました。
(オブジェクトと常時監視する部分だったりとか)
凄く便利なんですけどねAS3!
もともとAS3のビルドインクラスをまるっと移植しようと思ってたわけじゃなく、
僕自身がInteractiveObject、DisplayObjectContainerの機能がまるっと欲しかっただけなので、
下記のような表示系クラスには全然力入れてません。というより中途半端 or ほぼ未実装です、、

  • MovieClip(初期には作ってたが早い段階で開発中断の為どこまで実装したかも覚えてない)
  • BitmapData(未実装。いらないでしょ)
  • GraphicsクラスのAPIは不十分(CurveToやDrawTrianglesは未実装)

さらに下記の機能は未実装です。
+ フィルターまわり(これはいらないかと。。実装予定なし! 各自GLSLでやるでしょ)
+ マスクまわり(スクロールバー系のUIが欲しいからいつか実装しようとは思ってる)
+ ブレンドモードまわり(実装途中。というかどこまで実装してたか忘れた)
+ カラートランスフォームとかとか

もし、それでも興味があるという少数派の方の為に使い方を書いておきます。
※Windows10でも動作は確認していますが、今回はMacでの場合

ProjectGeneratorを使ってアドオンして新規プロジェクトを作成します。
スクリーンショット 2016-12-05 15.22.14.png

生成されたプロジェクトフォルダ内のbin/dataフォルダ内に
ofxSelflashのアドオンフォルダ内にあるbin/data/flフォルダをコピーします。
スクリーンショット 2016-12-05 15.42.31.png

プロジェクトを開いてビルド時すると下記のようなエラーがでると思います。
スクリーンショット 2016-12-05 15.35.24.png

これはアドオン内のfl2d/ui/Mouse.cppとutils/NativeWindow.cpp内で
ObjectiveC++のコードが使われている為です。
なのでこの2つのcppファイルのTypeを C++ Source から Objective C++ Srouce に変更しましょう。

_スクリーンショット-2016-12-05-15.35.55.png

__スクリーンショット-2016-12-05-15.35.55.png

これで無事にビルドできると思います。

記述の際の基本

次に実際のコードについてなのですが、
このアドオンを使うにあたって、記述方法がC++をごりごり書く人にとっては
到底受けいられない様な記述方法を強いられます・・(むやみやたらにポインタ使うな。とか)

が、あくまでAS3に慣れ親しんだ人がC++わかんなくてopenFrameworks使えないよ・・
って人の為のアドオンだと割り切っていますのでご了承ください! orz

+基本的にポインタを使った宣言をする事

flSprite* sprite = NULL;
sprite = new Sprite();

+なのでシェアードポインタを未使用な分、AS3と同じでメモリリークは自己管理で

delete sprite;
sprite = NULL;

+C++にはセッター、ゲッターのサクセサがありません。
なのでプロパティへのアクセスは全て関数を通して行います。
引数を渡せばセッター、引数を渡さなければ値を返すゲッターとしての役割を果たします。

//ゲッター
float x = sprite->x();

//セッター
sprite->x(100);

この時点でC++使いの人達からの怒号が聞こえてきそうですが・・
AS3の人たちにとってはそこまで抵抗ないはず、、(多分)
ソースを見られると「こんなウンコードなC++書きやがって!!」とか思われると
思いますが、バグなくそとづらの挙動がAS3であればOK。なアドオンです。
シェアードポインタに頼らなくても、AS3な人達はメモリー管理も自前でやってきたから大丈夫ですよね(遠い目)

サンプルコード

続いてサンプルの最小コードです。
ヘッダーファイル内でアドオンをインクルードします。
Stageはシングルトン的な扱いなのでインスタンスを作成する事はできません。
スタティックなStageへの参照に対してSpriteを一つ作ってaddChildし、
そのSpriteのグラフィックスに矩形を描く
といったコードになってます。
Stage.hを覗くとわかりますが、AS3でのstage.rootも用意してたのですが、MovieClipクラスが
まだ実装を途中でやめてしまったのでまだ使用不可です。

ofApp.h
#include "ofxSelflash.h"
ofApp.cpp
void ofApp::setup() {
    //Ready ofxSelflash
    ofxSelflash::setup();

    //Get Stage Reference
    flStage* stage = ofxSelflash::stage();

    //Create New Sprite
    flSprite* sprite = new flSprite();

    //Set Position
    sprite->x(100);
    sprite->y(200);

    //Draw Graphics
    flGraphics* g;
    g = sprite->graphics();
    g->clear();
    g->lineStyle(1, 0xff0000);
    g->beginFill(0xffffff);
    g->drawRect(0, 0, 100, 100);
    g->endFill();

    //Add to Display-Object-Tree
    stage->addChild(sprite);
}

続いてみんな大好きaddEventListenerの書き方はこんな感じ。

ofApp.h
void eventHandler(flEvent& event);
ofApp.cpp
void ofApp::setup() {
    sprite.addEventListener(flEvent::ENTER_FRAME, this, &ofApp::eventHandler);
}

void ofApp::eventHandler(flEvent& event) {
    cout << "イベントタイプ = " << event.type() << endl;
    cout << "カレントターゲットのアドレス = " << event.currentTarget() << endl;
    cout << "カレントターゲット名 = " << ((flDisplayObject*) event.currentTarget())->name() << endl;
    cout << "ターゲットのアドレス = " << event.target() << endl;
    cout << "ターゲット名 = " << ((flDisplayObject*) event.target())->name() << endl;

    if(event.type() == flEvent::ENTER_FRAME) {
        cout << "エンターフレームイベント" << endl;
    }
}

AS3に馴染みのある人にとっては大分取っつきやすい記述になっているかと思います。
イベントのターゲット、カレントターゲットも実装してます。
(※実際使った時にAS3と返り値が違うよって時にはおしらせください)

サンプルプロジェクト

ofxSelflashではいくつかのサンプルプロジェクトも用意してます。

ディスプレイオブジェクトツリーの挙動と記述のサンプル


example-DisplayObjectTree


ヒットエリアプロパティの挙動と記述のサンプル


example-HitArea


各UIコンポーネントの挙動と記述のサンプル


example-UIComponents-1


テンプレートとして用意されているBasicControllerクラスのサンプル


example-BasicController


テンプレートとして用意されているBasicDraggableObjectクラスのサンプル


example-BasicDraggableObject


Spriteを継承して生のoFのコードと組み合わせるサンプル


このサンプルに関しては補足を。
基本的にはofxSelflash上のクラスがprotected virtualとして宣言されている
_setup, _update, _draw関数がありますのでそちらの中にいつものoFの記述をすれば大丈夫です。
クラスの外からはsetup(), update(), draw()を呼ばれた時に適時これらの関数が呼ばれます。
継承する時にひとつ気をつけなければいけないのはwidth,heightです。
Bitmapクラスを使わずに_draw関数内で自分でofImageなどを描画した場合widthとheightが適切に更新されません。
マウスによるインタラクティブ周りを司っているInteractiveObjectクラスの恩恵を受ける必要がないならそれでもいいのですが、
もし必要であれば、透明なGraphicsで塗る事で自分で拡張したSpriteクラスでも
マウスイベント周りが適切に動作します。具体的には以下の様な感じです。

ofApp.h
ofImage image;
ofApp.cpp
void MySprite::_draw() {
    image.draw(0, 0, 500, 500);

    //--------------------------------------
    //ヒットエリアとしてofImageの描画した領域と
    //同じ面積分透明のグラフィックスを塗る
    flGraphics* g;
    g = graphics();
    g->clear();    
    g->beginFill(0x0000cc, 0);
    g->drawRect(0, 0, 500, 500);
    g->endFill();
    //--------------------------------------
}

最後に

本アドオンは基本的に僕が案件で使いたいAS3のクラスやプロパティをごりごり実装してきたので、
裏を返せば僕が案件で困らなかったAS3のビルドインクラスの実装にはあまり前向きじゃありません。
ですが、もし「AS3のこれを是非移植してほしい・・」などの要望があれば対応出来る範囲ではこつこつやっていこうかなとは思ってます。
(むしろAS3大好きな有志を募ってみんなで完成させませんかね・・? デバッグ協力隊でも助かります・・)
AS3のビルドインンクラスのほとんどが再現できればofxProgressionも作れますね!

なにはともあれ、お気軽にご相談ください!
バグやC++的な書き方で間違っているなどのFBの方もお待ちしております。

という事でメリークリスマス!
良いAS3ライフを!