javascriptから加速度センサの情報を取得

せっかくiPhoneがあるので、加速度センサを使用してそれっぽいのを作ってみました。CreateJSを使用しています。

AndroidやWindowsPhone等では動作確認していませんので、動かないかもしれません。

https://www.mizunagi-works.com/gadgets/nanoge/ss01/index.html

ゲーム性はないです…

.

加速度センサを使用するアプリ開発は、加速度センサがない環境だと開発出来ないため、CreateJSのstagemousemoveイベントで擬似的に傾き(というか、今回の処理に必要な情報)を生成しています。

作成していて気付いたのですが、画面のロックをしていない状態だとセンサの情報と画面の向きが一致しなくなるため、動作がおかしくなるようです。

こういうのはどうすりゃいいんだろう…

着せ替えアプリの試作(2)

先日作成したCreateJSによる試作アプリをバージョンアップさせてみました。

http://mizuvm01.cloudapp.net/gadgets/form_collage/

IMG_0079 初代iPadでの動作例

単に色を塗っただけに見えますが、モデルデータと服データに階層構造をもたせて、それっぽい重ね合わせを実現しています。

ついでにiPhoneやiPadの縦横リサイズにも対応させてみました。

fig_01 こういう組み合わせです。

アインハルトさんの例だと、

  • BODY_BASEにキャラクター本体、BODY_BACKに髪の毛(後頭部)、BODY_FOREに右手
  • WEAR_BASEに長袖シャツ、WEAR_FOREに長袖の右腕部分
  • DROPに背景

といった感じで描画を行っています。(Photoshopのレイヤーなんかと同じです。)

前述したように複数画像を組み合わせて一つの画像として扱っているのですが、それぞれが別レイヤーに配置されているため、Containerによる結合が行えません。

そのため、今回は空のContainerを生成し、そこにhitAreaとマウスイベントを設定するようにし、割り当てたContainerのidをキーとする連想配列に構成画像を格納しています。

こうすることで

  1. hitAreaの領域がクリックされたことを検知。
  2. クリックされたContainerのidを調べる。
  3. ドラッグされた場合は、
    1. Containerの位置を更新。
    2. Containerのidを使用して連想配列から画像を取り出す。
    3. 画像の位置を更新する。

といった処理を実現しています。

また実画像とhitAreaを別に管理することで、

  • 判定処理以下のアルファ値の画像もクリック可能になる。
  • 体を動かすことは出来ないが、ツインテール部分はドラッグ可能という動作を実現しています。

着せ替えアプリの試作(1)

イラストの服装を考える時に便利かもしれないな…という事で、Twitterに投稿した絵の派生でアプリを作ってみました。

CIRW8x-UcAA97Te.jpg-orig これがネタ元

form:Collage

http://mizuvm01.cloudapp.net/gadgets/form_collage/

form_collage 操作にはマウスが必要です。(タッチパッド対応になりました)

着せ替え遊びを楽しむには、かなり寂しいボリュームですけど…

CreateJS(EaselJS + PreloadJS)で実装をしています。以下はTypeScriptから生成されたものになりますが、かなりの機能をCreateJS側で処理出来たため、かなり少ないコード量で実現できました。

これ以外の選択肢としてはenchant.jsD3.jsによる実装を考えていましたが、CreateJSは実現したい事と提供されている機能の相性が良かったため、正解だったかも。

// ===========================================================================
/*!
 * @brief Character Image Collage
 * @author @MizunagiKB
 */
// -------------------------------------------------------------- reference(s)
/// <reference path="easeljs/easeljs.d.ts" />
/// <reference path="preloadjs/preloadjs.d.ts" />
var E_LAYER;
(function (E_LAYER) {
    E_LAYER[E_LAYER["BODY"] = 0] = "BODY";
    E_LAYER[E_LAYER["WEAR"] = 1] = "WEAR";
})(E_LAYER || (E_LAYER = {}));
var GLOBAL = {
    STAGE: null,
    STAGE_LAYER: {},
    STAGE_RESOURCE_IMG: {},
    PRELOADER_QUEUE: null,
    PRELOADER_ORDER: {}
};
/*!
 */
