How to set up event handling with prebuild class - c#

I am very new to C# and try to use Microsoft.MixedReality.QR for reading some QRCodes with the Hololense 2 and have problems accessing the events of the QRCodeWatcher. According to the API, the QRCodeWatcher class has 4 events but it seems that these are never triggered. (QR_Added, QR_Updated,... are never called). Am I setting up something wrong?
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Microsoft.MixedReality.QR;
using QRTracking;
namespace QRTracking {
public class Detector: MonoBehaviour {
public TextMesh debugScreen;
public bool IsSupported {get; private set; }
private QRCodeWatcher qrTracker;
private bool QRstarted=false;
private QRCodeWatcherAccessStatus accessStatus;
// Start is called before the first frame update
async protected virtual void Start() {
debugScreen.text="Hello";
if (QRCodeWatcher.IsSupported()) {
try {
accessStatus=await QRCodeWatcher.RequestAccessAsync();
debugScreen.text=accessStatus.ToString();
}
catch {
debugScreen.text="No World";
}
}
}
// Update is called once per frame
void Update() {
if (accessStatus.ToString()=="Allowed") {
if (QRstarted==false) {
debugScreen.text="Setup Tracking";
SetupQRTracking();
QRstarted=true;
}
}
}
private void SetupQRTracking() {
qrTracker=new QRCodeWatcher();
qrTracker.Added+=QR_Added;
qrTracker.Updated+=QR_Updated;
qrTracker.Removed+=QR_Removed;
qrTracker.EnumerationCompleted+=QR_Enum;
qrTracker.Start();
debugScreen.text="Tracking started";
}
private void QR_Added(object sender, QRCodeAddedEventArgs args) {
debugScreen.text="Added";
}
private void QR_Updated(object sender, QRCodeUpdatedEventArgs args) {
debugScreen.text="Updated";
}
private void QR_Removed(object sender, QRCodeRemovedEventArgs args) {
debugScreen.text="Removed";
}
private void QR_Enum(object sender, object e) {
debugScreen.text="Enum";
}
}
}
(While the script is running the HoloLense is able to read the content of a QRCode. The debugScreen is a TextMesh so that I can see the text while wearing the HoloLense.)

Workaround to get only the recently seen QR codes to mimic the update event:
private IReadOnlyList<QRCode> detectedQR;
private DateTimeOffset timestamp;
private int timetolerance = 1; // seconds
detectedQR = qrTracker.GetList();
timestamp = DateTimeOffset.Now;
foreach (var QR in detectedQR)
{if ((timestamp - QR.LastDetectedTime ).Duration() < TimeSpan.FromSeconds(timetolerance))
{
//Do stuff with QR Codes seen in the last second
}
}
However, with too many QR codes, performance could suffer.

Hi I’m Wayne Wang from the Microsoft for Founders Hub team!
We do have an example from my colleague on your current context.
Seems the implementation you may need is here:
https://github.com/chgatla-microsoft/QRTracking/blob/master/SampleQRCodes/Assets/Scripts/QRCodesManager.cs
it was little bit of old but I believe the Interfaces and pipelines still the same

Related

How to know when field or property initialization is done?

