Unable to play video clip downloaded from url using VideoPlayer in Unity - c#

I am trying to download a video(Bunny Video for testing) from a url and then play it using a video player component.
The issue I have here is that if I pass a pre downloaded clip or url to the video player in editor mode it plays video without any issue but when I try to add the VideoPlayer component dynamically to my gameobject and assign video clip to the video player, it never plays the video. Clip option always shows null in VideoPlayer component during play mode. Adding url also doesn't work when done via script.
Here is my script which downloads the video and assign it to the videoplayer.
public class DownloadScript : MonoBehaviour
{
private WWW Url;
private VideoPlayer Player;
public VideoClip clip;
public float test;
// Use this for initialization
void Start ()
{
this.gameObject.AddComponent<VideoPlayer>();
Player = this.gameObject.GetComponent<VideoPlayer>();
_Download();
/*Player.source=VideoSource.VideoClip;
Player.clip = clip;
Player.Prepare();*/
// Player.url = "http://www.quirksmode.org/html5/videos/big_buck_bunny.mp4";
}
// Update is called once per frame
void Update () {
if (!Player.isPlaying && Player.isPrepared)
{
Player.Play();
}
}
private IEnumerator WaitForDownload(string sURL)
{
Url = new WWW(sURL);
//WWW www = new WWW(sURL);
Debug.Log(Url.bytesDownloaded);
yield return Url;
if (Url.isDone)
{
Debug.Log("Player ready");
Invoke("PlayVideo",20f);
}
else
{
Debug.Log(Url.bytesDownloaded);
}
yield return Url;
}
private void _Download()
{
try
{
StartCoroutine(WaitForDownload("http://www.quirksmode.org/html5/videos/big_buck_bunny.mp4"));
// return Url.bytes;
}
catch(Exception e)
{
Debug.Log(e.ToString());
}
}
public void PlayVideo()
{
Debug.Log("url download complete");
File.WriteAllBytes(Application.dataPath + "/Resources"+"/bunny.mp4", Url.bytes);
clip =(VideoClip) Resources.Load("bunny.mp4");
Player.source=VideoSource.VideoClip;
Player.clip = clip;
test = 5.0f;
// Player.url = "http://www.quirksmode.org/html5/videos/big_buck_bunny.mp4";
if (Player.isPrepared)
{
Player.Play();
}
}
}
Here is the screenshot of the inspector panel at runtime.
I added a test variable in my script to verify if anything is getting updated at runtime or not. So the value of test variable is updated in the inspector panel but the video clip value doesn't update and that's why my video doesn't get played.
Any solution for this ?

So I had the same problem. Just got it resolved.
CODE :
private void Start()
{
StartCoroutine(this.loadVideoFromThisURL(videoUrl));
}
[SerializeField]
internal UnityEngine.Video.VideoPlayer myVideoPlayer;
string videoUrl ="http://www.quirksmode.org/html5/videos/big_buck_bunny.mp4";
private IEnumerator loadVideoFromThisURL(string _url)
{
UnityWebRequest _videoRequest = UnityWebRequest.Get (_url);
yield return _videoRequest.SendWebRequest();
if (_videoRequest.isDone == false || _videoRequest.error != null)
{ Debug.Log ("Request = " + _videoRequest.error );}
Debug.Log ("Video Done - " + _videoRequest.isDone);
byte[] _videoBytes = _videoRequest.downloadHandler.data;
string _pathToFile = Path.Combine (Application.persistentDataPath, "movie.mp4");
File.WriteAllBytes (_pathToFile, _videoBytes);
Debug.Log (_pathToFile);
StartCoroutine(this.playThisURLInVideo (_pathToFile));
yield return null;
}
private IEnumerator playThisURLInVideo(string _url)
{
myVideoPlayer.source = UnityEngine.Video.VideoSource.Url;
myVideoPlayer.url = _url;
myVideoPlayer.Prepare ();
while (myVideoPlayer.isPrepared == false)
{ yield return null;}
Debug.Log ("Video should play");
myVideoPlayer.Play ();
}
SOLUTION :
From your code, I see that you save your bytes to the Resources folder. That is not needed. Consequently you don't have to load it using Resources.Load.
Just pass in absolute filepath. I have tested in iOS and Mac OS. It works.

Related

NativeGallery work on Unity Editor but doesn't work on Android device?

