I created a game that uses Unity's OpenXR. I am trying to make the game compatible with both Oculus and HTC Vive.
In the code below, I am just trying to detect when the user presses their right primary button. This works just fine on Oculus, but I've gotten many Vive users who say that this doesn't work.
bool primary = false;
bool secondary = false;
bool trigger = false;
bool grip = false;
public InputDevice device;
void Start()
{
List<InputDevice> devices = new List<InputDevice>();
InputDevices.GetDevicesAtXRNode(XRNode.RightHand, devices);
device = devices[0];
}
public void Update(){
bool newGrip;
bool newTrigger;
bool newSecondary;
bool newPrimary;
device.TryGetFeatureValue(CommonUsages.gripButton, out newGrip);
device.TryGetFeatureValue(CommonUsages.triggerButton, out newTrigger);
device.TryGetFeatureValue(CommonUsages.secondaryButton, out newSecondary);
device.TryGetFeatureValue(CommonUsages.primaryButton, out newPrimary);
if (newGrip != grip)
{
if (newGrip)
{
//grip pressed
}
else
{
//grip released
}
grip = newGrip;
}
if (newTrigger != trigger)
{
if (newTrigger)
{
//trigger pressed
}
else
{
//trigger released
}
trigger = newTrigger;
}
if (newSecondary != secondary)
{
if (newSecondary)
{
//secondary pressed
}
else
{
//secondary released
}
secondary = newSecondary;
}
if (newPrimary != primary)
{
if (newPrimary)
{
//primary pressed
}
else
{
//primary released
}
primary = newPrimary;
}
}
Note that the triggers and grips get detected just fine on both HTC Vive and Oculus. The Secondary button isn't supported on Vive, so I'm assuming device.TryGetFeatureValue(CommonUsages.secondaryButton, out newSecondary) always returns false. It is just the primary button that is giving me an unexpected issue.
Now according to unity (https://docs.unity3d.com/Manual/xr_input.html), the Vive's primary button is supported.
Does anyone know why the above implementation wouldn't recognize the Vive's primary button being pressed?
Also, is there any way to detect the menu button for Vive players with unity's OpenXR? The link above says the menu button is not supported, but having an extra button to work with would be useful.
Thank you in advance!
The problem was fixed by converting to unity's new action-based input system.
Related
I'm working on photon game where I have to show the player name over their everything is working fine but there's an issue , when you create a room and join it , the host name is properly visible to everyone but when it comes to the client Client name is some random numbers even though client is also setting their user name before joining the room , here's my code which is handling the naming system
public void JoinRoom()
{
connectButton.interactable = false;
PhotonNetwork.NickName = userId.text;
PhotonNetwork.JoinRoom("Basic");
}
Try this
PhotonView view;
void Start()
{
view = GetComponent<PhotonView>();
}
void JoinRoom()
{
if(view.IsMine)
{
userId.text = PhotonNetwork.NickName;
}else
{
userId..text = view.Owner.NickName;
}
}
public AudioSource menumusic;
public Button SoundButton;
public Sprite Soundoff;
public Sprite SoundOn;
bool isClicked = true;
private void Awake()
{
DontDestroyOnLoad(this.gameObject);
SoundButton.image.sprite = SoundOn;
}
// Start is called before the first frame update
void Start()
{
menumusic = GetComponent<AudioSource>();
}
// Update is called once per frame
void Update()
{
SoundButton.onClick.AddListener(BtnOnClick);
}
void BtnOnClick()
{
changeState();
}
private void changeState()
{
isClicked = !isClicked;
if(isClicked)
{
SoundButton.image.sprite = SoundOn;
menumusic.Play();
}
else
{
SoundButton.image.sprite = Soundoff;
menumusic.Stop();
}
}
I have script like this And I am using on menu scene but when I switch to game scene with other button , this script works in there to except that he can not find soundbutton , how can I link to sound button from another scene should I create same to game scene too ? , sorry I am new on unity.
It looks like you're setting the reference to your "SoundButton" object in the inspector. When you change scenes, you'll need to update the reference on this script to refer to the new button.
There are many ways to do this. You could just tag the soundButtons with "soundButton".
Then, wherever you're loading a new scene, update the reference by searching for the tag.
SoundButton = GameObject.FindObjectWithTag("soundButton").GetComponent<Button>();
It would also be possible to have a SoundButton script which you place on every sound button. This could then reference a static SoundManager object, and tell it to toggle the sound on or off when clicked. This is probably a cleaner way to do it.
Also note: You're adding a listener to the soundButton in Update() (i.e. every frame). This is unnecessary. Add the listener one time in Start(), or when you change Scenes, and it will trigger the event whenever the button is clicked. There is no need to use Update() for this process.
If it still isn't working, you can also check the variable before using it, and get the reference again if it's null:
if(soundButton == null)
{
SoundButton = GameObject.FindObjectWithTag("soundButton").GetComponent<Button>();
}
When the Continue Ads gameobject is closed, the Game over button works, but when the Continue Ads is turned on, the Game over button does not work. I cannot understand why I make a mistake?
!Me fixed the problem, the problem is that I noticed that it is related to the continue ads dimension, thank you for your time.
void Update()
{
if (Application.internetReachability == NetworkReachability.NotReachable)
{
//Debug.Log("No internet access");
Destroy(GameObject.FindWithTag("ContinuePanel"));
}
else
{
//Debug.Log("internet connection");
if (tag == "Skull")
{
ContinueAds();
}
}
}
public void ContinueAds()
{
gameObject.AddComponent<AdsScript1>();
gameoverPanel.SetActive(false);
PausedGamePanel();
}
public void GameoverButton()
{
gameoverPanel.SetActive(false);
SceneManager.LoadScene(0);
}
Your continue ads object is in gameover panel. When you set gameoverPanel.SetActive(false) you also do the same on the child objects.
Hi! If you want to save time and still help please read this section and the last one to get glimpse of my problem (part 1 and 6). So much code was needed to fully present problem
Using Unity 2019.3.0b2
1.
Im creating WebGL application that allows you to customize your character via assets from downloaded asset bundles. So far I got downloading and instantiating work, but I also want to change downloaded gameobject material to custom color from color-picker. In this case I need to refer to adequate Renderer. I've got function that sends request and it goes like so:
private IEnumerator SendRequestCoroutine(UnityWebRequest request, UnityAction<UnityWebRequest> OnDownloadCompleteHandler, UnityAction<float> OnDownloadProgressHandler = null)
{
request.SendWebRequest();
while(!request.isDone)
{
if(OnDownloadProgressHandler != null)
OnDownloadProgressHandler.Invoke(request.downloadProgress);
yield return null;
}
// Has to fire once more because progress stops at around 0.87,
// never returning 1 unless download is finished.
if (OnDownloadProgressHandler != null)
OnDownloadProgressHandler.Invoke(1);
OnDownloadCompleteHandler.Invoke(request);
}
2.
I fire this coroutine like that:
public void DownloadAssetBundle(string url, ProgressBar bar = null)
{
if (isRequestSend)
{
Alerter.ShowMessage("Request has been already send, please wait untill complete.");
return;
}
UnityWebRequest request = HttpService.Instance.GetAssetBundleRequest(url);
if(bar != null)
{
HttpService.Instance.SendDownloadRequest
(
request,
(rq) => { OnDownloadAssetBundleCompleteHandler(rq); },
(rq) => OnDownloadProgressHandler(rq, bar)
);
isRequestSend = true;
}
else
{
HttpService.Instance.SendDownloadRequest
(
request,
(rq) => { OnDownloadAssetBundleCompleteHandler(rq); }
);
isRequestSend = true;
}
}
3.
OnDownloadAssetBundleCompleteHandler looks like this:
//Function that will handle asset bundle when completed.
private void OnDownloadAssetBundleCompleteHandler(UnityWebRequest request)
{
isRequestSend = false;
if(request.isHttpError || request.isNetworkError)
{
//Handle downloading error
Alerter.ShowMessage("Seems like there was a problem with downloading, try again.");
}
else
{
AssetBundle bundle;
//Handle content update
bundle = DownloadHandlerAssetBundle.GetContent(request);
AssetBundleInfo assetBundleInfo = bundle.LoadAllAssets<AssetBundleInfo>().FirstOrDefault();
if (assetBundleInfo == null)
{
//Handle error
Alerter.ShowMessage("Couldn't read information about this Character Part. AssetBundleInfo null exception.");
bundle.Unload(false);
return;
}
GameObject goToLoad = null;
goToLoad = bundle.LoadAsset<GameObject>(assetBundleInfo.ObjectName);
if (goToLoad == null)
{
Alerter.ShowMessage("Couldn't read information about this Character Part. Downloaded asset's gameobject null exception.");
bundle.Unload(false);
return;
}
Sticher.ConnectComponent(goToLoad, assetBundleInfo.PartType);
ColorSetter.Instance.ChangeSelectedBodyPart(assetBundleInfo.PartType);
bundle.Unload(false);
}
}
4.
Now the final step is to set adequate transform so my script will search for component of type Renderer, get its material of index 0 as current material to modify, class that contain ChangeSelectedBodyPart function looks like so:
using Assets.Scripts.Models.Enums;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ColorSetter : MonoBehaviour
{
public Renderer rend;
public ColorPicker picker;
public static ColorSetter Instance;
private void Awake()
{
if(Instance != null)
{
Destroy(this);
}
else
{
Instance = this;
DontDestroyOnLoad(this);
}
}
// Start is called before the first frame update
void Start()
{
picker.onValueChanged.AddListener(color =>
{
if (rend == null)
return;
rend.material.SetColor("_BaseColor", color);
}
);
}
public void ChangeSelectedBodyPart(AvatarPartType p)
{
switch(p)
{
case AvatarPartType.ClothesUpper:
SetActiveMaterial(Sticher.Instance.Clothes_Upper);
break;
case AvatarPartType.ClothesLower:
SetActiveMaterial(Sticher.Instance.Clothes_Lower);
break;
case AvatarPartType.Shoes:
SetActiveMaterial(Sticher.Instance.Shoes);
break;
case AvatarPartType.Hair:
SetActiveMaterial(Sticher.Instance.Hair);
break;
case AvatarPartType.Skin:
SetActiveMaterial(Sticher.Instance.Skin);
break;
}
}
private void SetActiveMaterial(Transform parent)
{
rend = parent.GetComponentInChildren<Renderer>();
}
}
5.
PS. parent has only one child that contains Renderer component
Now, finally, problem is that I don't get proper material reference, I got the old one that is being set via toggle button, simply as that:
public void OnValueChanged(bool value)
{
if(value)
{
ColorSetter.Instance.ChangeSelectedBodyPart(PartType);
ButtonManager.Instance.RemoveAllButtonsFromPartsWindow();
ButtonManager.Instance.PopulatePartsPanelWithAvatarPartsOfType(PartType);
}
}
6.
So in conclusion when I press on "toggle button" that represents some avatar body/clothing part it sets its parent and material properly via function, even after asset bundle has been downloaded (but I have to click the same toggle again to make it work), but when I fire the same function in OnDownloadAssetBundleCompleteHandler just after asset been downloaded it doesn't work :S Why? Is it related with asset unloading speed? Any tips on fixing this?
In Game View it behave like that:
I fixed it. Since you can't use DestoyImmediate like #derHugo said because it can cause reference errors or even it could destroy assets permamently I had to use Destroy() instead before Instantiating new gameobject from assetbundle, however Destroy() will delete given object at the end of the frame while I try to access Renderer component on freshly instantiated GameObject just at the same frame, before old one is acutally destoryed. I fixed it yielding one frame using yield return new WaitForEndOfFrame(); just after function that takes care of destroying old GameObjects before trying to access new GameObject.
I know how to hide or display Virtual Keyboard using InputMethodManager.
But I want to enter text in EditText using Physical keyboard but I don't want to display Virtual Keyboard in Unity 3D Android.
How can I do that in Unity 3D?
There is no such thing as EditText in Unity. InputField is used to receive input from a device.
You can disable Virtual Keyword with InputField on Android. Not sure if this will work for other platforms.
Your InputField:
public InputField inputField;
Disable Virtual Keyboard:
inputField.keyboardType = (TouchScreenKeyboardType)(-1);
Enable Virtual Keyboard:
inputField.keyboardType = TouchScreenKeyboardType.Default;
If you run into weird issues, consider deriving your script from InputField, then disable Virtual Keyboard and finally, call the base Start function of the InputField:
public class HideVirtualKeyboard : InputField
{
protected override void Start()
{
keyboardType = (TouchScreenKeyboardType)(-1);
base.Start();
}
}
Old question I know, but what you really want is to set inputField.touchScreenKeyboard.active = false; You may need to hold it false in a LateUpdate
[SerializeField]
private bool keyboardEnabled = true;
// toggle this to toggle native keyboard activity
public bool KeyboardEnabled { get { return keyboardEnabled; } set { keyboardEnabled = value; } }
private void LateUpdate()
{
#if UNITY_ANDROID
if (inputField.touchScreenKeyboard != null && !keyboardEnabled)
{
inputField.touchScreenKeyboard.active = false;
}
#endif
}