KLabGames Tech Blog

KLabは、多くのスマートフォン向けゲームを開発・提供しています。 スマートフォン向けゲームの開発と運用は、Webソーシャルゲームと比べて格段に複雑で、またコンシューマゲーム開発とも異なったノウハウが求められる領域もあります。 このブログでは、KLabのゲーム開発・運用の中で培われた様々な技術や挑戦とそのノウハウについて、広く紹介していきます。

やまだです。 SIMD.jsについてゆるく話をします。

SIMDとは何か?

まず、SIMD(Single Instruction Multiple Data)とは何かから簡単にお話しします。 SIMDは命令1つで複数のデータの演算を一括して行う計算方式です。 複数データに対する演算を一括して行うため、 同じような演算を大量に実行しなければならない場合に威力を発揮します。 しかし、SIMDを使うためにはCPUによって異なる命令を実行する必要があります。 たとえばIntel系のCPUであればSIMD拡張の命令セットであるSSEを使い、 ARM系CPUならNEONというように使い分けないといけません。

ブラウザとSIMD

今まではこれらはJavaScriptからは使うことができませんでした。 しかし、ブラウザのパフォーマンスを求め続けた結果ある実装が生まれました。

そう、Dartです。 DartはJavaScriptとは異なる高速なVM上で動作しながらも、ECMAScript 5に変換もできるという優れた処理系でした。 この野心的なプロジェクトは1年以上前にSIMDに対応していました。 記事によると3D分野でしばしば用いられる4x4行列の乗算で300%のパフォーマンスを発揮したようです。

そして、この実装はJavaScriptにも取り入れられようとしています。 JavaScriptの標準仕様であるECMAScript 2015が承認されたことは記憶に新しいと思います。 ECMAScript 2015ではアロー演算子やデストラクチャリングなど様々な拡張が行われました。 今、ECMAScriptは次なる仕様ECMAScript 7に向けて仕様の策定が進められています。 その中の一つに今回紹介するSIMD.jsがあります。 ECMAScriptの策定プロセスのうち、Stage 2、つまりDraft段階にあり主要な機能の定義が進められています。 まだまだ仕様として安定はしていないのですが、様々なプラットフォームでSIMD.jsを試すことができます。

SIMD.jsを実際に動かしてみる

今回はChromiumでSIMD.jsのパワーを体感してみましょう。 SIMD.jsの試験的な実装がなされたChromiumは こちらから手に入ります。 Windows、Mac、Linuxそれぞれのバイナリが用意されていますが、 実行時に--js-flags=--simd-objectをコマンドライン引数として与える必要があります。

このChromiumを使用してSIMDのパフォーマンスを体感できるコードを書いてみましょう。 合計値を計算するsum関数を実装してみます。 sum1は配列を1つずつ加算して行くのに対し、sum2は4つ同時にまとめて加算しています。

//NO SIMD
var sum1 = function(list) {
  var total = 0;
  var length = list.length;
  for(var i = 0; i < length; ++i) {
    total += list[i];
  }

  return total;
}

//SIMD
var sum2 = function(list) {
  var i32x4list = new Int32x4Array(list.buffer);
  var total = SIMD.int32x4.splat(0);
  var length = list.length / 4;
  for(var i = 0; i < length; ++i) {
    total = SIMD.int32x4.add(total, i32x4list.getAt(i));
  }

  return total.x + total.y + total.z + total.w;
}
SIMD ops/s
Yes 1509ops/s
No 620ops/s

結果はご覧のとおり、2倍以上のスピードが出ています。 ただの加算ですがここまで違いが出てしまいます。 こういった大量のデータ処理はSIMDの得意分野です。

とくにWebGLではベクトル演算の需要が大いにあります。 衝突演算、物理演算に威力を発揮することでしょう。

SIMD.jsと未来

SIMDを使ったチューニングはC言語などを使い、低レイヤーで行われることが多いですが、 JavaScriptによるSIMDチューニングができる時代がすぐそこまできています。 もちろん、OSやデバイスを意識せずに手軽に使うことができます。 Mac、Windows、Linux、そしてスマートフォンでも!

工夫次第で大きくパフォーマンスを向上できるSIMD.jsの今後にワクワクしてきませんか?

