I’m trying to emulate the “swipe to look” controls found in iPhone games such as Dead Trigger (made in Unity), Modern Combat and Minecraft, in that swiping your finger rotates the camera to your fingers’ position, then stops. I’ve tried using both the mouselook script and various joysticks/joypads positioned in the center of the screen and set to invisible but none of them have the “stopping” affect (if you hold your finger down the camera will spin in circles until you let go). I therefore have two questions:
Are there any existing control mechanisms that emulate this style (I’m willing to pay)
If not, how would I go about achieving this effect?
Here is a c# script that I’ve used. You can tweak it to your needs. The setup is outlined in the comments in the script.
using UnityEngine;
using System.Collections;
/// MouseLook rotates the transform based on the mouse delta.
/// Minimum and Maximum values can be used to constrain the possible rotation
/// To make an FPS style character:
/// - Create a capsule.
/// - Add a rigid body to the capsule
/// - Add the MouseLook script to the capsule.
/// -> Set the mouse look to use LookX. (You want to only turn character but not tilt it)
/// - Add FPSWalker script to the capsule
/// - Create a camera. Make the camera a child of the capsule. Reset it's transform.
/// - Add a MouseLook script to the camera.
/// -> Set the mouse look to use LookY. (You want the camera to tilt up and down like a head. The character already turns.)
[AddComponentMenu("Camera-Control/Mouse Look")]
public class MouseLookiPhone : MonoBehaviour {
public enum RotationAxes { MouseXAndY = 0, MouseX = 1, MouseY = 2 }
public RotationAxes axes = RotationAxes.MouseXAndY;
public float sensitivityX = 2F;
public float sensitivityY = 2F;
private float index1;
private float index2;
public int Invert;
public float minimumX = -360F;
public float maximumX = 360F;
public float minimumY = -60F;
public float maximumY = 60F;
public float rotationX = 0F;
float rotationY = 0F;
public float smoothing = 1.6F;
private float inputX = 0F;
private float inputY = 0F;
private Transform thisXform;
public void CustomUpdate ()
{
if (axes == RotationAxes.MouseX)
{
rotationX += inputX * sensitivityX;
rotationX = ClampAngle (rotationX, minimumX, maximumX);
thisXform.localRotation=Quaternion.Lerp(thisXform.localRotation,Quaternion.Euler(0,rotationX,0),Time.deltaTime*10/smoothing);
}
else
{
rotationY += inputY * sensitivityY;
rotationY = ClampAngle (rotationY, minimumY, maximumY);
thisXform.localRotation=Quaternion.Lerp(thisXform.localRotation,Quaternion.Euler(rotationY,0,0),Time.deltaTime*10/smoothing);
}
}
void Awake () {
thisXform = transform;
index1 = PlayerPrefs.GetFloat("MoveSensX");
index2 = PlayerPrefs.GetFloat("MoveSensY");
Invert = PlayerPrefs.GetInt("Invert");
sensitivityX = index1;
sensitivityY = index2;
smoothing = PlayerPrefs.GetFloat("Cont2Smooth");
}
public static float ClampAngle (float angle, float min, float max)
{
if (angle < -360F)
angle += 360F;
if (angle > 360F)
angle -= 360F;
return Mathf.Clamp (angle, min, max);
}
public void SetInput (Vector2 rightStickInput)
{
inputX = rightStickInput.x * 0.1F;
if (Invert==0)
inputY = rightStickInput.y * -0.1F;
if (Invert==1)
inputY = rightStickInput.y * 0.1F;
CustomUpdate ();
}
public void zoom (int ZS){
if (ZS == 0){
sensitivityX = index1;
sensitivityY = index2;
}
if (ZS == 1){
sensitivityX = 0.2f;
sensitivityY = 0.2f;
}
}
}
I have a settings menu that allows the player to adjust the speed/feeling of the movement and load them under the Awake function above.
Here is the joystick script. I pulled it out of one of my games, so you will have to tweak it.
#pragma strict
#pragma downcast
//////////////////////////////////////////////////////////////
// Joystick.js
// Penelope iPhone Tutorial
//
// Joystick creates a movable joystick (via GUITexture) that
// handles touch input, taps, and phases. Dead zones can control
// where the joystick input gets picked up and can be normalized.
//
// Optionally, you can enable the touchPad property from the editor
// to treat this Joystick as a TouchPad. A TouchPad allows the finger
// to touch down at any point and it tracks the movement relatively
// without moving the graphic
//////////////////////////////////////////////////////////////
//@script RequireComponent( GUITexture )
// A simple class for bounding how far the GUITexture will move
class Boundary
{
var min : Vector2 = Vector2.zero;
var max : Vector2 = Vector2.zero;
}
static private var joysticks : Joystick[]; // A static collection of all joysticks
static private var enumeratedJoysticks : boolean = false;
static private var tapTimeDelta : float = 0.3; // Time allowed between taps
//var touchPad : boolean; // Is this a TouchPad?
var touchZone : Rect;
// Normalize output after the dead-zone?
var position : Vector2; // [-1, 1] in x,y
var tapCount : int; // Current tap count
private var lastFingerId = -1; // Finger last used for this joystick
private var tapTimeWindow : float; // How much time there is left for a tap to occur
private var fingerDownPos : Vector2;
private var fingerDownTime : float;
private var firstDeltaTime : float = 0.5;
var defaultRect : Rect; // Default position / extents of the joystick graphic
//private var guiBoundary : Boundary = Boundary(); // Boundary for joystick graphic
private var guiTouchOffset : Vector2; // Offset to apply to touch input
//private var guiCenter : Vector2; // Center of joystick
var GUITouchPad = false;
var GUITouch : UIButton;
var WeaponController : MainWeaponsController;
var FrezRHStick = false;
private var Walkstat = false;
var look1 : MouseLookiPhone;
var look2 : MouseLookiPhone;
private var TouchStat = false;
var AccelCont = 0;
function Awake () {
AccelCont = PlayerPrefs.GetInt ("AccelCont");
}
function Start(){
if (GUITouchPad)
defaultRect = Rect (0, 0, Screen.width * .27, Screen.height * .4);
if (!GUITouchPad)
defaultRect = Rect (-Screen.width * .5, 0, Screen.width * .5, Screen.height * .6);
defaultRect.x += transform.position.x * Screen.width;// + gui.pixelInset.x; // - Screen.width * 0.5;
defaultRect.y += transform.position.y * Screen.height;// - Screen.height * 0.5;
transform.position.x = 0.0;
transform.position.y = 0.0;
// If a texture has been assigned, then use the rect ferom the gui as our touchZone
// if ( gui.texture )
touchZone = defaultRect;
}
function Disable()
{
gameObject.active = false;
enumeratedJoysticks = false;
}
function Enable()
{
gameObject.active = true;
enumeratedJoysticks = true;
}
function ResetJoystick()
{
// Release the finger control and set the joystick back to the default position
// gui.pixelInset = defaultRect;
lastFingerId = -1;
position = Vector2.zero;
//var fingerDownPosition = Vector2.zero;
TouchStat = false;
if (!GUITouchPad AccelCont == 2) {
look1.SetInput (Vector2.zero);
look2.SetInput (Vector2.zero);
}
if (GUITouchPad Walkstat){
WeaponController.NoWalk ();
Walkstat = false;
}
}
function IsFingerDown() : boolean
{
return (lastFingerId != -1);
}
function LatchedFinger( fingerId : int )
{
// If another joystick has latched this finger, then we must release it
if ( lastFingerId == fingerId )
ResetJoystick();
}
function Update()
{
if ( !enumeratedJoysticks )
{
// Collect all joysticks in the game, so we can relay finger latching messages
joysticks = FindObjectsOfType( Joystick );
enumeratedJoysticks = true;
}
var count = Input.touchCount;
// Adjust the tap time window while it still available
if ( tapTimeWindow > 0 )
tapTimeWindow -= Time.deltaTime;
else
tapCount = 0;
if ( count == 0 )
ResetJoystick();
else
{
for(var i : int = 0;i < count; i++)
{
var touch : Touch = Input.GetTouch(i);
var guiTouchPos : Vector2 = touch.position - guiTouchOffset;
var shouldLatchFinger = false;
if ( touchZone.Contains( touch.position ) )
shouldLatchFinger = true;
// Latch the finger if this is a new touch
if ( shouldLatchFinger ( lastFingerId == -1 || lastFingerId != touch.fingerId ) )
{
lastFingerId = touch.fingerId;
fingerDownPos = touch.position;
fingerDownTime = Time.time;
lastFingerId = touch.fingerId;
// Accumulate taps if it is within the time window
if ( tapTimeWindow > 0 )
tapCount++;
else
{
tapCount = 1;
tapTimeWindow = tapTimeDelta;
}
// Tell other joysticks we've latched this finger
for ( var j : Joystick in joysticks )
{
if ( j != this )
j.LatchedFinger( touch.fingerId );
}
}
if ( lastFingerId == touch.fingerId )
{
// Override the tap count with what the iPhone SDK reports if it is greater
// This is a workaround, since the iPhone SDK does not currently track taps
// for multiple touches
if ( touch.tapCount > tapCount )
tapCount = touch.tapCount;
// For a touchpad, let's just set the position directly based on distance from initial touchdown
if (!FrezRHStick GUITouchPad){
position.x = Mathf.Clamp( ( touch.position.x - fingerDownPos.x ) / ( touchZone.width * .5 ), -1, 1 );
position.y = Mathf.Clamp( ( touch.position.y - fingerDownPos.y ) / ( touchZone.height * .5 ), -1, 1 );
if ( AccelCont == 2){
look1.SetInput (Vector2.zero);
look2.SetInput (Vector2.zero);
}
}
if (!FrezRHStick !GUITouchPad AccelCont == 2) {
look1.SetInput (touch.deltaPosition);
look2.SetInput (touch.deltaPosition);
TouchStat = true;
}else
if (!FrezRHStick !GUITouchPad AccelCont != 2) {
position.x = Mathf.Clamp( ( touch.position.x - fingerDownPos.x ) / ( touchZone.width * .5 ), -1, 1 );
position.y = Mathf.Clamp( ( touch.position.y - fingerDownPos.y ) / ( touchZone.height * .5 ), -1, 1 );
TouchStat = true;
}
if (GUITouchPad !Walkstat){
WeaponController.Walk ();
Walkstat = true;
TouchStat = true;
}
if ( touch.phase == TouchPhase.Ended || touch.phase == TouchPhase.Canceled TouchStat)
ResetJoystick();
}
}
}
}