For example, there's two classes here.
Class One:
using UnityEngine;
using System.Collections.Generic;
public class One:MonoBehavior
{
private List<string> _assetBundleList;
private void Start()
{
InitializeList();
}
private void InitializeList()
{
//Add assetBundleList field some value.
}
public IEnumerator<string> GetEnumerator()
{
return _assetBundleList.GetEnumerator();
}
}
Class two:
public class Two:MonoBehavior
{
public GameObject gameObjectWithScriptOne;
private One _scriptOne;
private void Start()
{
scriptOne = gameObjectWithScriptOne.GetComponent<One>();
DoSomething();
}
private void DoSomething()
{
foreach(var assetBundle in scriptOne)
{
//Load asset
}
}
}
Script one is just like a manager things, I use this for storing asset bundle data, perhaps the list value will change. Script two must wait for initializing done. Is there any way to wait for it except adjusting script order?
It can be handled with easily creating a context that initialize things sequentially. In short you need to block the main thread.
Let's say you have a root or persistent scene that do must things like loading assets etc.
//The only object in the Root Scene.
public class RootContext : MonoBehaviour
{
private const string _playerAddress = "Player";
public void Awake()
{
//Load assets
//wait for assets loading
//instantiate things.
//new instantiated things can also initalize with this way.
var handle = Addressables.LoadAssetAsync<GameObject>(_playerAddress);
var asset = handle.WaitForCompletion();
var go = Instantiate(bla bla bla);
}
}
I think the what you want is a framework that organize things. You can look at Strange. It is MVCS + IoC framework. Addionality what you said about "when field or property initialization is done" with [PostConstruct] attribute with Strange.
[Inject(ContextKeys.CONTEXT)]
public IContext Context { get; set; }
/*Initialization of injections are guaranteed at here. Their initialization
is also quaranteed like this class.
*/
[PostConstruct]
public void Initalize()
{
}
I'd go with refactoring, but if this is not the case C# events might be the solution.
Class One
public class One:MonoBehavior
{
private List<string> _assetBundleList;
public event Func Initialized;
private void Start()
{
InitializeList();
}
private void InitializeList()
{
//Add assetBundleList field some value.
Initialized?.Invoke();
}
public IEnumerator<string> GetEnumerator()
{
return _assetBundleList.GetEnumerator();
}
}
Class Two:
public class Two:MonoBehavior
{
public GameObject gameObjectWithScriptOne;
private One _scriptOne;
private void Start()
{
scriptOne = gameObjectWithScriptOne.GetComponent<One>();
scriptOne.Initialized += DoSomething;
}
private void DoSomething()
{
foreach(var assetBundle in scriptOne)
{
//Load asset
}
}
}
Also, you should unsubscribe from events when disabling an object, but you'll figure it out by yourself.

How to get finger positions in Leap Motion using C#

I want to get finger positions in Leap motion using c#.I'm new to leap motion controller. So i Couldn't find any example code for detecting finger positions, but i tried to come up with some code. I think it has lot of errors.
So, how to get finger positions in Leap Motion using C#?
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Leap;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form, ILeapEventDelegate
{
private Controller controller;
private LeapEventListener listener;
public Form1()
{
InitializeComponent();
this.controller = new Controller();
this.listener = new LeapEventListener(this);
controller.AddListener(listener);
}
delegate void LeapEventDelegate(string EventName);
public void LeapEventNotification(string EventName)
{
if (!this.InvokeRequired)
{
switch (EventName)
{
case "onInit":
MessageBox.Show("onInit");
break;
case "onConnect":
MessageBox.Show("onConnect");
break;
case "onFrame":
MessageBox.Show("onFrame");
break;
}
}
else
{
BeginInvoke(new LeapEventDelegate(LeapEventNotification), new object[] {
EventName });
}
}
private void Form1_Load(object sender, EventArgs e)
{
}
}
public interface ILeapEventDelegate
{
void LeapEventNotification(string EventName);
}
public class LeapEventListener : Listener
{
ILeapEventDelegate eventDelegate;
public LeapEventListener(ILeapEventDelegate delegateObject)
{
this.eventDelegate = delegateObject;
}
public override void OnInit(Controller controller)
{
this.eventDelegate.LeapEventNotification("onInit");
}
public override void OnConnect(Controller controller)
{
this.eventDelegate.LeapEventNotification("onConnect");
}
public override void OnFrame(Controller controller)
{
this.eventDelegate.LeapEventNotification("onFrame");
}
public override void OnExit(Controller controller)
{
this.eventDelegate.LeapEventNotification("onExit");
}
public override void OnDisconnect(Controller controller)
{
this.eventDelegate.LeapEventNotification("onDisconnect");
}
}
}
Your code looks like it is derived from the documentation example. However, you have left out the actual event handlers so nothing will ever happen.
You will see in the linked example that there is a newFrameHandler function. You can change that to access the finger positions in the frame object, which is a snapshot of the tracked hands at a moment in time.
void newFrameHandler(Frame frame)
{
foreach(Finger in frame.Fingers){
Vector fingerPosition = finger.TipPosition;
//Do something with this information...
}
}
You can similarly get the hands and then access each hand's fingers, which often is the more natural approach:
foreach(Hand hand in frame.Hands){
foreach(Finger in hand.Fingers){
Vector fingerPosition = finger.TipPosition;
//Do something with this information...
}
}

Trying to use functions from different scripts requires static update, but breaks the code unity

