JavaScript」タグアーカイブ

window.innerWidth

3D を扱うスマホゲームの大半は Unity で作成されているようだ。
ウチ姫もその中の一つ、知らなかったけどもうスタンダードに近いのね。
Unity – Gallery

Linux に締め出された Mono がこんなところに生息していたとは。
ま、本格的に 3D ゲームを作りたくなったら手を出すか考えればいいだろう。
今は Web アプリのほうが伸びそうだし。

さて、ゲームというかグラフィックを表示するには画面サイズを得る必要がある。
JavaScript で横幅は screen.width と window.innerWidth がある。
スマホの横向きも考慮してどう違うのか確認。

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>画面サイズ取得</title>
<!-- for Smart Phone -->
<meta name="viewport" content="
    width=device-width,
    initial-scale=1.0,
    minimum-scale=1.0,
    maximum-scale=1.0,
    user-scalable=no" />
<script type="text/javascript"><!--

    var setText = function() {
        var br = "\<br /\>";
        // 画面はディスプレイサイズ、表示領域はクライアント領域
        // device-width 指定だとクライアントサイズは 320 になる
        var s = "画面 幅x高さ: " + screen.width + "x" + screen.height + br +
                "表示領域 幅x高さ: " + window.innerWidth + "x" + window.innerHeight + br;

        document.getElementById("ID_TEXT").innerHTML = s;
    }
    var init = function() {
        setText();
        // スマホ横向きで再取得
        // iPhone では横向きでも画面サイズは変わらないと確認できる
        window.onorientationchange = setText;
    }
    //-->
</script>

</head>
<body onLoad="init()">
<p id="ID_TEXT"></p>

<br />
<p><a href=".">Back</a></p>
</body>
</html>

すぐにスマホで確認できるようにテストページを作った。
JavaScript Test Page – L’Isola di Niente

スマホで viewport がこの指定だとクライアント幅は 320 になるようだ。
後は高さを都度取得して調節すれば問題なくグラフィック表示できそう。

window.onorientationchange ハンドラにて再取得で横向き時も得られる。
とやってみたら。

size_iphone

size_android

