SimpleControls.cs – First Person Keyboard/Mouse Control

Posted by in Unity

This script is intended to replace all of the scripts attached to the standard first person controller (FPSInputController, CharacterMotor, both MouseLooks)

The first person controller in Unity’s standard assets isn’t bad for getting a shooter prototype done, but I needed a controller with simple controls for an audience that might not have much experience with gaming. Watching someone try to navigate with a mouse look and WASD movement control scheme for the first time is usually painful.

This controller is very simple. The vertical input axis (W, S, Up Arrow, Down Arrow by default) moves the player forward and backward. The horizontal input axis (A, D, Left Arrow, Right Arrow by default) rotates the player instead of the usual strafing. This allows a player to navigate around the environment without needing the mouse to change the direction in which the player is looking. The rotation speed increases over time so it doesn’t take forever to turn around.

The biggest problem I have with a mouse look is interacting with objects in a scene or GUI elements. This script alleviates that problem by freeing up the mouse to click anywhere on the screen. Additionally, clicking and dragging will move the camera around. When you begin to walk around again the camera straightens back out to the forward position.

Attach this script to the GameObject on which your CharacterController is attached. You’ll have to assign the CharacterController and cameraPivot in the inspector. I still need to add a Y-axis rotation constraint.

using UnityEngine;
using System.Collections;

public class SimpleControls : MonoBehaviour {
	public Transform cameraPivot;
	public CharacterController character;
	private Vector3 initialPosition;
	private Vector3 currentPosition = Vector3.zero;
	private float speed = 2.5f;
	private Vector2 rotationSpeed = new Vector2 (75, 50);
	private float currentRotation = 75f;
	private float rotationLimitX = 150f;
	private bool changingView = false;

	void Update ()
	{
		if (Mathf.Abs (Input.GetAxis ("Horizontal")) < 0.1f)
			currentRotation = rotationSpeed.x;
		else
			currentRotation += 1f;
		if (currentRotation > rotationLimitX) currentRotation = rotationLimitX;
		character.SimpleMove(character.transform.forward * speed * Input.GetAxis("Vertical"));
		transform.Rotate(0, currentRotation * Time.deltaTime * Input.GetAxis ("Horizontal"), 0, Space.World);
		if (Input.GetMouseButtonDown(0)) {
			changingView = true;
			initialPosition = Input.mousePosition;
		}
		if (Input.GetMouseButtonUp(0))
			changingView = false;
		if (changingView) {
			currentPosition.x = Mathf.Clamp ((Input.mousePosition.x - initialPosition.x) / 100, -3, 3);
			currentPosition.y = Mathf.Clamp ((Input.mousePosition.y - initialPosition.y) / 100, -2, 2);
			currentPosition.x *= rotationSpeed.x;
			currentPosition.y *= rotationSpeed.y;
			currentPosition *= Time.deltaTime;
			transform.Rotate (0, currentPosition.x, 0, Space.World);
			cameraPivot.Rotate (-currentPosition.y, 0, 0);
		}
		if (Mathf.Abs( Input.GetAxis("Vertical")) > 0.25f )
			cameraPivot.rotation = Quaternion.Slerp (cameraPivot.rotation, transform.rotation, Time.deltaTime);
	}
}


Share

Disclaimer

These posts contain my own opinions and work and do not necessarily reflect the opinions of my employer or any other organization with which I have been affiliated. Feel free to use and share the content of this post.
Read More

CollisionAlert.cs with an Example

Posted by in Unity

I created this class to attach to the player GameObject that contains a CharacterController. Its only purpose is to pass OnCollision and OnTrigger messages to any listeners. For more info about the events and delegates, check out Prime31′s tutorial on this subject. Actually, watch all of his tutorials!

There isn’t much happening at all here, but I use this method to detect when my player enters specifically named triggers in a scene.

using UnityEngine;
using System.Collections;

public enum CollisionAlertType
{
	enter,
	exit,
	stay
}
public class CollisionAlert : MonoBehaviour {

	public delegate void CollisionAlertEventHandler (string colName, CollisionAlertType alertType);
	public static event CollisionAlertEventHandler onCollisionAlert;

	void OnTriggerEnter (Collider other)
	{
		if (onCollisionAlert != null)
			onCollisionAlert(other.gameObject.name,CollisionAlertType.enter);
	}
	void OnTriggerExit (Collider other)
	{
		if (onCollisionAlert != null)
			onCollisionAlert (other.gameObject.name, CollisionAlertType.exit);
	}
	void OnTriggerStay (Collider other)
	{
		if (onCollisionAlert != null)
			onCollisionAlert (other.gameObject.name, CollisionAlertType.stay);
	}
	void OnCollisionEnter (Collision col)
	{
		if (onCollisionAlert != null)
			onCollisionAlert (col.gameObject.name, CollisionAlertType.enter);
	}
	void OnCollisionExit (Collision col)
	{
		if (onCollisionAlert != null)
			onCollisionAlert (col.gameObject.name, CollisionAlertType.exit);
	}
	void OnCollisionStay (Collision col)
	{
		if (onCollisionAlert != null)
			onCollisionAlert (col.gameObject.name, CollisionAlertType.stay);
	}
}

Here’s an example of waiting for my player to enter a trigger in my scene. You would need to have a GameObject named ‘ApproachTheBench’ with a collider or trigger attached for this to work.

using UnityEngine;
using System.Collections;

public class Example : MonoBehaviour {

