Subscribe event Unity VR - c#

Unity share differents assets/script to use VR. I try to develop a simple experience to improve my knowledge of the differents Unity features but I'm in trouble with a call to an event.
In the script MenuButton.cs you can subscribe to an OnButtonSelected event but I don't know how :
MenuButton.cs
public class MenuButton : MonoBehaviour
{
public event Action<MenuButton> OnButtonSelected; // This event is triggered when the selection of the button has finished.
...
private IEnumerator ActivateButton()
{
// If anything is subscribed to the OnButtonSelected event, call it.
if (OnButtonSelected != null)
OnButtonSelected(this);
}
}
I try multiple unsuccess way to subscribe this event from another script like :
namespace VRStandardAssets.Menu
{
public class GetDiscover : MonoBehaviour
{
[SerializeField] private MenuButton m_MenuButton; // This controls when the selection is complete.
void OnEnable()
{
m_MenuButton.OnButtonSelected += Teleport;
}
void OnDisable()
{
m_MenuButton.OnButtonSelected -= Teleport;
}
void Teleport()
{
Debug.Log("Hello");
}
}
}
But I have the error : "error CS0123: A method or delegate VRStandardAssets.Menu.GetDiscover.Teleport()' parameters do not match delegateSystem.Action(VRStandardAssets.Menu.MenuButton)' parameters".
What does that mean ? I'm just looking for the simplest way to call the event...
I also try with the delegate method but it won't work too...
Maybe I don't understand well how function the event system Unity and some clear explanation would be welcome even if I already follow some tutorial :
https://unity3d.com/fr/learn/tutorials/topics/scripting/events-creating-simple-messaging-system
https://unity3d.com/fr/learn/tutorials/topics/scripting/events

What this error is saying is that when you subscribe to the "OnButtonSelected" event, the method you target (in your case, "Teleport") must accept a parameter of type VRStandardAssets.Menu.MenuButton.
This is how the event system tells your listener which button was selected.
So, you could use something like this:
void Teleport(VRStandardAssets.Menu.MenuButton buttonPressed)
{
// if you care which button, access buttonPressed parameter here..
Debug.Log("Hello");
}
(Note: for good programming practice though I would suggest naming this something other than "Teleport" - calling it something like "HandleMenuButton" or "MenuButtonPressed" keeps its intent clear; then inside that method you can call a separate "Teleport" function. In the future if you need to change the interaction, it will be easier to update the code if you maintain that level of separation.)

Related

Unity-LeapMotion: How to setup an event with an attached InteractionBehavior script with c#