@やまだ

バーチャルリアリティーは漢のロマン

昔、私が高校生くらいの時にもVRブームがあり、バーチャルリアリティエキスポなるイベントで、 筋斗雲的な何かに乗ったり、高速に上下するLEDアレイディスプレイで三次元を表示するゲームなどに、 ワクワクしたことを思い出します。

そして、ここ数年、再びバーチャルリアリティー(VR)が盛り上がっています。 Oculus Riftや SCEのProject Morpheusといった ヘッドマウントディスプレイも格段に進歩し、 コンピュータグラフィックスやGPUなどの技術の進歩のおかげで、 非常にリアルな仮想現実没入体験が、簡単に手の届く領域にきています。

そんななか、私がもっともワクワクしているのが、 Googleの「Cardboard」です。 なぜなら、非常に安く手軽に手に入れ使うことができ、特にユーザーに身近なものになりうるからです。

本稿では、Cardboardを使ってVRでロボットに搭乗して操縦するデモを作成したことを紹介します。

Cardboardを使ってみる

CardboardはGoogleが公開した段ボールでできたスマフォ用VRアタッチメントです。 設計図も公開されているので、100円ショップで買ったルーペのレンズと、 段ボールと、テープと工具があれば自分で作ることができます。

fig1

簡単!Cardboardを自作する

実際にGoogleが公開しているデータで、 Cardboardを二つほど作ってみました。 レンズは、100円ショップで手に入るルーペを分解すると手に入ります。 私は、ダイソーで写真のルーペを買いました。なんとレンズが二つ入っていて、 一つ買うだけで両目分のレンズが手に入ります。

fig2

レーザーカッターがあれば簡単なのでしょうが、残念ながらないので、 プリントアウトした型紙を段ボールに貼り、 地道に普通のカッターで切り抜いて組み立てます。

純正のCardboardには、頭に固定するバンドがないので、 手芸用のゴム紐とマジックテープで簡単な固定用バンドを追加しました。

以下が実際に自作してみたものです。

fig3

UnityでCardboard(Durovis Dive編)

最初に試したのが、このDurovis Diveです。 リンク先の開発者ページにあるSDKをダウンロードし、 unitypackageをインポートして使います。

インポートしたDive/Prefabs/にあるDive_CameraというPrefabを、 シーンに追加すると、ヘッドトラッキング付両眼カメラになります。 このカメラをメインカメラにすることで、非常に簡単にCardboardなどの スマフォ用VRアタッチメントに対応させることができます。

UnityでCardboard(CardboardSDK編)

次に、Google Cardboard公式のUnitySDKを試してみましょう。 Cardboard Developper Page(Unity)を見ると、Download and Samplesからunitypackageをダウンロードできます。 プロジェクトにインポートすると、Cardboardフォルダができます。

Durovis Diveと同様に、 Cardboard/Prefabs/にある、CardboardMainというPrefabをシーンに追加することで、カメラになります。 これだけで、Cardboardに対応させることができます。

どちらにしたのか

今回は、両方とも実装してみての確認は行いましたが、 詳細な評価と比較はできていません。 実際のアプリではDurovis Diveを使いましたが、 これは作り始めた初期の時点でCardboardのUnity対応SDKがリリースされてなかったという理由によります。

どうやって操作するの?

スマフォVRアタッチメントを使用する場合、大きな問題となるのがユーザーインターフェースです。 Cardboardでは、これを磁石とスマフォの磁気センサーでスイッチを作って解決しています。 磁石二つの位置関係による磁力の変化を、スマフォの磁気センサーで検出することで、一ボタンに対応する入力を実現しています。 非常に面白い仕組みです。 Ver.2のCardboardでは、導電性の布とレバー機構を使うことで、ボタンを押すと画面にタッチされる機構が作られています。これも面白い仕組みですね。 一方で、どちらの入力方式でもアクション性の高いゲームに利用するには入力の少なさと、反応速度と操作しやすさから難しいです。

そうだ、スマフォ二台使おう

ここで、私が考えたのが、スマフォの加速度センサーやタッチパネルをコントローラーとして使えないかということでした。 スマフォの普及率が爆発的に増加した昨今、二つくらい前に使っていたスマフォが机の引き出しの中に眠っていたりしないでしょうか。 そうです、それらをコントローラとして使えばいいのです!!

