House Cleaning: KittenVR gets a mini-refactor

Blog Posts ,Programming ,Unity ,Virtual Reality
May 28, 2015

KittenVR is a project I’m using to practice virtual reality development and document my experiences with different stages of the development process. You can find everything about the project at KittenVR.com.

In my last post, I mentioned that I had finally gotten the kitten colliders working with the OVR Player Controller, but after all of the trial and error, the code was kind of messily hacked together and I wanted to take some time to separate out the logic based on whether or not there was an Oculus Rift attached so that I could test various components in VR and non-VR modes. I decided to spend an hour or so yesterday reorganizing the code in my Player Controller script so that I could use the two different collision detection methods depending on the character controller type.

Right now, I’m testing components of the game play in two different scenes, one for VR and one for normal game play, but I will eventually write in the logic to detect whether the headset is enabled and load in the appropriate controllers accordingly to keep the overall size of the project down.

My current Player Controller setup looks like this:

VR: 

  • PlayerController.cs is attached to an anchor object under the CenterEyeAnchor object. This object has an ‘anchor’ tag.
  • A raycast is sent out in the forward direction from the anchor 10 units

Non-VR:

  • PlayerController.cs is attached to the FPSController object directly. This object has a ‘NoVRPlayer’ tag.
  • A raycast is sent out using Camera.main.ScreenPointToRay(Input.mousePosition)

The Update()function first checks if the mouse button has been clicked. When the mouse button is clicked, it tests to see if the click came from the VR controller or the FPS controller, and raycasts accordingly. The method then calls a helper function, KittenClick(RaycastHit _hit) , to handle the collision. I think that there’s still probably a way to optimize this by doing one check on whether the game is in VR mode, but for now, checking on the mouse click doesn’t seem to be a bottleneck so I’ll come back to this in the future once the rest of the game is polished up a bit more and I better understand what else will need to be handled differently.

Now, one thing to note is that the way I currently handle the raycasting means I have slightly different game play for VR and non-VR modes. With non-VR, the player clicks directly on the kittens to collect them, but in VR mode, they generally just need to be within a certain distance and focusing on the kitten before mouse clicking, as the Input.mousePosition location isn’t used for detecting VR collisions. For now, I’m okay with keeping it that way, since it doesn’t feel unnatural and prevents weird offsets from happening with the mouse & world position coordinates in VR. I actually prefer the VR method, because once I begin integrating in new input methods, this won’t really end up mattering anyway.

Other changes in this mini-refactor:

  1. The non-VR player now has a fall position detection back on it as part of the ResetOnFall() function, so players can’t jump off the edge of the world and fall forever. When the Y-position is checked on Update() and is found to be less than 0 (the board floats at a Y-position of 1) then the level resets.
  2. The GUI function is temporarily removed from both versions of the game until I get them sorted out nicely for each
  3. The KittenClick() function has been written to detect what object was clicked on and whether it’s relevant to the game.

The full code of the PlayerController.cs script:

using UnityEngine;
using UnityEngine.UI;
using System.Collections;

public class PlayerController : MonoBehaviour
{

	public int kittens_collected = 0;
	public bool isVisible = false;
	private float timer = 0.0f;
	private string message;

	/**
	 * Use this for initialization
	 **/
	void Start ()
	{

	}
	
	/**
	 * Update is called once per frame.
	 * Check for various game play components. 
	 **/
	void Update ()
	{
			MouseClickListener ();
	}

	/**
	 * A function to check if the player has clicked on a kitten.
	 * Eventually update this with a different, more interactive input type.
	 **/
	void MouseClickListener ()
	{
		if (Input.GetMouseButtonDown (0)) {
			RaycastHit hit;
			/** 
			 * Logic for OVR Player with floating trigger box
			 * If the tag is not 'NoVRPlayer', the trigger is from
			 * the 'anchor' object on an OVR Controller. If the tag is 'NoVRPlayer',
			 * we are using a typical FPS controller and need to do a little extra.
			 **/ 
			if (this.tag != "NoVRPlayer") {
				if (Physics.Raycast (transform.position, transform.forward, out hit, 10)) {
					KittenClick (hit);
				}
			} else {
				Ray _ray = Camera.main.ScreenPointToRay(Input.mousePosition);
				if(Physics.Raycast(_ray, out hit))
				{
					KittenClick(hit);
				}
				ResetOnFall();
			}
			GUIFunction ();
		}
	}
	/**
	 * When we detect a hit on our input method, call this method.
	 * This will be the same regardless of input device; the calling
	 * check for raycast collisions will vary depending on the character
	 * player type.
	 **/
	void KittenClick (RaycastHit hit)
	{
		Collider _hit = hit.collider;
		GameObject _VICTIM = hit.collider.gameObject;
		if (_VICTIM.tag.Equals ("kitten")) {
			kittens_collected++;
			Destroy (hit.transform.transform.gameObject);
			Debug.Log ("You've collected " + kittens_collected + " kittens!");
		} else if (_VICTIM.tag.Equals ("leader")) {
			Debug.Log ("Hit Leader");
			if (kittens_collected == 0) {
				message = "Help! My kittens have gone missing! Can you help collect all 7 of them?";
				isVisible = true;
			} else if (kittens_collected == 1) {
				message = "You've found one kitten so far! Just 6 more to go!";
				isVisible = true;
			} else if (kittens_collected < 7) { 				message = "You've found " + kittens_collected + " kittens so far! Just " + (7 - kittens_collected) + " left to go!"; 				isVisible = true; 			} else { 				message = "You've found them all! Thank you so meouch!"; 				isVisible = true; 				System.Threading.Thread.Sleep (2000); 				Application.LoadLevel (Application.loadedLevel); 			} 		} 	} 	/** 	 * Display GUI when Leader Kitten is clicked 	 **/ 	void GUIFunction () 	{ 		if (isVisible) { 			timer++; 		} 		 		if (timer > 300f) {
			isVisible = false;
			timer = 0f;
		}
		Display ();
	}
	/**
	 * Helper method for GUI Function 
	 **/
	void Display ()
	{
		if (isVisible) {

		}
	}
	/**
	 * Check if character has falled off level (FPSController only currently) 
	 **/
	void ResetOnFall()
	{
		if (this.transform.position.y < 0) {
			Application.LoadLevel (Application.loadedLevel);
		}
	}
}

As always, you can find the latest and greatest code up on GitHub as well as make requests for what you’d like to see added to KittenVR. The website can get you caught up if you’re just starting to follow the project!

Related Posts

Leave a Reply