Detect when VideoPlayer has finished playing - c#

I have a MovieController class that manages videos in my project. I'm using the new video player component introduced in Unity 5.6.
I would like to call a method when a movie has finished playing. So far, this method is only a Debug.Log, as you can see:
using UnityEngine;
using UnityEngine.Video;
public class MovieController : MonoBehaviour
{
private VideoPlayer m_VideoPlayer;
void Awake ()
{
m_VideoPlayer = GetComponent<VideoPlayer>();
m_VideoPlayer.loopPointReached += OnMovieFinished; // loopPointReached is the event for the end of the video
}
void OnMovieFinished(VideoPlayer player)
{
Debug.Log("Event for movie end called");
player.Stop();
}
}
My issue is that the OnMovieFinished is not called at the end of the video, and I just end up with a black screen and nothing in the console.

I don't trust or use the events from the VideoPlayer API because I've ran into problems with them too. What you see is a bug that hasn't been fixed for months.
If you want to detect if video has finished playing, use a coroutine to to check videoPlayer.isPlaying every frame in a while loop. When the while loop exists, it means that the video has finished playing.
while (videoPlayer.isPlaying)
{
yield return null;
}
//Done Playing
I can't test this right now to check if the pause function affects videoPlayer.isPlaying but you can test that by yourself.
Another way to do this is to check if frame is the-same as frameCount. When this is true, you know that the video has finished playing.
if(videoPlayer.frame == videoPlayer.frameCount)
{
//Video has finshed playing!
}
If none of these solved your problem then file for a bug report.

