jsdo.itでenchant.jsを動かす

enchant.jsを使ったゲーム作り実習をやったので、その時のメモを公開。
jsdo.it で作ると、スマフォで動かすのが楽。
ちなみに私はスマフォ用に「シュシュっと手裏剣」を作りました。

(1) jsdo.it でアカウントを作る
http://jsdo.it/ で好きなサービスを使ってログイン。アカウント名はログイン後に決める。

(2) Hello World
画面に文字を表示しよう! by shi3z – code.9leap.net
http://code.9leap.net/codes/show/203
をコピペして動かしてみる。

手順:
1. Start coding を押下。
2. JavaScript タブを選択。
3. + Add Library を押下。
Major Library から enchant.js v0.6.2 – js を選択し、Add ボタンを押下。
4. 9leapのサンプルから main.js をコピペ。
5. Save ボタンを押下。実行結果を確認。

もし正しく動作しない場合は、JavaScript コンソールを確認。

(3) Start 画面追加
jsdo.it 上の別コードをインポートして、Start 画面を表示してみる。

手順:
1. JavaScript タブを選択。
2. + Add Library を押下。
Input URL に 9leap / nineleap.enchant を入力し、Add ボタンを押下。
3. ソースコードを修正。
// game = new Game();
game = new enchant.nineleap.Game();

(4) 画像を表示
スプライトを表示しよう by shi3z – code.9leap.net
http://code.9leap.net/codes/show/202
をコピペ+修正して動かしてみる。画像ファイルのURLを指定する必要がある。
使える素材は
Image Materials | enchant.js – A simple JavaScript framework for creating games and apps.
http://enchantjs.com/ja/image-materials/
を参照。商用利用でなければ、画像を改変して使うのも自由 (by @enchantjs_jaさん)。

手順:
1.
// var game = new Game(320, 320);
var game = new enchant.nineleap.Game(320, 320);

2.
// game.preload(‘chara1.gif’); // chara1.gifを読み込む
var chara1 = ‘http://enchantjs.com/images/materials/chara1.png’;
game.preload(chara1); // chara1.gifを読み込む

3.
// bear.image = game.assets[‘chara1.gif’]; // chara1.gifの中にある
bear.image = game.assets[chara1]; // chara1.gifの中にある

(5) 操作パッド
十字キーは Pad, アナログの操作キーは APad。
ソースコードは http://jsdo.it/takatama/Tn9F/ を参照のこと。

手順:
1. JavaScript タブを選択。
2. + Add Library を押下。
Input URL に 9leap / ui.enchant と入力し Add ボタンを押下。
3. ソースコードを入力。

var pad = new enchant.ui.Pad();
pad.moveTo(10, 220);
game.rootScene.addChild(pad);

bear.addEventListener('enterframe', function () {
    var input = game.input;
    //キーに応じて移動
    if (input.left) {
        this.x -= 4;
    }
    if (input.right) {
        this.x += 4;
    }
    if (input.up) {
        this.y -= 4;
    }
    if (input.down) {
        this.y += 4;
    }
    if (game.frame %4 === 0) {
        this.frame = 2- this.frame;
    }
});

var apad = new enchant.ui.APad();
apad.moveTo(120, 220);
game.rootScene.addChild(apad);

bear.addEventListener('enterframe', function () {
    if (apad.isTouched) {
        this.x += apad.vx*4;
        this.y += apad.vy*4;
    }
});

(6) スマフォで操作する
URLを jsdo.it から jsrun.it に変更して、スマフォのブラウザーからアクセス。
例えば http://jsdo.it/takatama/Tn9F/ なら http://jsrun.it/takatama/Tn9F/ にすればよい。

(7) End画面を追加
上記(3)でStart画面を追加済みなら、ゲーム終了時に、game.end(score, message); を呼び出せば良い。

(8) Clear画面を追加
ちょっとした工夫が必要。Gameをnewする前後にソースコードを追記する。

enchant.nineleap.assets.push('http://wise9.github.com/enchant.js/images/clear.png');
var game = new enchant.nineleap.Game();
game.clear = function(score, message) {
    this.endScene = new SplashScene();
    this.endScene.image = this.assets['http://wise9.github.com/enchant.js/images/clear.png'];
    this.end(score, message);
};