うーん、当然縦サイズが横サイズになるよね。
横向きでは使わないでね!が一番簡単(それでいいのか…

しかし iPhone は横にしても画面サイズは同じってなんでかな?
Android の画面サイズが解像度のままってのも変な感じ。
それとフォントサイズ、まだ調べなきゃいけないこと多いな。

どのブラウザでも動くのでなければ Web アプリの意味が無い。
でも IE はガン無視でいいと思う、タッチで使う人いなさそうだし。

lineTo

iPhone は本当に恐ろしい。

何が恐ろしいかというと、パソコンの電源を入れることがほとんどなくなった。
Fedora をデスクトップで使い更にこんなブログをやっている筆者が、である。

スマホで十分という人はパソコンでもたいしたことをしていない人だけ。
というのは思い込みです、Windows しか使えない人の妄想であると断言する。

今やパソコンを起動するのは動画を見る時とプログラミングをする時のみ。
いや短い Web 動画ならスマホで見る、本当に何かを作る時以外にパソコンは不要。
Xperia の時はこうはならなかったのに iPhone にしたとたんにこうだ。

直感的に使えるというウリ文句は嘘、だが少ない操作量で簡単に使えるし常に手元にある。
大型バイクのサブで 125cc スクーターを買ったらソッチにしか乗らなくなる現象と同じかも。
必要十分な速度で動いて手軽なほうを使うのは余程の理由がなければ当然かと。

もっと恐ろしいのは、、、ウチ姫。

ガールフレンド(仮)というゲームが少し話題になった時にブラウザ版という HTML5, JavaScript で動くバージョンがあると知り面白そうなので試した。
タッチで一行ずつ文字列を表示する JavaScript ネタを以前ココに書いたはず。
丁度そのころ同じ会社のウチ姫とコラボ企画をやっていたのでついでに試す。

結果、会社で絶賛流行中のパズドラにはピンとこなかった筆者がドハマり。

おかげでブログのネタが作れず三ヶ月近く更新が止まったことは内緒だよ。
少額とはいえ課金までしてしまう事態に、ドロシーちゃんカワイイよ。

uchihime

絶妙な位置を狙って弾くという行為がパチンコに似て…いや何でもない。
貫通したり分身したり巨大化したり、しまいにゃ波動砲を打ったりもするけど。
そういえば筆者はテトリスとかのパズルものが大の苦手だったな。

しかしウチ姫もブラウザ版が出ないものか、アメーバコインは iPhone から使えないし。
3D だから JavaScript では厳しいかな?

**********

長い前置きはこれくらいにして。
とにかく HTML5, JavaScript でウチ姫と似たようなことをやってみたい。

いきなり 3D では挫折が見えているので Canvas に描写してみる。
canvas のどこかをタッチして引っ張るとそこまで line を引くというように。

現在 iPhone でしか試していないけどなんとか上手くいったコード。

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>タッチして引っ張る</title>
<!-- for Smart Phone -->
<meta name="viewport" content="
    width=device-width,
    initial-scale=1.0,
    minimum-scale=1.0,
    maximum-scale=1.0,
    user-scalable=no" />
<style>
body {
    margin: 0;
    overflow: hidden;
}
#ID_CANVAS {
    background-color: #FFFF00;
}
</style>
<script type="text/javascript"><!--

    var canvas = null;
    var context = null;
    var cx = 0;
    var cy = 0;

    // Event Handler
    var onTouchStart = function(e) {
        var touch = e.touches[0];
        cx = touch.pageX;
        cy = touch.pageY;
        // cancel the operation of default (iPhone)
        e.preventDefault();
    }
    var onTouchMove = function(e) {
        context.clearRect(0, 0, canvas.width, canvas.height);
        var touches = e.touches;
        for (var i=0; i<touches.length; i++) {
            var touch = touches[i];
            var x = touch.pageX;
            var y = touch.pageY;
            context.beginPath();
            context.moveTo(cx, cy);
            context.lineTo(x, y);
            context.stroke();
        }
    }
    var onTouchEnd = function(e) {
        context.clearRect(0, 0, canvas.width, canvas.height);
    }
    // Connect
    var init = function() {
        if (window.TouchEvent) {
            canvas = document.getElementById("ID_CANVAS");
            canvas.width = window.innerWidth;
            canvas.height = window.innerHeight - 100;
            canvas.addEventListener("touchstart",onTouchStart);
            canvas.addEventListener("touchmove",onTouchMove);
            canvas.addEventListener("touchend",onTouchEnd);
            context = canvas.getContext("2d");
        }
    }
    //-->
</script>

</head>
<body onLoad="init()">
<canvas id="ID_CANVAS"></canvas>

<br />
<p><a href=".">Back</a></p>
</body>
</html>

lineto

残念だけどパソコンのブラウザとマウスでは動かない、サーフェスは知らない。
後は sim 無し Xperia を充電しなきゃ。
そういえば Windows を前回起動したのはいつだったかな?

とにかく iPhone Safari 対策で preventDefault 呼び出しは必須みたい。
何かのソシャゲでツールバーが出なくて困ったけどコレをページ全体でやってたのか。

引っ張りゲーを作りたいのでマルチタッチは関係ないけどこんな面倒なコードになった。
ハンドラで Touch オブジェクトのリストを取得し for 文にする必要あり。
for in 文が使えないって面倒臭いな、何故メソッド列挙なんかにしたのだろう?

Canvas については Windows でデバイスコンテキストと同様と思っていい。
clearRect で以前の線を塗り潰し lineto にて新たに書き込まないと残ってしまう。

おまけで body の css で margin: 0; にしないとゼロ位置にならない。
今まで気が付かなかったけどそうなっていたんだね。

後は開始位置を固定して球を弾いてソレが障害物に当たると反射してそれから…
うおぉ先は長そうだ。

**********

筆者は local でこんなことをするので Linux というより Apache が必要なんだが。
何を作るでもない人であれば今の時代スマホがあれば本当にパソコンは必要ないかもね。
「パソコン?キモッ!」な反応をする人が増えることだけは間違いない。

JavaScript Touch Events

iPhone に変更してからスマホゲームを始めた。
ひたすらポチポチするだけだとバカにしていたがコレが意外に面白い。

こんなのを Web アプリで作ってみたい、ということで。

とりあえず画面をポチると少しずつ一行文字列が流し込まれていく方法を。
エロゲでよく見かけるやつね、凄く古いのしか知らないけど。

