「Unity」カテゴリーアーカイブ

VRに使ったスクリプト(Unity側)

(関連記事)

  • VRについての記事はこちら
  • SerialHandlerについての記事はこちら

上記リンク先の記事SerialHandlerを使ったUnityの制御です.

 

using UnityEngine;
using System.Collections;
using System;//use for time
using UnityEngine.UI;//use for UI


//アセットSuperSplinesのメニューを追加
	[AddComponentMenu("SuperSplines/Animation/Regular Animator")]


	public class mySpline2 : MonoBehaviour{
		public Spline spline;
		public WrapMode wrapMode = WrapMode.Clamp;//SuperSplineの初期モード実際はLOOPで使用
		public float speed = 0f;//初期速度ゼロでないと起動したら動いてしまう
		public float offSet = 0f;//オフセット値/スタート位置を変えたい場合など.

		public float passedTime = 0f;//何に使ったかな?
	float myspeed = 0f;//何に使ったかな?

		public Text myText; //UIで使う用
		public SerialHandler serialHandler;//serialHandlerスクリプトがアサインされたゲームオブジェクトをアサインする用.

		//for arduino
		public bool spBool = false;//停止用

		//時間計算用,試行錯誤してるので要らない変数あるかも
		private int starttime;
		private int now;
		private int duration;
		private float truespeed;
		private float myPos = 0f;
		private float mytime = 0f;
		float lerpspeed =0f;
	float firstSpeed = 0f;

	float mymillsec= 0f;
	string kmspeed;



	void Start(){

			myText.text = "0km/h";//GUI表示用に0km/hいれる
		starttime = DateTime.Now.Hour * 60 *60 * 1000 + DateTime.Now.Minute * 60 * 1000 + DateTime.Now.Second * 1000 + DateTime.Now.Millisecond;//起動した時間をstarttimeにミリセカンドで入れる.ペダル停止検知用
		//信号を受信したときに、そのメッセージの処理を行う
		serialHandler.OnDataReceived += OnDataReceived;
		}



		void Update( ) {
			mytime += Time.deltaTime * speed;//いまの時間とスピードかけてspline動かす指標にする
		if (spBool) {//ペダル動いてるとき
		myPos += Mathf.LerpUnclamped (speed ,firstSpeed, mytime);//スプライン上の位置myPosを時間でslerpする
		}
		if (Input.GetKeyDown ("s")) {//停止用
			myPos += Mathf.LerpUnclamped (0f, 0f, mytime);
				spBool = false;
			myspeed = 0f;
		}

			if (Input.GetKeyDown ("r")) {//リセット用
				myPos = 0;
				spBool = true;
				myspeed = 0f;
			speed = 0f;
			}
// 下3行がsplineを動かすやつ
			float clampedParam = WrapValue (myPos + offSet, 0f, 1f, wrapMode);
			transform.position = spline.GetPositionOnSpline (clampedParam);
			transform.rotation = spline.GetOrientationOnSpline (clampedParam);

////
		}



	void OnDataReceived(string message){//arduinoから文字列を受信したら
		
		firstSpeed = speed;初期速度をさっきの速度に入れ替え(2回目以降用)
		mymillsec = float.Parse (message);//車輪が1回転した間隔を受信してるので,それをフロート値に変換
			myspeed = (3600000f / mymillsec) * 0.000628f;//速度km/hを計算
		if (myspeed < 0.9) {//速度が1km未満ならゼロに
			myspeed = 0f;
		}

			kmspeed = myspeed.ToString ("N2");//小数点2桁でカット
		speed = myspeed * 0.00005f;//乗数で速度感変わります
		//}
	
		myText.text = kmspeed + "km/h";//速度をUIに表示
		//以下使ってない(タブでデータ区切り用)
		var data = message.Split(
			new string[]{"\t"}, System.StringSplitOptions.None);
		if (data.Length < 2) return;

		try {
		} catch (System.Exception e) {
			Debug.LogWarning(e.Message);
		}

	}



	public void stopper(){ //使っていません.ペダルの回転間隔を調べて回っていないのならゼロにしようと思いましたが,惰性で進むことを優先してこちらはカット(ただしBoolだけ使ってる)
		now = DateTime.Now.Hour * 60 *60 * 1000 + DateTime.Now.Minute * 60 * 1000 + DateTime.Now.Second * 1000 + DateTime.Now.Millisecond;
		duration = now - starttime;

		if (duration > 3000) {
			spBool = false;
			kmspeed = "0";
			myText.text = kmspeed + "km/h";
		} else {
			spBool = true;
		}
		//Debug.Log (spBool);
		starttime = DateTime.Now.Hour * 60 *60 * 1000 + DateTime.Now.Minute * 60 * 1000 + DateTime.Now.Second * 1000 + DateTime.Now.Millisecond;
	}



	public void testBtn(){//センサを使っていないときに,GUIのボタンでテスト出来るよう

		int myrand = UnityEngine.Random.Range (150, 1800);
		string randStr = myrand.ToString ();
		OnDataReceived (randStr);


	}



//splineのモード設定用みたい
		private float WrapValue( float v, float start, float end, WrapMode wMode )
		{
			switch( wMode )
			{
			case WrapMode.Clamp:
			case WrapMode.ClampForever:
				return Mathf.Clamp( v, start, end );
			case WrapMode.Default:
			case WrapMode.Loop:
				return Mathf.Repeat( v, end - start ) + start;
			case WrapMode.PingPong:
				return Mathf.PingPong( v, end - start ) + start;
			default:
				return v;
			}
		}

	}