jsdo.itでProcessingを動かす

メディアアートのためのプログラミング言語ProcessingProcessing.jsを使うことで、作った作品をJavaScriptのコミュニティサイトjsdo.itに公開することができる。

jsdo.itでの手順は次の通り。

(1) jsdo.itで新しいコードを書き始める(Start Coding)

スクリーンショット 2013-03-21 13.14.10

(2) JavaScriptのライブラリ追加(Add Library)

スクリーンショット 2013-03-21 13.16.46

(3) Processing.js v1.4.0 – js を選択して Add

(4) HTMLタブに以下のテンプレートを記述

<script type="application/processing" data-processing-target="pjs">
//Processing code is here
</script>
<canvas id="pjs"> </canvas>

(5) //Processing code is here のところに、Processingのコードを記述

このやり方では、JavaScriptタブにはコードを書かない。

Built with Processing[Ver. 1.x対応版] -デザイン/アートのためのプログラミング入門 3-6-4 力をためる、をベースに作ったコードを jsdo.it で公開してみた。

力をためる – jsdo.it
スクリーンショット 2013-03-21 13.03.26

Built with Processingはとても丁寧な解説で、プログラミング自体の入門書としてもオススメ。小学校4年生の娘もこの本(の前半)を使って自習していて、思い通りに動いたときには喜びに身悶えしてる。

Google Maps API v3 と jsdo.it を使ってツイートを地図上に表示する

Google Maps API v3 と jsdo.it を使って、地図上にその位置でつぶやかれたツイートを表示するアプリを作った。

ツイートを地図上に表示(コメント付) – Google Maps JavaScript API v3 – jsdo.it – Share JavaScript, HTML5 and CSS
http://jsdo.it/takatama/zNmW

ソースコードについて解説する。

(1) ツイートの取得
GET search | Twitter Developersでツイートを取得する。JSONPを使ってブラウザ上から取得ができる。jQueryのgetJSONメソッドを使った。
ポイントはurlのquery文字列にcallback=?を加えること。?の部分はjQueryがよきにはからってくれる。

//緯度経度、半径(km)、ツイートに含めたい文字を指定して、ツイートを取得する。
function fetchTweets(latLng, query, radiusKM) {
    var url = 'http://search.twitter.com/search.json?callback=?';
    var data = {
        q: query,
        rpp: 100, 
        geocode: latLng.lat() + ',' + latLng.lng() + ',' + radiusKM
    };
    $.getJSON(url, data, function (data, status) {
        if (status === 'success') {
            addTweetsAsMarkers(data);
        } else {
            alert('twiter error');
        }
    });
}

(2) ツイートをマーカーとして地図に追加
取得したツイートをマーカーとして追加する。マーカーがクリックされたときに情報ウィンドウを開くためのコールバックを追加しておく。
コールバック関数内ではクリックされたマーカーはthisで参照できるところがポイント。

//マーカーとして未追加で、緯度経度を含むツイートのみ追加する。
function addTweetsAsMarkers(data) {
    var i;
    for (i = 0; i < data.results.length; i++) {
        var id = data.results[i].id_str;
        var geo = data.results[i].geo;
        if (!markersHash[id] && geo && geo.type === 'Point') {
            var marker = new google.maps.Marker({
                map: map,
                position: new google.maps.LatLng(parseFloat(geo.coordinates[0]), parseFloat(geo.coordinates[1])),
                title: '@' + data.results[i].from_user,
                icon: data.results[i].profile_image_url,
                result: data.results[i] //情報ウィンドウにツイートを表示するために使う
            });
            google.maps.event.addListener(marker, 'click', function() {
                //クリックされたmarkerはthisで参照できる。
                showInfoWindow(this);
            });
            markersHash[id] = marker;
            markersArray.push(marker);
        }
    }
}

