How to calculate sensitivity based on the width/height of the screen? - unity3d

Here's my idea: I wanted to have a scrollview where the user could both scroll the component in it, and click on it.
After hours of testing/search, I've finally managed to make this working with the following code.
My problem is in the comparison Math.Abs(eventData.delta.x) > 1.0f. I consider that if the mouse "moves more than 1.0f" then it's dragging, else I consider this as a click.
The value 1.0f works perfectly on all the devices with a mouse = it's easy not to move, and big screen (tablets), when you click. But on smartphones, ie my Galaxy S6 or the 3 other ones I've tried, it's very sensitive and almost impossible to make a click.
How could you programmatically handle this? Is there a DPI or something to take in account and based on this, multiply my 1.0f by the resolution?
public class BoardHandler : EventTrigger
{
private static GameObject _itemDragged;
private static bool _isDragging;
private static ScrollRect _scrollRect;
public override void OnPointerClick(PointerEventData data)
{
if (_isDragging) {
return;
}
Debug.Log("Click");
}
public override void OnBeginDrag(PointerEventData eventData)
{
if (Math.Abs(eventData.delta.x) > 1.0f ||
Math.Abs(eventData.delta.y) > 1.0f) {
_scrollRect.OnBeginDrag(eventData);
_isDragging = true;
}
}
public override void OnDrag(PointerEventData eventData)
{
if (_isDragging) {
_scrollRect.OnDrag(eventData);
}
}
public override void OnEndDrag(PointerEventData eventData)
{
if (!_isDragging) {
return;
}
_scrollRect.OnEndDrag(eventData);
_isDragging = false;
}
private void Start()
{
_scrollRect = GetComponentInParent<ScrollRect>();
}
}

I've used TouchScript in the past to cover multiple devices within one project, like from 9 screens 6k screen array to tablets. They have a set of utilities to handle multiple resolution and dpi.
Check out the the UpdateResolution method in TouchManagerInstance.
hth.

Related

Know How many Buttons of the Joystick Are pressed in unity input unity system