です.

VRアプリ開発メモ

実質2日間でOculusRift(DK2)対応のコンテンツを作っていました.

このレベルなら文系の私でもモデリング含めて2日(徹夜なし)で作れるので,卒研とかはこんなレベルじゃダメですよ〜

(ていうか,そもそも組み立てただけで研究でもなんでもない,,,)

そもそもOculusって何かってっつーと,PSVRのようなものです.

 

Oculus Riftはコケられない、だから値段も高くなる|WIRED.jp

製品版「Oculus Rift」の予約受付がはじまり、その価格の高さに驚いた人も多いだろう。しかしソニーやHTCといった手ごわいライヴァル企業と競わなければならないOculusにとって、いちばんの問題はその価格ではないようだ。

LINK: wired.jp/2016/01/11/oculus-rift-price/

 

このOculus社がDK1を出したのはずいぶん前です.当時は衝撃でしたね.私はジェットコースターで酔ってOculusは触らないことにしました.

でもこのあたりの衝撃が現在のVRブームを作ったとも言えます.

さて,このVRコンテンツ,作るのは超簡単です.

どのくらい簡単かというと,ドライバなどインストールしてれば,見るだけなら3分くらいで作れます.

研究室に前の住人が残しておいたエアロバイクがあったので,ちょいと分解.データが取れそうでした.

そこで,,,,

Arduinoでちょいちょいといじって,

bike1

こんなものができました.

八事の空を空中散歩♪

オーキャンで高校生が絶叫していました〜(それを見てる方が楽しい)

bike2

センサのデータ変換にはAruinoを使っています.

 

今回書いたプログラムは全部で200行以下です.

そのうち,エアロバイクのセンサをArduinoからシリアルで送るプログラム

unsigned long mytime = 0;
unsigned long hittime;
unsigned long duration;
boolean sensorBool = true;
int input_value;

void setup(){
Serial.begin(9600);
mytime = millis();
}


void loop(){
 input_value = analogRead(3);
if(sensorBool == true){
if(input_value < 200){
 sensorBool = false;
hittime = millis();
duration = hittime - mytime;
mytime = hittime;
send();
}
}else if(!sensorBool){
  if(input_value > 500){
    sensorBool = true;
  }
}
  
}

void send(){
Serial.println(duration);

}

これをUnityに送って,コースのスプラインを走るだけです.

うん,簡単ですね.

Unity側のプログラムは別記します

なお,数値変化をやさしくするために,

Mathf.LerpUnclamped

を使っています.

Mathf.Lerpでもいいのですが,これは0から1でしか動作しません.

Splineは0(始点)から1(終点)で動くので,1を入れると1周します.Mathf.Lerpですと,1周しかしません.

Mathf.LerpUnclampedにすると,1を越えても数値補完してくれるので2周3周したいお客さんがいるときにも対応できます.

もちろん,適宜データのリセットは大事ですが,,

Pokémon GO(ポケモンGO)はUnityでつくってるとかなんとか

どうもネイティブっぽくないらしい.

逆にUnityにバグがあると,かえってUnityが問題になる予感.

ちょいちょいバグの入った(Unityの)バージョンがリリースされっからな,,知らずにビルドしちゃったりとかして,,,

 

『Pokémon GO』公式サイト

『Pokémon GO』は、位置情報を活用することにより、現実世界そのものを舞台として、ポケモンを捕まえたり、交換したり、バトルしたりするといった体験をすることのできるゲーム! 『Pokémon GO』は、2016年サービス開始予定!!

LINK: www.pokemon.co.jp/ex/PokemonGO/

 

位置ゲーで人が能動的に動く様子を見ています.つまり仕事なんです.わりと本当です.

ArduinoのデータをUnityに送る

Uniduino(Arduinoでなく)ではシリアル通信のデータ=Serial.println(hogehoge);したもの=が見えない.

サイクルコンピュータのようなものを作ろうとしたのですが,UnityのFPSを越える間隔のデータはUnityでは処理しきれないため,Arduino側でデータを処理し,Unityのupdateより遅い速度でUnityへ送る必要がある.