for interacting with an object in Unity via LeapMotion controller you need to attach the "IneractionBehavior" script. With this script you can create new interaction events with the unity GUI.
What i want is to generate a couple of GameObjects with an attached InteractionBehavior script and i want to add these types of events via C# code.
For example i want to generate automaticaly an object with a attached InteractionBehavior script and want to define per code that this object turns to red by BeginContact.
I use:
- Unityversion 2019.1.10
- LeapMotion Core Asset v4.4.0
- LeapMotion InteractionEngine v1.2.0
- HTC Vive Pro
If it is possible i want to know how i can add an interactionevent to an attached InteractionBehavior script with C#
This is the implementation of BeginContact in the InteractionBehaviour :
public void BeginContact(List<InteractionController> controllers)
{
foreach (var controller in controllers)
{
_contactingControllers.Add(controller);
OnPerControllerContactBegin(controller);
}
if (_contactingControllers.Count == controllers.Count)
{
OnContactBegin();
}
}
as you can see the according actions called are
public Action OnContactBegin;
public Action<InteractionController> OnPerControllerContactBegin;
So depending on your needs either
Directly add callbacks via script
e.g. using
private void Awake()
{
var interactionBehaviour= objectReference.GetComponent<InteractionBehaviour>();
// either as Lambda Expression
interactionBehaviour.OnContactBegin += () =>
{
// do something here
};
// or with a method
// use this if you also want to be able to remove callbacks later!
// it is always save to remove a callback before adding it
// even if it's not there yet. Makes sure it is only added exactly once
interactionBehaviour.OnContactBegin -= DoSomething;
interactionBehaviour.OnContactBegin += DoSomething;
}
// make sure to always remove any callbacks as soon as possible or when they are not needed anymore
private void OnDestroy()
{
interactionBehaviour.OnContactBegin -= DoSomething;
}
private void DoSomething()
{
// do something here e.g. turn red
// for efficiency you would ofcourse move the GetComponent
// call to Awake as well. Just wanted to keep the example clean
GetComponent<Renderer>().material.color = Color.red;
}
Or make it more general
Inherit from the script and customize it with UnityEvents which bring their own Inspector visiable interface (exactly like the Button.onClick event).
Theoretically you could add UnityEvent for every single Action available .. I will just do it for the two above.
[Serializeable]
public class InteractionControllerEvent : UnityEvent<InteractionController> { }
public class ExtendedInteractionBehaviour : InteractionBehaviour
{
public UnityEvent OnContactBeginEvent;
public InteractionControllerEvent OnPerControllerContactBegin;
// and just as before add those as callbacks to the actions
private void Awake()
{
OnContactBegin -= OnContactBeginInvokeEvent;
OnContactBegin += OnContactBeginInvokeEvent;
OnPerControllerContactBegin -= OnPerControllerContactBeginInvokeEvent;
OnPerControllerContactBegin += OnPerControllerContactBeginInvokeEvent;
}
private void OnDestroy()
{
OnContactBegin -= OnContactBeginInvokeEvent;
OnPerControllerContactBegin -= OnPerControllerContactBeginInvokeEvent;
}
private void OnContactBeginInvokeEvent()
{
OnContactBeginEvent?.Invoke();
}
private void OnPerControllerContactBeginInvokeEvent(InteractionController controller)
{
OnPerControllerContactBegin?.Invoke(controller);
}
}
Now you could simply reference GameObjects and their components & methods via the Inspector as you probably know it from the mentioned UI.Button.onClick event (it also is an UnityEvent).
Especially the InteractionControllerEvent is an example that allows to be called with an dynamic parameter, meaning the according InteractionController reference can be passed on to a referenced method taking a InteractionController as paramter.

inheritance and event handlers c#

Ok guys I'm totally new to stackoverflow, let me know if I stuff something up.
I am making a class library for sockets and then using the class library to make a server. The issue I've run into is this:
In the class library I have a class called sockets that has event handlers (you know like connection made, closed so on) and in the server I'm making, I have a class named player which inherits from sockets.
The problem pretty much comes down to this.
I use the OnConnectionMade event handler to create my player, but because I cant do instanceOfInheritedClass = intanceOfBaseClass, even when I make use of use of the event handlers, the event handlers wont trigger because instanceOfInheritedClass isn't pointing to intanceOfBaseClass event, if I pass through the parameters.
void hostManager_OnConnectionMade(object source, ConnectionArgs e)
{
Player socket = new Player(e.GetSocket());
socket.OnDataRecivedPostConvert += Socket_OnDataRecivedPostConvertLogin;
}
public Player(DDSocket socket)
{
this.Host = socket.Host;
this.Socket = socket.Socket;
//this.OnConnectionClosed += socket.OnConnectionClosed;
//this.OnDataRecivedPostConvert += socket.OnDataRecivedPostConvert;
//this.OnDataRecivedPostConvertHost += socket.OnDataRecivedPostConvertHost;
}
One of the solutions I came up with was that instead of inheriting, I can just make it a intanceOfBaseClass parameter in the player class. But that will prevent me from making proper use of object source from my event handlers which will mean I'll need to use linq or something to find the player from the socket or something like that.
The other thing I thought about doing was somehow passing the event handlers over, which you can see I tried, but don't know how to do.
Now after hours of looking it up I'm stuck. Any help is greatly appreciated
and any answer that solves this issue is fine. I'm not picky with how its solved.
Despite that your Player class is inherited from DDSocket, but in this scenario, it acts as the wrapper class of DBSocket, so there is one hack to achieve that, I think you have to do some further steps:
class DDSocket
{
public event Action OnConnectionClosed;
public void Raise()
{
if (OnConnectionClosed != null)
{
OnConnectionClosed();
}
}
}
class Player :DDSocket
{
// make new event look as the same base class
public new event Action OnConnectionClosed;
public Player(DDSocket socket)
{
socket.OnConnectionClosed += Socket_OnConnectionClosed;
}
private void Socket_OnConnectionClosed()
{
if (OnConnectionClosed != null)
{
OnConnectionClosed();
}
}
}
// test those 2 classes
static void Main()
{
DDSocket d = new DDSocket();
Player pl = new Player(d);
pl.OnConnectionClosed += () => MessageBox.Show("test");
d.Raise();
}

