CardboardにAndroidを入れて、
iPhoneをくくりつけた傘を動かすと、画面の剣も動くのでこれで敵を倒すゲーム。
実装
剣(iOS)
剣にくくりつけたiPhoneの傾きの値をUnity(Android)に送信している。 iOSはClassic Bluetoothを自由に使えないので、Androidと通信する場合はBLEを使う。 BLEは通常だと20byteしか一度に送れないので、これを超えないよう注意する必要がある。
BLEで通信するところは
で作ったので、端末の傾きを取得して送るだけ。
import UIKit
import CoreMotion
class Motion{
let peripheral: BLEPeripheral
let accelHandler:CMDeviceMotionHandler
let manager = CMMotionManager()
public init(peripheral :BLEPeripheral, label :UILabel){
self.peripheral = peripheral
accelHandler = {
(data: CMDeviceMotion?, error: Error?) -> Void in
let str = String(format: "%.1f %.1f %.1f",
arguments: [data!.attitude.pitch * 180 / M_PI,
data!.attitude.roll * 180 / M_PI,
data!.attitude.yaw * 180 / M_PI]
)
let res = peripheral.update(str.data(using: String.Encoding.utf8))
label.text = str + " " + String(res)
}
}
func start(){
if manager.isDeviceMotionAvailable {
manager.deviceMotionUpdateInterval = 1 / 12;
manager.startDeviceMotionUpdates(to: OperationQueue.current!, withHandler: accelHandler)
}
}
}
ゲーム(Unity & Android)
オライリーから
UnityによるVRアプリケーション開発――作りながら学ぶバーチャルリアリティ入門
という本が出ていたので買った。Unityの初歩的なところやBlenderとかも説明があるのでおすすめ。
とりあえずGoogleのSDKをimportして、
PrefabのGvrViewerMain
を置くと二眼のそれっぽい感じになる。
スコアとかゲームオーバーの状態と処理。
using UnityEngine;
using UnityEngine.UI;
using System.Collections;
public class Game : MonoBehaviour {
public static int score = 0;
public static bool gameOver = false;
public static bool killed = false;
public Text gameOverText;
public Text scoreText;
public GameObject enemy;
void Start () {
gameOverText.enabled = false;
}
void Update () {
scoreText.text = "Score: " + score;
if (gameOver) gameOverText.enabled = true;
if (killed) {
nextLevel ();
killed = false;
}
}
public void nextLevel(){
var old = enemy;
var x = Random.Range (-10, 10);
enemy = (GameObject) Instantiate (
enemy, new Vector3(x, 0, Mathf.Sqrt(10 * 10 - x * x)), Quaternion.Euler(0, 0, 0));
Destroy (old);
}
}
敵の当たり判定。剣に当たったら爆発して次のが出てくる。 体(見えないCapsule Colliderを設定している)に当たるとゲームオーバー。
using UnityEngine;
using UnityEngine.UI;
using System.Collections;
public class MonsterHit : MonoBehaviour {
public GameObject killEffect;
void OnCollisionEnter(Collision collision) {
switch (collision.gameObject.name) {
case "sword":
if (!Game.gameOver) {
Game.score += 1;
Instantiate (killEffect, transform.position, transform.rotation);
Game.killed = true;
}
break;
case "Body":
Game.gameOver = true;
break;
}
}
}
敵を動かす。スコアが上がるとスピードも上がる。
using UnityEngine;
using System.Collections;
public class MonsterMove : MonoBehaviour {
private float moveRate;
void Start () {
moveRate = 0.0f;
}
void Update () {
transform.position = Vector3.Lerp (transform.position, new Vector3 (0, 0, -1.5f), moveRate);
moveRate += 0.0001f * (Game.score + 1);
transform.LookAt (new Vector3 (0, 0, -1.5f));
}
}
センサーの値を受け取って、剣を動かす。ここで使っているネイティブライブラリは前作ったもの。
UnityでAndroidのBLEを使うネイティブプラグインを作る
using UnityEngine;
using System.Collections;
public class SwordMotion : MonoBehaviour {
private AndroidJavaObject ble;
private Quaternion q;
void Start () {
ble = new AndroidJavaObject ("net.sambaiz.unity_ble.BLE", this.gameObject.name, "received");
q = Quaternion.Euler (0, 0, 0);
}
void Update () {
transform.rotation = q;
}
void received(string message){
var motionData = message.Split (' '); // pitch roll yaw
q = Quaternion.Euler (
90 - float.Parse (motionData [0]),
float.Parse (motionData [1]),
0) ;
}
void OnApplicationPause (bool pauseStatus)
{
if (ble != null) {
if (pauseStatus) {
ble.Call ("onPause");
} else {
ble.Call ("onActive");
}
}
}
}