123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235 |
- // This example demonstrates how to display text in the UI that involves action bindings.
- // When the player switches control schemes or customizes controls (the latter is not set up
- // in this example but if supported, would work with the existing code as is), text that
- // is shown to the user may be affected.
- //
- // In the example, the player is able to move around the world and look at objects (simple
- // cubes). When an object is in sight, the player can pick the object with a button. While
- // having an object picked up, the player can then either throw the object or drop it back
- // on the ground.
- //
- // Depending on the current context, we display hints in the UI that reflect the currently
- // active bindings.
- using UnityEngine.UI;
- namespace UnityEngine.InputSystem.Samples.InGameHints
- {
- public class InGameHintsExample : MonoBehaviour
- {
- public Text helpText;
- public float moveSpeed;
- public float rotateSpeed;
- public float throwForce;
- public float pickupDistance;
- public float holdDistance;
- private Vector2 m_Rotation;
- private enum State
- {
- Wandering,
- ObjectInSights,
- ObjectPickedUp
- }
- private PlayerInput m_PlayerInput;
- private State m_CurrentState;
- private Transform m_CurrentObject;
- private MaterialPropertyBlock m_PropertyBlock;
- // Cached help texts so that we don't generate garbage all the time. Could even cache them by control
- // scheme to not create garbage during control scheme switching but we consider control scheme switches
- // rare so not worth the extra cost in complexity and memory.
- private string m_LookAtObjectHelpText;
- private string m_ThrowObjectHelpText;
- private const string kDefaultHelpTextFormat = "Move close to one of the cubes and look at it to pick up";
- private const string kLookAtObjectHelpTextFormat = "Press {pickup} to pick object up";
- private const string kThrowObjectHelpTextFormat = "Press {throw} to throw object; press {drop} to drop object";
- public void Awake()
- {
- m_PlayerInput = GetComponent<PlayerInput>();
- }
- public void OnEnable()
- {
- ChangeState(State.Wandering);
- }
- // This is invoked by PlayerInput when the controls on the player change. If the player switches control
- // schemes or keyboard layouts, we end up here and re-generate our hints.
- public void OnControlsChanged()
- {
- UpdateUIHints(regenerate: true); // Force re-generation of our cached text strings to pick up new bindings.
- }
- private int m_UpdateCount;
- public void Update()
- {
- var move = m_PlayerInput.actions["move"].ReadValue<Vector2>();
- var look = m_PlayerInput.actions["look"].ReadValue<Vector2>();
- Move(move);
- Look(look);
- switch (m_CurrentState)
- {
- case State.Wandering:
- case State.ObjectInSights:
- // While looking around for an object to pick up, we constantly raycast into the world.
- if (Physics.Raycast(transform.position, transform.forward, out var hitInfo,
- pickupDistance) && !hitInfo.collider.gameObject.isStatic)
- {
- if (m_CurrentState != State.ObjectInSights)
- ChangeState(State.ObjectInSights);
- m_CurrentObject = hitInfo.transform;
- // Set a custom color override on the object by installing our property block.
- if (m_PropertyBlock == null)
- {
- m_PropertyBlock = new MaterialPropertyBlock();
- m_PropertyBlock.SetColor("_Color", new Color(0.75f, 0, 0));
- }
- m_CurrentObject.GetComponent<MeshRenderer>().SetPropertyBlock(m_PropertyBlock);
- }
- else if (m_CurrentState != State.Wandering)
- {
- // No longer have object in sight.
- ChangeState(State.Wandering);
- if (m_CurrentObject != null)
- {
- // Clear property block on renderer to get rid of our custom color override.
- m_CurrentObject.GetComponent<Renderer>().SetPropertyBlock(null);
- m_CurrentObject = null;
- }
- }
- if (m_PlayerInput.actions["pickup"].triggered && m_CurrentObject != null)
- {
- PickUp();
- ChangeState(State.ObjectPickedUp);
- }
- break;
- case State.ObjectPickedUp:
- // If the player hits the throw button, throw the currently carried object.
- // For this example, let's call this good enough. In a real game, we'd want to avoid the raycast
- if (m_PlayerInput.actions["throw"].triggered)
- {
- Throw();
- ChangeState(State.Wandering);
- }
- else if (m_PlayerInput.actions["drop"].triggered)
- {
- Throw(drop: true);
- ChangeState(State.Wandering);
- }
- break;
- }
- }
- private void ChangeState(State newState)
- {
- switch (newState)
- {
- case State.Wandering:
- break;
- case State.ObjectInSights:
- break;
- case State.ObjectPickedUp:
- break;
- }
- m_CurrentState = newState;
- UpdateUIHints();
- }
- private void UpdateUIHints(bool regenerate = false)
- {
- if (regenerate)
- {
- m_ThrowObjectHelpText = default;
- m_LookAtObjectHelpText = default;
- }
- switch (m_CurrentState)
- {
- case State.ObjectInSights:
- if (m_LookAtObjectHelpText == null)
- m_LookAtObjectHelpText = kLookAtObjectHelpTextFormat.Replace("{pickup}",
- m_PlayerInput.actions["pickup"].GetBindingDisplayString());
- helpText.text = m_LookAtObjectHelpText;
- break;
- case State.ObjectPickedUp:
- if (m_ThrowObjectHelpText == null)
- m_ThrowObjectHelpText = kThrowObjectHelpTextFormat
- .Replace("{throw}", m_PlayerInput.actions["throw"].GetBindingDisplayString())
- .Replace("{drop}", m_PlayerInput.actions["drop"].GetBindingDisplayString());
- helpText.text = m_ThrowObjectHelpText;
- break;
- default:
- helpText.text = kDefaultHelpTextFormat;
- break;
- }
- }
- // Throw or drop currently picked up object.
- private void Throw(bool drop = false)
- {
- // Unmount it.
- m_CurrentObject.parent = null;
- // Turn physics back on.
- var rigidBody = m_CurrentObject.GetComponent<Rigidbody>();
- rigidBody.isKinematic = false;
- // Apply force.
- if (!drop)
- rigidBody.AddForce(transform.forward * throwForce, ForceMode.Impulse);
- m_CurrentObject = null;
- }
- private void PickUp()
- {
- // Mount to our transform.
- m_CurrentObject.position = default;
- m_CurrentObject.SetParent(transform, worldPositionStays: false);
- m_CurrentObject.localPosition += new Vector3(0, 0, holdDistance);
- // Remove color override.
- m_CurrentObject.GetComponent<Renderer>().SetPropertyBlock(null);
- // We don't want the object to be governed by physics while we hold it so turn it into a
- // kinematics body.
- m_CurrentObject.GetComponent<Rigidbody>().isKinematic = true;
- }
- private void Move(Vector2 direction)
- {
- if (direction.sqrMagnitude < 0.01)
- return;
- var scaledMoveSpeed = moveSpeed * Time.deltaTime;
- // For simplicity's sake, we just keep movement in a single plane here. Rotate
- // direction according to world Y rotation of player.
- var move = Quaternion.Euler(0, transform.eulerAngles.y, 0) * new Vector3(direction.x, 0, direction.y);
- transform.position += move * scaledMoveSpeed;
- }
- private void Look(Vector2 rotate)
- {
- if (rotate.sqrMagnitude < 0.01)
- return;
- var scaledRotateSpeed = rotateSpeed * Time.deltaTime;
- m_Rotation.y += rotate.x * scaledRotateSpeed;
- m_Rotation.x = Mathf.Clamp(m_Rotation.x - rotate.y * scaledRotateSpeed, -89, 89);
- transform.localEulerAngles = m_Rotation;
- }
- }
- }
|