そうなると、表示用と左手用右手用の都合三台のスマフォが必要です。 我が家には、幸い私と妻が以前使っていたiPhone4とiPhone4sが余っていました。 これをコントローラーとして使えるようにしてみましょう。

無線で本体とコントローラーをつなぐ

では、どのように表示+ゲーム実行用の本体スマフォと、コントローラー用スマフォをつなげばいいのでしょうか。 ここで今回は、WebSocketを使ってみました。 サーバーを介して、本体とコントローラをWebSocketでつないでみました。

fig4

クライアント側のUnityでは、WebSocketSharpのUnity対応のためのKLab改変版を使って、以下のような受信用プログラムが動いています。

using UnityEngine;
using System.Collections;
using WebSocketSharp;

public class WebSocketClient : MonoBehaviour {
    public const int RIGHT = 0;
    public const int LEFT = 1;
    public const int B1 = 0;
    public const int B2 = 1;
    public Vector3[] accel = new Vector3[2];
    public bool[,] button = new bool[2,2]{{false,false},{false,false}};

    private WebSocket ws;

    // Use this for initialization
    void Start () {
        ws = new WebSocket ("ws://URL/chat/test");
        ws.OnMessage += (object sender, MessageEventArgs e) => {
            string [] message = e.Data.Split(new char[]{':'});
            int controllerNo=RIGHT;// Default Right it is not good
            switch(message[1]){
            case "R":
                controllerNo = RIGHT;
                break;
            case "L":
                controllerNo = LEFT;
                break;
            default:
                break;
            }
            switch(message[2]){
            case "AC":
                accel[controllerNo] = new Vector3(float.Parse(message[3]),
                                                  float.Parse(message[4]),
                                                  float.Parse(message[5]));
                break;
            case "B":
                button[controllerNo,int.Parse(message[3])-1] =
                    (message[4].Equals("DOWN")) ? true : false;
                break;
            default:
                break;
            }
        };
        ws.Connect ();
    }

    // Update is called once per frame
    void Update () {
    }
}

Playerのコントロールスクリプトなどで、このクラスから情報を取得して操作します。

一方で、コントローラーは、HTMLファイルで作りました。ブラウザで表示するだけでコントローラーになります。

<!DOCTYPE html>
<html><head>
<meta name="viewport" content="width=device-width; initial-scale=1.0; maximum-scale=1.0; user-scalable=no;">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
</head>

<body style='user-select: none; -webkit-user-select: none;'>
<script type="text/javascript">
document.addEventListener('touchmove', function(e) {
  e.preventDefault();
},false);

var ws;
ws = new WebSocket("ws://URL/chat/test");

function send(message){
  ws.send(message);
}

window.addEventListener("devicemotion",function(evt){
  var x = evt.accelerationIncludingGravity.x; //横方向の傾斜
  var y = evt.accelerationIncludingGravity.y; //縦方法の傾斜
  var z = evt.accelerationIncludingGravity.z; //上下方向の傾斜
  send("1:L:AC:"+x+":"+y+":"+z);
},false);
</script>
<div style="width: 100px; height: 100px; background-color: red; margin: 20px; float: right;" ontouchstart="send('1:L:B:1:DOWN')" ontouchend="send('1:L:B:1:UP')" ></div>
<div style="clear: right;"></div>
<br>
<br>
<div style="width: 100px; height: 100px; background-color: blue; margin: 20px; float: right;" ontouchstart="send('1:L:B:2:DOWN')" ontouchend="send('1:L:B:2:UP')" ></div>
</body></html>

このHTMLをコントローラーの識別番号を変えて左手用と右手用の二つ作ります。

中継用サーバーは、単純なWebSocketで届いたメッセージを接続しているクライアントにブロードキャストするだけのサーバーです。 clojure-aleph-chatを参考にしました。

バーチャロンへのオマージュ

二本のスティックでロボットを操作すると言えばあれですね。 加速度センサにより、端末の傾きをとることにより、 左手右手の両方のスティックを前に傾けると前進、 後ろに傾けると後退、前後に互い違いに倒すと回転、 横方向に同じ方向に倒すと横移動、 横方向に開くと上昇、閉じると下降という操作体系です。 皆さん、よくご存じですよね。