I am implementing an user reporting function, trying to give user the ability to post picture to their report. Here the code:
public class ResponseImageHandler : MonoBehaviour
{
[SerializeField] GameObject[] imgHolder;
[SerializeField] Texture2D defaultTexture;
[SerializeField] TMP_Text imgIndicator;
Texture2D tempTexture;
int curIdx = 0;
void Start()
{
for (int i = 0; i < 4; i++)
{
imgHolder[i].transform.GetChild(0).gameObject.AddComponent<ButtonIndex>();
imgHolder[i].transform.GetChild(0).gameObject.GetComponent<ButtonIndex>().Init(i + 1);
imgHolder[i].GetComponent<Button>().interactable = true;
imgHolder[i].GetComponent<Button>().onClick.AddListener(ChoosePictureOnClick);
}
}
void ChoosePictureOnClick()
{
ChoosePicture(SystemInfo.maxTextureSize);
}
void ChoosePicture(int maxSize)
{
NativeGallery.Permission permission = NativeGallery.GetImageFromGallery((path) =>
{
if (path != null)
{
tempTexture = NativeGallery.LoadImageAtPath(path, maxSize, false);
if (tempTexture == null)
{
Debug.Log($"cant load image from path: {path}");
return;
}
else
{
imgHolder[curIdx].GetComponent<Button>().interactable = false;
imgHolder[curIdx].transform.GetChild(0).gameObject.SetActive(true);
imgHolder[curIdx].GetComponent<RawImage>().texture = tempTexture;
if (curIdx < 3)
{
curIdx++;
}
imgIndicator.text = $"{curIdx}/4";
}
}
});
imgHolder[curIdx].SetActive(true);
}
In Unity editor, i was able to insert all 3 slot with picture and an empty slot
Unity Editor
But in Android Studio Emulator and also on android device, i can only insert one picture and then no further slot appear to insert more picture
Android Studio Emulator
I have no idea what happen can any one give me an explanation? And how to fix the problem?
I'm using Unity 2021.3.5f1 version and NativeGallery asset to get picture from device.

Recycling a Button for individual object - Unity3D

I am a beginner in Unity and I am currently making a simple game. I have a problem where the button should reappear on the object I am getting contact with. It only shows on the first object that I put the script on.
What I wanted to do is to have a recyclable button that will appear whenever I have contact with objects like the "view" button in most of the games. What keeps happening in my project is that the collision is triggered but the position of the object is in the first object where I put the script to set it active and set the position. I put it in another object because I want the button to show in that object but it keeps appearing in the first object.
This is the script I used:
using UnityEngine;
using UnityEngine.UI;
public class InteractNPC : MonoBehaviour
{
//public Button UI;
[SerializeField] GameObject uiUse, nameBtn, dialogBox;
[SerializeField] TextAsset characterData;
// [SerializeField] UnityEngine.UI.Text dialogMessage;
private Transform head;
private Vector3 offset = new Vector3(0, 1.0f, 0);
// Start is called before the first frame update
void Start()
{
//uiUse = Instantiate(UI, FindObjectOfType<Canvas>().transform).GetComponent<Button>();
uiUse.gameObject.SetActive(true);
head = this.transform.GetChild(0);
uiUse.transform.position = Camera.main.WorldToScreenPoint(head.position + offset);
nameBtn.transform.position = Camera.main.WorldToScreenPoint(head.position + offset);
nameBtn.GetComponent<Button>().onClick.AddListener(onNameClick);
}
// Update is called once per frame
void Update()
{
uiUse.transform.position = Camera.main.WorldToScreenPoint(head.position + offset);
nameBtn.transform.position = Camera.main.WorldToScreenPoint(head.position + offset);
}
private void OnTriggerEnter(Collider collisionInfo)
{
if (collisionInfo.CompareTag("Player"))
{
//Character characterJson = JsonUtility.FromJson<Character>(characterData.text);
nameBtn.gameObject.SetActive(true);
// Text lbl = nameBtn.gameObject.GetComponentInChildren(typeof(Text), true) as Text;
// lbl.text = "BOBO";
// nameBtn.GetComponent<Button>().GetComponentInChildren<Text>().text = characterJson.name;
}
}
private void OnTriggerExit(Collider collisionInfo)
{
if (collisionInfo.CompareTag("Player"))
{
nameBtn.gameObject.SetActive(false);
}
}
// DIALOGUE SYSTEM
public void onNameClick()
{
Text dialogMessage, dialogName;
if (dialogBox.gameObject.activeInHierarchy)
{
dialogName = GameObject.Find("Canvas/DialogBox/DialogueName").GetComponent<Text>();
dialogMessage = GameObject.Find("Canvas/DialogBox/Dialogue").GetComponent<Text>();
if (dialogMessage != null && dialogName != null)
{
loadCharacterData(dialogMessage, dialogName);
Debug.Log("not null dialog message");
}
else
{
Debug.Log("null dialog message");
}
}
}
public void loadCharacterData(Text dialogMessage, Text dialogName)
{
Character characterJson = JsonUtility.FromJson<Character>(characterData.text);
dialogName.text = characterJson.name;
dialogMessage.text = characterJson.dialogs[0].choices[1];
}
}
Well your issue is that each and every instnce of your scrip will all the time overwrite
void Update()
{
uiUse.transform.position = Camera.main.WorldToScreenPoint(head.position + offset);
nameBtn.transform.position = Camera.main.WorldToScreenPoint(head.position + offset);
}
every frame.
What you want is set it only once in
[SerializeField] private Button nameBtn;
pivate Character character;
private void Start()
{
...
// do this only once!
character = JsonUtility.FromJson<Character>(characterData.text);
}
private void OnTriggerEnter(Collider collisionInfo)
{
if (collisionInfo.CompareTag("Player"))
{
nameBtn.gameObject.SetActive(true);
var lbl = nameBtn.gameObject.GetComponentInChildren<Text>(true);
lbl.text = characterJson.name;
var position = Camera.main.WorldToScreenPoint(head.position + offset);
uiUse.transform.position = position;
nameBtn.transform.position = position;
}
}
Further you will have another issue: Each and every instance of your script attaches a callback
nameBtn.GetComponent<Button>().onClick.AddListener(onNameClick);
that is not good! Now whenever you click the button once, the allback is fired for each and every instance of your script.
You would rather do this only if the click was actually invoked for the current object.
For this you could attach the listener only add-hoc when needed like e.g.
private void OnTriggerEnter(Collider collisionInfo)
{
if (collisionInfo.CompareTag("Player"))
{
...
nameBtn.onClick.RemoveListener(onNameClick);
nameBtn.onClick.AddListener(onNameClick);
}
}
private void OnTriggerExit(Collider other)
{
if (collisionInfo.CompareTag("Player"))
{
nameBtn.onClick.RemoveListener(onNameClick);
nameBtn.gameObject.SetActive(false);
}
}
ok we didn't know before your objects move
so well you will need to go back to what you had before and update the position continuously, BUT only while you are the current active object so e.g.
private static InteractNPC buttonOwner;
private Camera mainCamera;
void Update()
{
if(buttonOwner != this) return;
if(!mainCamera) mainCamera = Camera.main;
var position = mainCamera.WorldToScreenPoint(head.position + offset);
uiUse.transform.position = position;
nameBtn.transform.position = position;
}
private void OnTriggerEnter(Collider collisionInfo)
{
if (collisionInfo.CompareTag("Player") && !buttonOwner)
{
...
buttonOwner = this;
}
}
private void OnTriggerExit(Collider other)
{
if (collisionInfo.CompareTag("Player") && buttonOwner == this)
{
nameBtn.onClick.RemoveListener(onNameClick);
nameBtn.gameObject.SetActive(false);
buttonOwner = null;
}
}
As far as I see you never create a new Button, I guess that Unity may think, you want the same Button on all NPCs.
Maybe try to call the constructor for a new Button in the Start Method and see if it then generates a new Button per NPC.

How to use BroadcastMessage() for a function that was detached from the gameobject sending the message?

I'm probably not doing this properly at all. I'm trying to make a script that will allow dropping and throwing of a weapon, and I'm trying to keep the script on the weapon for prefab.
void PrepareObject() {
Rigidbody gameObjectsRigidBody = Stick.AddComponent<Rigidbody>();
gameObjectsRigidBody.mass = 0.1f;
Debug.Log("added rigidbody to weapon");
}
public void OnThrow() {
anims.SetTrigger("isThrowing");
PrepareObject();
StartCoroutine(LetGo());
}
private IEnumerator LetGo() {
Debug.Log("let go of weapon");
yield return new WaitForSeconds(1f);
Hand.transform.DetachChildren();
beingThrown = true;
}
public void OnInteract() {
playerInRange = Physics.CheckSphere(Stick.transform.position, interactRange, layerPlayer);
Debug.Log("Pressed interact");
if (playerInRange && beingThrown) {
Debug.Log("attempted pickup");
Stick.transform.SetParent(HandTransform);
} else return;
beingThrown = false;
}
When using this code, I get OnInteract() while I'm holding the weapon, but as soon as I LetGo(), I no longer get OnInteract(). I am using the Unity Input System BroadcastMessage() on the parent.. I'm kind of lost on what to do, any suggestions?

itineration problem with function after resume app

this probably is a noob question, but I can't figure out how resolve it.
Im opening/resuming my android app from a notification (intent data).
the app is opening fine from the intent.
the problem is if send the app to the background again after opening from the intent and later I resume again, the coroutine executes again every time I resume, because keeps getting the data from the "old" intent.
*is there some way of clear the intent data? (without close the app)
im trying something like: AndroidJavaObject saveIntent = curActivity.Call<AndroidJavaObject>("setData");
to replace the intent data so the next itineration dont get te correct values
*Im thinking to limit to 1, the times what the coroutine can be executed, but not is a "clean"solution.
can someone give me some guidance?.
this is my code so far:
void OnApplicationPause(bool appPaused)
{
if (!isOnAndroid || Application.isEditor) { return; }
if (!appPaused)
{
//Returning to Application
Debug.Log("Application Resumed");
StartCoroutine(LoadSceneFromFCM());
}
else
{
//Leaving Application
Debug.Log("Application Paused");
}
}
IEnumerator LoadSceneFromFCM()
{
AndroidJavaClass UnityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
AndroidJavaObject curActivity = UnityPlayer.GetStatic<AndroidJavaObject>("currentActivity");
AndroidJavaObject curIntent = curActivity.Call<AndroidJavaObject>("getIntent");
string sceneToLoad = curIntent.Call<string>("getStringExtra", "sceneToOpen");
//string extraInfo = curIntent.Call<string>("getStringExtra", "extraInfo"); use this for do some extra stuff
Scene curScene = SceneManager.GetActiveScene();
if (!string.IsNullOrEmpty(sceneToLoad) && sceneToLoad != curScene.name)
{
// If the current scene is different than the intended scene to load,
// load the intended scene. This is to avoid reloading an already acive
// scene.
Debug.Log("Loading Scene: " + sceneToLoad);
Handheld.SetActivityIndicatorStyle(AndroidActivityIndicatorStyle.Large);
Handheld.StartActivityIndicator();
yield return new WaitForSeconds(0f);
SceneManager.LoadScene(sceneToLoad);
}
}
this is working for me now:
void Awake()
{
DontDestroyOnLoad(gameObject);
}
void OnApplicationPause(bool appPaused)
{
AndroidJavaClass unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
AndroidJavaObject currentActivity = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity");
AndroidJavaObject intent = currentActivity.Call<AndroidJavaObject>("getIntent");
Scene curScene = SceneManager.GetActiveScene();
string sceneToLoad = intent.Call<string>("getStringExtra", "sceneToOpen");
if (sceneToLoad != null && sceneToLoad.Trim().Length > 0 && sceneToLoad != curScene.name)
{
Debug.Log("Load the Video Scene");
SceneManager.LoadScene(sceneToLoad);
//redirectToAppropriateScreen(intent, onClickScreen);
}
intent.Call("removeExtra", "sceneToOpen"); //this remove the data from the intent, so the function cant be completed after resume again.
Debug.Log("Extra data removed");
}

How do I go about muting a specific audio source in Unity?

So I'm trying to mute a specific audio source once my death menu pops up ingame. I tried with:
public void toggleEndMenu(float score)
{
GameObject jumpGameObject = GameObject.Find("Jump");
if (jumpGameObject != null)
{
AudioSource jumpAudio = jumpGameObject.GetComponent<AudioSource>();
if (jumpAudio != null)
{
jumpAudio.mute = true;
}
}
gameObject.SetActive(true);
scoreText.text = ((int)score).ToString();
isShown = true;
This did not work the audiosource does not get muted, maybe it cannot find the gameobject or something?

Categories