using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
namespace Google.Maps.Demos.Utilities {
///
/// This class implements a simple joystick meant for mobile deployment
/// that allows users to move the camera around the scene.
/// It includes support for: Vertical motion up and down, Forward, Reverse, Yaw.
///
/// The rotations and forward/reverse motion are controlled by dragging the joystick knob
/// on the circular area. Rotations and Forward are exclusive from each other,
/// and triggered when the knob reaches specific angles.
/// This is meant to create a smooth user experience.
/// Rotation and forward/reverse speeds increase proportionally to the distance of the knob
/// from the joystick center, up to a specified max value.
///
/// Up and down motion are controlled by two separate buttons.
///
///
public class JoystickController : MonoBehaviour, IDragHandler, IPointerUpHandler,
IPointerDownHandler {
#region properties
// Joystick bounds
public Image Background;
// Camera Rig to move
public Transform CameraRig;
// Direction to follow
public Vector3 InputDirection;
// Lever knob to control forward/reverse, CW and CCW rotations
public Image Lever;
// Maximum rotation speed
public float MaxRotationSpeed = 1f;
// Maximum forward/reverse speed
public float MaxForwardSpeed = 200f;
// Controls going down
public JoystickButton DownButton;
// Min altitude (in meters)
public float MinAltitude = 5;
// Controls going up
public JoystickButton UpButton;
// Max altitude (in meters)
public float MaxAltitude = 300;
// Vertical displacement speed
public float MaxVerticalSpeed = 200f;
// Reference to camera (a child of the camera rig)
private Camera Camera;
#endregion
///
/// On start, set joystick defaults.
/// If the app is deployed on mobile device, show the joystick in the scene,
/// and initialize the cameraRig.
///
///
private void Start() {
InputDirection = Vector3.zero;
// Hide by default
gameObject.transform.parent.gameObject.SetActive(false);
// If the example isn't run on mobile devices, hide the joystick
#if (UNITY_IOS || UNITY_ANDROID) && !UNITY_EDITOR
gameObject.transform.parent.gameObject.SetActive(true);
// Initialize the camera container's position
Camera = CameraRig.GetComponentInChildren();
if (Camera != null) {
CameraRig.position = new Vector3(
Camera.transform.position.x,
Camera.transform.position.y,
Camera.transform.position.z);
Camera.transform.localPosition = Vector3.zero;
}
#endif
}
///
/// On Updates, adjust the position and CW, CCW rotations of the Rig.
/// The code also applies vertical motions if Up and Down buttons are continously pressed.
///
///
private void Update() {
// Move the camera at a speed that is linearly dependent on the height of the camera above
// the ground plane to make camera manual camera movement practicable. The movement speed
// is clamped between 1% and 100% of the configured MovementSpeed.
float forwardSpeed = Mathf.Clamp(
CameraRig.transform.position.y,
MaxForwardSpeed * 0.01f,
MaxForwardSpeed)*Time.deltaTime;
// Max speed in high altitude
// Min speed at 0
// 0 < Pos y < max altitude
float verticalSpeed = Mathf.Clamp(
CameraRig.transform.position.y,
MaxVerticalSpeed * 0.01f,
MaxVerticalSpeed)*Time.deltaTime;
if (InputDirection.magnitude != 0 && CameraRig != null) {
float rotationDirection = 1f;
float angle = Vector3.Angle(InputDirection, Vector3.right);
if (angle > 90f) rotationDirection = -1f;
if (angle < 80f || angle > 100) {
// Rotate target around y axis
CameraRig.transform.RotateAround(
CameraRig.transform.position,
Vector3.up,
rotationDirection * MaxRotationSpeed * InputDirection.magnitude * Time.deltaTime);
}
else {
float dir = InputDirection.y >= 0 ? 1f : -1f;
CameraRig.transform.position += CameraRig.transform.forward * forwardSpeed * dir
* InputDirection.magnitude;
}
}
if (UpButton != null && CameraRig != null && UpButton.IsButtonPressed()) {
CameraRig.transform.position += CameraRig.transform.up * verticalSpeed;
}
if (DownButton != null && CameraRig != null && DownButton.IsButtonPressed()) {
CameraRig.transform.position -= CameraRig.transform.up * verticalSpeed;
}
CameraRig.transform.position = new Vector3(
CameraRig.transform.position.x,
Mathf.Clamp(CameraRig.transform.position.y, MinAltitude, MaxAltitude),
CameraRig.transform.position.z);
}
#region event listeners
///
/// Implements the IDragHandler interface.
/// The function converts the drag of the joystick knob on the UI overlay
/// to a direction vector in worldspace that can be applied to our target.
///
/// The pointer event data
public void OnDrag(PointerEventData ped) {
// Current position
var pos = Vector2.zero;
var rect = Background.rectTransform;
// Move the target based on the Lever's position
RectTransformUtility.ScreenPointToLocalPointInRectangle(
rect,
ped.position,
ped.pressEventCamera,
out pos);
pos.x = pos.x / rect.sizeDelta.x;
pos.y = pos.y / rect.sizeDelta.y;
InputDirection = new Vector3(pos.x, pos.y, 0f);
InputDirection = InputDirection.magnitude > 1 ? InputDirection.normalized : InputDirection;
Lever.rectTransform.anchoredPosition = new Vector3(
InputDirection.x * (rect.sizeDelta.x / 3),
InputDirection.y * rect.sizeDelta.y / 3);
}
///
/// Implements the IPointerUpHandler interface.
/// Applies changes in similar ways to the OnDrag function.
///
/// The pointer event data
public void OnPointerDown(PointerEventData ped) {
OnDrag(ped);
}
///
/// Implements the IPointerDownHandler interface.
/// Resets the position of the joystick knob and the direction vector.
///
///
public void OnPointerUp(PointerEventData ped) {
Lever.rectTransform.anchoredPosition = Vector3.zero;
InputDirection = Vector3.zero;
}
#endregion
}
}