using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Events;
using UnityEngine.EventSystems;
public class IndividualSelection : MonoBehaviour
{
// Use this to position a cursor or selection highlight for each player.
public RectTransform cursor;
// Choose which UI element this player should select by default.
public Selectable defaultSelection;
// Names for this player's axes & buttons,
// so they're not both listening to the same inputs.
public string horizontalAxis;
public string verticalAxis;
public string acceptButton;
public string cancelButton;
// To make an analog stick "clicky" - you might need to soup-up the
// sensitivity & gravity values beyond their defaults in the Input Manager.
public float activationThreshold = 0.1f;
// Debouncing / rejecting holds on the axis.
Vector2 _axisMemory;
// Keeping track of current selection state.
Selectable _currentSelection;
static Dictionary<Selectable, int> _selectionCount = new Dictionary<Selectable, int>();
// Expose an event for when the player's selection changes.
// Hook into this to update your portrait / etc.
[System.Serializable]
public class SelectionEvent : UnityEvent<Selectable> { }
public SelectionEvent onSelect;
public UnityEvent onAccept;
public Selectable GetCurrentSelection() { return _currentSelection; }
void Start() {
// Selection has to start somewhere
// (you can also inject this from the script that spawns the player
// or handles joining in progress, or choose a default selection randomly)
Select(defaultSelection);
}
public void Select(Selectable target) {
// A more complete solution might see us create our own EventSystem
// or InputModule that's multiplayer-aware, but this will do for now.
var fakeEvent = new BaseEventData(EventSystem.current);
// Deselect the old selection if we're the last one to leave it.
// (But don't deselect something another player is still hovering over)
if (_currentSelection != null && --_selectionCount[_currentSelection] == 0) {
_currentSelection.OnDeselect(fakeEvent);
_selectionCount.Remove(_currentSelection);
}
// Handle highlighting our current selection.
_currentSelection = target;
if (_selectionCount.ContainsKey(_currentSelection))
_selectionCount[_currentSelection]++;
else {
_selectionCount.Add(_currentSelection, 1);
target.OnSelect(fakeEvent);
}
// Position our cursor over the selected control.
// There are nicer ways to animate this, but this served for a quick test.
var rect = ((RectTransform)_currentSelection.transform);
cursor.anchorMinposition = rect.anchorMin;
cursor_currentSelection.anchorMax = recttransform.anchorMax;position;
// Let listeners know that this player's selection has changed.
if(onSelect != null)
onSelect.Invoke(_currentSelection);
}
void Update() {
Vector2 input = new Vector2(Input.GetAxis(horizontalAxis), Input.GetAxis(verticalAxis));
Selectable nextSelection = null;
// Don't keep bouncing down the list until the player releases/reverses.
// TODO: handle repeat rate for holding in one direction, if desired.
if (input.x * _axisMemory.x <= 0f) {
_axisMemory.x = 0f;
if (Mathf.Abs(input.x) > activationThreshold) {
if (input.x > 0f) {
nextSelection = _currentSelection.FindSelectableOnRight();
_axisMemory.x = 1f;
} else {
nextSelection = _currentSelection.FindSelectableOnLeft();
_axisMemory.x = -1f;
}
}
}
if (input.y * _axisMemory.y <= 0f) {
_axisMemory.y = 0f;
if (Mathf.Abs(input.y) > activationThreshold) {
if (input.y > 0f) {
nextSelection = _currentSelection.FindSelectableOnUp();
_axisMemory.y = 1f;
} else {
nextSelection = _currentSelection.FindSelectableOnDown();
_axisMemory.y = -1f;
}
}
}
if(nextSelection != null) {
Select(nextSelection);
} else if(Input.GetButtonDown(acceptButton)) {
// If we're selecting something clickable,
// handle the accept button like a click.
var clickable = _currentSelection.GetComponent<IPointerClickHandler>();
if (clickable != null) &&{
if (_currentSelection.IsInteractable()) {
clickable.OnPointerClick(new PointerEventData(EventSystem.current));
}
// Otherwise, pass the accept on to any other interested listeners.
} else if (onAccept != null) {
onAccept.Invoke();
}
}
}
}
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
public class IndividualSelection : MonoBehaviour
{
// Use this to position a cursor or selection highlight for each player.
public RectTransform cursor;
// Choose which UI element this player should select by default.
public Selectable defaultSelection;
// Names for this player's axes & buttons,
// so they're not both listening to the same inputs.
public string horizontalAxis;
public string verticalAxis;
public string acceptButton;
public string cancelButton;
// To make an analog stick "clicky" - you might need to soup-up the
// sensitivity & gravity values beyond their defaults in the Input Manager.
public float activationThreshold = 0.1f;
// Debouncing / rejecting holds on the axis.
Vector2 _axisMemory;
// Keeping track of current selection state.
Selectable _currentSelection;
static Dictionary<Selectable, int> _selectionCount = new Dictionary<Selectable, int>();
// Expose an event for when the player's selection changes.
// Hook into this to update your portrait / etc.
[System.Serializable]
public class SelectionEvent : UnityEvent<Selectable> { }
public SelectionEvent onSelect;
public Selectable GetCurrentSelection() { return _currentSelection; }
void Start() {
// Selection has to start somewhere
// (you can also inject this from the script that spawns the player
// or handles joining in progress, or choose a default selection randomly)
Select(defaultSelection);
}
public void Select(Selectable target) {
// A more complete solution might see us create our own EventSystem
// or InputModule that's multiplayer-aware, but this will do for now.
var fakeEvent = new BaseEventData(EventSystem.current);
// Deselect the old selection if we're the last one to leave it.
// (But don't deselect something another player is still hovering over)
if (_currentSelection != null && --_selectionCount[_currentSelection] == 0) {
_currentSelection.OnDeselect(fakeEvent);
_selectionCount.Remove(_currentSelection);
}
// Handle highlighting our current selection.
_currentSelection = target;
if (_selectionCount.ContainsKey(_currentSelection))
_selectionCount[_currentSelection]++;
else {
_selectionCount.Add(_currentSelection, 1);
target.OnSelect(fakeEvent);
}
// Position our cursor over the selected control.
// There are nicer ways to animate this, but this served for a quick test.
var rect = ((RectTransform)_currentSelection.transform);
cursor.anchorMin = rect.anchorMin;
cursor.anchorMax = rect.anchorMax;
// Let listeners know that this player's selection has changed.
if(onSelect != null)
onSelect.Invoke(_currentSelection);
}
void Update() {
Vector2 input = new Vector2(Input.GetAxis(horizontalAxis), Input.GetAxis(verticalAxis));
Selectable nextSelection = null;
// Don't keep bouncing down the list until the player releases/reverses.
// TODO: handle repeat rate for holding in one direction, if desired.
if (input.x * _axisMemory.x <= 0f) {
_axisMemory.x = 0f;
if (Mathf.Abs(input.x) > activationThreshold) {
if (input.x > 0f) {
nextSelection = _currentSelection.FindSelectableOnRight();
_axisMemory.x = 1f;
} else {
nextSelection = _currentSelection.FindSelectableOnLeft();
_axisMemory.x = -1f;
}
}
}
if (input.y * _axisMemory.y <= 0f) {
_axisMemory.y = 0f;
if (Mathf.Abs(input.y) > activationThreshold) {
if (input.y > 0f) {
nextSelection = _currentSelection.FindSelectableOnUp();
_axisMemory.y = 1f;
} else {
nextSelection = _currentSelection.FindSelectableOnDown();
_axisMemory.y = -1f;
}
}
}
if(nextSelection != null) {
Select(nextSelection);
} else if(Input.GetButtonDown(acceptButton)) {
var clickable = _currentSelection.GetComponent<IPointerClickHandler>();
if(clickable != null && _currentSelection.IsInteractable()) {
clickable.OnPointerClick(new PointerEventData(EventSystem.current));
}
}
}
}
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Events;
using UnityEngine.EventSystems;
public class IndividualSelection : MonoBehaviour
{
// Use this to position a cursor or selection highlight for each player.
public RectTransform cursor;
// Choose which UI element this player should select by default.
public Selectable defaultSelection;
// Names for this player's axes & buttons,
// so they're not both listening to the same inputs.
public string horizontalAxis;
public string verticalAxis;
public string acceptButton;
public string cancelButton;
// To make an analog stick "clicky" - you might need to soup-up the
// sensitivity & gravity values beyond their defaults in the Input Manager.
public float activationThreshold = 0.1f;
// Debouncing / rejecting holds on the axis.
Vector2 _axisMemory;
// Keeping track of current selection state.
Selectable _currentSelection;
static Dictionary<Selectable, int> _selectionCount = new Dictionary<Selectable, int>();
// Expose an event for when the player's selection changes.
// Hook into this to update your portrait / etc.
[System.Serializable]
public class SelectionEvent : UnityEvent<Selectable> { }
public SelectionEvent onSelect;
public UnityEvent onAccept;
public Selectable GetCurrentSelection() { return _currentSelection; }
void Start() {
// Selection has to start somewhere
// (you can also inject this from the script that spawns the player
// or handles joining in progress, or choose a default selection randomly)
Select(defaultSelection);
}
public void Select(Selectable target) {
// A more complete solution might see us create our own EventSystem
// or InputModule that's multiplayer-aware, but this will do for now.
var fakeEvent = new BaseEventData(EventSystem.current);
// Deselect the old selection if we're the last one to leave it.
// (But don't deselect something another player is still hovering over)
if (_currentSelection != null && --_selectionCount[_currentSelection] == 0) {
_currentSelection.OnDeselect(fakeEvent);
_selectionCount.Remove(_currentSelection);
}
// Handle highlighting our current selection.
_currentSelection = target;
if (_selectionCount.ContainsKey(_currentSelection))
_selectionCount[_currentSelection]++;
else {
_selectionCount.Add(_currentSelection, 1);
target.OnSelect(fakeEvent);
}
// Position our cursor over the selected control.
// There are nicer ways to animate this, but this served for a quick test.
cursor.position = _currentSelection.transform.position;
// Let listeners know that this player's selection has changed.
if(onSelect != null)
onSelect.Invoke(_currentSelection);
}
void Update() {
Vector2 input = new Vector2(Input.GetAxis(horizontalAxis), Input.GetAxis(verticalAxis));
Selectable nextSelection = null;
// Don't keep bouncing down the list until the player releases/reverses.
// TODO: handle repeat rate for holding in one direction, if desired.
if (input.x * _axisMemory.x <= 0f) {
_axisMemory.x = 0f;
if (Mathf.Abs(input.x) > activationThreshold) {
if (input.x > 0f) {
nextSelection = _currentSelection.FindSelectableOnRight();
_axisMemory.x = 1f;
} else {
nextSelection = _currentSelection.FindSelectableOnLeft();
_axisMemory.x = -1f;
}
}
}
if (input.y * _axisMemory.y <= 0f) {
_axisMemory.y = 0f;
if (Mathf.Abs(input.y) > activationThreshold) {
if (input.y > 0f) {
nextSelection = _currentSelection.FindSelectableOnUp();
_axisMemory.y = 1f;
} else {
nextSelection = _currentSelection.FindSelectableOnDown();
_axisMemory.y = -1f;
}
}
}
if(nextSelection != null) {
Select(nextSelection);
} else if(Input.GetButtonDown(acceptButton)) {
// If we're selecting something clickable,
// handle the accept button like a click.
var clickable = _currentSelection.GetComponent<IPointerClickHandler>();
if (clickable != null) {
if (_currentSelection.IsInteractable()) {
clickable.OnPointerClick(new PointerEventData(EventSystem.current));
}
// Otherwise, pass the accept on to any other interested listeners.
} else if (onAccept != null) {
onAccept.Invoke();
}
}
}
}
We can do this with a MonoBehaviour
script that represents one player's selection state. Our scene can contain one or more instances of this script, each reading different inputs and updating their own internal selection state and selection feedbackor even spawn/enable more on the fly as players join.
This script is responsible for:
reading one player's inputs
updating the selected item based on those inputs
reflecting the current selection using that player's cursor and the UI selected states (think button highlights, etc)
notifying other scripts when this player's selection has changed, via an
onSelect
event(Your selected character name & portrait panel can subscribe to this event and peek at the selected item to determine what to show)
Here's a proof of concept:
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
public class IndividualSelection : MonoBehaviour
{
// Use this to position a cursor or selection highlight for each player.
public RectTransform cursor;
// Choose which UI element this player should select by default.
public Selectable defaultSelection;
// Names for this player's axes & buttons,
// so they're not both listening to the same inputs.
public string horizontalAxis;
public string verticalAxis;
public string acceptButton;
public string cancelButton;
// To make an analog stick "clicky" - you might need to soup-up the
// sensitivity & gravity values beyond their defaults in the Input Manager.
public float activationThreshold = 0.1f;
// Debouncing / rejecting holds on the axis.
Vector2 _axisMemory;
// Keeping track of current selection state.
Selectable _currentSelection;
static Dictionary<Selectable, int> _selectionCount = new Dictionary<Selectable, int>();
// Expose an event for when the player's selection changes.
// Hook into this to update your portrait / etc.
[System.Serializable]
public class SelectionEvent : UnityEvent<Selectable> { }
public SelectionEvent onSelect;
public Selectable GetCurrentSelection() { return _currentSelection; }
void Start() {
// Selection has to start somewhere
// (you can also inject this from the script that spawns the player
// or handles joining in progress, or choose a default selection randomly)
Select(defaultSelection);
}
public void Select(Selectable target) {
// A more complete solution might see us create our own EventSystem
// or InputModule that's multiplayer-aware, but this will do for now.
var fakeEvent = new BaseEventData(EventSystem.current);
// Deselect the old selection if we're the last one to leave it.
// (But don't deselect something another player is still hovering over)
if (_currentSelection != null && --_selectionCount[_currentSelection] == 0) {
_currentSelection.OnDeselect(fakeEvent);
_selectionCount.Remove(_currentSelection);
}
// Handle highlighting our current selection.
_currentSelection = target;
if (_selectionCount.ContainsKey(_currentSelection))
_selectionCount[_currentSelection]++;
else {
_selectionCount.Add(_currentSelection, 1);
target.OnSelect(fakeEvent);
}
// Position our cursor over the selected control.
// There are nicer ways to animate this, but this served for a quick test.
var rect = ((RectTransform)_currentSelection.transform);
cursor.anchorMin = rect.anchorMin;
cursor.anchorMax = rect.anchorMax;
// Let listeners know that this player's selection has changed.
if(onSelect != null)
onSelect.Invoke(_currentSelection);
}
void Update() {
Vector2 input = new Vector2(Input.GetAxis(horizontalAxis), Input.GetAxis(verticalAxis));
Selectable nextSelection = null;
// Don't keep bouncing down the list until the player releases/reverses.
// TODO: handle repeat rate for holding in one direction, if desired.
if (input.x * _axisMemory.x <= 0f) {
_axisMemory.x = 0f;
if (Mathf.Abs(input.x) > activationThreshold) {
if (input.x > 0f) {
nextSelection = _currentSelection.FindSelectableOnRight();
_axisMemory.x = 1f;
} else {
nextSelection = _currentSelection.FindSelectableOnLeft();
_axisMemory.x = -1f;
}
}
}
if (input.y * _axisMemory.y <= 0f) {
_axisMemory.y = 0f;
if (Mathf.Abs(input.y) > activationThreshold) {
if (input.y > 0f) {
nextSelection = _currentSelection.FindSelectableOnUp();
_axisMemory.y = 1f;
} else {
nextSelection = _currentSelection.FindSelectableOnDown();
_axisMemory.y = -1f;
}
}
}
if(nextSelection != null) {
Select(nextSelection);
} else if(Input.GetButtonDown(acceptButton)) {
var clickable = _currentSelection.GetComponent<IPointerClickHandler>();
if(clickable != null && _currentSelection.IsInteractable()) {
clickable.OnPointerClick(new PointerEventData(EventSystem.current));
}
}
}
}
We can do this with a MonoBehaviour
script that represents one player's selection state. Our scene can contain one or more instances of this script, each reading different inputs and updating their own internal selection state and selection feedback.
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
public class IndividualSelection : MonoBehaviour
{
// Use this to position a cursor or selection highlight for each player.
public RectTransform cursor;
// Choose which UI element this player should select by default.
public Selectable defaultSelection;
// Names for this player's axes & buttons,
// so they're not both listening to the same inputs.
public string horizontalAxis;
public string verticalAxis;
public string acceptButton;
public string cancelButton;
// To make an analog stick "clicky" - you might need to soup-up the
// sensitivity & gravity values beyond their defaults in the Input Manager.
public float activationThreshold = 0.1f;
// Debouncing / rejecting holds on the axis.
Vector2 _axisMemory;
// Keeping track of current selection state.
Selectable _currentSelection;
static Dictionary<Selectable, int> _selectionCount = new Dictionary<Selectable, int>();
// Expose an event for when the player's selection changes.
// Hook into this to update your portrait / etc.
[System.Serializable]
public class SelectionEvent : UnityEvent<Selectable> { }
public SelectionEvent onSelect;
void Start() {
// Selection has to start somewhere
// (you can also inject this from the script that spawns the player
// or handles joining in progress, or choose a default selection randomly)
Select(defaultSelection);
}
void Select(Selectable target) {
// A more complete solution might see us create our own EventSystem
// or InputModule that's multiplayer-aware, but this will do for now.
var fakeEvent = new BaseEventData(EventSystem.current);
// Deselect the old selection if we're the last one to leave it.
// (But don't deselect something another player is still hovering over)
if (_currentSelection != null && --_selectionCount[_currentSelection] == 0) {
_currentSelection.OnDeselect(fakeEvent);
_selectionCount.Remove(_currentSelection);
}
// Handle highlighting our current selection.
_currentSelection = target;
if (_selectionCount.ContainsKey(_currentSelection))
_selectionCount[_currentSelection]++;
else {
_selectionCount.Add(_currentSelection, 1);
target.OnSelect(fakeEvent);
}
// Position our cursor over the selected control.
// There are nicer ways to animate this, but this served for a quick test.
var rect = ((RectTransform)_currentSelection.transform);
cursor.anchorMin = rect.anchorMin;
cursor.anchorMax = rect.anchorMax;
// Let listeners know that this player's selection has changed.
if(onSelect != null)
onSelect.Invoke(_currentSelection);
}
void Update() {
Vector2 input = new Vector2(Input.GetAxis(horizontalAxis), Input.GetAxis(verticalAxis));
Selectable nextSelection = null;
// Don't keep bouncing down the list until the player releases/reverses.
// TODO: handle repeat rate for holding in one direction, if desired.
if (input.x * _axisMemory.x <= 0f) {
_axisMemory.x = 0f;
if (Mathf.Abs(input.x) > activationThreshold) {
if (input.x > 0f) {
nextSelection = _currentSelection.FindSelectableOnRight();
_axisMemory.x = 1f;
} else {
nextSelection = _currentSelection.FindSelectableOnLeft();
_axisMemory.x = -1f;
}
}
}
if (input.y * _axisMemory.y <= 0f) {
_axisMemory.y = 0f;
if (Mathf.Abs(input.y) > activationThreshold) {
if (input.y > 0f) {
nextSelection = _currentSelection.FindSelectableOnUp();
_axisMemory.y = 1f;
} else {
nextSelection = _currentSelection.FindSelectableOnDown();
_axisMemory.y = -1f;
}
}
}
if(nextSelection != null) {
Select(nextSelection);
} else if(Input.GetButtonDown(acceptButton)) {
var clickable = _currentSelection.GetComponent<IPointerClickHandler>();
if(clickable != null && _currentSelection.IsInteractable()) {
clickable.OnPointerClick(new PointerEventData(EventSystem.current));
}
}
}
}
We can do this with a MonoBehaviour
script that represents one player's selection state. Our scene can contain one or more instances of this script, or even spawn/enable more on the fly as players join.
This script is responsible for:
reading one player's inputs
updating the selected item based on those inputs
reflecting the current selection using that player's cursor and the UI selected states (think button highlights, etc)
notifying other scripts when this player's selection has changed, via an
onSelect
event(Your selected character name & portrait panel can subscribe to this event and peek at the selected item to determine what to show)
Here's a proof of concept:
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
public class IndividualSelection : MonoBehaviour
{
// Use this to position a cursor or selection highlight for each player.
public RectTransform cursor;
// Choose which UI element this player should select by default.
public Selectable defaultSelection;
// Names for this player's axes & buttons,
// so they're not both listening to the same inputs.
public string horizontalAxis;
public string verticalAxis;
public string acceptButton;
public string cancelButton;
// To make an analog stick "clicky" - you might need to soup-up the
// sensitivity & gravity values beyond their defaults in the Input Manager.
public float activationThreshold = 0.1f;
// Debouncing / rejecting holds on the axis.
Vector2 _axisMemory;
// Keeping track of current selection state.
Selectable _currentSelection;
static Dictionary<Selectable, int> _selectionCount = new Dictionary<Selectable, int>();
// Expose an event for when the player's selection changes.
// Hook into this to update your portrait / etc.
[System.Serializable]
public class SelectionEvent : UnityEvent<Selectable> { }
public SelectionEvent onSelect;
public Selectable GetCurrentSelection() { return _currentSelection; }
void Start() {
// Selection has to start somewhere
// (you can also inject this from the script that spawns the player
// or handles joining in progress, or choose a default selection randomly)
Select(defaultSelection);
}
public void Select(Selectable target) {
// A more complete solution might see us create our own EventSystem
// or InputModule that's multiplayer-aware, but this will do for now.
var fakeEvent = new BaseEventData(EventSystem.current);
// Deselect the old selection if we're the last one to leave it.
// (But don't deselect something another player is still hovering over)
if (_currentSelection != null && --_selectionCount[_currentSelection] == 0) {
_currentSelection.OnDeselect(fakeEvent);
_selectionCount.Remove(_currentSelection);
}
// Handle highlighting our current selection.
_currentSelection = target;
if (_selectionCount.ContainsKey(_currentSelection))
_selectionCount[_currentSelection]++;
else {
_selectionCount.Add(_currentSelection, 1);
target.OnSelect(fakeEvent);
}
// Position our cursor over the selected control.
// There are nicer ways to animate this, but this served for a quick test.
var rect = ((RectTransform)_currentSelection.transform);
cursor.anchorMin = rect.anchorMin;
cursor.anchorMax = rect.anchorMax;
// Let listeners know that this player's selection has changed.
if(onSelect != null)
onSelect.Invoke(_currentSelection);
}
void Update() {
Vector2 input = new Vector2(Input.GetAxis(horizontalAxis), Input.GetAxis(verticalAxis));
Selectable nextSelection = null;
// Don't keep bouncing down the list until the player releases/reverses.
// TODO: handle repeat rate for holding in one direction, if desired.
if (input.x * _axisMemory.x <= 0f) {
_axisMemory.x = 0f;
if (Mathf.Abs(input.x) > activationThreshold) {
if (input.x > 0f) {
nextSelection = _currentSelection.FindSelectableOnRight();
_axisMemory.x = 1f;
} else {
nextSelection = _currentSelection.FindSelectableOnLeft();
_axisMemory.x = -1f;
}
}
}
if (input.y * _axisMemory.y <= 0f) {
_axisMemory.y = 0f;
if (Mathf.Abs(input.y) > activationThreshold) {
if (input.y > 0f) {
nextSelection = _currentSelection.FindSelectableOnUp();
_axisMemory.y = 1f;
} else {
nextSelection = _currentSelection.FindSelectableOnDown();
_axisMemory.y = -1f;
}
}
}
if(nextSelection != null) {
Select(nextSelection);
} else if(Input.GetButtonDown(acceptButton)) {
var clickable = _currentSelection.GetComponent<IPointerClickHandler>();
if(clickable != null && _currentSelection.IsInteractable()) {
clickable.OnPointerClick(new PointerEventData(EventSystem.current));
}
}
}
}
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
public class IndividualSelection : MonoBehaviour
{
// Use this to position a cursor or selection highlight for each player.
public RectTransform cursor;
// Choose which UI element this player should select by default.
public Selectable defaultSelection;
// Names for this player's axes & buttons,
// so they're not both listening to the same inputs.
public string horizontalAxis;
public string verticalAxis;
public string acceptButton;
public string cancelButton;
// To make an analog stick "clicky" - you might need to soup-up the
// sensitivity & gravity values beyond their defaults in the Input Manager.
public float activationThreshold = 0.1f;
// Debouncing / rejecting holds on the axis.
Vector2 _axisMemory;
// Keeping track of current selection state.
Selectable _currentSelection;
static Dictionary<Selectable, int> _selectionCount = new Dictionary<Selectable, int>();
// Expose an event for when the player's selection changes.
// Hook into this to update your portrait / etc.
[System.Serializable]
public class SelectionEvent : UnityEvent<Selectable> { }
public SelectionEvent onSelect;
void Start() {
// Selection has to start somewhere
// (you can also inject this from the script that spawns the player
// or handles joining in progress, or choose a default selection randomly)
Select(defaultSelection);
}
void Select(Selectable target) {
// A more complete solution might see us create our own EventSystem
// or InputModule that's multiplayer-aware, but this will do for now.
var fakeEvent = new BaseEventData(EventSystem.current);
// Deselect the old selection if we're the last one to leave it.
// (But don't deselect something another player is still hovering over)
if (_currentSelection != null && --_selectionCount[_currentSelection] == 0) {
_currentSelection.OnDeselect(fakeEvent);
_selectionCount.Remove(_currentSelection);
}
// Handle highlighting our current selection.
_currentSelection = target;
if (_selectionCount.ContainsKey(_currentSelection))
_selectionCount[_currentSelection]++;
else {
_selectionCount.Add(_currentSelection, 1);
target.OnSelect(fakeEvent);
}
// Position our cursor over the selected control.
// There are nicer ways to animate this, but this served for a quick test.
var rect = ((RectTransform)target_currentSelection.transform);
cursor.anchorMin = rect.anchorMin;
cursor.anchorMax = rect.anchorMax;
// Let listeners know that this player's selection has changed.
if(onSelect != null)
onSelect.Invoke(_currentSelection);
}
void Update() {
Vector2 input = new Vector2(Input.GetAxis(horizontalAxis), Input.GetAxis(verticalAxis));
Selectable nextSelection = null;
// Don't keep bouncing down the list until the player releases/reverses.
// TODO: handle repeat rate for holding in one direction, if desired.
if (input.x * _axisMemory.x <= 0f) {
_axisMemory.x = 0f;
if (Mathf.Abs(input.x) > activationThreshold) {
if (input.x > 0f) {
nextSelection = _currentSelection.FindSelectableOnRight();
_axisMemory.x = 1f;
} else {
nextSelection = _currentSelection.FindSelectableOnLeft();
_axisMemory.x = -1f;
}
}
}
if (input.y * _axisMemory.y <= 0f) {
_axisMemory.y = 0f;
if (Mathf.Abs(input.y) > activationThreshold) {
if (input.y > 0f) {
nextSelection = _currentSelection.FindSelectableOnUp();
_axisMemory.y = 1f;
} else {
nextSelection = _currentSelection.FindSelectableOnDown();
_axisMemory.y = -1f;
}
}
}
if(nextSelection != null) {
Select(nextSelection);
} else if(Input.GetButtonDown(acceptButton)) {
var clickable = _currentSelection.GetComponent<IPointerClickHandler>();
if(clickable != null && _currentSelection.IsInteractable()) {
clickable.OnPointerClick(new PointerEventData(EventSystem.current));
}
}
}
}
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
public class IndividualSelection : MonoBehaviour
{
// Use this to position a cursor or selection highlight for each player.
public RectTransform cursor;
// Choose which UI element this player should select by default.
public Selectable defaultSelection;
// Names for this player's axes & buttons,
// so they're not both listening to the same inputs.
public string horizontalAxis;
public string verticalAxis;
public string acceptButton;
public string cancelButton;
// To make an analog stick "clicky" - you might need to soup-up the
// sensitivity & gravity values beyond their defaults in the Input Manager.
public float activationThreshold = 0.1f;
// Debouncing / rejecting holds on the axis.
Vector2 _axisMemory;
// Keeping track of current selection state.
Selectable _currentSelection;
static Dictionary<Selectable, int> _selectionCount = new Dictionary<Selectable, int>();
// Expose an event for when the player's selection changes.
// Hook into this to update your portrait / etc.
[System.Serializable]
public class SelectionEvent : UnityEvent<Selectable> { }
public SelectionEvent onSelect;
void Start() {
// Selection has to start somewhere
// (you can also inject this from the script that spawns the player
// or handles joining in progress, or choose a default selection randomly)
Select(defaultSelection);
}
void Select(Selectable target) {
// A more complete solution might see us create our own EventSystem
// or InputModule that's multiplayer-aware, but this will do for now.
var fakeEvent = new BaseEventData(EventSystem.current);
// Deselect the old selection if we're the last one to leave it.
// (But don't deselect something another player is still hovering over)
if (_currentSelection != null && --_selectionCount[_currentSelection] == 0) {
_currentSelection.OnDeselect(fakeEvent);
_selectionCount.Remove(_currentSelection);
}
// Handle highlighting our current selection.
_currentSelection = target;
if (_selectionCount.ContainsKey(_currentSelection))
_selectionCount[_currentSelection]++;
else {
_selectionCount.Add(_currentSelection, 1);
target.OnSelect(fakeEvent);
}
// Position our cursor over the selected control.
// There are nicer ways to animate this, but this served for a quick test.
var rect = ((RectTransform)target.transform);
cursor.anchorMin = rect.anchorMin;
cursor.anchorMax = rect.anchorMax;
}
void Update() {
Vector2 input = new Vector2(Input.GetAxis(horizontalAxis), Input.GetAxis(verticalAxis));
Selectable nextSelection = null;
// Don't keep bouncing down the list until the player releases/reverses.
// TODO: handle repeat rate for holding in one direction, if desired.
if (input.x * _axisMemory.x <= 0f) {
_axisMemory.x = 0f;
if (Mathf.Abs(input.x) > activationThreshold) {
if (input.x > 0f) {
nextSelection = _currentSelection.FindSelectableOnRight();
_axisMemory.x = 1f;
} else {
nextSelection = _currentSelection.FindSelectableOnLeft();
_axisMemory.x = -1f;
}
}
}
if (input.y * _axisMemory.y <= 0f) {
_axisMemory.y = 0f;
if (Mathf.Abs(input.y) > activationThreshold) {
if (input.y > 0f) {
nextSelection = _currentSelection.FindSelectableOnUp();
_axisMemory.y = 1f;
} else {
nextSelection = _currentSelection.FindSelectableOnDown();
_axisMemory.y = -1f;
}
}
}
if(nextSelection != null) {
Select(nextSelection);
} else if(Input.GetButtonDown(acceptButton)) {
var clickable = _currentSelection.GetComponent<IPointerClickHandler>();
if(clickable != null && _currentSelection.IsInteractable()) {
clickable.OnPointerClick(new PointerEventData(EventSystem.current));
}
}
}
}
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
public class IndividualSelection : MonoBehaviour
{
// Use this to position a cursor or selection highlight for each player.
public RectTransform cursor;
// Choose which UI element this player should select by default.
public Selectable defaultSelection;
// Names for this player's axes & buttons,
// so they're not both listening to the same inputs.
public string horizontalAxis;
public string verticalAxis;
public string acceptButton;
public string cancelButton;
// To make an analog stick "clicky" - you might need to soup-up the
// sensitivity & gravity values beyond their defaults in the Input Manager.
public float activationThreshold = 0.1f;
// Debouncing / rejecting holds on the axis.
Vector2 _axisMemory;
// Keeping track of current selection state.
Selectable _currentSelection;
static Dictionary<Selectable, int> _selectionCount = new Dictionary<Selectable, int>();
// Expose an event for when the player's selection changes.
// Hook into this to update your portrait / etc.
[System.Serializable]
public class SelectionEvent : UnityEvent<Selectable> { }
public SelectionEvent onSelect;
void Start() {
// Selection has to start somewhere
// (you can also inject this from the script that spawns the player
// or handles joining in progress, or choose a default selection randomly)
Select(defaultSelection);
}
void Select(Selectable target) {
// A more complete solution might see us create our own EventSystem
// or InputModule that's multiplayer-aware, but this will do for now.
var fakeEvent = new BaseEventData(EventSystem.current);
// Deselect the old selection if we're the last one to leave it.
// (But don't deselect something another player is still hovering over)
if (_currentSelection != null && --_selectionCount[_currentSelection] == 0) {
_currentSelection.OnDeselect(fakeEvent);
_selectionCount.Remove(_currentSelection);
}
// Handle highlighting our current selection.
_currentSelection = target;
if (_selectionCount.ContainsKey(_currentSelection))
_selectionCount[_currentSelection]++;
else {
_selectionCount.Add(_currentSelection, 1);
target.OnSelect(fakeEvent);
}
// Position our cursor over the selected control.
// There are nicer ways to animate this, but this served for a quick test.
var rect = ((RectTransform)_currentSelection.transform);
cursor.anchorMin = rect.anchorMin;
cursor.anchorMax = rect.anchorMax;
// Let listeners know that this player's selection has changed.
if(onSelect != null)
onSelect.Invoke(_currentSelection);
}
void Update() {
Vector2 input = new Vector2(Input.GetAxis(horizontalAxis), Input.GetAxis(verticalAxis));
Selectable nextSelection = null;
// Don't keep bouncing down the list until the player releases/reverses.
// TODO: handle repeat rate for holding in one direction, if desired.
if (input.x * _axisMemory.x <= 0f) {
_axisMemory.x = 0f;
if (Mathf.Abs(input.x) > activationThreshold) {
if (input.x > 0f) {
nextSelection = _currentSelection.FindSelectableOnRight();
_axisMemory.x = 1f;
} else {
nextSelection = _currentSelection.FindSelectableOnLeft();
_axisMemory.x = -1f;
}
}
}
if (input.y * _axisMemory.y <= 0f) {
_axisMemory.y = 0f;
if (Mathf.Abs(input.y) > activationThreshold) {
if (input.y > 0f) {
nextSelection = _currentSelection.FindSelectableOnUp();
_axisMemory.y = 1f;
} else {
nextSelection = _currentSelection.FindSelectableOnDown();
_axisMemory.y = -1f;
}
}
}
if(nextSelection != null) {
Select(nextSelection);
} else if(Input.GetButtonDown(acceptButton)) {
var clickable = _currentSelection.GetComponent<IPointerClickHandler>();
if(clickable != null && _currentSelection.IsInteractable()) {
clickable.OnPointerClick(new PointerEventData(EventSystem.current));
}
}
}
}