Cross Road

Unity、VR、MR、ARを中心とした技術ブログ

Oculus RiftとPSmoveでシューティングゲームを作るための基本準備

====
2018/4/21
ブログ整理の観点で記事見直し、統合をしました
====

今回作りたいゲームはガンシューティングに近いものなので、銃型デバイスとしてPSMoveに注目しています。

PSMoveはカメラがないとポジション取得ができないので、今回はPSMoveの傾きをコントローラとして使用し、先端から弾が出るところまでを作ります。

====

目次

1. PSMoveの傾きをつかってPlayerをコントロールする

2. PSMoveの先に照準を作る

3. PSMoveの照準から弾を発射する

4. 終わりに

====

1. PSMoveの傾きをつかってPlayerをコントロールする

PSMoveをジョイスティックのように地面と垂直に持ち、前後左右の傾きと同じ向きにPlayerが進むようにしました。

実際はOVRPlayerController.csにそれぞれ以下のように追加しました。

//enumで移動方向を追加
        public enum MoveDirection{MoveForward,MoveLeft,MoveRight,MoveBack,MoveJump,MoveDown}

//グローバル宣言で移動方向を追加
	private	bool moveForward = false;
	private	bool moveLeft  	 = false;
	private	bool moveRight   = false;
	private	bool moveBack    = false;

	private	bool moveJump = false;
	private	bool moveDown = false;

//途中の関数内で追加
public virtual void UpdateMovement(){
		bool moveForward = false;
		bool moveLeft  	 = false;
		bool moveRight   = false;
		bool moveBack    = false;
		bool moveJump = false;  //moveJumpとmoveDownは自分で追加
		bool moveDown = false;

 //元のGetKeyの下に追加。これを追加しないと、キーボードで一度でもWASDか上下左右の矢印を押すと、ずっとtrueで進み続けてしまう。また、moveJumpやmoveDownが効かなくなります。
             
		// WASD
		if (Input.GetKeyUp(KeyCode.W)) moveForward = false;
		if (Input.GetKeyUp(KeyCode.A)) moveLeft	 = false;
		if (Input.GetKeyUp(KeyCode.S)) moveBack 	 = false; 
		if (Input.GetKeyUp(KeyCode.D)) moveRight 	 = false; 
		// Arrow keys
		if (Input.GetKeyUp(KeyCode.UpArrow))    moveForward = false;
		if (Input.GetKeyUp(KeyCode.LeftArrow))  moveLeft 	  = false;
		if (Input.GetKeyUp(KeyCode.DownArrow))  moveBack 	  = false; 
		if (Input.GetKeyUp(KeyCode.RightArrow)) moveRight   = false; 
		if(Input.GetKeyUp(KeyCode.R)) moveJump = false;
		if((Input.GetKeyUp (KeyCode.V)) && !Controller.isGrounded) moveDown = true;
		//

・・・
}

//新規追加
public void setPlayerMoveDirection(bool flag, int directionEnum){
		
	switch(directionEnum){
	    case((int)MoveDirection.MoveForward):
		moveForward = flag;
		   break;
           case((int)MoveDirection.MoveLeft):
		moveLeft = flag;
	           break;
	  case((int)MoveDirection.MoveRight):
		moveRight = flag;
		   break;
	  case((int)MoveDirection.MoveBack):
		moveBack = flag;
		  break;
	  case((int)MoveDirection.MoveJump):
		moveJump = flag;
		  break;
	  case((int)MoveDirection.MoveDown):
		moveDown = flag;
		  break;		
	}			
}	

次にPSMoveを動かすスクリプト(ベースはUnimoveTest.cs)のUpdate関数に以下を追加しました。

//Forward
  if( move.Magnetometer.y >130 )   ovrPlayerController.setPlayerMoveDirection(true, 0);
  if( move.Magnetometer.y < 130 )  ovrPlayerController.setPlayerMoveDirection(false,0);
			
//Left
  if( move.Magnetometer.x >200 )   ovrPlayerController.setPlayerMoveDirection(true, 1);
  if( move.Magnetometer.x < 200 )  ovrPlayerController.setPlayerMoveDirection(false,1);
			
//Right
  if( move.Magnetometer.y >200 )   ovrPlayerController.setPlayerMoveDirection(true, 2);
  if( (move.Magnetometer.y < 200)&&(move.Magnetometer.y > 150) )  ovrPlayerController.setPlayerMoveDirection(false,2);
			
//Back
  if( move.Magnetometer.z >100 )   ovrPlayerController.setPlayerMoveDirection(true, 3);
  if( move.Magnetometer.y < 100 )  ovrPlayerController.setPlayerMoveDirection(false,3);			

まだ調整は必要ですが、各数値は実際にセンサの値を調べて、今回のケースで使えそうと判断したものです。

このスクリプトもOVRPlayerControllerにアタッチして、プロジェクトをコンパイルして起動すると、PSMoveを傾けることでOVRPlayerControllerが動きます。

移動を止めるときは、PSMoveを地面と垂直に持ち直して下さい。