I recently asked a similar question with keyboard, and was able to solve it, now I'm using input unity system to do the same thing but with joystick control.
With this code it detects 1, but when I press a second button it appears as if the first one had stopped pressing it. I would like that if I press 1 it increases to 1 and if I press two it increases to two and so on.
public class JoystickInput : MonoBehaviour
{
ControlOfInputs ControlOfInputsScript;
private InputAction test;
private InputActionReference Actionreference;
public float[] Contadores;
public int BotonesPresionados;
void Awake()
{
ControlOfInputsScript = new ControlOfInputs();
// ControlOfInputsScript.Gameplay.Arriba. += ctx => Fire();
}
private void OnEnable()
{
ControlOfInputsScript.Enable();
}
private void OnDisable()
{
ControlOfInputsScript.Disable();
}
// Update is called once per frame
private void Update()
{
//Boton Arriba
if (ControlOfInputsScript.Gameplay.Arriba.WasPerformedThisFrame())
{
Debug.Log("Presionado");
BotonesPresionados++;
}
if (ControlOfInputsScript.Gameplay.Arriba.WasReleasedThisFrame())
{
Debug.Log("DejoDePresionar");
BotonesPresionados--;
}
//Boton Abajo
if (ControlOfInputsScript.Gameplay.abajo.WasPerformedThisFrame())
{
Debug.Log("Presionado");
BotonesPresionados++;
}
if (ControlOfInputsScript.Gameplay.abajo.WasReleasedThisFrame())
{
Debug.Log("DejoDePresionar");
BotonesPresionados--;
}

Does Unity have a built in function that is triggered when Screen.height or Screen.width change?

I have build some custom UI scaling features for a mobile app built in Unity to compensate for various phone screen ratios. I would also like protection against screen size changes, as with the rise of foldable phones, I presume that is a risk.
The preliminary solution I have implemented is to have a script attached to the objects that must potentially resize, structured like this:
public class ScreenElementSizer : MonoBehaviour {
private int screenWidth_1 = Screen.width;
private int screenHeight_1 = Screen.height;
// Start is called before the first frame update
void Start() {
ScreenResized();
}
// Resizing functions here
private void ScreenResized() {
}
// On every screen refresh
void Update()
{
if ((Screen.width != screenWidth_1) || (Screen.height != screenHeight_1)) {
ScreenResized();
screenWidth_1 = Screen.width;
screenHeight_1 = Screen.height;
}
}
As you can see it's a pretty simple solution where I'm storing the prior sample's Screen.width and Screen.height in variables (screenWidth_1 & screenHeight_1), then comparing them on each sample (update) and if there is a discrepancy (screen change) trigger the resizer script.
It works fine of course. I think this is pretty standard coding logic. It's probably not expensive to run one/two extra if statements like this per object per sample.
I'm just new to Unity and wondering if there's any better, built in, or more efficient way to do this task.
To be clear, I am aware of the built in canvas resizer tools that scale based on width and height. I specifically am referring to the case where you want to apply some special script based resizing logic like this when the screen size changes.
Thanks for any thoughts.
There is no built-in event, but you can make your own event.
DeviceChange.cs -
using System;
using System.Collections;
using UnityEngine;
public class DeviceChange : MonoBehaviour
{
public static event Action<Vector2> OnResolutionChange;
public static event Action<DeviceOrientation> OnOrientationChange;
public static float CheckDelay = 0.5f; // How long to wait until we check again.
static Vector2 resolution; // Current Resolution
static DeviceOrientation orientation; // Current Device Orientation
static bool isAlive = true; // Keep this script running?
void Start() {
StartCoroutine(CheckForChange());
}
IEnumerator CheckForChange(){
resolution = new Vector2(Screen.width, Screen.height);
orientation = Input.deviceOrientation;
while (isAlive) {
// Check for a Resolution Change
if (resolution.x != Screen.width || resolution.y != Screen.height ) {
resolution = new Vector2(Screen.width, Screen.height);
if (OnResolutionChange != null) OnResolutionChange(resolution);
}
// Check for an Orientation Change
switch (Input.deviceOrientation) {
case DeviceOrientation.Unknown: // Ignore
case DeviceOrientation.FaceUp: // Ignore
case DeviceOrientation.FaceDown: // Ignore
break;
default:
if (orientation != Input.deviceOrientation) {
orientation = Input.deviceOrientation;
if (OnOrientationChange != null) OnOrientationChange(orientation);
}
break;
}
yield return new WaitForSeconds(CheckDelay);
}
}
void OnDestroy(){
isAlive = false;
}
}
Implementation -
DeviceChange.OnOrientationChange += MyOrientationChangeCode;
DeviceChange.OnResolutionChange += MyResolutionChangeCode;
DeviceChange.OnResolutionChange += MyOtherResolutionChangeCode;
void MyOrientationChangeCode(DeviceOrientation orientation)
{
}
void MyResolutionChangeCode(Vector2 resolution)
{
}
Credit - DougMcFarlane

How To Change CineMachine Dead Zone Width With Code? UNITY

So I was wondering how I could change my dead zone width or height with coding I have a collision detector so when my player collides with that rectangle I want to change the dead zone width to 2 but I have no idea on how to do this I tried to search online for an answer but couldn't so I wanted to ask here to see if anyone knows the answer.
private void OnTriggerEnter2D(Collider2D other)
{
if (other.gameObject.CompareTag("dead"))
{
Debug.Log("Dead Zone Width Changed");
}
}
Rest of My Code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Cinemachine;
public class stopcamera : MonoBehaviour
{
public CinemachineVirtualCamera cam;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
}
private void OnTriggerEnter2D(Collider2D other)
{
if (other.gameObject.CompareTag("dead"))
{
var composer = cam.GetCinemachineComponent<CinemachineComposer>();
composer.m_DeadZoneWidth = 2f;
Debug.Log("saman the third");
}
}
}
I want to change the dead zone width to 2
To change a specific value of your Cinemachine virtual camera. You first need to get the ComponentBase of either (Aim, Body, or Noise), depending on which stage you want to change the values at. You can do that with GetCinemachineComponent(CinemachineCore.Stage.Body).
To then change the value you need to ensure the type your ComponentBase is set to the same value as in the Inspector. In your case that would be the CineMachineFramingTransposer.
Example:
[SerializeField]
private CinemachineVirtualCamera virtualCam;
private CinemachineComponentBase componentBase;
private void Start() {
// Get the componentBase of our virutalCam to adjust its values via code.
componentBase = virtualCam.GetCinemachineComponent(CinemachineCore.Stage.Body);
}
private void OnTriggerEnter2D(Collider2D other) {
if (!other.gameObject.CompareTag("dead")) {
return;
}
// Check if the CinemachineCore.Stage.Body ComponentBase,
// is set to the CinemachineFramingTransposer.
if (componentBase is CinemachineFramingTransposer) {
var framingTransposer = componentBase as CinemachineFramingTransposer;
// Now we can change all its values easily.
framingTransposer.m_DeadZoneWidth = 2f;
Debug.Log("Dead Zone Width Changed");
}
}

How to set the position of a gameobject into initial position after grabbing ended using hand in SteamVR using Unity?

Pretty new to VR.I took a gameobject from an intial position by grabbing.When I grab a helmet and touch my body collider it hides the helmet.So next I may pick glasses and apply it to my body(Hides the GameObject).Next when I put the Incorrect Helmet the first helmet should go back to its initial position and should be seen in the scene.Similarily there are many GameObjects in the scene
private void OnTriggerEnter(Collider other)
{
if (other.gameObject.tag == "Helmet")
{
HideGameObject();
}
if (other.gameObject.tag == "Glasses")
{
HideGameObject();
}
if (other.gameObject.tag == "EarMuff")
{
HideGameObject();
}
if (other.gameObject.tag == "IncorrectHelmet")
{
HideGameObject();
}
if (other.gameObject.tag == "IncorrectGlasses")
{
HideGameObject();
}
if (other.gameObject.tag == "IncorrectEarMuff")
{
HideGameObject();
sendPickValues.Invoke(2, 0);
}
}
//Another script to set the GameObjects position
public class BackToPosition : MonoBehaviour
{
private Vector3 initialPosition;
private Quaternion initialRotation;
GameObject prevObject;
GameObject currObject;
// Start is called before the first frame update
void Start()
{
initialPosition = transform.position;
initialRotation = transform.rotation;
}
// Update is called once per frame
void Update()
{
}
public void BackToInitialPosition()
{
Debug.Log("Entered");
transform.position = initialPosition;
transform.rotation = initialRotation;
}
}
I am not trying to set the previous grabbed object to initial position.I may select wrong helmet first and pick many other matching gameobjects and later change to correct helmet.At that time first helmet should go to initial position.
This is a script that I use in SteamVR to grab and release a boat's rudder handle but it should be usable for you too:
[RequireComponent(typeof(Interactable))]
public class HandAttacher : MonoBehaviour
{
public UnityEvent OnGrab;
public UnityEvent OnRelease;
public UnityEvent OnHandEnter;
public UnityEvent OnHandLeave;
private Interactable interactable;
void Awake()
{
interactable = GetComponent<Interactable>();
}
/// this magic method is called by hand while hovering
protected virtual void HandHoverUpdate(Hand hand)
{
GrabTypes startingGrabType = hand.GetGrabStarting();
if (interactable.attachedToHand == null && startingGrabType != GrabTypes.None)
{
hand.AttachObject(gameObject, startingGrabType, Hand.AttachmentFlags.DetachFromOtherHand | Hand.AttachmentFlags.ParentToHand);
OnGrab?.Invoke();
}
}
protected virtual void OnHandHoverBegin(Hand hand)
{
OnHandEnter?.Invoke();
}
protected virtual void OnHandHoverEnd(Hand hand)
{
OnHandLeave?.Invoke();
}
protected virtual void HandAttachedUpdate(Hand hand)
{
if (hand.IsGrabEnding(gameObject))
{
hand.DetachObject(gameObject);
OnRelease?.Invoke();
}
}
}
Basically it creates Unity Events that you can add Listeners to in the Editor's Inspector window, or in code.
So in your use case, I would add a listener to OnRelease, and reset the GameObject's position and rotation to whatever it was before.
I tried using BackToPosition or something similar in Update comparing the original position to its current to reset the object's position, and the object keeps resetting on a loop instead of resetting to its original position and stopping.

How do I stop my objects from going through walls when I pick them up and drop them down?

When I pick an object up I can glitch it through the map making it fall out of the world. This happens when I pick it up and drop the object half way through the floor. The outcome I receive is not what I was expecting what can I do to fix this. Also yes the colliders and rigidbody's are setup correctly.
public GameObject PressEtoInteractText;
public bool pickup, inrange;
public Collider Playercol;
public Vector3 guide;
private GameObject temp;
private Rigidbody rb;
void Update()
{
if (Input.GetKeyDown(KeyCode.E) && inrange == true)
{
PressEtoInteractText.SetActive(false);
pickup = true;
}
if (Input.GetMouseButtonDown(0) && pickup == true)
{
pickup = false;
Playercol.isTrigger = true;
}
UpdatePickUpFollow();
}
void OnTriggerEnter(Collider other)
{
if (other.gameObject.tag == "Interact")
{
PressEtoInteractText.SetActive(true);
temp = other.gameObject;
inrange = true;
}
if (other.gameObject.tag == "Interact" && temp.transform.position == guide)
{
return;
}
}
void OnTriggerExit(Collider other)
{
if (other.gameObject.name != "Interact")
{
PressEtoInteractText.SetActive(false);
inrange = false;
}
}
public void PickUp()
{
rb = temp.GetComponent<Rigidbody>();
rb.MovePosition(transform.position += guide);
Playercol.isTrigger = false;
}
public void UpdatePickUpFollow()
{
if (pickup == true)
{
PickUp();
}
}
If you deactivate the objects collider with tempCol.enabled = false; it will not register any collisions and you can just push or pull it through walls all day. Make sure to remove that from your script.
Also, using MoveTowards can cause issues with collision detection. In my experience it is best to use AddForce or MovePosition to move the Rigidbody component instead of modifying the Transform directly. Try Rigidbody.MovePosition(Vector3 position). Maybe this works better for you.
Here is a link to the documentation page. It's basically your exact use case:
Rigidbody.MoveTowards Unity Docs
(Hint: notice how they use FixedUpdate instead of regular Update. You should also always do this when working with Rigidbodies because it is synced with physics updates.)
//Edit:
This is a little implementation of your code in a cleaner and hopefully correct way. I have not tested this and there is propably something I missed. Use this as a reference to build your own working solution.
//set in inspector
public Transform guidePosition;
public GameObject pressEToInteractText;
private bool isObjectInRange;
private GameObject inRangeObject;
private bool isObjectPickedUp;
private Rigidbody pickedUpRigidBody;
/* check if button is pressed.
* If no object is in hand -> pick up;
* if object is in hand -> drop;
*/
private void FixedUpdate()
{
if (Input.GetKeyDown(KeyCode.E) && isObjectInRange && !isObjectPickedUp)
{
PickupObject();
}
if(Input.GetKeyDown(KeyCode.E) && isObjectPickedUp)
{
DropObject();
}
if (isObjectPickedUp)
{
PickedUpObjectFollow();
}
}
//save references
private void PickupObject()
{
pickedUpRigidBody = inRangeObject.GetComponent<Rigidbody>();
isObjectPickedUp = true;
}
//remove saved references
private void DropObject()
{
isObjectPickedUp = false;
pickedUpRigidBody = null;
}
//move position to guidePosition
private void PickedUpObjectFollow()
{
pickedUpRigidBody.MovePosition(guidePosition.position);
}
private void OnTriggerEnter(Collider other)
{
if (other.CompareTag("Interact") && !isObjectPickedUp)
{
pressEToInteractText.SetActive(true);
isObjectInRange = true;
inRangeObject = other.gameObject;
}
}
private void OnTriggerExit(Collider other)
{
if(other.CompareTag("Interact") && other.gameObject == inRangeObject)
{
pressEToInteractText.SetActive(false);
isObjectInRange = false;
inRangeObject = null;
}
}
You could do multiple things. Unfortunately the way VR tracks the hands and there is no way to stop ur hands in real life your hand will defy the physics in the application.
I would either make the item drop from the users hand when it gets close enough to a certain area that you dont want it going through.
Or you can make it that if it does pass through a space you can report the item right above the floor it went through.
Ive looked long and hard for an answer to this and it doesnt seem like it can at this point be blocked from moving through if its currently grabbed.