Google Chrome または Safari を使用される事を強くお薦めしますw
他のブラウザでは Javascript の処理が重いです(汗)


2010/12/28

外部JSの読み込みと実行

ここへきてようやくリドルの話題ですww
リドルっつーのは同じようなページを何枚も作るので、基本1つ作ってコピペして変更してということをするわけですが、ページごとに違うスクリプトで処理したい場合に、

1ページ目が <script type="text/javascript" src="hoge.js"></script>
2ページ目が <script type="text/javascript" src="piyo.js"></script>

だったりすると、あ、なんか前のページと違うことしてるーとバレやすい気がしないでもありません。

どっちみちソースおっかけられたら防ぎようはないんですが、ちょっとでも分かりにくくするために js を動的に読み込んで実行するというのを「赤と黒」や「Overheated 2」でやってたんですが、それをマイナーチェンジしたバージョンを紹介します。

ぶっちゃけのソースはこうです。
if( document.body.className ) {
    var fn = document.body.className;
    var sc = document.createElement('script');
    sc.type = 'text/javascript';
    if( document.all ) {
        var bDone = false;
        sc.onreadystatechange = function(){
            if( sc.readyState == 'complete' || sc.readyState == 'loaded'  ) {
                if( !bDone ) {
                    bDone = true;
                    window[fn]();
                }              
            }
        }
    }
}
else {
    sc.onload = function(){
        window[fn]();
    }
}

sc.src = fn+'.js';
document.body.appendChild(sc);
HTML の方には <body class="hoge"> と書いておきます。
これで hoge.js が読み込まれ、関数 hoge() が実行されます。
簡単のためにファイル名と関数名(エントリーポイント)を同じ名前にしています。body の class に設定するのはヤダって人はどっかの要素の class や id や name を読み取るように変更してください。
スクリプトの方も IE の判定に document.all かよ、とかツッコミどころあるかとは思います^^;