コントローラー画面には、攻撃ボタンとブーストボタンを表示してみました。 タップするとそれぞれの動作をします。

fig7

写真にあるように、ブラウザでコントローラー画面を表示させます。 赤が攻撃ボタン、青がブーストボタンです。 AndroidとiOSのどちらでも動きますが、AndroidとiOSで加速度センサーの軸が違っているので気をつけましょう。

プレイ風景・・・

遊んでいるところ(筆者近影)

fig5

実際の画面のスクリーンショット

fig6

まとめ

今回の実験は、三台のスマフォが必要というネックがありますが、 最近のスマフォの普及率を考えると意外と何とかなるのではないでしょうか。 実際我が家には、iOSだけで、3GS,4,4s,5,5s,6,初代iPadがありますし、 AndroidはHTC AriaとKindle Fire HDとKobo Arcがあります。 3台くらいきっとなんとかなりますよね。

また、WebSocketで一回サーバーを介するレイテンシも、プレイしてる分にはあまり気にならず、 作り方次第で十分隠蔽可能なのではないかという感想です。

そして最高ですね。この没入感、飛翔感。楽しいです。文章ではこの楽しさが伝わらないのが残念です。 今回は、搭乗して操縦する体験を目標にしたので、ゲーム性は皆無に等しいのですが、 今後ゲームとして成り立たせる方向も模索したいと思います。


oho-s

連載目次

はじめに

@tenntennです。

もうすぐGoのバージョン1.5がリリースされる予定ですが, みなさまはどの機能に注目しているでしょうか? コンカレントGCだったりshared libraryが作れるようになったりと,Go 1.5は非常に楽しみです。 その中でも私は,Go 1.4で入ったGo Mobileのアップデートに注目しています。

Go Mobileは,Goを使ってモバイルアプリを書くためのツール類を提供するプロジェクトです。 Go 1.5では,iOS向けのアプリがビルドできるようになったり,Androidのサポートが強化されるようです。 masterブランチの最新(2015年7月19日 時点)では,すでにiOS向けのビルドもできるようになっているみたいです。 実際に,GoチームによってIvyというAPLライクな言語のインタプリタがiOSAndroid向けにリリースされています。

私も先日,Go Conference 2015 SummerのLT大会で使用したLTタイマーのGoFunソースコード)を試しに個人でGoogle Play Storeでリリースしてみました。 私が作成したGoFunというアプリは,Javaを使わずすべてGoだけで書いています。 JavaとGoを使った記事はよく見かけるのですが,Goだけで書いている記事はあまり見かけません。 また,Google Play Storeに公開するところまで扱っている記事も見かけません。 そこでこの記事では,複数回に分けてその時に調べたGoだけでAndroidアプリを作ってリリースするまでの手順を説明します。 まず今回は,Go Mobileの簡単な説明とインストール方法を説明したいと思います。

この記事で扱う手順やノウハウは,記事執筆時(2015年7月19日)のものです。 Go Mobileはまだまだ開発途中です。 現在(執筆当時)も毎日のように破壊的な変更がされています。 そのため,ビルド方法やパッケージ名,各ライブラリやツールの使い方が変わる可能性があります。

なお,この記事ではMac OSX Yosemite(10.10.3)とNexus 9(Android 5.1.1)で動作検証をしていますが,他のOSやAndroid端末では動作検証をしてまいません。 Go MobileはLinuxでは動くようですが,Windowsではまだ動かないようです。 Go Mobileのコード上にはWindowsの記述があるので,そのうち対応されるでしょう。

Go Mobileのインストール

最新のGo Momobileを使用するには,Go 1.5が必要です。 Go 1.5のベータ版をダウンロードするかソースコードからビルドしてください。 なお,Go 1.5のビルドには,Go 1.4が必要です。

Go 1.5のインストールが終わったら,次はgomobileコマンドをgo getします。

$ go get golang.org/x/mobile/cmd/gomobile

