FANDOM


These two scripts allow for touch-screen pinching and double-tapping to move forward and backward between different cameras that are focused on different Hotspots - behaviour popularised by The Room series of mobile games.

  1. Set the "Movement method" to "None"
  2. Create a new Tag named CameraHotspot
  3. For any Hotspot you want to be able to focus on with its own camera, create either a SimpleCamera or Advanced Third Person Camera (available from AC's Downloads page), and attach the Camera Zoom component to it.
  4. In this Camera Zoom component, assign the Hotspot it is intended for, as well as (optionally) a camera to switch to if when pinching out in the "Exit Camera" field.
  5. Create an ActionList asset named "SwitchCamera", and define a GameObject parameter named "New camera".
  6. In the ActionList, create a "Camera: Switch" Action with a 2s transition, and override the "New camera" field with the parameter.
  7. Add the GestureHotspot component to either a scene object, or a spawned prefab e.g. a custom EventSystem assigned in the Menu Manager
  8. In this component, assign "SwitchCamera" as the "Switch Camera Action List", and tweak the other fields to suit

You should now be able to double-click / double-tap Hotspots to transition to their associated camera, as well as screen-pinch / scroll the middle-mouse button to transition back and forth.

CameraZoom.cs:

using UnityEngine;

namespace AC
{

public class CameraZoom : MonoBehaviour
{

#region Variables

[SerializeField] private Hotspot associatedHotspot = null;
[SerializeField] private _Camera exitCamera = null;

private Camera _camera;
private float originalFOV;
private float targetZoom;
private LerpUtils.FloatLerp zoomLerp = new LerpUtils.FloatLerp ();

private const float zoomSpeed = 5f;

#endregion


#region UnityStandards

private void Start ()
{
if (associatedHotspot != null)
{
associatedHotspot.gameObject.tag = GestureHotspot.cameraHotspotTag;
}

_camera = GetComponentInChildren <Camera> ();
originalFOV = _camera.fieldOfView;
targetZoom = 0f;
}


private void OnEnable ()
{
EventManager.OnSwitchCamera += OnSwitchCamera;
}


private void OnDisable ()
{
EventManager.OnSwitchCamera -= OnSwitchCamera;
}


private void Update ()
{
if (KickStarter.mainCamera.attachedCamera.Camera == _camera)
{
_camera.fieldOfView = zoomLerp.Update (_camera.fieldOfView, originalFOV + targetZoom, zoomSpeed);
}
}

#endregion


#region PublicFunctions

public void SetZoomAmount (float amount)
{
targetZoom = amount;
}

#endregion


#region PrivateFunctions

private void OnSwitchCamera (_Camera fromCamera, _Camera toCamera, float transitionTime)
{
if (toCamera == _camera)
{
targetZoom = 0f;
zoomLerp.Reset ();
_camera.fieldOfView = originalFOV;
}
}

#endregion


#region GetSet

public _Camera ExitCamera
{
get
{
return exitCamera;
}
}

#endregion

}

}

GestureHotspot.cs:

#if (UNITY_IOS || UNITY_ANDROID) && !UNITY_EDITOR
#define MOBILE_INPUT
#endif

using UnityEngine;
using System.Collections.Generic;

namespace AC
{

public class GestureHotspot : MonoBehaviour
{

#region Variables

[Header("Pinching")]
[SerializeField] private float pinchThreshold = 0.2f;
[SerializeField] private float doubleTapWindow = 0.3f;
[SerializeField] private float sphereCastRadius = 0.2f;

[Header("Zooming")]
[SerializeField] private AnimationCurve zoomCurve = new AnimationCurve (new Keyframe(-1, -1), new Keyframe(1, 1));
[SerializeField] private float zoomFactor = 3f;

[Header ("Interactions")]
[SerializeField] private ActionListAsset switchCameraActionList = null;

private float doubleTimeActive;
private LayerMask hotspotLayerMask;
private Vector2 lastTapPosition;

public const string cameraHotspotTag = "CameraHotspot";

private bool isPinching = false;
private float startPinchDistance;
private CameraZoom activeCameraZoom;

#endregion


#region UnityStandards

private void Start ()
{
hotspotLayerMask = 1 << LayerMask.NameToLayer (KickStarter.settingsManager.hotspotLayer);
}


private void OnEnable ()
{
EventManager.OnSwitchCamera += OnSwitchCamera;
}


private void OnDisable ()
{
EventManager.OnSwitchCamera -= OnSwitchCamera;
}


private void Update ()
{
if (doubleTimeActive > 0f)
{
doubleTimeActive -= Time.deltaTime;
}

if (!KickStarter.stateHandler.IsInGameplay ())
{
isPinching = false;
return;
}

if (Input_StartPinch)
{
isPinching = true;
startPinchDistance = 0f;
}
else if (Input_EndPinch)
{
isPinching = false;
}

if (isPinching)
{
float currentPinchDistance = Input_PinchDistance;

if (startPinchDistance == 0f)
{
startPinchDistance = currentPinchDistance;
}

float pinchDistance = (currentPinchDistance - startPinchDistance) / Screen.dpi;
float relativePinchDistance = Mathf.Clamp (pinchDistance / pinchThreshold, -1f, 1f);

if (activeCameraZoom != null)
{
SetZoomAmount (-relativePinchDistance);
}

if (relativePinchDistance >= 1f)
{
ActivateHotspot (new Vector2 (ACScreen.width / 2f, ACScreen.height / 2f), true);
}
else if (relativePinchDistance <= -1f)
{
ExitCurrentCamera ();
}
return;
}
else if (activeCameraZoom != null)
{
SetZoomAmount (0f);
}

if (Input_Tap)
{
if (doubleTimeActive > 0f)
{
doubleTimeActive = 0f;
ActivateHotspot (lastTapPosition, true);
}
else
{
doubleTimeActive = doubleTapWindow;
ActivateHotspot (lastTapPosition, false);
}
}
}

#endregion


#region PrivateFunctions

private void ActivateHotspot (Vector2 screenPosition, bool cameraHotspots = false)
{
Ray ray = KickStarter.CameraMain.ScreenPointToRay (screenPosition);
RaycastHit raycastHit;

if (cameraHotspots)
{
if (Physics.SphereCast (ray, sphereCastRadius, out raycastHit, KickStarter.settingsManager.hotspotRaycastLength, hotspotLayerMask))
{
if (raycastHit.collider.tag != cameraHotspotTag) return;

Hotspot hotspot = raycastHit.collider.GetComponent<Hotspot> ();
if (hotspot != null)
{
hotspot.RunUseInteraction ();
}
}
}
else
{
if (Physics.Raycast (ray, out raycastHit, KickStarter.settingsManager.hotspotRaycastLength, hotspotLayerMask))
{
if (raycastHit.collider.tag == cameraHotspotTag) return;

Hotspot hotspot = raycastHit.collider.GetComponent<Hotspot> ();
if (hotspot != null)
{
hotspot.RunUseInteraction ();
}
}
}
}


private void OnSwitchCamera (_Camera fromCamera, _Camera toCamera, float transitionTime)
{
SetZoomAmount (0f);
activeCameraZoom = toCamera.GetComponent<CameraZoom> ();

AdvancedThirdPersonCamera fromTPCam = fromCamera as AdvancedThirdPersonCamera;
AdvancedThirdPersonCamera toTPCam = toCamera as AdvancedThirdPersonCamera;
if (fromTPCam != null && toTPCam == null)
{
fromTPCam.BeginAutoMove (1f, fromTPCam.GenerateTargetAngles (toCamera.Camera.transform), false);
}
}


private void SetZoomAmount (float amount)
{
if (activeCameraZoom != null)
{
float zoomAmount = zoomCurve.Evaluate (amount);
activeCameraZoom.SetZoomAmount (zoomAmount * zoomFactor);
}
}


private void ExitCurrentCamera ()
{
if (activeCameraZoom != null && switchCameraActionList != null)
{
_Camera exitCamera = activeCameraZoom.ExitCamera;
if (exitCamera != null)
{
List<ActionParameter> newParameterValues = new List<ActionParameter> ();
ActionParameter newParameter = new ActionParameter (switchCameraActionList.DefaultParameters[0]);
newParameter.SetValue (exitCamera.gameObject);
newParameterValues.Add (newParameter);

switchCameraActionList.AssignParameterValues (newParameterValues);
switchCameraActionList.Interact ();
}
}
}

#endregion


#region GetSet

private bool Input_Tap
{
get
{
#if MOBILE_INPUT
if (Input.touchCount == 1 && Input.GetTouch (0).phase == TouchPhase.Began)
{
lastTapPosition = Input.GetTouch (0).position;
return true;
}
#else
if (Input.GetMouseButtonDown (0))
{
lastTapPosition = Input.mousePosition;
return true;
}
#endif
return false;
}
}


private float Input_PinchDistance
{
get
{
#if MOBILE_INPUT
return (Input.GetTouch (0).position - Input.GetTouch (1).position).magnitude;
#else
return Input.mousePosition.y;
#endif
}
}


private bool Input_StartPinch
{
get
{
if (isPinching) return false;

#if MOBILE_INPUT
return Input.touchCount == 2 && Input.GetTouch (1).phase == TouchPhase.Began;
#else
return Input.GetMouseButtonDown (2);
#endif
}
}


private bool Input_EndPinch
{
get
{
if (!isPinching) return false;

#if MOBILE_INPUT
return (Input.touchCount != 2);
#else
return !Input.GetMouseButton (2);
#endif
}
}

#endregion

}

}
Community content is available under CC-BY-SA unless otherwise noted.