そこで,シリアル通信を送るサンプルを探していたのですが,以下リンクより見つけることができました.

 

UnityとArduinoをシリアル通信 – Qiita

はじめに (読み飛ばしてください 笑) ちょっとした趣味でデータグローブを作ってUnityで遊べたらなと思いました。 ハードウェアの部分はArduinoで制御すればいいんですがUnityとArduino間の通信とどうしたもんじゃと。 調べたらUniduinoという大変素晴らしいAssetがあるみたいですが、使うのに$30するようです。 ふえぇ…学生には高いよぉ… ということでいろんなネットの情報を参考にUniduinoを使わずにUnityとArduin…” name=”description

LINK: qiita.com/yjiro0403/items/54e9518b5624c0030531

 

このプログラムのうち,SerialHandlerをそのまま使わせていただき,別の呼び出すスクリプトだけを変更します.

以下は,シリアルデータを読み出すスクリプトです.別途,SerialHandlerが必要です.

using UnityEngine;
using System.Collections;


	public class mySpline2 : MonoBehaviour{

	public SerialHandler serialHandler;


		void Start(){
		serialHandler.OnDataReceived += OnDataReceived;
		}



		void Update(){

		}



	void OnDataReceived(string message){
		Debug.Log(message);

		var data = message.Split(
			new string[]{"\t"}, System.StringSplitOptions.None);
		if (data.Length < 2) return;

		try {
		} catch (System.Exception e) {
			Debug.LogWarning(e.Message);
		}


	}
	}

 

7行目でInspectorにボックスが出ますので,serialHandlerがアサインされたオブジェクトを入れてください.

11行目は必須です.これがないとエラーがでます.

23行目にあるようにmessageでString形式でデータが戻ってきます.

最終的にはこれをintParseして数値として使用する予定です.

Uniduinoが動かないとき

UnityでArduinoと通信できる魔法のプラグインUniduino(おそらく開発停止)をインポートして動かないとき,

PlayerSettings.NET 2.0 subset.NET 2.0に変更する.これ大事.Uniduinoは以下リンクより.

 

Uniduino

LINK: www.uniduino.com/

 

 

なお,UnityでArduinoを扱えるシステムの最新版は,

 

ARDUnity Home Page

Innovative IoT base Physical Computing DIY Solution ‘ARDUnity’ Official Web Site

LINK: www.ardunity.com/

 

があり,しかも基本的なDOUT.INやAINなどは無料で使用可能です.

外部センサの入力間隔を速度に変更