$GOPATH/bin以下にgomoibleコマンドががインストールされます。 $PATH$GOPATH/binが含まれていない場合は追加しておきましょう。 これでgomobileコマンドが使えるようになったはずです。

$ gomobile -h
Gomobile is a tool for building and running mobile apps written in Go.

To install:
....

つづいて,gomobile initを実行しましょう。 このコマンドは,Go Mobileを使うために必要なものをインストールしてくれます。 どうやら,モバイル端末向けにクロスコンパイルするためのGoツールチェーンやAndroid NDK,OpenAL(libopenal)がインストールされるようです。 執筆当時のバージョンではandroid-ndk-r10d$GOPATH/pkg/gomobile/以下にインストールされるようでした。 なお,実行には結構時間がかかるので,-vをつけて進捗を確認するとよいでしょう。

$ gomobile init -v

Goだけでモバイルアプリを書く

Go Mobileでは,以下の2種類の方法でGoを使ってモバイルアプリを開発することができます。

  • Java(Android)やObjective-C(iOS)からGoで書かれた処理を呼び出す
  • GoでOpenGLやOpenALを使ってアプリを書く

Go Mobileは,AndroidやiOSが提供するAPIのラッパーをすべて用意することを目的としてはいません。 GUIなどは通常のAndroidやiOSのアプリの開発と同じように,JavaやObjective-Cを使い,Goが得意なところはGoに任せるといった具合に使用することを想定しています。 (Swiftから呼び出せるのかは調べてません。すいません。)

一方で,OpenGLやOpenALの関数が呼び出せるようになっています。そのため,ゲームなどOS標準のUIを使わない場合は,これらを使用して開発できるようになっています。 しかしながら,まだ提供されている機能は低レベルな関数が多く,多くのパッケージがexp以下に配置されていることから分かるように,まだまだ実験的に実装されているだけのようです。

この記事では,GoだけでAndroidアプリを作る方法について説明します。 JavaからGoで書かれた処理を呼び出す方法については,いくつか日本語でも記事があるので探してみるとよいでしょう。

なお,Go Mobileのアプリからでも,ほとんどのGoの標準パッケージで提供される機能が使用できるようです。

サンプルを動かしてみる

Go Mobileのリポジトリには,exampleというディレクトリがあります。 この中には,Go Mobileを触れてみるのにちょうど良いサンプルがいくつか入っています。 ここでは最もシンプルなサンプルのexample/basicを使って動かし方を説明していきます。

まずはMac上で動かしてみましょう。 サンプルのあるディレクトリまで行き,go runコマンドで実行します。

$ cd $GOPATH/src/golang.org/x/mobile/example/basic/
$ go run main.go

うまく実行できると下のような赤い背景に緑の三角形がでるはずです。 緑の三角形はドラッグできるので,ぜひ動かしてみてください。

basic

Mac上で動かせることは確認できましたので,次にAndroid上で動かしてみましょう。 gomobile buildコマンドを使うと,モバイルアプリ用にビルドができます。 デフォルトではapkが生成されます。-target iosと指定すると,iOS向けのappファイルがビルドされます。 appからipaを作ればiOSでも動かせるとは思いますが,私の環境では実行できるipaは生成できませんでした。

$ cd ~/Desktop #どこでもよい
$ gomobile build golang.org/x/mobile/example/basic

上記のコマンドを打つと,カレントディレクトリにbasic.apkが生成されているかと思います。 adb installでAndoridにインストールして実行してみましょう。

$ adb install basic.apk

うまくいくと以下のようにAndroid上でアプリが実行できます。

basic_android

上記の例では,gomobile buildを使用しましたが,gomoible installを使えば,ビルド後にadb installを使って自動でAndroidへのインストールするところまでやってくれます。 gomobile installを使用するには,adbコマンドが必要となります。 Android SDKの開発環境を用意して,adbコマンドにパスを通しておきましょう。 なお,gomobile installコマンドはビルドターゲットがandroidでないと動作しませんので,注意してください。

$ gomobile install golang.org/x/mobile/example/basic

いかがだったでしょうか?Goだけで書かれたアプリがAndroid端末上で動くのは感動しますよね。 次回は,サンプルを見ながらGo Mobileで提供されているspriteパッケージについて説明する予定です。

↑このページのトップヘ