	private bool waitingOnTriggerCollision = false;
	private CollisionAlertType alertType = CollisionAlertType.enter;
	private string goTriggerName = "";

	// Use this for initialization
	void Start ()
	{
		CollisionAlert.onCollisionAlert += onCollisionAlert;
		StartCoroutine(WaitForTriggerCollision());
	}

	IEnumerator WaitForTriggerCollision ()
	{
		goTriggerName = "ApproachTheBench";
		alertType = CollisionAlertType.enter;
		waitingOnTriggerCollision = true;
		//this stops the coroutine from ending until the trigger is entered
		while (waitingOnTriggerCollision) {
			yield return new WaitForEndOfFrame ();
		}
		Debug.Log("You entered the trigger!");
	}

	void onCollisionAlert (string colName, CollisionAlertType currentAlertType)
	{
		if (colName == goTriggerName && alertType == currentAlertType)
		{
			waitingOnTriggerCollision = false;
			goTriggerName = "none";
		}
	}
}


Share

Disclaimer

These posts contain my own opinions and work and do not necessarily reflect the opinions of my employer or any other organization with which I have been affiliated. Feel free to use and share the content of this post.
Read More

OnTouchOrClick.cs – Simple Input

Posted by in Unity

Nearly every project on which I work targets multiple platforms. My typical workflow involves working with an iOS build target and testing functionality whenever I can in the editor. This is crucial when I don’t have a device available to use the Unity Remote app or create a test build. Since the corresponding mouse code is most likely needed anyway, I typically combine both touch and click detection into one script.

The following code shows an implementation of testing whether or not a touch or click has hit something in your scene. Basically it shoots a ray from the camera out into the scene toward the spot on the screen where you touch or mouse is positioned. I’ve put in a preprocessor check to only use touch portion if you’re on a mobile build, otherwise the mouse click will be used. Either way, the same CheckHit function will be called with the appropriate ray passed.

Let’s assume either of the following conditions is true:

  • A camera exists in the scene and this script will be attached to it OR
  • The camera is tagged as ‘MainCamera’ and this script is attached to another GameObject

If our Raycast hits something with a collider or trigger on it, we will execute the code in our ‘if (Physics.Raycast… ‘ conditional statement. In this example I’m performing three different actions if we hit something.

The first thing we’ll do is a fire an event. This is a clean way to allow other objects to listen for these touches or clicks as needed. In this scenario, I’m just passing along the name of object with which our ray collided to any listeners of that event. The other script can figure out what to do with this info. For more info about events and delegates, I highly recommend Prime31′s tutorial on the subject.

The next line of code just shows an instantiation of a particle prefab at the point where our ray collided with another object. By checking the name or tag of the collider, you could easily switch to a different prefab depending on the surface your touching or clicking (think about kicking up dust when touching the ground or spurting blood when touching an enemy).

The final action is probably the weakest but it makes prototyping easier and faster. If any of the components on the collider’s GameObject have a ‘Touched’ method, it will be called. Very simple. I tend to end up with a handful of throwaway scripts with a few variables and a ‘Touched’ method to accomplish simple behaviors when I’m starting on a project. I’ll share some examples in future updates. One of the most helpful is an ObjectRotate class that I throw on objects that I need to toggle between two positions, primarily for doors.

using UnityEngine;
using System.Collections;

public class OnTouchOrClick : MonoBehaviour
{
	public delegate void TouchClickHandler (string touchedObj);
	public static event TouchClickHandler onTouchedGameObjectName;

	//in the inspector, drag over a particle prefab with OneShot and AutoDestruct enabled
	public GameObject firework;
	//how far in Unity units our ray will shoot
	public float range = 25f;
	//camera component from which our ray will shoot
	private Camera cam;

	void Start () {
		//if there isn't a camera attached to this GameObject, use the main camera
		if (!camera) { cam = Camera.main; }
	}
	void Update () {
		//sanity check, return if no camera found
		if (!cam)
			return;
		#if (UNITY_IPHONE || UNITY_ANDROID)
		foreach (Touch touch in Input.touches) {
			if (touch.phase == TouchPhase.Began) {
				Ray ray = cam.ScreenPointToRay (touch.position);
				CheckHit(ray);
			}
		}
		#else
		if (Input.GetButtonDown ("Fire1")) {
			Ray ray = cam.ScreenPointToRay (Input.mousePosition);
			CheckHit (ray);
		}
		#endif
	}
	void CheckHit (Ray ray) {
		//the Raycast() call will return true if you click or touch an object
		//with a collider attached within the specified range.
		RaycastHit hit;
		//if our touch or click hits anything, do something
		if (Physics.Raycast (ray, out hit, range)) {
			//fire an event for any listeners to onTouchedGameObjectName
			if (onTouchedGameObjectName != null)
				onTouchedGameObjectName (hit.collider.gameObject.name);

			//create a firework at the point of collision
			Instantiate (firework, hit.point, Quaternion.identity);

			//inefficient method I lazily use frequently, if any component on the colliding GO
			//has a method named 'Touched', it will be called whenever clicked or touched
			//a common use case is toggling 3d objects between two rotations (opening doors, etc)
			hit.collider.gameObject.SendMessage ("Touched", SendMessageOptions.DontRequireReceiver);
		}
	}
}
Share

Disclaimer

These posts contain my own opinions and work and do not necessarily reflect the opinions of my employer or any other organization with which I have been affiliated. Feel free to use and share the content of this post.
Read More