【スポンサーリンク】

【Unity 3D】クリック(タップ)先のゲームオブジェクトおよび座標の取得

Physics.Raycastでクリック先のオブジェクトと交点座標を取得できる。引数に必要なRayオブジェクトはメインカメラ一つの場合、
Camera.main.ScreenPointToRay(Input.mousePosition)
で取得できる。

なお、Physics.Raycastの返り値はbool値となっており、肝心のクリック先オブジェクトは参照渡しした変数に返すため、コードの見やすさを考慮すると用途に応じてラップしたほうが良いと思われる。

using UnityEngine;
using System.Collections;

public class Test : MonoBehaviour {
	void Update() {
		if(Input.GetKeyDown(KeyCode.Mouse0)) {
			Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
			Debug.Log(HitGameObject(ray) == null ? null : HitGameObject(ray).name);
			Debug.Log(HitPoint(ray));
		}
	}

	//クリック先のオブジェクトを取得する関数
	GameObject HitGameObject (Ray ray) {
		RaycastHit hit;
		return Physics.Raycast(ray, out hit) ? hit.transform.gameObject : null;
	}

	//クリック先とオブジェクトの交点座標を取得する関数
	Vector3? HitPoint (Ray ray) {
		RaycastHit hit;
		return Physics.Raycast(ray, out hit) ? hit.point : (Vector3?)null;
	}

}

【Unity, Android】AdMobテスト広告を出すためのデバイスIDを取得する

テスト用端末でAdMobの誤クリックを避けるためにデバイスIDが必要となったが、取得方法がわかるまで苦しんだのでメモ。

AdRequest request = new AdRequest.Builder ().AddTestDevice("ここに必要なデバイスIDの取得方法がなかなか分からなかった").Build ();

bannerView.LoadAd (request);

デバイスIDの取得方法

apkファイルを端末に入れる際に使うadbコマンドを使えばよいことがわかった。
まずAdMobを実装したアプリをテスト端末に入れる。

adb install -r /path/to/hoge.apk 

次にアプリを起動し、広告を出してみる。
そうしたら、下記のコマンドを叩く。

./adb logcat | grep "AdRequest"

すると、
Use AdRequest.Builder.addTestDevice("デバイスID") to get test ads on this device.
といった表示が出てくるので、出てきたデバイスIDを使えばOK。

【画像処理】HTML5+JavaScriptでリアルタイムに顔認識して顔を隠してみた

HTML5+JavaScriptでリアルタイム画像処理が割と簡単にできるっぽい。いろいろ調べてみるとclmtrackr.js を使えば顔認識も簡単にできるみたいなので、サンプルコード http://artak.hatenablog.com/entry/2014/08/11/195619を参考に、顔を追跡して隠してくれる(モザイク+黒目線)プログラムを作ってみた。

<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8">
    <script src="clmtrackr.js"></script>
    <script src="models/model_pca_20_svm.js"></script>
    <title>Title</title>
    <style>
        #container {
            position: relative;
            width: 370px;
            /*margin : 0px auto;*/
        }
        #overlay {
            position: absolute;
            top: 0px;
            left: 0px;
            -o-transform: scaleX(-1);
            -webkit-transform: scaleX(-1);
            transform: scaleX(-1);
            -ms-filter: fliph;
            /*IE*/
            
            filter: fliph;
            /*IE*/
        }
        #videoel {
            -o-transform: scaleX(-1);
            -webkit-transform: scaleX(-1);
            transform: scaleX(-1);
            -ms-filter: fliph;
            /*IE*/
            
            filter: fliph;
            /*IE*/
        }
    </style>
</head>