I have a number of functions on different scripts that I am trying to using in a win.cs
for example.
team.cs
PlayGame();
.
score.cs
UpdateScore();
I have some of the variable set to static, but not all of them, because it isn't needed (yet?)
I tried to put PlayGame(); and UpdateScore(); on the win.cs. This requires me to do a:
public static void UpdateScore(){}
now that this is static I have to go and make all the other vairables associated with the UpdateScore static and then the code breaks.
Is there a better way to do what I am doing?
I have tried using team.PlayGame() as the variable, but that requires the whole static thing too.
Sorry this is kind of hard to explain.
Note:All of the functions are public.
if Win.cs needs to call UpdateScore on Score.cs then you go as such:
public class Win : MonoBehaviour{
[SerializeField]private Score score = null;
private void Start(){
if(this.score == null){
this.score = this.gameObject.GetComponent<Score>();
}
}
// Then you can use this.score.UpdateScore when needed
}
It is possible to do the same action the other way around using event.
public class Win:MonoBehaviour{
public EventHandler<System.EventArg> RaiseUpdateScore;
protected void OnUpdateScore(EventArg args){
if(RaiseUpdateScore != null){
RaiseUpdateScore(this, args);
}
}
public void SomeAction(){
if(someCondition){
OnUpdateScore(null);
}
}
void OnDestroy(){ RaiseUpdateScore = null; }
}
then on Score.cs
public class Score:MonoBehaviour{
private Win win = null;
private void Start(){
this.win = this.gameObject.GetComponent<Win>();
this.win.RaiseUpdateScore += HandleUpdateScore;
}
private void HandleUpdateScore(object sender , System.EventArg args){}
private void OnDestroy(){
if(this.win != null){
this.win.RaiseUpdateScore -= HandleUpdateScore;
}
}
}
You can simply use Component.SendMessage to call a method of a script from another script.
Someting like this from win.cs script:
team teamScript = GameObject.Find("Object that team script is on that").GetComponent<team>();
teamScript.SendMessage("PlayGame", you parameter);

TextBox not updating in C#

Specifically looking at the arrive method in the Customer class. I am using a for loop to create instances of the customer class, and when I try to write out their arrival times to a textBox (Just for testing purposes) the text box does not update. Why is this?
This is just a small simulation project for my Computing class. It is in its early stages, and is probably wrong in a lot of places!
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;
namespace QueueSimulation
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
public void Form1_Load(object sender, EventArgs e)
{
MessageBox.Show("The form has loaded");
}
public void goButton_Click(object sender, EventArgs e)
{
Initialisers init = new Initialisers();
Customer customer = new Customer();
customer.Arrive();
}
private void stopButton_Click(object sender, EventArgs e)
{
// put code here to break out of the program
}
}
public class Customer : Initialisers
{
int waitingTime;
int arrivalTime;
int arrivalInterval;
Initialisers init = new Initialisers();
public void Arrive()
{
Customer[] customer = new Customer[1000];
int counter = 0;
for (int i = 1; i <= 10; i++)
{
customer[i] = new Customer();
customer[i].TimeArrived();
displayArrival.Text = displayArrival.Text + customer[i].TimeArrived().ToString();
// Implement something to either show the time in the queue if needed
Thread.Sleep(init.CustomerArriveTime*100);
}
MessageBox.Show("All of the customers have arrived");
}
public string TimeArrived()
{
return Convert.ToString(DateTime.Now);
}
public void Leave()
{
}
public void GetServed()
{
}
}
public class Server
{
bool servingStatus;
int servingTime;
public void Serve()
{
}
}
public class Initialisers : Form1
{
private int cust_no = 3;
public int CustomerArriveTime
{
get
{
return cust_no;
}
set
{
cust_no = value;
}
}
private int s_time = 4;
public int serveTime
{
get
{
return s_time;
}
set
{
s_time = value;
}
}
}
}
Pass to the Arrive the instance of the textbox object created on your Form1.
public void Arrive(TextBox displayArrival)
Why are you inheriting the Form1 in Initialiserz? It's better to pass the reference to Form1 instead of inheritance in this case.
This seems overly complex. Try to model the real world. What is Initialisers, and why do you have an inheritance tree: Customer > Initialisers > Form1?
You're customer is writing to its own TextBox, instead of the TextBox you're looking at (the one from the Form that is visible).
Why not have a method Arrive that sets a private field to DateTime.Now. Then, ask the Customer its TimeArrived, which returns this field. In your Form, call these methods as much as needed in your loop.
This also seperaties command (Arrive) from query (TimeArrived) + keeps your inheritance more logical.
You might not even need Initialisers anymore. And don't let Customer inherit from Form, because a Customer isn't a Form.
I think there is more of a design issue here, you are creating instances of customer inside customer.
Your customer Arrive method should probably be a function inside the another class, like below, customer should just define what a customer is. Processing them should be handled by a different class.
class Customer
{
int waitingTime;
int arrivalTime;
int arrivalInterval;
// etc...
}
class ProcessCustomers
{
pubic void Arrive()
{
// etc...
}
}
public void goButton_Click(object sender, EventArgs e)
{
Initialisers init = new Initialisers();
ProcessCustomers CustomerQueue = new ProcessCustomers();
CustomerQueue .Arrive();
}
But for the text box issue you will have to expose a property in the form class and set it like that,
string ArrivalTime
{
get
{
return textBox1.Text;
}
set
{
textBox1.Text = value;
}
}