Getting a class to 'inform' another class that it needs to change (Events?)

Okay, I've been searching on the site and Google and can't quite get my head around where things need to be in terms of delegates and eventhandlers and the like so hopefully someone here can help/explain what I need to do.
So, I am writing a simple database application (using SQLite). There is a mainform that is the MDI parent (that's basically a big window with menus at the top). The menus launch other windows that allow view, edit and insert into various tables of the database.
One of those windows is a LOG window which shows my log table.
At the moment, if a user changes something in the window showing the data in TABLE. The operation also writes into the log table. If the Log window is open, however, the log view doesn't update.
So, I've figured out I probably need to 'fire' an event from my TABLE UPDATE code that my LOG window 'subscribes' to (so it can update the DataGridView).
What I can't figure out is where the different 'bits' of the event go.
Should the MdiParent have the public delegate void EventHandler();? If not where?
which class gets the public static event EventHandler logGoneStale;?
The only bit I'm reasonably sure about is that the Window that displays the log (which has a method called public void UpdateLogDataGridView() - which calls the database object/methods to (re-)populate the datagridview) needs to have:
something like logGoneStale += new EventHandler(UpdateLogDataGridView); in it. Is that at least right?
Totally befuddled - it seems none of the event examples/tutorials on MSDN are trying to do what I want to achieve.
You need to define an event in the class that is sending the event, and append an event handler in the class that should receive the event. To make things slightly easier, starting with C# 3.5 you can forget about the delegate keyword altogether and use a lamba expression as event handler. Also note that it in most cases it makes no sense to make an event static, since usually events are fired by an instance, not by a class.
Example:
class SendsEvent
{
public event EventHandler MyEvent;
public void FireEvent()
{
if(MyEvent != null) // MyEvent is null if no handlers have been attached
{
MyEvent(this, new EventArgs()); // event fired here
}
}
}
class ReceivesEvent
{
private SendsEvent eventSource;
public ReceivesEvent(SendsEvent eventSource)
{
this.eventSource = eventSource;
// Attach event handler - can be a lambda expression
// or method with signature
// "void HandleEvent(object sender, EventArgs e)"
this.eventSource.MyEvent += (sender, args) =>
{
// do something when event was fired
Console.Out.WriteLine("Hello. Event was fired.");
};
}
}
class Program
{
public static void Main()
{
var eventSource = new SendsEvent();
var eventReceiver = new ReceivesEvent(eventSource);
eventSource.FireEvent();
}
}
I hope this helps you.
Working with events requires you to have both an event publisher and an event subscriber.
#chris' answer is correct.
Besides, you need to raise the event on the closest point where the action for which you want to be notified takes place.
For example, implementing the INotifyPropertyChanged interface.
public class Customer : INotifyPropertyChanged {
public string Name { get; set; }
public string Address {
get { return address; }
set {
address = value;
if (thereArePropertyChangedEventSubcribers())
raisePropertyChangedEventFor("Address");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void raisePropertyChangedEventFor(string propertyName) {
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
private bool thereArePropertyChangedEventSubcribers() {
return PropertyChanged != null;
}
private string address;
}
So here, the Customer class allows for the publishment of its change of address. So, whenever anyone is interested to be notified when the address has changed, it subscribes to the event like so:
Customer.PropertyChanged += new PropertyChangedEventHandler(customerPropertyChanged);
Or else like so:
Customer.PropertyChanged += customerPropertyChanged;
You might even have noticed that the closest point where the address has changed in directly after it has actually changed. The only requirement is that the method used as the event handler has the same signature as the event itself. If you take a look at the PropertyChangedEventHandler Delegate, one may notice that it signature awaits an object as the first parameter, that is, the object that fired the event, and a PropertyChangedEventArgs instance to notify about the property that has changed.
To come back to your example, you wish to be noticed whenever a log has been inserted into the underlying database so that a refresh of your Log window may occur. There are two questions that need to be answered whenever you want to use events.
What shall my publisher be?
What shall my subscriber be?
What shall my publisher be?
Should the MdiParent have the public delegate void EventHandler();?
Short answer: No!
If not where?
The event declaration best fits the publisher. Should you have a class responsible for logging, then this is where the public delegate void EventHandler(); should reside, as it is it that is responsible to raise the event whenever there are subscribers.
Whenever there is a successful Log inserted, it shall notify whatever subscriber interested to know about the new Log Entry.
public class Log {
public void UpdateLog(string description) {
// insert the new Log line into your database.
if (thereIsAtLeastOneNewLogEntryAddedSubscriber())
raiseTheNewLogEntryAddedEvent();
}
public event EventHandler NewLogEntryAdded;
private raiseTheNewLogEntryAddedEvent() {
NewLogEntryAdded(this, EventArgs.Empty);
}
private bool thereIsAtLeastOneNewLogEntryAddedSubscriber() {
return NewLogEntryAdded != null;
}
}
What shall my subscriber be?
This question can be answered through another question:
What do you need to do when the event fires?
In your case, you wish to update a Log window whenever it is opened.
The only bit I'm reasonably sure about is that the Window that displays the log (which has a method called public void UpdateLogDataGridView() - which calls the database object/methods to (re-)populate the datagridview) needs to have:
something like logGoneStale += new EventHandler(UpdateLogDataGridView); in it. Is that at least right?
Yes, you're right! =D
You actually subscribe to the event per this line. So, it tells the application that the window that displays the log is interested to know about log changes in your database.
public class WindowThatDisplaysTheLog : Form {
public WindowThatDisplaysTheLog() {
InitializeComponent();
log = new Log();
log.NewLogEntryAdded += UpdateLogDataGridView;
}
private void UpdateLogDataGridView(object sender, EventArgs e) {
// Reload your Log entries from the underlying database.
// You now shall see the LogDataGridView updating itself
// whenever a new log entry is inserted.
}
private Log log;
}

Mouse Event from one class, how do I listen for it in another class?

I have a UserControl on a Form,
when I MouseMove on that UserControl I want to do something in the Form.
How can I make the Form 'listen' for this event?
I am using Visual C#, .Net framework 3.5, winforms
I suppose you're referring to a use control or something like that.
You can add a public event, and trigger it inside your class when detecting the inner class event.
Then you have to subscribe to the published event in the second class.
This is a sample so that you see the sintax:
public class WithEvent
{
// this is the new published event
public EventHandler<EventArgs> NewMouseEvent;
// This handles the original mouse event of the inner class
public void OriginalEventhandler(object sender, EventArgs e)
{
// this raises the published event (if susbcribedby any handler)
if (NewMouseEvent != null)
{
NewMouseEvent(this, e);
}
}
}
public class Subscriber
{
public void Handler(object sender, EventArgs e)
{
// this is the second class handler
}
public void Subscribe()
{
WithEvent we = new WithEvent();
// This is how you subscribe the handler of the second class
we.NewMouseEvent += Handler;
}
}
If you are talking about Windows Forms (it's not clear from the question) you need to define
a new event in the class who recieves the mouse-event. After reciving it raises a new custom-event. Another class is subcribed to that (custom-event) a recieves notification.
For moe information (it's not something that can be presenteed in a couple of lines)
can have alook here:
How to propagate an Event up to the MainForm?
If you are talking about WPF, there are different concept of events: event routing. If your class is UI element present in UI tree of the component that recieves actually mouse-event, it will be propagated to your class too. So no need of more coding.
To expand a little on the answer from JotaBe, there are two scenarios that I could see you having:
a) class A calls a method in class B, and an exception happens. In this case, you don't need to do anything: exception will walk the stack, until it finds a catch statement. So, really, all you need to do is NOT catch an exception, or if you do need to catch it (for logging purposes and such), then rethrow it.
b) if you need to have a code triggered in some unrelated class, as a result of exception, then the best way is to use events. In your class declare:
public class ClassA
{
public static event EventHandler<Exception> OnException;
public void Notify(Exception ex)
{
if (OnException != null)
{
OnException(this, ex);
}
}
}
and then, in order to be notified, all you need is to
ClassA.OnException += (sender, exeption) =>
{
... some GetHashCode ..
};
... I guess JotaBe already added all necessary example code as I was typing

Postsharp 3rd party components events interception

I have a question about events interception with c# and Postsharp.
I would like to cancel the execution of events like BeforeDropDown, RowSelected MouseClick with EventInterceptionAspect in postsharp.
But i can not find a proper place where i can write the code.
example:
i tried something like this:
[Serializable]
class EventInter : EventInterceptionAspect
{
public override bool CompileTimeValidate(System.Reflection.EventInfo targetEvent)
{
return "FormClosed".Equals(targetEvent.Name);
}
public override void OnInvokeHandler(EventInterceptionArgs args)
{
if condition executes method otherwise no
}
}
in the form:
[EventInter]
public partial class Frm_RomperMesa : KryptonForm
But it didnĀ“t work. So i want to know if it is possible to achieve what i want.
Thanks in advace. I hope be clear.
yes, it is possible. The problem is, you're trying to apply an event interception aspect to an event defined in another assembly which you can't do within your code. You can't even override the event because it's setup to be handled using the base Form type in the designer code behind
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.Form1_FormClosing);
you will have to modify the assembly to do this. Use the following aspect and the links to modify your
public class EventAspectProvider : TypeLevelAspect , IAspectProvider
{
public IEnumerable<AspectInstance> ProvideAspects(object targetElement)
{
Type t = (Type)targetElement;
EventInfo e = t.GetEvents().First(c => c.Name.Equals("FormClosing"));
return new List<AspectInstance>() { new AspectInstance(e, new EventInter()) };
}
}
[Serializable]
public class EventInter : EventInterceptionAspect
{
public override void OnInvokeHandler(EventInterceptionArgs args)
{
int x = 0;
if (x > 0) //Do you logic here
{
args.ProceedInvokeHandler();
}
}
}
http://programmersunlimited.wordpress.com/2011/07/27/applying-aspects-to-3rd-party-assemblies-using-postsharp/
http://programmersunlimited.wordpress.com/2011/08/16/exposing-internal-methods-in-3rd-party-assemblies-for-external-use/
Basically it boils down to modifying the System.Windows.Forms.dll which I don't recommend. But if it's some other 3rd party vendor library, then go for it.
A workaround is to do it the other way around: Use an aspect on the method that is hooked to the event and cancel the normal execution of the method if the condition is met. This does not prevent the event form being raised but it prevents your event handling code from being executed.
[EventInter]
private void someForm_FormClosed(object sender, EventArg arg) {}
We use this approach a lot in our project. We have several aspects that apply to event handling methods (exception handling, cursors handling, etc...).
We go a little further, we apply the aspects at the assembly level and we use CompileTimeValide to recognize the signature of an event handling method. In theory, it's not 100% reliable, but we have not found any problems with this approach so far.

Categories