<body>
    <div id="container">
        <video id="videoel" width="640" height="480" preload="auto" loop></video>
        <canvas id="overlay" width="640" height="480"></canvas>
    </div>
    <input class="btn" type="button" value="wait, loading video & images" disabled="disabled" onclick="startVideo()" id="startbutton"></input>
    <script>
        var vid = document.getElementById('videoel');
        var overlay = document.getElementById('overlay');
        var overlayCC = overlay.getContext('2d');

        var buffer = document.createElement('canvas');
        var bufferContext = buffer.getContext('2d');

        var ctrack = new clm.tracker({
            useWebGL: true
        });
        ctrack.init(pModel);

        function enablestart() {
            var startbutton = document.getElementById('startbutton');
            startbutton.value = "start";
            startbutton.disabled = null;
        }

        navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;
        window.URL = window.URL || window.webkitURL || window.msURL || window.mozURL;

        if (navigator.getUserMedia) {
            var videoSelector = {
                video: true
            };
            if (window.navigator.appVersion.match(/Chrome\/(.*?) /)) {
                var chromeVersion = parseInt(window.navigator.appVersion.match(/Chrome\/(\d+)\./)[1], 10);
                if (chromeVersion < 20) {
                    videoSelector = "video";
                }
            };

            navigator.getUserMedia(videoSelector, function(stream) {
                if (vid.mozCaptureStream) {
                    vid.mozSrcObject = stream;
                } else {
                    vid.src = (window.URL && window.URL.createObjectURL(stream)) || stream;
                }
                vid.play();
            }, function() {
                alert("There was some problem trying to fetch video from your webcam, using a fallback video instead.");
            });
        } else {
            alert("Your browser does not seem to support getUserMedia, using a fallback video instead.");
        }

        vid.addEventListener('canplay', enablestart, false);

        function startVideo() {
            vid.play(); //videoスタート
            ctrack.start(vid); //トラッキング開始
            drawLoop();
        }
        var flg = 0;

        function drawLoop() {
            requestAnimationFrame(drawLoop); //ずっと同様の処理をおこなう
            overlayCC.clearRect(0, 0, 400, 300);

            if (ctrack.getCurrentPosition()) {
                var data = ctrack.getCurrentPosition(); //顔の各パーツの座標を取得
                //顔領域の矩形座標を求める
                var max = [0, 0];
                var min = [100000, 100000];
                for (var i = 0; i < data.length; i++) {
                    max[0] = max[0] < data[i][0] ? data[i][0] : max[0];
                    max[1] = max[1] < data[i][1] ? data[i][1] : max[1];
                    min[0] = min[0] > data[i][0] ? data[i][0] : min[0];
                    min[1] = min[1] > data[i][1] ? data[i][1] : min[1];
                }
                var width = vid.videoWidth;
                var height = vid.videoHeight;
                if (width == 0 || height == 0) {
                    return;
                }

                buffer.width = overlay.width = width;
                buffer.height = overlay.height = height;
                bufferContext.drawImage(vid, 0, 0);

                var src = bufferContext.getImageData(0, 0, width, height); // カメラ画像
                var dest = bufferContext.createImageData(buffer.width, buffer.height); // 出力画像

                //ちょっとモザイク領域を広めにする処理
                min[1] = min[1] - (max[1] - min[1]) / 2;
                ex = (max[0] - min[0]) / 4;
                min[0] = min[0] - ex;
                max[0] = max[0] + ex;

                //モザイク化。シンプルに各領域の左上の色と同じ色に書き換える
                var size = 25;
                for (var y = Math.floor(min[1]); y < Math.floor(max[1]); y++) {
                    for (var x = Math.floor(min[0]); x < Math.floor(max[0]); x++) {
                        for (var c = 0; c < 3; c++) {
                            var i = ((y - y % size) * width + (x - x % size)) * 4 + c;
                            var j = (y * width + x) * 4 + c;
                            dest.data[j] = src.data[i];
                        }
                        dest.data[(y * width + x) * 4 + 3] = 255; // Alpha値の設定
                    }
                }

                //目周辺の座標で矩形を作る
                max = [0, 0];
                min = [100000, 100000];
                for (var i = 0; i < 2; i++) {
                    max[0] = max[0] < data[i][0] ? data[i][0] : max[0];
                    max[1] = max[1] < data[i][1] ? data[i][1] : max[1];
                    min[0] = min[0] > data[i][0] ? data[i][0] : min[0];
                    min[1] = min[1] > data[i][1] ? data[i][1] : min[1];
                }
                for (var i = 13; i < 32; i++) {
                    max[0] = max[0] < data[i][0] ? data[i][0] : max[0];
                    max[1] = max[1] < data[i][1] ? data[i][1] : max[1];
                    min[0] = min[0] > data[i][0] ? data[i][0] : min[0];
                    min[1] = min[1] > data[i][1] ? data[i][1] : min[1];
                }
                for (var y = Math.floor(min[1]); y < Math.floor(max[1]); y++) {
                    for (var x = Math.floor(min[0]); x < Math.floor(max[0]); x++) {
                        for (var c = 0; c < 3; c += 1) { //RGBを0に
                            var i = (y * width + x) * 4 + c;
                            dest.data[i] = 0;
                        }
                        dest.data[(y * width + x) * 4 + 3] = 255; // Alpha値の設定
                    }
                }

                overlayCC.putImageData(dest, 0, 0); //canvasに変換後の画像を書き出す
            }
        }
    </script>