ソレっぽいワードで検索すると基本みたいなのは日本語でゴロゴロ出る。
筆者メインの Python for GTK+ じゃ英語しか見つからないので逆に戸惑うよ。

JavaScriptプログラミング講座【タッチスクリーン操作について】

addEventListener で touchend のハンドラを処理すればよさそう。
それだけだとパソコンからは動かないので mouseup も同じハンドラで。
touchstart, mousedown ではないのはオンラインソフトでの経験から。

JavaScriptを解説します

文字列流し込みはこの上書きする手段がよさそう。
しかし clearInterval しなくてもいいのかな?
しかしみんな関数名が、GTK+ を主に使っていると凄く気になる。

結構簡単に作れたけど色々問題があった。
touchend, mouseup 両方を Android Chrome は拾ってしまう。
表示が終る前にポチるとハンドラが多重発生してしまう。
表示が終わるまで何らかの手段で排他制御を行うしかないな。

removeEventListener で一時ハンドラを外す方法が沢山見付かる。
でも筆者の実装手段が悪いのかうまくいかない。
最適ではないかもだけど下記がうまくいった方法。

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<!-- for Smart Phone -->
<meta name="viewport" content="
    width=device-width,
    initial-scale=1.0,
    minimum-scale=1.0,
    maximum-scale=1.0,
    user-scalable=no" />
<title>test</title>
<script type="text/javascript"><!--

    var count = 0;
    var lock = false;
    var lines = [
        "JavaScript で一文字ずつ表示する。",
        "日本語(Japanese)も一文字として扱ってくれるよ。",
        "end." ];

    // Text Out Function
    var writeLine = function(line) {
        lock = true;
        var i = 0;
        var id = null;
        var setText = function() {
            if (i <= line.length) {
                document.getElementById("text_area").innerHTML = line.substring(0, i);
                i++;
            } else {
                clearInterval(id);
                lock = false;
            }
        }
        id = setInterval(setText, 50);
    }
    // Event Handler
    var onTouchEnd = function() {
        if (lock == false) {
            if (count < lines.length) {
                writeLine(lines[count]);
                count++;
            }
        }
    }
    // Connect
    var init = function() {
        if (window.TouchEvent) {
            document.getElementById("event_check_area").innerHTML = "Support the Touch Events.";
            document.addEventListener("touchend",onTouchEnd);
        }
        document.addEventListener("mouseup",onTouchEnd);
    }
    //-->
</script>
</head>
<body onLoad="init()">
<p id="event_check_area">Don't Support the Touch Events.</p>
<p id="text_area"></p>
</body>
</html>

touch_event

iPhone, Android でもうまくいった、IE はしらない。
しかし Google Chrome は Linux デスクトップ版もタッチ対応なのか。
意味があるのかな?

あとはエレメントを特定 canvas にして美少女画像を別に表示すれば
くるぉおえるめぇぇるですおおお^q^
みたいなのが作れるかも、まだ先は長いけど。

JavaScript tuple

Gjs では関数の戻り値を配列で2つ以上受け取ることができる。
Python 等の言語仕様に tuple がある言語では常識なのだが。
というか、JavaScript の配列って型が違っても問題ないのね。

Python

#!/usr/bin/env python3

def get_tuple():
    return "CBR", 1000, "RR"

name, cc, ext = get_tuple()
print("{}{}{}".format(name, cc, ext))

Gjs

#!/usr/bin/env gjs

let get_tuple = function() {
    return ["CBR", 1000, "RR"];
}

let [name, cc, ext] = get_tuple();
print(name + cc + ext)

これって Web ページにも使えるのかな?
実験、2014.03.15 現在の最新環境にて。

Google Chrome 33
iPhone 5S Safari
iPhone 5S Google Chrome
FireFox 27
Internet Explorer 11

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Tuple Test</title>
<script type="application/javascript"><!--
var get_tuple = function() {
    var name = "iPhone";
    var num  = 6;
    return [true, name, num];
}
function init() {
    var arr = get_tuple();
    if (arr[0]) {
        document.write(arr[1] + arr[2]);
    }
}
//-->
</script>
</head>

<body onLoad="init()">
</body>
</html>

上記ブラウザ全部で関数の戻り値に配列を指定することができた。
しかし tuple のように戻り値を受け入れてくれるのは FireFox のみ。
Gjs は Firefox のエンジンを使っているので同様のようです。