(3) 情報ウィンドウのコンテンツを作成し、表示
情報ウィンドウを開く際、取得したツイートから表示する内容を作る。ツイートのテキストに http:// から始まるリンクが含まれている場合に、クリック可能にする。
JavaScript の replace メソッドにコールバック関数を渡すこともできる。arguments[1] には正規表現でグルーピングした値が入っている。詳しくはjavascriptのreplaceにfunctionを渡す – Webtech Walkerを参照のこと。

//情報ウィンドウを表示する
function showInfoWindow(marker) {
    marker.setZIndex(9999); //表示位置を一番上にする
    var linkToTheTweet = '<a target="_blank" href="http://twitter.com/' + marker.result.from_user
        + '/status/' + marker.result.id_str + '"> 開く </a></p>';
    //ツイート内のリンクをクリック可能にする。
    var text = marker.result.text.replace(/(https?:\/\/[\x21-\x7e]+)/gi, function () {
         var uri = arguments[1];
         return '<a target="_blank" href="' + uri + '">' + decodeURI(uri) + '</a>';
     });
     infoWindow.content = '<p style="font-size:0.9em;">' + text + '<div style="font-size:0.7em;">'
         + linkToTheTweet + '</div></p>';
     infoWindow.open(map, marker);
}

(4) 検索する範囲を計算し、ツイートを取得
位置情報に基づいてツイートを取得する際は、中心となる緯度・経度に加え、検索する半径も指定する必要がある。
表示している地図の北東、南西の2点の位置はMapのgetBounds()メソッドで取得できる。また、2点間の距離はGeometry Libraryのgoogle.maps.geometry.spherical.computeDistanceBetween()関数で計算できる。 ポイントはhtmlのscriptタグでGoogle Maps API を呼び出す際、srcのquery文字列に&libraries=geometryを入れること。

//検索する範囲を表示画面の大きさから計算して、ツイートを取得する。
function fetchTweetsWithRadius() {
    var distanceKM = 3.0;
    var bounds = map.getBounds();
    if(bounds) {
        var latlng = new google.maps.LatLng(bounds.getSouthWest().lat(), bounds.getNorthEast().lng());
        var distanceM = google.maps.geometry.spherical.computeDistanceBetween(bounds.getNorthEast(), latlng);
        distanceKM = Math.floor(distanceM / 2 / 100) / 10;
    }
    fetchTweets(map.getCenter(), $('#filter').val(), distanceKM + 'km');
}

(5) 地図の操作に対する動作
ここまででツイートの検索からマーカーとしての表示までは説明できた。後は各種操作のための初期化について説明する。
ここではgoogle.maps.event.addListenerを使って、地図上での操作に対する処理を定義している。
また、自動的にツイートの検索と表示を実行するため、fetchTweetsWithRadius と showNextTweet を定期的に実行する。

//ページが読み込み終わったら実行する。
function initialize() {
    var mapOptions = {
        center: new google.maps.LatLng(35.698619, 139.773288),
        zoom: 11,
        mapTypeId: google.maps.MapTypeId.ROADMAP
    };
    map = new google.maps.Map(document.getElementById("map_canvas"), mapOptions);
    infoWindow = new google.maps.InfoWindow();
    //地図内をクリックしたら情報ウィンドウを閉じる
    google.maps.event.addListener(map, 'click', function () {
        infoWindow.close();
    });  
    //ドラッグが終わった位置で、ツイートを検索する。
    google.maps.event.addListener(map, 'dragend', function () {
         fetchTweetsWithRadius();
    });
    //ダブルクリックしズームした位置で、ツイートを検索する。
    google.maps.event.addListener(map, 'dblclick', function () {
         fetchTweetsWithRadius();
    });
    fetchTweetsWithRadius();
    setInterval(fetchTweetsWithRadius, 30000); //30秒ごとにツイートを検索する。
    setInterval(showNextTweet, 4000); //4秒ごとに情報ウィンドウを表示する。
    geocoder = new google.maps.Geocoder();
}

(6) ツイートの自動表示
あらかじめ取得していたツイートを定期的に表示していく。
マーカーが枠内に含まれている場合にのみ、表示するようにしている。