</body>

</html>

なかなか楽しい

【Unity】 I◯◯Handlerインターフェースメソッドが動かない時のチェックリスト

どれもちょくちょくやらかして時間を無駄にした経験があるのでメモ

  • チェック1.メソッド名は正しいか
  • チェック2.対象のゲームオブジェクトにCollider(またはCollider 2D)はついているか
  • チェック3.シーンにEventSystemはあるか
  • チェック4.CameraにPhysics Raycaster(またはPhysics 2D Raycaster)はついているか

個人的にはチェック4が一番忘れがち。

【Unity5.3】JsonUtility使い方まとめ

JsonUtilityとは?

オブジェクトをJSON形式の文字列にしたり、JSON形式の文字列をオブジェクトにしたりできるJSONパーサ。今までMiniJSON、SimpleJSONなど外部ライブラリがあったが、Unity5.3で公式にJSONパーサが実装された。

変換メソッドについて

オブジェクト → JSONにしたい場合は、
string json = JsonUtility.ToJson(myObject);

JSON → オブジェクトにしたい場合は、
MyObject myObject = JsonUtility.FromJson(json);

サンプルコード

変換対象クラスのサンプル
using UnityEngine;
using System.Collections;

[System.Serializable]
class MyObject {
	public Vector3 vector;
	public int[] intArray;
	[SerializeField]
	private float privateFloat;

	public float PrivateFloat {
		get {return privateFloat;}
		set {privateFloat = value;}
	}
}
確認用コード(適当なゲームオブジェクトに貼り付ける)
using UnityEngine;
using System.Collections;

public class JSONTest : MonoBehaviour {

	void Start () {
		MyObject myObject = new MyObject();
		myObject.vector = new Vector3(1, 2, 3);
		myObject.intArray = new int[3];
		myObject.intArray[0] = 4;
		myObject.intArray[1] = 5;
		myObject.intArray[2] = 6;
		myObject.PrivateFloat = 1.23f; /* private変数は[SerializeField]がないと保存されない */

		/* オブジェクトからJSONへの変換 */
		string json = JsonUtility.ToJson(myObject);
		Debug.Log(json);

		/* JSONからオブジェクトへの変換(その1) */
		MyObject obj1 = JsonUtility.FromJson<MyObject>(json);
		Debug.Log(
			obj1.vector + ", [" +
			obj1.intArray[0] + ", " + obj1.intArray[1] + ", " + obj1.intArray[2] + "], " +
			obj1.PrivateFloat
		);

		/* JSONからオブジェクトへの変換(その2) */
		MyObject obj2 = new MyObject(); //初期化が必要
		JsonUtility.FromJsonOverwrite(json, obj2); //obj2が初期化されてないとエラーになる
		Debug.Log(
			obj2.vector + ", [" +
			obj2.intArray[0] + ", " + obj2.intArray[1] + ", " + obj2.intArray[2] + "], " +
			obj2.PrivateFloat
		);
	}

}

実行結果

きちんと変換できてます。
f:id:Phalusamil:20151210200117p:plain

その他メモ

変換対象クラスの4行目にある[System.Serializable]はあってもなくても動作する。が、あったほうが高速らしい。

JSONに変換される変数はpublic変数、または[SerializeField]がついている変数だけ。なのでprivate変数を変換したい時はサンプルにあるように[SerializeField]をつける。

JSON → オブジェクトにする方法としてFromJsonOverwriteメソッドもある。
公式では、すでにインスタンス化されてるオブジェクトに入れたい時はこっちを使うと書いてあったが、FromJsonメソッドでも正しく動作した。
もしかしたら、メモリ効率や速度などに利点があるのかもしれないので、一応FromJsonOverwriteメソッドを使ったほうがいいかもしれない。

参考文献

docs.google.com

【スポンサーリンク】