IE 以外であれば該当するファイルが無かったら onerror でキャッチすることができます。が、IE ではそれができません。もしできたらリドルの答えを js のファイル名にしてページ遷移のないリドルなんかも作れるんですけどね (Dragon's eye でやろうとして挫折しましたw)。

まあこんなことしても Firebug なんかで見られたら <script> タグが挿入されていることはバレバレではございます。


スクリプトそのものを暗号化してしまうって手も考えられます。
(↓擬似コードです)
var scrumbledSource = "WWWWWWWWwwwwwwwwwwWWWWWWWWwwWWwwww...(ずっと続く)...wwWWWWWWWWWWWw";
eval( decodeScrumbledSource(scrumbledSource) );
みたいな感じで。
decode するルーチンは丸見えなんで解読する気になればできちゃいますけど。

2010/12/21

モーションブラー

MMD でモーションブラーやってみたという動画を見て おお と思ったのでやってみました。
とはいえ、2D の画像のスライドなので見てもちっとも面白くないです^^;;;

方法は馬鹿みたいでして、通常の描画をした後に1テンポ遅れた位置に薄い絵を描画してやれば良いわけです。下のサンプルでは2テンポ遅れた絵も表示しています(都合3枚の絵)。
シャッター速度が遅ければ遅いほど(あるいは物体の移動速度が速ければ速いほど)絵の枚数を増やせば良い、と。

本当に厳密にモーションブラーやろうと思ったらもっと面倒な計算をやらんといかんですが、別にこんなんでも十分ではなかろうかと。



絵は一定速で動いているので、ブレ具合(テンポの遅れ具合)も決め打ちで 5 pixel としています。

別窓で見たい方は→こちら

3D のポリゴンの描画とかでブラーするのも理屈は同じです。
canvas 使って 3D ポリゴン描画とかめっちゃ面倒くさいんでw、そのサンプルはまたやる気になったときにwww
webGL みたいなのが浸透したらもっと簡単にいろいろできるんでしょうけどね。

2010/12/16

円を描く

どっかの掲示板で、

Q「Javascript で円を描画することはできますか?」
A「SVG, VML, あるいは canvas を使わないと無理です」

と書いてあった。

できるわい!(遅いけど) やってみもせずに安易に教科書的な答を言わないでもらいたい。
とゆーわけでいつものパターン、 div 大量発生でめっちゃ遅いです^^;;;




ボタンを押すとランダムな5つの円を描画します。
そして例によって Google Chrome の速さに感動w

ブログに埋め込まれたらソース見づらいんじゃぼけぇ!な人は別窓でどうぞw→こちら

円を描画するロジックが知りたい人は Bresenham(ブレゼンハム)で検索。定番のアルゴリズムです。

2010/12/14

Pacman

これもよく知られてるっぽいですが。 ワシがモノ知らずなんで。
Googleがパックマン作ってましたw


javascript でできてます。ソースは見にくいですw
パックマンってプログラムとしてはかなり易しい部類なので、ゲーム作成入門者は作ってみるのは良いかもしれない。
ワシも昔作りました。アセンブラでしたけどww

iframe の透過

きっとみんな知ってるんだろうなあ。私は知りませんでしたwww

「Dragon's eye」ならびに「疾走物たちへ」のエンドロールは iframe を配置してそこに別の HTML を表示させて、scrollBy でスクロールさせています。

「疾走物たちへ」ではそのバックで迷路を逆走しているのですが、IE では iframe が透過しておらず、何にも見えてませんでした^^;;;

表示している HTML の body に background:transparent; を指定していて、IE 以外ではそれで OK なんですが、IE ではそれに加えて iframe 要素の方に、

<iframe  allowtransparency="true">

と設定しておく必要がありました。
今更ですが、これを書き加えてアップしました。


IE でもちゃんと透けてる証拠画像w

2010/12/11

画像の合成 その3

では解説です。例によって大したことはしてませんがw

要するに絵の縁の部分を半透明にしてやろうという考えです。
透過色となる背景色が白、絵の縁が黒のイメージを作ったときにその境目がグレーのグラデーションになったとします。こんな具合。

背景がブルー1色だとします。

通常だと透過色である白の部分だけが透過するので合成結果はこうなります。

そこで白の部分の透過率は 100%、黒の部分の透過率は 0% 、グラデーションの部分は徐々に透過率が小さくなるようにします。
実際には透過率ではなく 0 ~ 255 のアルファ値で計算します(透明が 0 で不透明が 255)。
この値を記述するとこんな感じ。
0 0 50 160 200 255 255

このように透過率を変化させることで合成結果をこんな風にしようというわけです。

残念ながら HTML のタグや属性をいくらいじってもピクセル単位で透過率を変えることはできません。
なので canvas を使用します。

canvas に描画した図は全てイメージとして扱われます。つまり DOM のツリーには現れません。
Windows で言うと GDI を使って DC に描画しているようなものです。
canvas に描画された図はピクセル列のデータとして読み取ることができます。またピクセル列のデータを canvas に書きこむことでイメージを表示させることもできます。
Windows の DIBSection みたいなものだと思えば良いかも。Windows API 知ってる人限定の話ですがw

読み取ったピクセルデータは左上から順に R, G, B, A, R, G, B, A, ・・・ と並んでいます。RGBQUAD ですね。データ4つで1ピクセルです。
今回はこの canvas のピクセルデータ列読み書き機能を用いてデータ列中のアルファ値、すなわち RGBA の A の値を書き換えてから、コンテキストに書き戻すことで部分的な半透明の画像を作成・表示しています。

縁の部分だけ半透明にするためには、ピクセルが透過色なのかそれ以外の色なのかを知る必要があります。
これはイメージ全体をなめて調べるしかありません。
ええ、時間かかりますともw

スキャンしていって透過色とそうでない色の境目にぶつかったら、その部分のアルファ値を設定します。
ロジックはそれだけです。

ちょっと面倒なのですが、ピクセルデータを取得するためにはまず一度イメージを canvas に描画しなければなりません。このためピクセルデータを読み取るために一旦イメージを表示する canvas と半透明処理した結果を表示する canvas の2つを用意しています。データ読み取り用の canvas は非表示になってます。

canvas 使用なのでサンプルはブログ埋め込みではなく外部リンクです→こちら

画像をスキャンする間しばらく待たされますが、我慢してください。
このサンプルではソースの可読性も考慮してますので無駄にループが回ってます。実際にはスキャンする部分をもっと効率化する必要があります。そこは自分で工夫してくださいね(^^)v

それから画像の左上(0,0)の位置のピクセルの色を透過色であるとみなしています。よくある手段ではあります。


尚、わざわざ外出しにしておきながらアレですが、実はこのサンプルは IE では動作しません^^;;;;;;;
FlashCanvas 内部でエラーになります。
どこか使い方を間違えているのか、IE 用に何かしないといけないのか、まだ調べていません。
(2010/12/16追記:これまでに以下の事柄が判明しました。
 ・非表示 (style.display='none') の canvas に対しての getImageData 呼び出しがエラーになる
 ・表示状態での getImageData はエラーにはならないが ピクセルデータが取得されない(中身が全て 0)
)

2010/12/10

画像の合成 その2

やってみた結果です。
教室の小悪魔ちゃん。


校庭の姉山先生w


どうでしょうか。機械的に処理しているので自然に見えるとまではいきませんが前ページのものより随分マシになってると思います。

何をどうやったのかは次回に。

画像の合成 その1

老若男女誰もが萌える学園モノを作ろう!と思い立ったとしますw
必要な画像の種類としては何よりも登場人物の立ち絵と背景でしょう。
まずこんな背景を用意したとします。

立ち絵として小悪魔ちゃん(ぉぃ を用意します。

これを透過pngとかgifにして背景の上に配置すると・・・

こんな残念な結果になります。
小悪魔ちゃんの回りに白いドットがあって非常に汚いですね。

小悪魔ちゃんの頭の羽(なんで頭にも羽があるのかは突っ込んではいけません)の先を拡大してみるとこんな風になっています。

絵の縁の部分ではお絵かきツールが画像のギザギザを目立たせないために背景色へのグラデーションとなるピクセルを配置してくれちゃっていまして、これが透過色として抜けてくれないので合成したときにゴミになってしまうのですね。

解決法としてはこのグラデーションの部分をお絵かきツールで地道にポチポチと1ピクセルずつ透過色に変更するというのがもっとも頭を使わなくて良い方法ですw
「技量の無さを体力でカバーする」というのは実は私はそんなに嫌いじゃないんですがw、ただこの方法には面倒だという以外にもう1つ問題があります。それは、

背景の色合いによっては縁のギザギザが目立ってカッコ悪い

ということです。お絵かきツールが滑らかに見えるようにしてくれた部分を消すのですから当然ですね。

「Dragon's eye」では体力勝負で縁のゴミを取り除いた画像を作りましたので、それを合成してみましょう。校庭で出会った姉山先生ということにしてください(笑)

上の小悪魔ちゃんほどひどい結果ではないですし、この程度なら気にしなくてもいいやん、と思うかもしれませんが、やっぱり顔の縁がギザギザなのは、せっかくの美人なのにちょっと可哀想ですw

というわけで、この立ち絵の回りのゴミやギザギザをスクリプトで目立たなくできないか?
というのが今回のテーマです。

何の検証もせずに記事を書き始めていますので、どこまで効果のある結果を得られるのか、あるいは全然効果がないのか、まったく分かりませんw

2010/12/08

迷路を通り抜けて目的地まで最短距離で進む その3

話は前回で終わっているんですが、最近動くものをアップしていないんで、サンプルを作ってみました。

Pattern 1 ~ Pattern 3 のボタンで迷路を選んで、START で実行です。
Show Arrow ボタンを押すと磁力線が表示されます。



前回も書きましたが手抜きしてループぐるぐるで方向をセットしていますので必ずしも最短距離になりません。ゴール地点から賢くトレースしていけばもっとマシになります。面倒なのでしませんがw


2010/12/07

迷路を通り抜けて目的地まで最短距離で進む その2

前回のあらすじ:

Yマサキ君「レールが敷いてあれば・・・」
ワシ「そうか、レールを敷けばいいんや!」


というわけでレールを敷く話ですw
迷路の中を手探りで進むのではなく、あらかじめ通る道を決めてやればいいんだ、という発想です。
このためには目的地である旗の場所から道を作って行くことになります。
まず旗がぽつんとあるとします。


この旗の回り8方向のマス目のどこかに戦車がいるとすると、戦車は旗の方向に1歩進むことになります。ですので旗の回りにこんなレールを敷きます。


旗から2回り離れたマス目はどうかというと、旗を目指すのではなく、ここで置いた赤の矢印を目指すレールにします。例えば左上の矢印を目指すレールはこうなります(水色の矢印)。


ミソはゴールである旗を目指すのではなく、に常に1つ隣の(1回り内側の)目標に向かうレールを敷くということです。こうすることでプログラムはとっても単純になります。ぐるぐるループを回すだけになりますので。

障害物の所は除外して、移動できるマスについてのみ順に全てこうやってレールを敷いていくとこうなります。


まるで磁力線ですが、まさに磁石に引かれるようにマップのどこに戦車を配置しても旗のところに吸い寄せられて行きます。

この程度のロジックだとよっぽどマップが広大でない限り処理速度は問題にならないので、動く敵の位置に応じて毎回レールを敷きなおしても大丈夫です。そうしてやれば敵を追いかけるロジックにそのまま適用できます。何れにしても戦車は単に現在地にある矢印を読み取ってその方向に移動するだけです。
(註:実際には戦車は前進と後退しかできないので、読み取った矢印の向きによっては移動ではなく旋回するケースもあります。具体的に言うと戦車の向きと読み取った矢印の向きが一致していなければ旋回処理です。)

矢印の向きを逆にすれば敵から逃げるロジックにもなります。


ところで「最短距離で進むは言いすぎじゃね?」と思ったなら、はい、その通りです。レールを敷設するロジックがいい加減だと多少遠回りしてしまうケースも出てきます。
ただ、本当に最短距離で走らせようとするとコードがめんどくさいことになりますので、戦車戦ロボコンではいい加減なコードのままでやりました。それでも旗取りで負けたことはないです。(砲撃で負けたことはありますがw)

壁に当たってから方向転換するロジックだと概ね下図の赤線の軌跡での移動になります。
レール敷設方式だと青線の軌跡になります。


後になってググってみたら、迷路を解くアルゴリズムの考え方は基本はこんな感じみたいです。もう少し複雑でしたが、移動結果はほぼ同じようでした。

迷路を通り抜けて目的地まで最短距離で進む その1

下のスクリーンショットは昔作った戦車ゲームです。


障害物のあるフィールドで、敵を撃破するか、敵よりも先に旗を取るかすれば勝ちです。逆に敵の弾に当たったり、敵に旗を取られると負けです。

ゲームと言ってはいますが、実際にはプレイヤーはキーボードもマウスも使いません。「戦闘開始」ボタンを押した後はただ眺めるだけです。

一体これは何かというと、いろんな人のプログラム同士を戦わせるものなんです。あらかじめ戦車のプログラムを作っておいて、それを実行します。ロボットコンテストみたいなものですね。

この昔作ったロボコン旗取りゲーム用の戦車アルゴリズムは迷路を解くアルゴリズムになっています。例えば下図のような地形でも戦車はちゃんと旗までの最短距離を移動します。


スタート時に壁を挟んで旗のある方を向いて配置していますが、


無駄な動きをすることなくちゃんと迂回して旗を目指します。

今作ってる「砲撃者たちへ」も戦車ですし、この戦車好きめ!な感じですがw、今回の話は「砲撃者たちへ」とは直接関係はありません。

単に障害物を避けながら目的地まで進む、という考え方の一例としてこの旗取りゲームの方法を紹介するのも面白いかなと思った次第です。

世の中にはこの手のアルゴリズムはいっぱいありますが、私の方法はいたってシンプルです。シンプル故に力技というのもいつものことですw

実は最初は誰もが最初に考えるであろう、まず進んでみて行き詰まったら別の行き方を模索するという方法で考えていました。で、自分が行き詰まりましたw
1手や2手戻ってやり直すぐらいならともかく、地形によっては何十手も戻ってやり直す必要があります。例えばこんな袋小路に入ってしまった場合などですね。


難しいロジックは理解できないんで、なんかもっと単純に解決できんもんかなー、世の中もっとシンプルに物事は解決するはずや!と悩んでたら、当時一緒に悩んでいたYマサキ君が「レールを敷いたみたいに旗まで寄り道せずにまっすぐ移動できたらいいんですけどねー」と。

「それや!Yマサキ君!!レールを敷けばいいんや!!」

まさに ユーリカー! な状態でしたw 裸で家に飛んで帰って、いや別に風呂に入っていたわけではないんですが、それぐらいの勢いで飛んで帰って、あっという間にコーディングできてしまいました。
それほどシンプルv

さて、その方法とは? 
勿体付けるほどのもんではないんですが、長くなったので次回に。

2010/12/02

IE6 対策

イベントリスナーについてちょっとだけ考える のところで IE でのイベントについて少し書きましたが、今回は IE6 だけに絞った Tips を3つ紹介します。なんて需要のなさそうな記事なんだww

まずは 三角形を描く で紹介し、「疾走者たちへ」で活用した DIV による三角形描画ですが、これが IE6 ではそのままできません。


↑この青で描かれた 3 つの border を transparent にすることで緑の三角形だけを残すわけですが、 IE6 では transparent 指定が効きません。

そこで、IE6 のみ特殊処理します。具体的には IE 独自の filter 属性を使って青を透明色として定義してやります。 GIF 画像の透明色指定みたいな感じですね。
透明色指定するコードはこうです。
nodeDiv.style.filter = 'chroma(color=blue)';
言うまでもないですが、ここで nodeDiv とは ある DIV に対して document.getElementById した結果です。


次も同じく DIV の話ですが、DIV の height を小さく設定しても IE6 ではある大きさ以下にはなりません。 IE6 では font-size 以下の高さにはならないためです。
ですので高さを例えば 10px にしたいのであれば、font-size を 10px 以下に設定する必要があります。

「疾走者たちへ」の画面左下のマップに表示されているドット1つ1つは DIV なのですが、


IE6 だとこれがドットにならず縦長の線になってしまいます。なのでこれら DIV について font-size:0px; を設定しています。
(2011/12/18追記: overflow:hidden; も必要でした。これがないと高さは 2px より小さくなりません。)



最後に FlashCanvas で <canvas> 要素を使用する場合です。
ちゃんと調べてないですが、 IE6 では getContext でコンテキストを取得する前に
nodeCanvas = G_vmlCanvasManager.initElement( nodeCanvas );

のコードが必要です。ここで変数 nodeCanvas は <canvas> 要素に対して document.getElementById した結果だと思ってください。

この G_vmlCanvasManager.initElement というのは Google 提供の excanvas.js を使用して IE での canvas 描画を VML を使って描画する際に必要な初期化なんですが、なぜ FlashCanvas でしかも IE6 のときだけこの呼び出しが必要なのかは謎です。

実際に使用する際に IE6 についての場合分けが面倒であれば try ~ catch で挟んでしまえばいいですね。
try {
    nodeCanvas = G_vmlCanvasManager.initElement( nodeCanvas );
}
catch(e) {
}
(2010/12/03追記: IE6 だけでなく IE 全般でこのコードが必要なようです。ただし常に必要なわけでなく、 <canvas> 要素を createElement で動的に生成した場合のみで、 HTML に最初から <canvas> の記述があれば不要でした。 なぜそうなのかは分かってません。)

(2010/12/11追記: G_vmlCanvasManager.initElement ではなく FlashCanvas.initElement を呼ぶ必要があるとのこと。 G_vmlCanvasManager.initElement 呼び出しも中では FlashCanvas.initElement を呼び出しているものと思われます。
結論として <canvas> 要素を createElement で動的に生成した場合にはそれを appendChild した後に以下コードを書いておくことになります。
if( typeof FlashCanvas != "undefined" ) {
    FlashCanvas.initElement( nodeCanvas );
}
必ず appendChild した後でなければなりませんので注意。)

2010/12/01

大地の怒り

いや、別に怒っていませんがw

火山が噴火するようにしました。


それだけなんですけどね。