しかし現在のトレンドは書くまでもなくスマートフォン。
Google Chrome, Safari で利用できないのでは使えない。
配列として受け入れるしか選べない。

JavaScript の Array は型が違ってもいいし関数の戻り値にできる。
それを利用すれば構造体を戻す関数やタプルのように扱える、ということで。
今後は解らないけど2つ以上の戻り値が必要な場合はこんな感じにすると便利。

ついでに let について。
FireFox で let 等の JavaScript 1.7 の機能を使うには 1.7 宣言が現時点では必須。
JavaScript 1.7 の新機能 – JavaScript | MDN

<script type="application/javascript;version=1.7"><!--
let get_tuple = function() {
    return ["FireFox", "JavaScript", 1.7];
}
function init() {
    let [name, js, version] = get_tuple();
    document.write(name + js + version)
}
//-->
</script>

Internet Explorer 11 はなんと let 等もそのまま利用可能。
ただしコチラは version=1.7 を書くと動作しない。

<script type="application/javascript"><!--
let get_tuple = function() {
    return ["Internet", "Explorer", 11];
}
function init() {
    let arr = get_tuple();
    document.write(arr.join(" "));
}
//-->
</script>

統一されるまで使い物にならないや。

Gjs I/O Stream

以前下記で Seed, Python で Gio による読み書きをやったのを Gjs にしてみた。
Gio Streaming I/O @ Seed and PyGI | PaePoi

GCancellable 等を指定(null でいい)しないといけなかった。
他色々と Seed より Python に似ていて困惑。

/*
    Gjs Streaming I/O File Read and Write Sample
*/

const Gio = imports.gi.Gio;
const Format = imports.format;

String.prototype.format = Format.format;

let filename = "test_gjs.txt";
const lines = "Japan\n\n日本語";

// Write
let f = Gio.file_new_for_path(filename);
let fstream = f.replace(null, false, Gio.FileCreateFlags.NONE, null);
try {
    // Error, This is no Exception.
    // let dstream = new Gio.DataOutputStream(fstream);
    // g_output_stream_write: assertion 'G_IS_OUTPUT_STREAM (stream)' failed
    let dstream = new Gio.DataOutputStream({base_stream:fstream});
    dstream.put_string(lines, null);
} catch (e) {
    // Without meaning
    log("*** ERROR WRITE: " + e.message);
}
fstream.close(null);

// Read
f = Gio.file_new_for_path(filename);
fstream = f.read(null);
// Same as above
let dstream = new Gio.DataInputStream({base_stream:fstream});
while (1) {
    let [text, len] = dstream.read_line_utf8(null);
    if (text == null) break;
    print("%s(%d)".format(text, len));
    // or print(text + "(" + len + ")");
}
fstream.close(null);

/* output
Japan(5)
(0)
日本語(9)
*/

DataOutputStream 等の引数は json で指定しないと認識してくれない。
現時点では理由が解らない、GOutputStream のサブクラスと認識できないのか?
とにかく Error で困ったら json 指定にすればなんとかなるっぽい。

しかも GError 引数なのに例外にならず普通にエラー。
try ブロックを書いたけど無意味だった、これはちと困る。

おまけで let 変数のスコープは try ブロック内でも有効だと解った。
Python と同様にするには var を利用、注意しよう。

// var is OK
let f = Gio.file_new_for_path("a.txt");
try {
    // var fstream = f.replace(null, false, Gio.FileCreateFlags.NONE, null);
    let fstream = f.replace(null, false, Gio.FileCreateFlags.NONE, null);
} catch (e) {
    //
}
fstream.close(null); // ReferenceError
# Python is OK
f = Gio.file_new_for_path(filename)
try:
    fstream = f.replace("", False, Gio.FileCreateFlags.NONE, None)
except Exception as e:
    pass
fstream.close(None)

Seed にある c_new という意味不明な指定が無いのはちょっぴり嬉しいね。
それ以外は全部 Gjs のほうが面倒臭いけど。

戻り値が Python 同様に 2 つ以上である場合がある。
Python はタプルなんだけど、タプルが無い言語でもこの手があったか。
まあどちらも引数に渡された変数の値を変更できない言語だし。

どうも PyGObject の手段を参考に作っているような気がする。
いや逆かも、というか GLib 側の意向でこうなったのかな?

とにかく Python 屋なら Seed より Gjs のほうがなじみやすいという結論で。