Any efficient/reliable way to expose one event?

Any efficient/reliable way to expose one event?
I have a class, MultipleDocumentCopier that copies multiple documents thru an instance of SingleDocumentCopier.
SingleDocumentCopier exposes an event CopyCompleted that is fired when a file is copied.
Suppose that, I am copying 10 files, instead of raising SingleDocumentCopier.CopyCompleted 10 times,
I would like to expose an event, MultipleDocumentCopier.MultipleCopyCompleted.
But is there a standard way/technique to combine multiple events and fire it once?
I would like to raise MultipleDocumentCopier.MultipleCopyCompleted only once
within 'MultipleDocumentCopier.singleDocumentCopier_CopyCompleted', instead of 10 times.
Here is the sample code
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
namespace CombineEvents
{
public class Program
{
public static void Main(string[] args)
{
var copier = new MultipleDocumentCopier();
copier.MultipleCopyCompleted += MultipleDocumentCopyCompleted;
copier.CopyDocuments(new[] {"File1", "File2", "File3"});
}
private static void MultipleDocumentCopyCompleted(
object sender, FileNameEventArgs e)
{
Debug.Print("Following documents have been copied");
foreach (var fileName in e.FileNames)
{
Debug.Print("\t\t\"{0}\"", fileName);
}
}
}
internal class SingleDocumentCopier
{
public event EventHandler CopyCompleted;
protected virtual void OnCopyCompleted()
{
if (CopyCompleted != null) CopyCompleted(this, EventArgs.Empty);
}
public void Copy(string fileName)
{
Debug.Print("Copying = '{0}'", fileName);
OnCopyCompleted();
}
}
public class MultipleDocumentCopier
{
public event EventHandler<FileNameEventArgs> MultipleCopyCompleted;
protected virtual void OnCopyCompleted(FileNameEventArgs e)
{
EventHandler<FileNameEventArgs> completed = MultipleCopyCompleted;
if (completed != null) completed(this, e);
}
public void CopyDocuments(IEnumerable<string> fileNames)
{
var copier = new SingleDocumentCopier();
copier.CopyCompleted += singleDocumentCopier_CopyCompleted;
foreach (var fileName in fileNames)
{
copier.Copy(fileName);
}
}
public static void singleDocumentCopier_CopyCompleted(
object sender, EventArgs e)
{
// I want to raise "MultipleDocumentCopier.MultipleCopyCompleted" when
// all files, `fileNames` in "CopyDocuments" have been copied,
// not for every file being copied.
}
}
public class FileNameEventArgs : EventArgs
{
private readonly List<string> _FileNames;
public List<string> FileNames
{
get { return _FileNames; }
}
public FileNameEventArgs(IEnumerable<string> fileNames)
{
_FileNames = fileNames.ToList();
}
}
}
Why not call MultipleDocumentCopier.OnCopyCompleted from the end of CopyDocuments, and forget singleDocumentCopier_CopyCompleted entirely?
Or maybe this is pseudocode, and your real code is more complicated? Maybe you could keep a collection of outstanding file names inside MultipleDocumentCopier, and each time the singleDocumentCopier_CopyCompleted is raised, you remove one document from the collection. Once the collection becomes empty you call MultipleDocumentCopier.OnCopyCompleted.
Edit: Re 'is there a standard way?' -- not that I'm aware of in C#; F# has an interesting set of mechanisms for combining events like this, but I assume a change in programming language isn't an option.

Categories