2. PSMoveの先に照準を作る

2-1. Unimoveのパッケージをimportし、Code/UnimoveTest.csを開く

Unimoveのパッケージ入手先

Unimove - Copenhagen Game Collective - an experimental games collective

2-2. UnimoveTest.csにFixedUpdate()を追加し、以下を宣言

private Vector3 rawAccelTargetMarker = Vector3.down;

2-3.FixedUpdate()に以下を入力

   foreach (UniMoveController move in moves) {
      if (Input.GetMouseButton(0)) {
         rawAccelTargetMarker = new Vector3(moves[0].Acceleration.x,moves[0].Acceleration.y,0);
	this.gameObject.transform.position = rawAccelTargetMarker*Time.fixedTime;
      }
   }

このスクリプトをSphereとか、適当なオブジェクトにアタッチしてから実行すると、マウスを左クリックしているときのPSMoveの動きによってオブジェクトが動きます。Sceneビューをxy軸表示にするとわかりやすいです。

ただ、現状はx軸の動きがいまいちです。ぶれがあって常に動いてしまいます。


3. PSMoveの照準から弾を発射する

Oculusをかぶったときの視線中心から弾を発射させます。以下の処理を追加します。

3-1.弾の生成

//OVRPlayerController.csまたはPSMoveTest.cs (OVRPlayerControllerにアタッチするスクリプトならばなんでもよい

public GameObject bulletObject;

void Update(){
   ・・・
      if(Input.GetKeyDown(KeyCode.Z)){
	  Instantiate(this.bulletObject, this.transform.position,Quaternion.identity);	
      }
 ・・・
}

このスクリプトを、Hierarchy ViewのOVRPlayerControllerなどにアタッチし、Inspector ViewのBullet Objectに弾のPrefabをアタッチします。

ここでは、弾のPrefabとして、cube ObjectをPrefab化したものを使いました。
cube ObjectにはRigidBodyを取り付け、Use Gravityのチェックは外しておきます。

3-2.弾の動き

次にcube Objectにアタッチするスクリプトです。

//bullet.cs

//弾が飛ぶ方向の指定に必要
    private Camera leftCamera;
	private Vector3 destinationPosition;
	private Vector3 distance=Vector3.zero;
	private Vector3 targetMarkerPosition;

//今のOculusで視線中心になるように調整した値。
	private int targetMarkerPositionX = (int) (Screen.width * 0.273);
	private int targetMarkerPositionY = (int)(Screen.height * 0.575);

//生成時に一度だけ呼ばれる。ここでOculus Riftのカメラ方向(装着者の視線方向)からcube Objectを発射。
void Start () {
	leftCamera = GameObject.Find ("OVRPlayerController/OVRCameraController/CameraLeft").GetComponentInChildren<Camera>();
	targetMarkerPosition = new Vector3(targetMarkerPositionX,targetMarkerPositionY,0);
	shootBullet();
	
}


public void shootBullet(){
	Ray ray;
	RaycastHit rayCastHit;
	ray = leftCamera.ScreenPointToRay(targetMarkerPosition);  /rayを飛ばす開始点を決定
	if(Physics.Raycast(ray, out rayCastHit, 10000)){  // 距離10000までの範囲にobjectがあればif文は真
		destinationPosition = new Vector3(rayCastHit.point.x,rayCastHit.point.y,rayCastHit.point.z); //rayで見つけたobjectの位置を決定
		distance = Vector3.Normalize(destinationPosition - transform.position);	//現在位置とオブジェクトの位置までの方向を算出
	}
   this.rigidbody.velocity = new Vector3(distance.x,distance.y,distance.z)*50;  //指定方向に向かって等速度を付加
}

//画面外に消えたcube Objectを破棄.これがないといつかメモリを食いつぶします。
void OnBecameInvisible(){
	Destroy(this.gameObject);
}

3-3. 動作確認

これらを実装してから実行すると、キーボードのZボタンを押すたびにcube Objectが前方に向かって発射します。

f:id:Takyu:20131108205311p:plain

Scene Viewの英文はPSMoveが接続されてないときに表示させてるメッセージで、今回とは無関係です。


f:id:Takyu:20131108205358p:plain

Zキーを押して弾を飛ばします。照準がずれてますがOculusで見ると立体視で中心に見えます。


ところで最近ダンガンロンパにはまっており、今回のガンシューティングゲームの動機だったりもします。

なので、ダンガンロンパをヒントにして、クイズとガンシューティングの融合を考えています。

例えばこんな感じで、雑音に向かって弾(コトダマ)を飛ばすと、

f:id:Takyu:20131108205813p:plain

コトダマがノイズを打ち砕き、

f:id:Takyu:20131108210005p:plain

どこかに飛んでいくようなイメージです。

f:id:Takyu:20131108210057p:plain

4.終わりに

とりあえず最低限の原理試作はできたので、後は時間見て作っていくつもりです。

開催時期未定ですが、次回のOcufesで出せたらいいなと思ってます。