function evt_pressmove(oCEvt) {
    var oCItem = GLOBAL.STAGE_RESOURCE_IMG[oCEvt.target.id];
    var oCInstance = oCEvt.target;
    var oCPos = oCInstance.offset;
    var nX = oCEvt.stageX + oCPos.x;
    var nY = oCEvt.stageY + oCPos.y;
    var nAdjustX = nX - oCItem.fit[0];
    var nAdjustY = nY - oCItem.fit[1];
    if (Math.abs(nAdjustX) < 16) {
        if (Math.abs(nAdjustY) < 16) {
            nX = nX - nAdjustX;
            nY = nY - nAdjustY;
        }
    }
    oCInstance.x = nX;
    oCInstance.y = nY;
    GLOBAL.STAGE.update();
}
/*!
 */
function evt_pressup(oCEvt) {
    var oCInstance = oCEvt.target;
}
/*!
 */
function evt_mousedown(oCEvt) {
    var oCInstance = oCEvt.target;
    var oCPos = oCInstance.offset;
    oCInstance.addEventListener("pressmove", evt_pressmove);
    oCInstance.addEventListener("pressup", evt_pressup);
    oCInstance.offset = new createjs.Point(oCInstance.x - oCEvt.stageX, oCInstance.y - oCEvt.stageY);
}
/*!
 */
function evt_preload_fileload(oCEvt) {
    var oCItem = GLOBAL.PRELOADER_ORDER[oCEvt.item.src];
    var oCBitmap = new createjs.Bitmap(oCEvt.result);
    oCBitmap.x = oCItem.pos[0];
    oCBitmap.y = oCItem.pos[1];
    GLOBAL.STAGE_LAYER[oCItem.layer].addChild(oCBitmap);
    GLOBAL.STAGE_RESOURCE_IMG[oCBitmap.id] = oCItem;
    switch (oCItem.layer) {
        case E_LAYER.BODY:
            {
            }
            break;
        case E_LAYER.WEAR:
            {
                oCBitmap.addEventListener("mousedown", evt_mousedown);
            }
            break;
    }
}
/*!
 */
function import_from_url(strUrl) {
    $.getJSON(strUrl, function (oCJson) {
        var loadItem = [];
        for (var _i = 0, _a = oCJson.body.items; _i < _a.length; _i++) {
            var o = _a[_i];
            o.layer = E_LAYER.BODY;
            GLOBAL.PRELOADER_ORDER[o.src] = o;
            loadItem.push({ "src": o.src });
        }
        for (var _b = 0, _c = oCJson.wear.items; _b < _c.length; _b++) {
            var o = _c[_b];
            o.layer = E_LAYER.WEAR;
            GLOBAL.PRELOADER_ORDER[o.src] = o;
            loadItem.push({ "src": o.src });
        }
        GLOBAL.PRELOADER_QUEUE.addEventListener("fileload", evt_preload_fileload);
        GLOBAL.PRELOADER_QUEUE.addEventListener("complete", function (oCEvt) {
            GLOBAL.PRELOADER_ORDER = null;
            GLOBAL.STAGE.update();
            console.log("Ready.");
        });
        GLOBAL.PRELOADER_QUEUE.loadManifest(loadItem, false);
        GLOBAL.PRELOADER_QUEUE.load();
    });
}
/*!
 */
function main() {
    GLOBAL.STAGE = new createjs.Stage("main_surface");
    GLOBAL.STAGE_LAYER[E_LAYER.BODY] = new createjs.Container();
    GLOBAL.STAGE_LAYER[E_LAYER.WEAR] = new createjs.Container();
    GLOBAL.STAGE.addChild(GLOBAL.STAGE_LAYER[E_LAYER.BODY]);
    GLOBAL.STAGE.addChild(GLOBAL.STAGE_LAYER[E_LAYER.WEAR]);
    GLOBAL.PRELOADER_QUEUE = new createjs.LoadQueue(false);
    import_from_url("ein.json");
}

やっている事はシンプルにCreateJSのマウスドラッグイベントで処理しているだけなのですが、CreateJSでは画像を対象とした際に、アルファ値を考慮した動作をしてくれます。

というわけで、自分よりも後ろの画像を消すのを兼ねてクリック可能領域を不透明に塗るだけです。

region 分かりやすく赤色にしてあります。

とまぁ、思わず作成してしまったのですが、着せ替え用のキャラクター画像って服を重ねる関係上、すこし細めに描いておいたほうが良さそうだって事がわかりました。