Funnily enough, this still doesn't work completely as expected in Unity 2018.4.25 (current version I'm working with). Playing videos on start-up will often result in isPlaying being false (jumping out of the video without playing it, whilst and testing for
(videoPlayer.frame == videoPlayer.frameCount)
doesn't seem to work reliably on mobile devices (android) as the video will stop playing (isPlaying == false) before the video has played all its frames (usually a couple short, which I assume is frame skipping).
I found
if (( videoPlayer.frame) > 0 && (videoPlayer.isPlaying == false))
captures the use case problems I was running into on mobile, however there may be more interesting use cases and combinations to deal with. I put the test above into an update() rather than a co-routine.
HTH.

using UnityEngine.Video;
private VideoPlayer vid;
private float beginning;
void Awake()
{
vid = GetComponent<VideoPlayer>();
beginning = Time.time;
vid.Play();
StartCoroutine(waitmethod());
}
IEnumerator waitmethod()
{
while (Time.time - beginning < vid.length)
{
yield return null;
}
runyournext();
}

You can use
videoPlayer.loopPointReached += OnMovieFinished;
//the action on finish
void OnMovieFinished(UnityEngine.Video.VideoPlayer vp)
{
vp.playbackSpeed = vp.playbackSpeed / 10.0F;
}
you can see more here https://docs.unity3d.com/ScriptReference/Video.VideoPlayer.html

Related

My audio only plays one time in unity c# (beginner)

I am making a gunshot code for a zombie shooter, but for some reason whenever I fire more than once in the same time period as the other shot, they don't overlap? Is there any way I can make it so that they do? Also, it only plays occasionally when I wait
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class audioplay : MonoBehaviour
{
public AudioSource source;
public AudioClip clip;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
if (Input.GetMouseButton(0))
{
source.PlayOneShot(clip);
}
}
}
You wouldn't want a shoot function without any cooldown, especially in update. Also check your audio file to see if has blank space in it, like the one I used:
I used Audacity to trim the audio file. (it's free and open source :D )
Code stuff:
using UnityEngine;
public class MyPlayAudio : MonoBehaviour {
public AudioSource audioSource;
public AudioClip audioClip;
public float shootCooldown; //change this according to how fast you want to shoot
private float currentShootCooldown;
void Update()
{
//if you are holding mouse button down and we are ready to shoot
if(Input.GetMouseButton(0) && currentShootCooldown <= 0)
{
Pewpew();
}
//make sure you are not changing the public value
currentShootCooldown -= Time.deltaTime;
}
public void Pewpew()
{
//code whatever gun is supposed to do, like dealing damage
//resets cooldown for pewpew
currentShootCooldown = shootCooldown;
//plays the audio
audioSource.PlayOneShot(audioClip);
}
}
I think .Play might be better if we are thinking about a gun. I don't know, try both and see which one is better for your needs
audioSource.clip = audioClip; //this is not necessary if your gun is only going to do 1 sound.
audioSource.Play();
`
If you want it to overlap, you could for example add that component to two different gameobjects in the scene, that way there will be one frame where the function is called twice. Since Update would run on two objects.

VR speedrun timer

I am working on a VR speedrun game and I need a timer. The timer doesn't need to be showed in the screen for the player, just on the map I made. It needs to start when the player (VR) passes a specific point and end when it reaches a different point. If anyone has an idea of how to make this work I would really appreciate it.
On the start line you could have an empty gameobject with a trigger collider on it, and in the OnTriggerEnter event you could start a Coroutine that keeps track of the time, and on the finish line you'd have another trigger collider that sets a flag and stops the timer.
Something along the lines of this should work:
using UnityEngine;
using System;
public class Player : MonoBehaviour {
private bool _isTimerStarted = false;
private float _timeElapsed = 0;
private void OnTriggerEnter(Collider other) {
if (other.gameObject.name.Equals("Start Line")) {
_isTimerStarted = true;
StartCoroutine(StartTimer());
} else if (other.gameObject.name.Equals("Finish Line") {
_isTimerStarted = false;
}
}
IEnumerator StartTimer() {
while (_isTimerStarted) {
_elapsedTime += Time.deltaTime;
yield return null;
}
yield break;
}
}
For this to work just make sure your player has a RigidBody attached or else no collision will be detected :)
If you want a "timer" as in "show the elapsed time since some event" you might take a look at Stopwatch
var sw = Stopwatch.StartNew();
...
Console.WriteLine(sw.Elapsed.ToString());
The stopwatch is primarily intended for performance measurements. But it is easy to use, so if it fits your use case you might as well make use of it, even if the resolution and accuracy is much greater than you need.

Why Camera.current returns null when I am in Unity Editor? And is there any other ways to catch the current camera?

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[ExecuteAlways]
public class SkyBox : MonoBehaviour
{
public Material[] skyboxes;
public Camera skyboxCamera;
public float skyboxMoveSpeed = 2f;
private int index = 0;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
if (Input.GetKeyDown(KeyCode.Escape))
{
SwitchSkybox();
}
if (RenderSettings.skybox == skyboxes[1])
{
RenderSettings.skybox.SetFloat("_Rotation", Time.time * skyboxMoveSpeed);
}
}
public void SwitchSkybox()
{
index++;
if (index == skyboxes.Length)
{
index = 0;
}
RenderSettings.skybox = skyboxes[index];
if (RenderSettings.skybox == skyboxes[1])
{
skyboxCamera.enabled = true;
Camera.current.enabled = false;
Time.timeScale = 1.0f;
}
else
{
skyboxCamera.enabled = false;
Camera.current.enabled = true;
Time.timeScale = 0.0f;
}
}
}
The script switch between skyboxes the default and my skybox and also switch between the currently active camera and the sky box camera.
But when I'm hitting the escape key it's throwing null exception in the editor on the line number 46 :
Camera.current.enabled = false;
The current of the Camera is null
I want to make that when I press the escape key it will switch to my skybox and to the skybox camera and also will pause the game (Later I will make a main menu when the game is paused).
This is the Camera.current, from the manual.
The camera we are currently rendering with.
Also worth noting the comment from Ruzihm.
The Unity engine typically assigns an already-instantiated instance of
Camera to Camera.current
So, from your scripts, I see 2 issues. The one directly related to this questions happens just in editor mode and I will start from that one.
Editor Issue: Camera.current is null
When working in the editor, Camera.current won't be just your own application's camera, but it could be any camera. It could even refer to the editor's scene view camera.
In this last case, if your scene view is not in focus (IE when you've focus on Game Window) Camera.current will be null.
Logical Issue: you couldn't switch back
When you try to switch back from skyboxCamera, your Camera.current will be the same skyboxCamera, and not your default camera. So you won't be able to retrieve the previous camera.
SOLUTION
Do not use Camera.current, but store all of your cameras in your script (this solution is also better for perfomance, since both Camera.current and Camera.Main are not performant scripts).
In your case you will need to add this piece of code to your script and use the EnableSkyBoxCamera method.
public Camera defaultCamera;
public Camera skyBoxCamera;
private Camera _currentCamera;
public void EnableSkyBoxCamera(bool enableSkyBox)
{
defaultCamera.enabled = !enableSkyBox;
skyBoxCamera.enabled = !enableSkyBox;
if (enableSkyBox) _currentCamera = skyBoxCamera;
else _currentCamera = defaultCamera;
}
If camera is null you can't set the enabled to false without getting a nullpointerexception. Instantiate the camera first or remove that line of code.
*** Edit ill take another crack at this
try
Camera.main.enabled = false;
instead of
Camera.current.enabled = false;
As per Unity docs in reference to Camera.current: 'Most of the time you will want to use Camera.main instead. Use this function only when implementing one of the following events: MonoBehaviour.OnRenderImage, MonoBehaviour.OnPreRender, MonoBehaviour.OnPostRender'

Why does why C# code keep crashing Unity? (I'm a beginner to Unity and C#)

Whenever I run my game it freezes, but it doesn't without this C# script.
I've tried changing around my code, and it works outside of Unity, in .NET (with some tweaks to certain functions) but when it's in Unity it crashes.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Throw : MonoBehaviour
{
public Rigidbody rb;
string final = "final:";
public float force = 1;
public float accuracy = 0;
void incto(float amount)
{
while (force < amount)
{
Debug.Log(force);
force++;
}
}
void decto(float amount)
{
while (force > amount)
{
Debug.Log(force);
force--;
}
}
void fstart()
{
while (true)
{
force = 1;
incto(200);
decto(1);
if(Input.GetKey(KeyCode.E))
{
Debug.Log(final + force);
break;
}
}
}
// Start is called before the first frame update
void Start()
{
fstart();
}
// Update is called once per frame
void FixedUpdate()
{
Debug.Log(force);
}
}
It should decrease and increase the force value, then stop when you press E, but Unity just crashes.
Unity takes care of the while(true) for you. Unity's while(true) calls your FixedUpdate, you just need to fill it in.
Unity only captures keystrokes once per frame, so Input.GetKey(KeyCode.E) will always return the same value. Unity crashes because of your while(true) is an infinite loop.
More info: https://docs.unity3d.com/Manual/ExecutionOrder.html
i belive that unity starts catching key strokes after the first frame, not before, try moving fstart() behind a first run bool in the FixedUpdate function
oh and this will hang the entire program every time it executes a frame.....
The code crashes because you have an infinite loop here:
while (true)
{
}
It will never exit the loop so nothing more happens. Just put that code into Update() method, which gets called by the engine on every frame, it will do the trick

Unity Animation playing from coroutine

As the title states the animation is not playing. the line telling it to play is in a coroutine and the code is before a waitforseconds(3f).
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class Play : MonoBehaviour {
public Animator AnimatorRef;
// Use this for initialization
void Start () {
if (AnimatorRef == null)
{
AnimatorRef = GetComponent<Animator>();
}
}
public void PlayGame()
{
StartCoroutine(TitlePlay());
Debug.Log("playing");
}
IEnumerator TitlePlay()
{
Debug.Log("playing1");
AnimatorRef.SetBool("Enlarge", true);
yield return new WaitForSeconds(3f);
Debug.Log("playing2");
SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex + 1);
}
}
it grabs the animator reference fine and all three of the comments show.
2 Considerations.
1st -
Did you check your transitions and AnimationController?
You can open up the AnimationController to see if the bool changes during runtime, if it does you'll know there is a transition error somewhere between your animation states.
2nd -
If you comment out the "LoadScene" part, does the animation then play correctly?
I suspect that the Animation bool is for some reason not allowed to carry out it's actions before the entire method has been run through, could be wrong though.
My unity shutdown without saving but it now works. If anyone has this problem remake the animation and MAKE SURE you go from the initial animation and click add clip. Then it worked for me :)

Categories

Resources