We have a script attached to an object that has a VideoPlayer and some video. We want to know (be subscribed to) when the video ends, so we can decide then to play another or do something else.
void Start()
{
videoPlayer.loopPointReached += Method();
}
VideoPlayer.EventHandler Method()
{
Debug.Log("it ended");
return null;
}
And the log is not coming out when the video ends.
The reference to videoPlayer exists, and the video plays, pauses and stops with no issues.
We find solutions that include counting frames and so on, but we're hoping for a more elegant solution.
Your code is written wrong. Actually this code is the same if you write videoPlayer.loopPointReached += null;. So you subscribe null for loopPointReached event. You messed up event handler delegate with its signature return type. Your code should look like this, I suppose:
void Start()
{
videoPlayer.loopPointReached += Method; // do not call method with '()', but just subscribe it for event
}
// here the signature of the method corresponds to VideoPlayer.EventHandler delegate,
// that is void(VideoPlayer)
private void Method(VideoPlayer source)
{
Debug.Log("it ended");
}
You can check signature of this delegate in Unity docs https://docs.unity3d.com/2017.2/Documentation/ScriptReference/Video.VideoPlayer.EventHandler.html
Also you can read move about events in this topic How to make a keypress form event in C#
Related
I have trouble figuring out how to remove listener for cloud function which is triggered every few minutes. I have the following code:
void InitializeFirebase() {
FirebaseDatabase.DefaultInstance.GetReference ("Main").ValueChanged += ListenForServerTimestamp;
}
void OnDisable() {
FirebaseDatabase.DefaultInstance.GetReference("Main").ValueChanged -= ListenForServerTimestamp;
}
The issue is that the even is still registered even when Unity is not running. Ie play button is turned off, it is still registering the events. I must be doing something wrong with removing the events, but after looking through all other answers, i cant figure out what is the issue.
Thanks.
Edit: If there is some way to disconnect Firebase in Unity, that would be also something i could try. But i cant again find anything anywhere on disconnecting Firebase in Unity.
My top suggestion will be to cache your database reference in the script where you're retrieving it. Something like this:
DatabaseReference _mainRef;
void InitializeFirebase() {
_mainRef = FirebaseDatabase.DefaultInstance.GetReference ("Main");
_mainRef.ValueChanged += ListenForServerTimestamp;
}
void OnDisable() {
if(_mainRef != null) {
_mainRef.ValueChanged -= ListenForServerTimestamp;
_mainRef = null;
}
}
If you inspect GetReference, you'll see something like this in ILSpy:
public DatabaseReference GetReference(string path)
{
return new DatabaseReference(this.internalDatabase.GetReference(path), this);
}
So you may be removing the listener on an object other than the one you register it on.
A few other things to double check. I'd recommend you do this on a MonoBehaviour rather than a ScriptableObject. Since you mention events getting triggered when the play button isn't selected, I expect that this is happening in Editor. ScriptableObject generally doesn't behave the way I'd expect in Editor and can cause odd issues around play/pause.
If it is on a MonoBehaviour, I would recommend that you not place it on one that has the [ExecuteInEditMode] attribute. You can also get odd behavior if you're invoking some of these calls from an Editor script (CustomEditor, PropertyDrawer, &c).
I don't think any of these (other than the ref) are your issue. But it's a few more things to look at.
I hope this helps!
--Patrick
I've been having trouble with using delegates and events in Unity. I am familiar with subscribing and unsubscribing.
The problem i have is a stack overflow exception. However my Console outputs another message while not even in playmode. I am confident this log is tied to the stackoverflow exception, because it happened at the same time.
What is also curious, is that the error just started happening, without me touching that part of the code for a while. I changed nothing in the subscribing of the event.
Note that i had this exact same problem earlier in another project and ended up ditching event subscription.
Do note, i am not talking about the UnityEvent. I'm talking about the scripting delegate & event.
Here is the definition of the delegate and event code:
public delegate void gameStart();
public event gameStart OnGameStart;
public delegate void gameEnd();
public event gameEnd OnGameEnd;
Here is a subscriber:
public override void OnInitialize()
{
GameManager.Instance.OnGameEnd += StopSound;
GameManager.Instance.OnGameStart += PlaySwipeClip;
}
public void PlaySound(AudioClip clip, bool doNotStopCurrent = false)
{
if (doNotStopCurrent)
{
popupAudioSource.clip = clip;
popupAudioSource.Play();
// AudioSource.PlayClipAtPoint(clip, Vector3.zero, 1f);
}
else
{
mainAudioSource.clip = clip;
mainAudioSource.Play();
}
}
public void PlaySwipeClip()
{
mainAudioSource.clip = SwipeClip;
mainAudioSource.Play();
}
I Only subscribe to the events once (I use the singleton pattern, OnInitialize() is called in Awake()).
I am positive that the subscription does not happen twice.
I never unsubscribe to the events. Reason for this is that I use the same scene and "Manager" objects for the entire app's lifecycle.
Is there something I am missing here? Should i unsubscribe in the OnDestroy?
Somehow i have a feeling that subscriptions are persistent between the lifecycles. The error goes away for a while when i rename the event variable.
Also I have tried setting all the events to null explicitly in the awake method, however this does not seem to resolve anything.
You have to unsubscribe otherwise you will leak memory. It is recommended to subscribe OnEnable() and unsubscribe OnDisable().
This question already has answers here:
Simple event system in Unity
(2 answers)
Closed 6 years ago.
I'm writing a game in Unity3D, and I'm wondering if I should be using events -- or some other construct entirely -- instead of delegates for my web requests.
At certain points in the game's lifecycle, we need to call out to a REST API and await its response. Consider a UI screen that retrieves a list of your friends, and displays the number of friends, along with an entry for each friend. Currently, the workflow looks something like this, using Action delegates as parameters to the various methods of my Server class:
class FriendsUI : MonoBehaviour
{
[SerializeField]
private Text friendCount; // A Unity Text component
private void OnStart()
{
Server.GetFriends(player, response => {
ShowFriendList(response.friends);
});
}
private void ShowFriendList(List<Friend> friends)
{
this.friendCount.text = friends.Count.ToString();
// Display friend entries...
}
}
The Server.GetFriends method does an HTTP request to our REST API, gets the response, and calls the delegate, returning the response and control to FriendsUI.
This usually works just fine. However, if I closed the friends UI screen before the delegate is called, the friendCount component -- and any other component in the class, for that matter -- has been destroyed, and an NPE is thrown trying to access it.
Knowing that, do you think C#'s event system be a better design for this? Server would expose something like a FriendsEvent event, and FriendsUI would subscribe to it. At the point when Server has validated/transformed the response, it would raise the event. FriendsUI would also clean up after itself and unregister the handler when the GameObject on which the script has been attached is destroyed/disabled. That way, at the point the event is raised in Server, if the event handler is no longer valid in FriendsUI, it just wouldn't get called.
I'm just trying to run this through in my head before implementing it, because it would end up becoming a somewhat large refactor. I'm looking for guidance as to whether or not people think it's a good route to go, if there are any other routes that might turn out better, and any pitfalls I might run into along the way.
Thanks, as ever, for your time.
Simply use C# delegate and event. Make each script that wants to be notified when friends list is received from the server to subscribe to the event.
public class FRIENDSAPI : MonoBehaviour
{
public delegate void friendListReceived(List<FRINDSINFO> _friends);
public static event friendListReceived onFriendListReceived;
//Function that gets friends from the network and notify subscribed functions with results
public void getFriends()
{
StartCoroutine(getFriendsCoroutine());
}
private IEnumerator getFriendsCoroutine()
{
List<FRINDSINFO> friendsFromNetwork = new List<FRINDSINFO>();
/*
NETWORK CODE
//FILL friendsFromNetwork with Result
friendsFromNetwork.Add();
*/
//Make sure a function is subscribed then Notify every subscribed function
if (onFriendListReceived != null)
{
onFriendListReceived(friendsFromNetwork);
}
yield return null;
}
}
Then in your FriendsUI class, subscribe in the Start() function and unsubscribe in the OnDisable() function. You usually subscribe in the OnEnable() function but sometimes it doesn't work because things don't initialize on time. Doing it in the Start() function is a fix for that.
public class FriendsUI: MonoBehaviour
{
void Start()
{
//Subscribe
FRIENDSAPI.onFriendListReceived += onFriendsReceived;
FRIENDSAPI friendAPI = gameObject.GetComponent<FRIENDSAPI>();
friendAPI.getFriends(); //Get friends
}
public void OnDisable()
{
//Un-Subscribe
FRIENDSAPI.onFriendListReceived -= onFriendsReceived;
}
//Call back function when friends are received
void onFriendsReceived(List<FRINDSINFO> _friends)
{
//Do Whatever you want with the result here(Display on the Screen)
}
}
I'm starting out in C#, coded a lot in Java but having some trouble here. I'm trying to learn how to use MouseKeyHook for an application I'm developing. I cannot get the actual listener to fire off an event. Here's my listener code:
using System;
using System.Windows.Forms;
using Gma.System.MouseKeyHook;
namespace ChromaHeatmap
{
class keyListener
{
private IKeyboardMouseEvents m_GlobalHook;
public void Subscribe()
{
// Note: for the application hook, use the Hook.AppEvents() instead
m_GlobalHook = Hook.GlobalEvents();
m_GlobalHook.KeyPress += GlobalHookKeyPress;
}
private void GlobalHookKeyPress(object sender, KeyPressEventArgs e)
{
Console.WriteLine("blah");
}
public void Unsubscribe()
{
m_GlobalHook.KeyPress -= GlobalHookKeyPress;
//It is recommened to dispose it
m_GlobalHook.Dispose();
}
}
}
And here's the part of my application code where I attempt to do something with the listener. If anyone can let me know what the best way is to loop here and wait for events, I'd appreciate it.
//Listen for key presses
keyListener heyListen = new keyListener();
heyListen.Subscribe();
while(true)
{
}
while(true) {}
This is a hold-and-catch-fire statement, the thread will burn 100% core and cannot execute the hook callback. You'll notice that the machine goes dead for 5 seconds when you press a key, the operating system is waiting for an opportunity to invoke the callback. But it won't wait forever and unceremoniously will destroy the hook so you regain control over the machine. Also the kind of mishap that will occur when you try to debug your event handler.
Windows needs an opportunity to safely call the hook callback. That requires your program to be "idle", not executing any code. The technical term for this is "pumping the message loop", your program must wait for a notification from the operating system that something interesting happened.
A very simple way is to use the Winforms project template as-is, you'll also get a window. Note how the Main() method in the project makes the call that you need instead of the while() loop. You must call Application.Run().
Check this post for code that avoids displaying a window.
I am creating a game in WPF. To manage sounds I've created a AudioController, which will create MediaPlayers and manage playing, stopping, looping, pausing, etc. I have the base of it working. If i'm in a control, I can call
AudioController.Play("MySound");
And it will decide if it has a MediaPlayer for that sound already, if not it will create one.
A problem appears in the following situations:
Task.Run(() => { DoSomething(); AudioController.Play("MySound"); });
_timer_tick(object sender...) { AudioController.Play("MySound"); }
When using a Task my Sound will play properly, but the MediaEnded event never gets fired, causing me not to be able to manage the MediaPlayers properly.
With the Timer Tick, the Sound will play the first time, but never again, even though its using a new MediaPlayer and also never fires the MediaEnded event
I've tried Dispatching the calls, but it doesn't seem to make a difference.
I can only imagine the problems lies with the idea that the Play() call is being sent from a different thread.
public static void Play(string sound)
{
Debug.WriteLine("Publish called on Thread Id {0}", Thread.CurrentThread.ManagedThreadId);
if (!Application.Current.CheckAccess())
{
Application.Current.Dispatcher.Invoke(() => Play(sound, TimeSpan.Zero), DispatcherPriority.Normal);
return;
}
Play(sound, TimeSpan.Zero);
}
private static void Play(string sound, TimeSpan position)
{
//...some preemptive code that's just searching a list
//reuse
if (ap == null)
{
ap = new AudioPlayer(entry);
_players.Add(ap);
}
ap.Play(position);
}
Note: AudioPlayer is a wrapper class around MediaPlayer so that I can add some additional properties such as LastActive, IsLooping, and handles replaying a sound by calling:
_player.Stop();
_player.Position = position;
_player.Play();
Update:
I just decided to run a different sound in the above scenario's and everything seems to work fine. The only difference between the audio's is that one is 40kb and the other 67kb. Could it be that the file is just so small MediaPlayer doesn't register the NaturalDuration and therefore assumes its 0 which stops the events from being triggered?