<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	xmlns:series="http://organizeseries.com/"
	>

<channel>
	<title>西田慎吾 &#8211; HTML5Experts.jp</title>
	<atom:link href="/knockknockjp/feed/" rel="self" type="application/rss+xml" />
	<link>https://html5experts.jp</link>
	<description>日本に、もっとエキスパートを。</description>
	<lastBuildDate>Sat, 07 Jul 2018 03:14:05 +0000</lastBuildDate>
	<language>ja</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>https://wordpress.org/?v=4.7.19</generator>
	<item>
		<title>WebGLとWebSocketによる3Dオンラインレースゲーム「JS-Racing」の全て！（後編）</title>
		<link>/knockknockjp/10481/</link>
		<pubDate>Tue, 02 Sep 2014 00:00:00 +0000</pubDate>
		<dc:creator><![CDATA[西田慎吾]]></dc:creator>
				<category><![CDATA[プログラミング]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[Node.js]]></category>
		<category><![CDATA[WebSocket]]></category>
		<category><![CDATA[Webアプリ]]></category>

		<guid isPermaLink="false">/?p=10481</guid>
		<description><![CDATA[連載： HTML5 Japan Cup 特集 (6)WebGLとWebSocketによる3Dオンラインレースゲーム「JS-Racing」の全て！（後編） 前回に引き続きHTML5 Japan Cup 2014にてWebG...]]></description>
				<content:encoded><![CDATA[<div class="seriesmeta">連載： <a href="https://html5experts.jp/series/5jcup-2/" class="series-207" title="HTML5 Japan Cup 特集" data-wpel-link="internal">HTML5 Japan Cup 特集</a> (6)</div><p>WebGLとWebSocketによる3Dオンラインレースゲーム「JS-Racing」の全て！（後編）</p>

<p><a href="https://html5experts.jp/knockknockjp/10226/" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">前回</a>に引き続き<a href="https://5jcup.org/" target="_blank" data-wpel-link="external" rel="follow external noopener noreferrer">HTML5 Japan Cup 2014</a>にて<strong>WebGL賞</strong>と<strong>優秀賞</strong>をいただいたオンラインレースゲーム、<a href="http://js-racing.knockknock.jp/" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">JS-Racing</a>の技術解説をさせていただきます。</p>

<p><img src="/wp-content/uploads/2014/08/img14.jpg" alt="img14" width="630" height="484" class="alignnone size-full wp-image-10498" srcset="/wp-content/uploads/2014/08/img14.jpg 630w, /wp-content/uploads/2014/08/img14-300x230.jpg 300w, /wp-content/uploads/2014/08/img14-207x159.jpg 207w" sizes="(max-width: 630px) 100vw, 630px" /></p>

<h2>サーバサイドの使用技術</h2>

<p>サーバサイドの技術として<a href="http://nodejs.jp/" target="_blank" data-wpel-link="external" rel="follow external noopener noreferrer">Node.js</a>を使用しています。Node.jsはサーバーサイドで動作するJavaScriptで、<strong>ノンブロッキングI/Oというモデルを採用しています。非同期処理でデータベースへのアクセスとWebページの表示を別々に行ってくれる</strong>ので、ストレスなく大量のページの表示が出来ます。また、Socket.ioというライブラリを扱うことで、WebSocketを使用したリアルタイム通信を実現するができます。</p>

<p>Node.jsでWebアプリケーションを構築する場合、実験的にローカルのみの開発の場合は問題ありませんが、公開するとなると、Node.jsをインストールして実行できるサーバが必要になります。そのためには共有サーバのレンタルではなく、<strong>管理者権限 (root) が付与される専用サーバや、仮想専用サーバ（VPS）のレンタルが必要</strong>になると思います。管理者権限のため自由に環境をセットアップする事ができるために、そのWebアプリケーションに合わせた環境構築が可能になります。</p>

<h3>ExpressによるWebアプリケーションフレームワークの利用</h3>

<p>Node.jsで作成したWebアプリケーションの場合、リクエストURIの解析からファイルの配信など、HTTPサーバの機能を実装しなくてはいけません。これらの<strong>基本機能が備わったWebアプリケーションフレームワークを利用することで、非常に手軽にWebアプリケーションを作成</strong>できます。今回は、多数開発されているWebフレームワークのなかでも、有名で多く利用されている<a href="http://expressjs.com/" target="_blank" data-wpel-link="external" rel="follow external noopener noreferrer">Express</a>を利用しました。</p>

<p></p><pre class="crayon-plain-tag">var express = require("express");
var app = express();
// HTMLエンジンとしてjadeを使用
app.set("view engine", "jade");
app.set("views", __dirname + "/views");
app.use(express.static(__dirname + "/public"));
// js-racing.knockknock.jp/にアクセスがあった場合、
// /views/index.jadeに「title=JS Racing」と「version=ver1.2」の値を渡して、HTMLを表示
app.get("/", function(req, res) {
    res.render("index", {
        title: "JS Racing",
        version: "ver1.2"
    });
});
// js-racing.knockknock.jp/controller.htmlにアクセスがあった場合、
// /views/controller.jadeに「title=JS Racing」と「version=ver1.2」と「id=URLパラメータのidの値」の値を渡して、HTMLを表示
app.get("/controller.html", function(req, res) {
    res.render("controller", {
        title: "JS Racing",
        version: "ver1.2",
        id: req.query.id
    });
});
app.set("port", process.env.PORT || 3000);
var server = require("http").createServer(app);
server.listen(app.get("port"), function(){
    console.log("Express server listening on port " + app.get("port"));
});</pre><p></p>

<p>22行目の<code>req.query.id</code>は<code>http://js-racing.knockknock.jp:3000/controller.html?id=000000</code>として渡された、URLパラメータのidの値が参照できます。この値は、ソケット通信時に発行されたソケットIDの値で、PCから発行されたスマフォ用のURL（QRコードか短縮URL）にURLパラメータとして付加されたもので、この値を元にPCとスマートフォンとのペアリングを行います。</p>

<p><strong>ExpressはHTMLのテンプレートエンジンとして<a href="http://jade-lang.com/" target="_blank" data-wpel-link="external" rel="follow external noopener noreferrer">Jade</a>か、<a href="http://embeddedjs.com/" target="_blank" data-wpel-link="external" rel="follow external noopener noreferrer">ejs</a>を選択</strong>することができます。Jadeはインデントが必須です。閉じタグを省略できる代わりに、DOMの入れ子構造に沿って適切にインデントを使用する必要があります。基本的な記法がHTMLとは違うために、HTMLに親しんだマークアップエンジニアからすれば、慣れるまで違和感があるものだと思います。ejsはJadeとは違い、基本的な記法はHTMLをベースにしていますので、ローカルでくみ上げたHTMLをejsに組み込むことが容易です。今回はより楽にコード量を少なくHTMLを構築したいという点で、閉じたタグなしで記述できるJadeを選択しています。</p>

<p></p><pre class="crayon-plain-tag">// オープニングタイトル表示部分
section#sceneOpening.scene-opening(style="display: none;")
    div.scene-opening__inner
        div.scene-opening__box5
            h1.scene-opening__ttl
                // expressから受け取ったパラメータ、titleの値を挿入
                span.scene-opening__ttl__txt #{title}
                    br
                // expressから受け取ったパラメータ、versionの値を挿入
                span.scene-opening__ttl__txt2 &amp;nbsp;#{version}</pre><p></p>

<p>Jadeはこのようにインデントによって、要素の入れ子構造を定義しています。そのため閉じタグが不要で、<strong>HTMLを非常に簡略化</strong>して記述することが可能になります。HTMLを簡略化できるだけがJadeの特徴ではありません。<strong>HTMLをコンポーネント化して再利用</strong>したり、条件分岐をさせたり、繰り返し処理をさせたり、これまでサーバサイドで行っていたようなことがNode.jsで可能になります。ちなみにCSSはSass（Compass）を使い、記法にはBEMという命名規則を採用しています。</p>

<h3>Socket.ioによるリアルタイム通信</h3>

<p>車の同時走行と、スマートフォンからの車の操作を実現するためには、ブラウザとサーバ双方から、任意のタイミングでデータを送受信する必要があります。このリアルタイム通信を実現するために、<a href="http://socket.io/" target="_blank" data-wpel-link="external" rel="follow external noopener noreferrer">Socket.io</a>を利用しています。Socket.ioは<strong>WebSocketなどのリアルタイム通信技術をラップして、シンプルなAPIを提供しているため、通信部分の煩雑さを意識することなく、リアルタイム通信を簡単に構築</strong>することができます。</p>

<p>スマートフォンからの車の操作を実現するために、接続時に発行されたソケットIDを共有することで、スマートフォンとPCをペアリングして相互通信しています。サーバを介しての通信なので、若干のタイムラグが発生する懸念もあったのですが、現状ではタイムラグを感じることはありませんでした。環境に依存することですので、あくまで今回の場合はということですが。</p>

<p></p><pre class="crayon-plain-tag">var socketArr = [];
io.sockets.on("connection", function(socket){
    // Socket.IDをPCに送信
    socket.emit("emit_id_form_server", socket.id);
    // PCから車の状態を受信
    socket.on("emit_carcondition_form_client", function(data){
        // 受信した車の状態
        var info = {
            id: socket.id,
            name: data.name,
            x: data.x,
            y: data.y,
            bodyAngle: data.bodyAngle,
            wheelAngle: data.wheelAngle,
            speed: data.speed,
            colorBody: data.colorBody,
            colorWing: data.colorWing,
            colorDriver: data.colorDriver
        };
        // 接続しているクライアントごとの情報を更新
        var flg = true;
        var i = 0, max;
        for (i = 0, max = socketArr.length; i &lt; max; i = i + 1) {
            if (socketArr[i].id == info.id) {
                socketArr[i] = info;
                flg = false;
            }
        }
        if (flg) {
            socketArr.push(info);
        }
    });
    // スマートフォンからの操作情報を受信
    socket.on("emit_controller_data_form_client", function(data){
        var id = data.id;
        var event = data.event;
        var value = data.value;
        var flg = false;
        var sockets = io.sockets.sockets;
        var i = 0, max;
        for (i = 0, max = sockets.length; i &lt; max; i = i + 1) {
            // ソケットIDが一致したクライアントに操作情報を送信
            if (sockets[i].id == id) {
                sockets[i].emit("emit_controller_data_from_server", {
                    id: id,
                    event: event,
                    value: value
                });
                flg = true;
            }
        }
        // ソケットIDが一致したクライアントが存在しなかった場合はスマートフォンに接続解除イベントを送信
        if (!flg) {
            socket.emit("emit_disconnect_client_from_server");
        }
    });
    // 接続しているクライアントの情報を1秒間に3回、接続しているクライアント全てに送信
    setInterval(function(){
        socket.emit("emit_other_carcondition_from_server", socketArr);
    }, 1000 / 3);
});</pre><p></p>

<p>接続している全てのクライアントに、接続している全てのクライアントの情報を送信するイベントは、ネットワークの負荷を考えて、1秒間に3回と制限をかけています。つまり3FPSのタイミングで同期をとることになりますが、クライアント側では30FPSでゲームが進行しているために、タイムラグが発生してしまいます。タイムラグによって発生する違和感をなくすために、クライアント側ではサーバ側から配信されるクライアント情報を元に、前回配信された情報との差分を10で割った値を算出して、クライアント側の1FPS毎の値として反映させ、違和感を解消しています。</p>

<h3>MongoDBでラップタイムの保存と走行データの保存</h3>

<p>ラップタイムを保存したり、走行データを保存するのにデータベースとして<a href="http://www.mongodb.org/" target="_blank" data-wpel-link="external" rel="follow external noopener noreferrer">MongoDB</a>を利用しています。MongoDBは、<strong>オープンソースのドキュメント指向データベース</strong>です。RDBMSのようにレコードをテーブルに格納するのではなく、ドキュメントと呼ばれる構造的データをオブジェクト形式でデータを管理します。
ちなみにNode.jsから<a href="http://mongoosejs.com/" target="_blank" data-wpel-link="external" rel="follow external noopener noreferrer">Mongoose</a>を利用し、MongoDBに接続しています。Mongooseでは、Schemaインスタンスを通してModelを定義する事ができます。以下のようなオブジェクト形式でデータを管理出来るのが、MongoDBの特徴です。</p>

<p></p><pre class="crayon-plain-tag">var UserSchema = new mongoose.Schema({
    id:String, // ソケットID
    name:String, // 名前
    date:String, // 登録日
    time:Number, // ラップタイム
    comment: String, // コメント
    color: {
        body: String, // ボディカラー
        wing: String, // ウィングカラー
        driver: String // ドライバーカラー
    },
    runningPath: Array // 走行データ
});
var Users = db.model("user", UserSchema);</pre><p></p>

<p>走行データは1秒間に30回、車の位置と角度を記録した配列ですので、全てのユーザーの分だけ保存すると、データ量が肥大してしまう可能性があります。データ量を押さえるために、ラップタイムでソートして10位圏外の走行データは保持していません。</p>

<p>サーバサイドではNode.jsのおかげで、Webアプリケーションを構築する環境がとても整ってきていると思います。
基本的な言語はJavaScriptですので、サーバサイドもフロントエンドエンジニアが押さえるべき領域になっているのではと思います。</p>

<h2>その他の技術的トピック</h2>

<p>サーバサイド、クライアントサイドの技術それぞれの紹介を軸として、説明させていただきましたが、これ以外にもいくつか工夫した、技術的なポイントがありますので、紹介します。</p>

<h3>コースデータの共有</h3>

<p>3D表現を利用するために使用したthree.jsと、ゲームエンジンとして使った2D物理エンジンBox2DJSで、コースデータを共有するために2次元配列を使用しました。各値には数値を格納して、数値によってthree.jsでは壁、道路、芝、タイヤ、木、等の3Dモデルが配置され、Box2DJSでは3Dモデルの形状に沿った障害物を配置します。ちなみに開発当初、コースエディット機能や、SNSでコース共有機能等を考えていましたので、テキストデータとして扱いやすい2次元配列をコースデータとして採用したという経緯があります。</p>

<p></p><pre class="crayon-plain-tag">module imjcart.logic.map.value {
    export class MapConst {
        // コースデータ用の2次元配列
        static MAP:any = [           
　　　　　　[1,1,1,1,1,1,1,1,1,　〜省略〜　1,1,1,1,1,1,1,1,1,1,1], 
　　　　　　[1,4,4,4,4,4,4,4,4,　〜省略〜　4,4,4,4,4,4,4,4,4,4,1],
           〜省略〜　　　　　　
　　　　　　[1,1,1,1,1,1,1,1,1,　〜省略〜　1,1,1,1,1,1,1,1,1,1,1]
        ]
        // コースデータ配列内の各値が、何を表しているかの定数
        static MAP_KEY_NONE:number = 0; // アスファルト
        static MAP_KEY_WALL:number = 1; // 外壁
        static MAP_KEY_BLOCK:number = 2; // ブロック
        static MAP_KEY_TIRE:number = 3; // タイヤ
        static MAP_KEY_GRASS:number = 4; // 芝
        static MAP_KEY_TREE:number = 5; // 木
        static MAP_KEY_CAR_START_POSITION:number = 6; // 車のスタート位置
        static MAP_KEY_LAP_MEDIAN_CENTER_02:number = 7; // ゴールライン（逆走制御）
        static MAP_KEY_LAP_MEDIAN_CENTER_01:number = 8; // ゴールライン（逆走制御）
        static MAP_KEY_LAP_START_POINT:number = 9; // ゴールライン
        static MAP_KEY_SAND:number = 10; // 砂地
    }
}</pre><p></p>

<p>ただ、このような膨大な量の2次元配列を、テキストエディタで作成、編集するのは非常に非効率だと考えて、途中からExcelによって管理する方法に切り替えました。Excelでは条件付き書式を設定して、セルの値によって、わかりやすいように色づけするように設定しています。データ書き出しの際には、ExcelからCSVデータとしてテキストデータを書き出して、CSVデータをテキストエディタで置換して2次元配列にしています。</p>

<p><img src="/wp-content/uploads/2014/08/img12.gif" alt="img12" width="630" height="300" class="alignnone size-full wp-image-10487" /></p>

<h3>コースの装飾</h3>

<p>コースは直線と直角だけではなく、カーブ等の曲線も再現できなくてはいけません。2次元配列をコースデータとして利用する場合は、この点を補完する必要があります。また、コースというのは大抵、両脇のシケインとの境目にラインが引いてあります。こういったアスファルトや芝生といった要素以外の装飾を、周りの要素の配置条件に応じて追加することで、よりコースらしい外観を作る事ができます。下のキャプチャが装飾やカーブの補完をかけている物と、コースデータをそのまま表示したものとの違いです。</p>

<p><img src="/wp-content/uploads/2014/08/img13.jpg" alt="img13" width="630" height="254" class="alignnone size-full wp-image-10492" srcset="/wp-content/uploads/2014/08/img13.jpg 630w, /wp-content/uploads/2014/08/img13-300x120.jpg 300w, /wp-content/uploads/2014/08/img13-207x83.jpg 207w" sizes="(max-width: 630px) 100vw, 630px" /></p>

<h2>まとめ</h2>

<p>以上が今回作成したJS-Racingの主な技術解説になります。Webアプリケーションを構築するには、今回解説したように多くの技術が必要となります。ご紹介した技術は、たくさんある中から必要なものを取捨選択した結果ですので、当然コンテンツが変われば、必要となる技術も変わります。このコンテンツで使用した主な技術の全体図をまとめました。</p>

<p><img src="/wp-content/uploads/2014/08/img02.jpg" alt="使用技術概要" width="630" height="400" class="alignnone size-full wp-image-10250" srcset="/wp-content/uploads/2014/08/img02.jpg 630w, /wp-content/uploads/2014/08/img02-300x190.jpg 300w, /wp-content/uploads/2014/08/img02-207x131.jpg 207w" sizes="(max-width: 630px) 100vw, 630px" /></p>

<p>オンラインレースゲームというシンプルな内容のコンテンツですが、プラットフォームとしてWebブラウザを使用している点に注目していただけると嬉しいです。今までご紹介した技術は、オンラインゲームを作るだけの技術ではなく、Webコンテンツを作る上で、大きな可能性を持っている技術になります。私はゲームを作る上でこれらの技術を利用していますが、Web上の様々なサービスと連携をとることで、もっと広がりを持つコンテンツを作ることができると思います。ぜひ皆さんも、HTML5とJavaScript（クライアントサイド、サーバサイド含む）を使って、Webアプリケーションを作ってみてください。</p>
]]></content:encoded>
		
		<series:name><![CDATA[HTML5 Japan Cup 特集]]></series:name>
	</item>
		<item>
		<title>WebGLとWebSocketによる3Dオンラインレースゲーム「JS-Racing」の全て！（前編）</title>
		<link>/knockknockjp/10226/</link>
		<pubDate>Fri, 29 Aug 2014 00:00:58 +0000</pubDate>
		<dc:creator><![CDATA[西田慎吾]]></dc:creator>
				<category><![CDATA[プログラミング]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[TypeScript]]></category>
		<category><![CDATA[WebGL]]></category>
		<category><![CDATA[Webアプリ]]></category>

		<guid isPermaLink="false">/?p=10226</guid>
		<description><![CDATA[連載： HTML5 Japan Cup 特集 (5)今回はHTML5JapanCup2014にてWebGL賞と優秀賞を受賞したオンラインレースゲーム、JS-Racingの技術解説を書かせていただきます。 このコンテンツは...]]></description>
				<content:encoded><![CDATA[<div class="seriesmeta">連載： <a href="https://html5experts.jp/series/5jcup-2/" class="series-207" title="HTML5 Japan Cup 特集" data-wpel-link="internal">HTML5 Japan Cup 特集</a> (5)</div><p>今回は<a href="https://5jcup.org/" target="_blank" data-wpel-link="external" rel="follow external noopener noreferrer">HTML5JapanCup2014</a>にて<strong>WebGL賞</strong>と<strong>優秀賞</strong>を受賞したオンラインレースゲーム、<a href="http://js-racing.knockknock.jp/" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">JS-Racing</a>の技術解説を書かせていただきます。</p>

<p><img src="/wp-content/uploads/2014/08/img03.jpg" alt="JS-Racing" width="630" height="397" class="alignnone size-full wp-image-10255" srcset="/wp-content/uploads/2014/08/img03.jpg 630w, /wp-content/uploads/2014/08/img03-300x189.jpg 300w, /wp-content/uploads/2014/08/img03-207x130.jpg 207w" sizes="(max-width: 630px) 100vw, 630px" /></p>

<p>このコンテンツは<strong>WebGLの3D表現を活かした3Dレースゲーム</strong>です。<br>
また、<strong>WebSocketを使用しサーバを介して、複数のクライアントでの同時走行が可能なオンラインゲーム</strong>になっています。同時に、ソケット通信時に発行されるソケットIDをPCとスマートフォンで共有することで、<strong>スマートフォンからPC上の車を操作することも可能</strong>です。</p>

<p><img src="/wp-content/uploads/2014/08/img04.jpg" alt="JS-Racing" width="630" height="500" class="alignnone size-full wp-image-10264" srcset="/wp-content/uploads/2014/08/img04.jpg 630w, /wp-content/uploads/2014/08/img04-300x238.jpg 300w, /wp-content/uploads/2014/08/img04-207x164.jpg 207w" sizes="(max-width: 630px) 100vw, 630px" /></p>

<h2>クライアントサイドの使用技術</h2>

<p>クライアントサイドの構築において、目的・用途に応じて使用した言語やライブラリに関して解説します。</p>

<h3>TypeScriptによるクラス設計</h3>

<p>クライアントサイドのメインとなるロジックを<a href="http://www.typescriptlang.org/" target="_blank" data-wpel-link="external" rel="follow external noopener noreferrer">TypeScript</a>を使用して設計しました。TypeScriptはマイクロソフトによって開発されたフリーでオープンソースのプログラミング言語です。<strong>大規模JavaScriptアプリケーションに向けて開発</strong>されたTypeScriptは、これまでのJavaScriptにはない以下の機能を有しています。</p>

<ul>
    <li><strong>型注釈とコンパイル時の型チェック</strong></li>
    <li><strong>クラス</strong></li>
    <li><strong>インターフェイス</strong></li>
    <li><strong>モジュール</strong></li>
</ul>

<p>TypeScriptはコンパイルして最終的にJavaScriptに出力することができます。今回のように多くの機能の実装が必要になるWebアプリケーションでは、クラスベースのオブジェクト指向言語的な特徴を持つTypeScriptは、設計しやすく非常に有効なものになります。</p>

<p>ActionScript3と同じように、1つのTSファイル（TypeScriptファイル）にクラスを1つだけ記述して、ファイル名とクラス名を一致させました。また、モジュール毎にディレクトリを作成して、クラスを格納しています。これにより<strong>機能毎にモジュールとクラスを整理</strong>する事ができ、再利用しやすく見やすいコードになりました。最終的には、クラス毎に作成したTSファイルを、クラス毎のJavaScriptファイルに出力（コンパイル）し、<a href="http://gruntjs.com/" target="_blank" data-wpel-link="external" rel="follow external noopener noreferrer">Grunt</a>で結合、圧縮して、実際に読み込むJavaScriptファイルにしています。</p>

<p><img src="/wp-content/uploads/2014/08/img05.gif" alt="img05" width="630" height="670" class="alignnone size-full wp-image-10419" /></p>

<p>今回の場合、モジュールの分け方は、まず表示要素とロジックにモジュールを分けました。表示要素はゲームの各シーンや、3D描画、画面下部のランキング表示というように、表示要素単位でさらにモジュールを細分化していきます。ロジックは、イベントや値管理、定数、ユーティリティ、コースデータ管理、物理演算、リアルタイム通信といった、機能単位でモジュールを細分化していきます。このようにモジュールを分けていくことで、大規模なコンテンツ開発の際には、<strong>各クラスの責任範囲が明確化でき、バグが発生した際にも、原因を特定しやすく、さらには仕様変更にも強く</strong>なります。</p>

<p>TypeScriptは大規模開発においては、非常に設計しやすく、クラスベースのオブジェクト指向言語のメリットを最大限に享受できるものだと思います。ただその分、設計における手間と、なによりコード記述量が多くなります。中規模以下の案件では、最低限の設計で手軽に構築できる<a href="http://coffeescript.org/" target="_blank" data-wpel-link="external" rel="follow external noopener noreferrer">CoffeeScript</a>も、よい選択肢ではないかと思います。</p>

<h3>Box2DJSを使った2D物理演算</h3>

<p>車を走らせたり、障害物と衝突したりといった、ゲームの中核となるロジックは<a href="http://box2d-js.sourceforge.net/" target="_blank" data-wpel-link="external" rel="follow external noopener noreferrer">Box2DJS</a>を使用しています。Box2DJSは、<strong>質量・速度・摩擦をシミュレーションするJavaScriptの2D物理演算エンジン</strong>です。</p>

<p>表現方法自体は3Dですが、物理演算エンジンは2Dを使用しました。今回のようなシンプルなゲームを作るには、3Dの物理演算エンジンより、2Dの物理演算エンジンの方が、ゲームバランスや操作感において向いていると感じます。コンシューマーゲームなどの本格的なゲームとなると、3D物理演算エンジンによる処理は、必要不可欠なものだとは思いますが、その分、マシンへの負荷も高くなります。</p>

<p>物理演算に関しては、別途サンプルを作成して、チューニングしていきました。ゲームの中核となる部分ですので、まずこのサンプルで楽しいと思えるかどうかで、最終的なゲームのゴールが見えてくると思います。可能な限りゲーム性向上のためのチューニングしていくことが大切です。</p>

<p><a href="http://www.knockknock.jp/sample/20140127/02/index.html" target="_blank" data-wpel-link="external" rel="follow external noopener noreferrer"><img src="/wp-content/uploads/2014/08/img06.gif" alt="img06" width="630" height="473" class="alignnone size-full wp-image-10421" /></a></p>

<p>初期段階のモックアップのイメージですが、まずはこのようにBox2Dを使って、2Dレースゲームを構築しました。この2Dレースゲームの情報を3D表示部分や情報部分（スピードメーター等）やコースデータ管理ロジック、リアルタイム通信ロジックに渡します。</p>

<p>物理演算クラス<code>imjcart.logic.physics.Physics</code>にて、マップ情報を元に<code>new Car</code>で車インスタンスを生成し、車の状態が変更したイベント<code>CHANGE_CAR_CONDITION_EVENT</code>をキャッチして、イベントを発行します。</p>

<p></p><pre class="crayon-plain-tag">public createCar() {
    // マップ情報を元に車を生成
    var x = 0;
    var y = 0;
    var i, j, max, max2;
    for (i = 0, max = imjcart.logic.map.value.MapConst.MAP.length; i &amp;amp;amp;lt; max; i = i + 1) {
        for (j = 0, max2 = imjcart.logic.map.value.MapConst.MAP[i].length; j &amp;amp;amp;lt; max2; j = j + 1) {
            if (imjcart.logic.map.value.MapConst.MAP[i][j] == imjcart.logic.map.value.MapConst.MAP_KEY_CAR_START_POSITION) {
                y = imjcart.logic.map.value.MapConst.MAP_BLOCK_SIZE * i;
                x = imjcart.logic.map.value.MapConst.MAP_BLOCK_SIZE * j;
                break;
            }
        }
    }
    this._car = new Car(this._context, this._world, x, y);
    // 車状態変更イベント
    this._car.addEventListener(event.PhysicsEvent.CHANGE_CAR_CONDITION_EVENT, (evt) =&amp;amp;amp;gt; {
        this.dispatchEvent(imjcart.logic.physics.event.PhysicsEvent.CHANGE_CAR_CONDITION_EVENT, {
            x: evt.x,
            y: evt.y,
            bodyAngle: evt.bodyAngle,
            wheelAngle: evt.wheelAngle,
            speed: evt.speed,
            power: evt.power,
            gear: evt.gear,
            direction: evt.direction
        });
    });
}</pre><p></p>

<p><code>imjcart.logic.physics.Car</code>クラスでは、<code>update</code>関数が実行されると、入力されている値をホイールに反映し、車の状態に変化を与えます。変化した車の情報を付加して、車の状態が変更したイベント<code>CHANGE_CAR_CONDITION_EVENT</code>を発行します。</p>

<p></p><pre class="crayon-plain-tag">public update() {
    // ホイールに力を適応
    var i = 0, max;
    for (i = 0, max = this._frontWheels.length; i &lt; max; i = i + 1) {
        var wheel = this._frontWheels[i];
        var direction = wheel.GetTransform().R.col2.Copy();
        direction.Multiply(this._enginePower);
        wheel.ApplyForce(direction, wheel.GetPosition());
    }
    // ホイールと車をつなぐジョイント部分に角度を適応
    for (i = 0, max = this._frontWheelJoints.length; i &lt; max; i = i + 1) {
        var wheelJoint = this._frontWheelJoints[i];
        var angleDiff = this._steeringAngle - wheelJoint.GetJointAngle();
        wheelJoint.SetMotorSpeed(angleDiff * this._steerSpeed);
    }
    // 車状態変更イベントの発行
    var position = this._body.GetPosition();
    var velocity = this._body.GetLinearVelocity();
    var speedX = Math.abs(velocity.x);
    var speedY = Math.abs(velocity.y);
    var speed = 0;
    if (speedX &lt; speedY) {
        speed = speedY;
    } else {
        speed = speedX;
    }
    this.dispatchEvent(imjcart.logic.physics.event.PhysicsEvent.CHANGE_CAR_CONDITION_EVENT, {
        x: position.x,
        y: position.y,
        bodyAngle: this._body.GetAngle(),
        wheelAngle: this._frontWheelJoints[0].GetJointAngle(),
        speed: speed,
        power: this._enginePower,
        gear: this._gear,
        direction: this._engineDirection
    });
}</pre><p></p>

<p><code>dispatchEvent</code>メソッドは、イベントを発行する<code>lib.event.EventDispacher</code>クラス（オリジナルで作成）に定義されたメソッドです。表示要素のクラスはこの<code>lib.event.EventDispacher</code>クラスを継承しています。</p>

<h3>three.jsの3D表現</h3>

<p>WebGLを使った3D表示部分には<a href="http://threejs.org/" target="_blank" data-wpel-link="external" rel="follow external noopener noreferrer">three.js</a>を使用しています。three.jsは、<strong>WebGLを扱うライブラリとしては、最もよく使われているJavaScriptライブラリ</strong>です。</p>

<p><a href="http://www.blender.org/" target="_blank" data-wpel-link="external" rel="follow external noopener noreferrer">Blender</a>というオープンソースの3次元コンピュータグラフィックスソフトウェアを使って、3Dモデルの作成を作成し、three.jsで読み込んで表示しています。Blenderで作成する3Dモデルデータ（今回のモデルデータはOBJ形式）は、ジオメトリ（形状）とUVテクスチャのみで、three.jsを使って、質感などの調整をしています。</p>

<p><img src="/wp-content/uploads/2014/08/img07.jpg" alt="img07" width="630" height="380" class="alignnone size-full wp-image-10422" srcset="/wp-content/uploads/2014/08/img07.jpg 630w, /wp-content/uploads/2014/08/img07-300x180.jpg 300w, /wp-content/uploads/2014/08/img07-207x124.jpg 207w" sizes="(max-width: 630px) 100vw, 630px" /></p>

<p>また、今回のように実行環境が制限しづらいWebアプリケーションでは、なるべく多くのユーザーが利用できるように、マシンへの負荷は極力減らさないといけません。特にレースゲームは、現在走っているコースの先が見える必要があり、視野を広くしなければならないために、処理するデータ量が増え、より負荷がかかってしまいます。この<strong>負荷軽減</strong>のために、以下のことを考慮しています。</p>

<ol>
    <li><strong>ポリゴンが少なくても成り立つデザイン</strong></li>
    <li><strong>影をつけない</strong></li>
    <li><strong>ジオメトリ（形状）の結合</strong></li>
    <li><strong>負荷の少ないマテリアル（質感）を選択</strong></li>
</ol>

<p>1つめの<strong>ポリゴンが少なくても成り立つデザイン</strong>に関しては、下のタイヤのサンプルで、左のタイヤはポリゴン数を極端に減らしていますので、光や深度の計算が少なくなり、マシンへの負荷も小さくなります。右のタイヤはリアルなタイヤを再現したために、ポリゴン数も多く、マシンへの負荷も大きくなります。配置する量が少数ならそれほど問題になりませんが、コースデータに沿って多数配置する障害物ですので、マシン負荷は大きくなります。ただ、ポリゴン数が少ない分、リアルな表現からは遠く離れてしまっています。コース全体のデザインも含めてですが、リアルな表現を求めないポップなデザインを採用することで、低ポリゴンでも違和感のないようにしています。</p>

<p><img src="/wp-content/uploads/2014/08/img10.jpg" alt="img10" width="630" height="247" class="alignnone size-full wp-image-10425" srcset="/wp-content/uploads/2014/08/img10.jpg 630w, /wp-content/uploads/2014/08/img10-300x117.jpg 300w, /wp-content/uploads/2014/08/img10-207x81.jpg 207w" sizes="(max-width: 630px) 100vw, 630px" /></p>

<p>2つめの<strong>影をつけない</strong>ですが、下のキャプチャを見ていただければ、影がない左側のキャプチャと影がある右側のキャプチャでは、3D空間としての説得力にだいぶ違いが出てしまっているかと思います。ここは苦渋の決断ですが、影をつけることはthree.jsではとても負荷のかかる処理なので、より多くのユーザにストレスなく遊んでもらうためにも影をつけない方を採用しました。</p>

<p><img src="/wp-content/uploads/2014/08/img08.jpg" alt="img08" width="630" height="227" class="alignnone size-full wp-image-10423" srcset="/wp-content/uploads/2014/08/img08.jpg 630w, /wp-content/uploads/2014/08/img08-300x108.jpg 300w, /wp-content/uploads/2014/08/img08-207x74.jpg 207w" sizes="(max-width: 630px) 100vw, 630px" /></p>

<p>ちなみに影を出すには、レンダラーの<code>shadowMapEnabled</code>を<code>true</code>にして、光源の<code>castShadow</code>を<code>true</code>にします。そして影を作る側の物体の<code>castShadow</code>を<code>true</code>にして、影を受ける側の物体の<code>receiveShadow</code>を<code>true</code>にします。デフォルトでは<code>false</code>のため影が出ません。</p>

<p></p><pre class="crayon-plain-tag">// レンダラー
renderer.shadowMapEnabled = true;
// 光源
light.castShadow = true;
// 影を作る側
mesh.castShadow = true;
// 影を受ける側
mesh.receiveShadow = true;</pre><p></p>

<p>3つめの<strong>ジオメトリの結合</strong>に関してですが、WebGLコンテンツの様にGPUを使用するコンテンツは、ジオメトリの数だけGPUに対してドローコール（描画命令）をします。このドローコールの回数はパフォーマンスに大きく影響します。ジオメトリを<code>THREE.GeometryUtils.merge(geometry, mesh)</code>メソッドで結合することで、不要なドローコールの発生を少なくすることができます。注意点としては、結合したものは個別に動かすことや、マテリアルを個別に設定することはできませんので、今回は壁やコースなど、個別に動くことのない、大量に描画する必要のある要素に対してジオメトリの結合をしています。</p>

<p></p><pre class="crayon-plain-tag">var geometry = new THREE.Geometry(); // 空のジオメトリを作成
var meshArr = [];
// モデルデータとマテリアルを読み込む
var loader = new THREE.OBJMTLLoader();
loader.load("models/block03/block03.obj", "models/block03/block03.mtl", (object) =&gt; {
    object.traverse(function (child) {
        if (child instanceof THREE.Mesh) {
            child.material = new THREE.MeshLambertMaterial(child.material);
            child.material.ambient = new THREE.Color(imjcart.display.main.view3d.value.View3dConst.AMBIENT_COLOR);
            meshArr.push({
                mesh: child,
                material: child.material
            });
        }
    });
    // 読み込んだモデルデータをマップデータに沿って、クローンして配置。
    var i, j, max, max2;
    for (i = 0, max = imjcart.logic.map.value.MapConst.MAP.length; i &lt; max; i = i + 1) {
        for (j = 0, max2 = imjcart.logic.map.value.MapConst.MAP[i].length; j &lt; max2; j = j + 1) {
            if (imjcart.logic.map.value.MapConst.MAP[i][j] == imjcart.logic.map.value.MapConst.MAP_KEY_BLOCK) {
                var tagX = imjcart.logic.map.value.MapConst.MAP_BLOCK_SIZE * j + (imjcart.logic.map.value.MapConst.MAP_BLOCK_SIZE / 2);
                var tagY = 1;
                var tagZ = imjcart.logic.map.value.MapConst.MAP_BLOCK_SIZE * i + (imjcart.logic.map.value.MapConst.MAP_BLOCK_SIZE / 2);
                var mesh = meshArr[1].mesh.clone();
                mesh.position.set(tagX, tagY, tagZ);
                THREE.GeometryUtils.merge(geometry, mesh); // 空のジオメトリにマージして結合していく
            }
        }
    }
    this._block = new THREE.Mesh(geometry, meshArr[1].material);
    this._scene.add(this._block);
});</pre><p></p>

<p>最後に4つめの<strong>負荷の少ないマテリアル（質感）の選択</strong>は、表面の質感を上げるマテリアルを選択すると、その分マシンへの負荷が上がるということです。マテリアルの種類は代表的なもので、以下のメリットがあります。</p>

<ol>
<li><strong>MeshBasicMaterial</strong> : 光の影響を受けない
<li><strong>MeshLambertMaterial</strong> : 影や光の反射などを描画できる（ランバート反射モデル）
<li><strong>MeshPhongMaterial</strong> : 影や光の反射などをより綺麗に描画できる（フォンシェーディング）
</ol>

<p>マシンへの負荷に関しては1、2、3の順に次第に高くなります。選択したマテリアルの違いもマシン負荷への影響は大きいですが、マテリアルにバンプマップを使うかどうかもマシン負荷への影響は大きくなります。バンプマップは表面に細かな凹凸を表現します。下のキャプチャはアスファルトにバンプマップを適応したもの（右）と、適応していないもの（左）の違いです。表現力を上げるためには、バンプマップを採用したいところですが、ここでもマシン負荷を考慮してバンプマップを使っていません。</p>

<p><img src="/wp-content/uploads/2014/08/img09.jpg" alt="img09" width="630" height="400" class="alignnone size-full wp-image-10424" srcset="/wp-content/uploads/2014/08/img09.jpg 630w, /wp-content/uploads/2014/08/img09-300x190.jpg 300w, /wp-content/uploads/2014/08/img09-207x131.jpg 207w" sizes="(max-width: 630px) 100vw, 630px" /></p>

<p>リアルな表現に近づければ近づけるほど、マシンへの負荷が増えてしまいます。ある程度の表現力は確保しながら、どれだけ負荷を減らせるかの調整は、これくらいのスペックなら問題なく表示できるようにしたいというターゲットとなるマシンを決めて、そのマシンで最低限確保したいフレームレートを決めます。あとは都度、フレームレートを確認しながら調整するということの繰り返しになります。マシンスペックだけでなく、ブラウザによっても表示速度に差異があることを意識していなければなりません。</p>

<h3>HTML5のAPIによる新しい表現力</h3>

<p>WebGL以外のHTML5の要素も、それぞれの特徴にあわせて適宜使用しています。</p>

<ul>
    <li>Canvas：コースマップ（コース縮尺図）</li>
    <li>SVG：スピードメーター</li>
    <li>WebFont：タイトルやテキスト全般</li>
    <li>CSS3：角丸やドロップシャドウやボタンデザイン</li>
</ul>

<p><img src="/wp-content/uploads/2014/08/img11.jpg" alt="img11" width="630" height="336" class="alignnone size-full wp-image-10446" srcset="/wp-content/uploads/2014/08/img11.jpg 630w, /wp-content/uploads/2014/08/img11-300x160.jpg 300w, /wp-content/uploads/2014/08/img11-207x110.jpg 207w" sizes="(max-width: 630px) 100vw, 630px" /></p>

<p>WebGLによる3D表示をしているCanvasの上に、各要素を配置していますが、上に乗っかっている要素を動かしたり、形状を変更させる場合には、背景に不透明な物を敷くなどの工夫が必要になります。そうしないと上に乗っかっている要素に変更があるたびに、下のCanvas全体まで、不要な再描画処理がかかってしまい、描画のパフォーマンスを著しく低下させてしまいます。</p>

<h2>まとめ</h2>

<p>以上が今回作成したJS-Racingで利用した、主なクライアントサイド技術になります。</p>

<p>3D表現として利用したWebGLは、IE11の対応によって最新のモダンブラウザでは、ほぼ実行可能な環境が揃ってきていると思います。</p>

<p>ユーザーのマシンスペックや、WebGL対応ブラウザの普及率を考えると、まだある程度ユーザーをしぼる必要がありますが、最近では話題を呼んでいる3Dコンテンツも次第に増えてきたように感じます。</p>

<p>次回は<strong>サーバサイドの使用技術</strong>と、その他の<strong>技術トピック</strong>に関して解説する予定です。</p>
]]></content:encoded>
		
		<series:name><![CDATA[HTML5 Japan Cup 特集]]></series:name>
	</item>
	</channel>
</rss>