//追加したマーカーが画面上にあれば情報ウィンドウを表示する。
var currentMarker = null;
function showNextTweet() {
    if (currentMarker) {
        currentMarker.setMap(null);
        infoWindow.close();
        currentMarker = null;
    }
    var marker = markersArray.shift();
    while (marker) {
        var bounds = map.getBounds();
        //はじめに表示したときには、boundsがundefinedになってしまう。
        if (!bounds || bounds.contains(marker.getPosition())) {
            map.panTo(marker.getPosition());
            showInfoWindow(marker);
            currentMarker = marker;
            return;
        } else {
            marker.setMap(null);
            marker = markersArray.shift();
        }
    }
}

(7) 地名を入力して移動
地図の上部に設置しているフォームの操作用。入力した地名を Geocoding APIで緯度経度に変換し、地図を移動させた上で、その地点でのツイートを検索する。

//地名から緯度経度を取得して、地図を移動させ、ツイートを検索する。
function codeAddress() {
    geocoder.geocode({
        'address': $('#address').val()
    }, function(results, status) {
        if (status == google.maps.GeocoderStatus.OK) {
            map.setCenter(results[0].geometry.location);
            fetchTweetsWithRadius();
        } else {
            alert("Geocode was not successful for the following reason: " + status);
        }
    });
}

(8) 検索したツイートすべてを消去して、再検索
地図の上部に設置しているフォームの操作用。ツイートに含めたい文字を変更したり、クリアした場合に呼び出す。検索したツイートから追加してきたマーカーをすべて消去して、現在地点でのツイートを検索しなおす。

//すべてのマーカーを削除してツイートを検索する。
function reload() {
    var i;
    for (i = 0; i < markersArray.length; i++) {
        markersArray[i].setMap(null);
    }
    markersArray.length = 0;
    markersHash = {};
    fetchTweetsWithRadius();
}

(9) フォームをクリアした上で、再読み込み
地図の上部に設置しているフォームの操作用。フォームの文字列をクリアした上で、ツイートの再読み込みを行う。

//ツイートをフィルターする文字列をクリアし、ツイートを検索する。
function clearFilter() {
    $('#filter').val('');
    reload();
 }

jsdo.it でお手軽に Google Maps JavaScript API を試す

Web ブラウザ上で JavaScript, HTML, CSS のコードを書き、そのまま公開できる jsdo.it で、Google Maps JavaScript API v3Getting Started に掲載されている Hello, World を試してみた。

Hello World – Google Maps JavaScript API v3
http://jsdo.it/takatama/jRDt
指定した緯度経度の地図を表示する、ごくごく簡単なもの。

前提
Google のアカウントを持っていること。
jsdo.it のアカウントを持っていること。

手順
(1) https://code.google.com/apis/console/ にアクセスする。ログインが求められる。ログイン後、Create Project… ボタンを押す。

(2) 右ペインの Services を選択すると、利用可能な API の一覧が表示される。Google Maps API v3 を ON にする。

なお、Google Maps API は一日あたり25,000リクエストまでは無料で、1,000リクエスト超過ごとに$4かかる (2012/08/25時点) 。詳しくは地図読み込み回数の超過分のオンライン購入価格を参照。

(3) terms of service の内容をよく確認し、同意するのであれば I agree to these terms. がチェックされているのを確認の上、Accept ボタンを押す。

(4) https://code.google.com/apis/console/ に戻り、右ペインの API Access を選択する。Simple API Access に API key: が表示されているのでメモを取っておく。

なお、デフォルトの設定だとどのサイトに置いた JavaScriptからでもこの API key が利用可能になっている。今回は jsdo.it でのみ実行を許可するため Edit allowed referers…をクリックして、
jsrun.it/*
を追加しておく(jsdo.it のスクリプトは http://jsrun.it/ に配置されている)。

(5) Hello, World の例 (html) を jsdo.it にコピーして、src=”http://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&sensor=SET_TO_TRUE_OR_FALSE&#8221;
の部分を修正する。

YOUR_API_KEY を先ほど取得した API key に変更する。
SET_TO_TRUE_OR_FALSE を false に変更する。大文字の FALSE だと動作しないので注意する。

(6) 表示する地図を調整する。今回は東京駅を表示するために、
center: new google.maps.LatLng(35.681382, 139.766084),
zoom: 14,
とした。