※現在はこの方法は使用していません.Arduino内で1周にかかるmsを検出し,シリアル通信でその数値をUnityに送信しています(現在の方法はこちら

自転車の速度計のように,円周nのタイヤが1周するのに何秒かかったかで,時速を計測するスクリプトです.

余談ですが,これはゲーム内の速度ではなく,実際に外部のデバイスの速度を計算するスクリプトです.

なお,Unity内の速度は1ユニット1mで計算することが多いです.

 

using UnityEngine;
using System;//時間計測するのに必要

public class mySpline : MonoBehaviour
{

	//時間計測に必要な変数
	private int starttime; //最初の初期値用
	private int now; //現在の時間を格納する用
	private int duration; //経過時間を格納する用

	void Start(){

		starttime = DateTime.Now.Hour * 60 *60 * 1000 + DateTime.Now.Minute * 60 * 1000 + DateTime.Now.Second * 1000 + DateTime.Now.Millisecond;
		//ゲームスタート時の時刻をミリセカンドに変換してstarttimeに格納します
	}


	public void cycle1(){ //1周ごとにセンサーなどでこの関数を呼びます.publicにしてあるのは,テスト用にGUIボタンで呼べるようにしてる
		now = DateTime.Now.Hour * 60 *60 * 1000 + DateTime.Now.Minute * 60 * 1000 + DateTime.Now.Second * 1000 + DateTime.Now.Millisecond; //この関数が呼ばれた時の時間をミリセカンドにして格納
		duration = now - starttime; //経過時間を算出してdurationに格納
		float myspeed = (3600000f / duration) * 0.00062f; //円周0.00062km(つまり62cm)と経過時間で時速を計算し,ローカル変数myspeedに格納する
		string kmspeed = myspeed.ToString ("N2"); //myspeedを文字列に変換し,ただし小数点2桁までで切り捨てて,文字用ローカル変数kmspeedに格納.文字列に変更しているのはGUIにすぐいれられるようにするため
			Debug.Log (kmspeed + "km/h");//コンソール画面に速度と単位Km/hを追加して表示
		starttime = DateTime.Now.Hour * 60 *60 * 1000 + DateTime.Now.Minute * 60 * 1000 + DateTime.Now.Second * 1000 + DateTime.Now.Millisecond;//次の計測開始用にstarttimeの時間を現在時間に更新

	}

}

speedscreen

ところで,Unityの計算間隔を超えた場合ってどうなるんでしょう?

敵と味方を見分けたり,敵のランクを区分ける「タグ」について

FPSなどのゲームをプレイしていると,味方を撃っても味方が死なないことがあります.つまりプレーヤーである兵士は,クーデター待遇改善要求はできない仕様になっています.ゲームの世界にまで社畜が浸透しています.

さて,この味方を撃っても影響が出ない件,Unityではこれをタグというシステムで実装します.

基本的なタグ(Player=自分,GameController=自分に影響を与えるもの)は用意されていますが,自作のタグを使用することもできます.

このタグ名が一致する,あるいは味方のタグ以外の場合において,ポイントを加算するということができます.

では実装してみましょう.

  1. まずタグを追加します.Sphereを選んで(タグを追加するだけなら,選ばなくてもいいのですが),,tag01
  2. InspectorのTagをプルダウンし,Add Tag…を選択します
  3. tag02
  4. +ボタンをクリックし,タグ名を追加します.今回はenemy1,2,3を追加しましたtag03
  5. タグを設定したいオブジェクト(敵)を選び,Tagをプルダウンし,enemy1やenemy2,3のどれかを選択します.他の敵オブジェクトにも同じ処理をしてください.
  6. tag04
  7. つづいて,スクリプトです.新規C#スクリプトを作成し,スクリプト名はhitcheckにし,全て下記に入れ替えてください.
using UnityEngine;
using System.Collections;

//use for UI
using UnityEngine.UI;

public class hitcheck : MonoBehaviour {

	//variable
	int countInt = 0;  //計算用に整数を使用する変数宣言
	public Text myText; //UnityUI内の文字を扱うための変数宣言
	string tagName; //タグ名を格納するための変数宣言

	void Start(){
		myText.text = "000"; //初期値を数字ではなく文字列として設定する
	}


	void OnTriggerEnter(Collider mycollider) { //トリガーに何か入ったらそのコライダを変数「mycollider」に格納する

		tagName = mycollider.tag; //mycolliderのタグ名をtagNameに入れる
		if (tagName == "enemy1") { //上でtagNameに入れたタグがenemy1と一致するなら
			countInt = countInt + 1; //countintに1を足す
		}else if (tagName == "enemy2") { //上でtagNameに入れたタグがenemy2と一致するなら
			countInt = countInt + 10; //countintに10を足す
		}else if (tagName == "enemy3") {
			countInt = countInt + 100;
		}



		Destroy(mycollider.gameObject); //mycolliderのゲームオブジェクトを消去する


		//sound

		//effect

		//add point
		//countInt++;
		//myText.text = countInt.ToString ();
	   myText.text = string.Format("{0:D3}", countInt);
	}

}

これでタグ(つまり当たった物)に応じて加算される点数を変えることができます.スクリプトの例はenemy1なら1点,enemy2なら10点,enemy3なら100点です.りんごにenemy1のタグを,みかんにenemy2のタグを指定すれば,りんごは1点,みかんは10点得ることができます.

これを応用すれば,味方に撃たれてもHP減らない!ってのができます.このタグの設定をプログラムを使って動的にで変更すれば,謀反も可能。下克上ですね.(違う)

プログラムのif文の中を変更すれば,同じタグの敵だけ消すなどということもできます.

Unity勉強会やるかも

やるかもしれません

たぶん,9月の前半です.

ゼミ,学年,学部不問.内容は入門の内容です.ゼミ落ちたかたも是非参加を.

私物のパソコンにUnityをインストールしてくることが参加の条件です.なおWindowsのバージョンによっては,日本語のユーザ名ではUnityがインストールできないことがあります.

フォルダ名やファイル名(特に業務用ソフトで使用する場合),ユーザ名は半角英数字で作成する習慣をつけましょう.

なお,ユーザ名を変更するといろいろ不都合が起きる場合もありますので,新規ユーザを作成することをおすすめします.

 

UGUI基本(4)

UGUIでボタンを作成する例

  1. 次のような画像を用意しますbutton1
  2. 画像を読み込み,Texture TypeはSpriteに,SpriteModeはMultipleにし,Applyをクリックし適用.
  3. その後SpriteEditorを開き,ドラッグして使用エリアを指定します.今回は真ん中で4分割指定しますbutton2
  4. 1枚の画像から4つのイメージを別々に使用できるようになりました.メモリの節約になるとか.button4
  5. メニューバー>GameObject>UI>Button を選択します
  6. 位置を調整し,幅と高さをSpriteのものを同じにします
  7. Source Imageには押される前のボタン画像を
  8. PressedImageには押された画像を指定しますbutton5
  9. 以下,動作例です