Function can't access Label - c#

I have two function that use a UIDocument Label
public class UIController : MonoBehaviour
{
private Label argentTexte;
private int currentInt;
private string currentString;
void Start()
{
var root = GetComponent<UIDocument>().rootVisualElement;
argentTexte = root.Q<Label>("currentMoney");
Debug.Log(argentTexte.text); //<- show some string
}
public void IncreaseMoney(int moneyToAdd)
{
currentString = GetComponent<UIDocument>().rootVisualElement.Q<Label>("currentMoney").text; // Don't show anything, throw error
Debug.Log(argentTexte.text); //throw an NullReferenceException: Object reference not set to an instance of an object
int.TryParse(currentString, out currentInt);
argentTexte.text = $"{currentInt + moneyToAdd}";
}
public void DecreaseMoney(int moneyToDecrease)
{
currentString = GetComponent<UIDocument>().rootVisualElement.Q<Label>("currentMoney").text; // show some string
int.TryParse(currentString, out currentInt);
argentTexte.text = $"{currentInt - moneyToDecrease}";
}
}
DecreaseMoney() is used on an object when i spawn it and it work, IncreaseMoney() is used on a child of an object when its destroyed and doesn't work.
Why DecreaseMoney() have access to argentTexte.text and not IncreaseMoney() ?

Related

pass data by reference during invoke

I can pass data from a different thread to the main thread in c# by using invoke (see below code), but how do I pass data the other direction? trying to pass by reference doesnt seem to be working. Using a global doesnt work either since there are multiple threads.
Ideally I would constantly be updating the local variable in the WS_Client function and then whenever I get a request in mywebsocketserver, it triggers the handle/action which would grab that constantly updated variable and pass it to the mywebsocketserver thread. Mywebsocketserver would then send the data out via the socket.
using UnityEngine;
using WebSocketSharp;
using WebSocketSharp.Server;
using System;
public class OrientationEvent
{
public event Action<Vector3> OnOrientationUpdate;
}
public class WS_Client : MonoBehaviour
{
public static GameObject _cube;
private Action<Vector3> callback;
WebSocket ws;
[SerializeField] float speed = 50.0f;
private Vector3 _orientation;
// Start is called before the first frame update
void Start()
{
_cube = GameObject.Find("MyCube");
if (_cube == null)
{
Debug.Log("couldnt find MyCube at first");
}
WebSocketServer wssv = new WebSocketServer(8080);
Action<Vector3> callback = HandleOrientationUpdate;
MyWebSocketServer wsService = new MyWebSocketServer(callback);
wssv.AddWebSocketService<MyWebSocketServer>("/MyWebSocket", () => wsService);
wssv.Start();
}
public WS_Client(OrientationEvent orientationEvent)
{
callback = HandleOrientationUpdate;
}
private void Update()
{
_cube.transform.rotation = Quaternion.Euler(_orientation);
}
private void HandleOrientationUpdate(Vector3 orientation)
{
Debug.Log("tries to rotate mycube");
Debug.Log(orientation);
// Update the cube's orientation using the data from the event
try
{
_orientation = orientation;
Debug.Log("translates the orientation");
}
catch (Exception ex)
{
Debug.LogError("An error occurred: " + ex.Message);
}
}
}
public class MyWebSocketServer : WebSocketBehavior
{
private Action<Vector3> callback;
public MyWebSocketServer(Action<Vector3> callback)
{
this.callback = callback;
}
protected override void OnMessage(MessageEventArgs e)
{
string data = e.Data;
string[] components = data.Split(' ');
Debug.Log("splits components");
float x = float.Parse(components[0]);
float y = float.Parse(components[1]);
float z = float.Parse(components[2]);
Debug.Log("parses components");
Vector3 orientation = new Vector3(x, y, z);
// Vector3 vector = new Vector3((float)x, (float)y, (float)z);
Debug.Log("puts them in vector3");
try
{
callback?.Invoke(orientation);
Debug.Log("invokes action");
}
catch (Exception ex)
{
Debug.LogError("An error occurred: " + ex.Message);
}
}
protected override void OnOpen()
{
// Handle client connection here
Debug.Log("someone connected");
}
protected override void OnClose(CloseEventArgs e)
{
// Handle client disconnection here
Debug.Log("someone disconnected");
}
}
As said I don't see the nead to pass anything as a ref here.
I would rather do it explicit through a kind of "MainThreadDispatcher" thing like e.g.
public class WS_Client : MonoBehaviour
{
...
// Thread-safe stack
readonly ConcurrentStack<Vector3> lastReceived = new();
void Update()
{
// On the main thread handle the latest received value
if(lastReceived.TryPop(out var value)
{
_cube.transform.rotation = Quaternion.Euler(value);
// erase the others as they are outdated
lastReceived.Clear();
}
}
void HandleOrientationUpdate(Vector3 v)
{
// here only store for being handled on main thread
lastReceived.Push(v);
}
}
The same way in the other direction, here it depends a bit in your needs whether you need those reliable as a queue or again only the latest value (in that case I'd rather use a stack as above)
public class MyWebSocketServer : WebSocketBehavior
{
readonly ConcurrentQueue<Vector3> toSend = new();
readonly WaitHandle wait = new AutoResetEvent(false);
public MyWebSocketServer(...)
{
...
var sendThread = new Thread(SendThread);
sendThread.Start();
}
public void Send(Vector3 v)
{
toSend.Enqueue(v);
wait.Set();
}
private void SendThread()
{
while(true)
{
// Allows this thread to go idle => not eating unnecessary performance
wait.WaitOne();
// if using the queue go through all items, otherwise again only pop the newest value and erase the others
while(toSend.TryDequeue(out var v))
{
// However you finally send your value using ToString etc
// tbh I would prefer a byte[] based protocol over string
// it would be faster to parse and also more efficient bandwidth wise
SubmitVector(v);
}
}
}
So now you only need to call Send with the newest values to submit.
You most probably will not want to do this every frame as this will overload the network bandwidth a lot. Rather use a time interval like e.g. 0.2 seconds and interpolate at the receiver side.
I also found a simpler way to do everything, just share the variable in a shared class:
using UnityEngine;
using WebSocketSharp;
using WebSocketSharp.Server;
using System;
public class MySharedData
{
public Vector3 handPosition { get; set; }
public Quaternion handRotation { get; set; }
// public Transform handData { get; set; }
}
public class WS_Client : MonoBehaviour
{
MySharedData sharedData = new MySharedData();
WebSocket ws;
public Transform handR;
void Start()
{
handR = GameObject.Find("LeftHandAnchor").GetComponent<Transform>();
// handR = GameObject.Find("RightHandAnchor").GetComponent<Transform>();
WebSocketServer wssv = new WebSocketServer(8080); //this websocketserver sets up a place for cpus to talk
MyWebSocketServer wsService = new MyWebSocketServer(sharedData); //this is actually a type of websocketbehavior (with a callback connected to the main thread) which
wssv.AddWebSocketService<MyWebSocketServer>("/MyWebSocket", () => wsService); //then gets implemented onto the websocketserver
wssv.Start();
}
private void Update()
{
sharedData.handPosition = handR.transform.position;
sharedData.handRotation = handR.transform.rotation;
}
}
public class MyWebSocketServer : WebSocketBehavior
{
private MySharedData _sharedData;
public MyWebSocketServer(MySharedData sharedData)
{
this._sharedData = sharedData;
}
protected override void OnMessage(MessageEventArgs e)
{
try
{
string data = e.Data;
Console.WriteLine("Received message: " + e.Data);
Vector3 handPosition = _sharedData.handPosition;
Quaternion handRotation = _sharedData.handRotation;
string responseMessage = string.Format("Position:({0},{1},{2}), Rotation:({3},{4},{5})",
handPosition.x, handPosition.y, handPosition.z,
handRotation.x, handRotation.y, handRotation.z);
Debug.Log(responseMessage);
Send(responseMessage);
}
catch (Exception ex)
{
Debug.LogError("An error occurred: " + ex.Message);
}
}
protected override void OnOpen()
{
// Handle client connection here
Debug.Log("someone connected");
}
protected override void OnClose(CloseEventArgs e)
{
// Handle client disconnection here
Debug.Log("someone disconnected");
}
}

Value from one script to another c#

i am trying to make a lives counter in unity with c# so i made a ui text in unity with the value Livesvalue = 3; in a other script i have the deatchscene so when i die the value goes to the other script en does -1 but i get an eroor that the value does not exist
UItext code:
public class livesscript : MonoBehaviour
{
public static int Livesvalue = 3;
Text Lives;
// Start is called before the first frame update
void Start()
{
Lives = GetComponent<Text>();
}
// Update is called once per frame
void Update()
{
Lives.text = "Lives: " + Livesvalue;
}
}
deathscene code:
private void StartDeathSequence()
{
state = State.Dead;
audioSource.Stop();
audioSource.PlayOneShot(death);
deathpart.Play();
mainEnginepart.Stop();
livesscript.Livesvalue -= 1;
Invoke("RespawnLevel", Waitdead); // parameterise time
LivesRespawn();
}
private void LivesRespawn()
{
if (Livesvalue == 0)
{
SceneManager.LoadScene(0);
}
}
When referencing a static variable you need to include the class name and a period before the variable.
private void LivesRespawn()
{
if (Livesvalue == 0)
{
SceneManager.LoadScene(0);
}
}
change to
private void LivesRespawn()
{
if (livesscript.Livesvalue < 1)
{
SceneManager.LoadScene(0);
}
}

Getting NullReferenceExeption when trying to GetComponentInParent from a instantiated buttom prefab

I'm instanciating buttons via script and need then to call a script in a parent object a few levels up
unity hierarchy
This is in the prefab script and gets called when one of the buttons gets clicked. Performance isn't relevant at all, so I just told it to go from the bottom up until it reaches the controller game object that has the mllhildTestController.cs script attached.
public void ClickMe()
{
string parentNode = ReturnLabel();
string path = this.GetComponentInParent<mllhildTestController>().path;
this.GetComponentInParent<mllhildTestController>().Clear();
this.GetComponentInParent<mllhildTestController>().Load(path, parentNode);
}
but it only results in an error
NullReferenceException: Object reference not set to an instance of an object
this.something is working fine, so it has to be an error in my logic with the GetComponentInParent<mllhildTestController>() part.
Could someone please help me?
EDIT: this function seems to work fine, but since it was asked
public string ReturnLabel()
{
return buttonText.text;
}
Controller script
public class mllhildTestController : MonoBehaviour
{
public mllhildTestLinq linq;
public mllhildTestDisplay display;
public mllhildTestButtons buttons;
public List<GameObject> listToStuff;
public string test = "smName";
public string smName = "mllhild";
public string displayText = "display.textWindow.font";
public string path = #"C:\SlaveMaker4\Slaves\";
// Start is called before the first frame update
void Start()
{
ClearText();
// linq.LinqTest(#"C:\SlaveMaker4\Rhia.xml");
// string filename = #"C:\SlaveMaker4\Rhia.xml";
// linq.QueryXML(filename, parentNode);
// Debug.Log(this.GetType().GetField("test").GetValue(this));
// Debug.Log(this.GetType().GetField(test).GetValue(this));
// Debug.Log(display.textWindow.font);
// Debug.Log(this.GetType().GetField("display").GetType().GetField("textWindow").GetType().GetField("font").GetValue(this));
// Debug.Log(typeof(TextMeshProUGUI).GetProperty(displayText).GetValue(this));
// Debug.Log(this.GetType().GetField(displayText).GetValue(this));
}
// Update is called once per frame
void Update()
{
}
public void SetText(string text)
{
display.textWindow.text = text;
}
public void AddText(string text)
{
display.textWindow.text += text;
}
public void ClearText()
{
display.textWindow.text = null;
}
public GameObject bfield;
public GameObject AddNewButtonList(string label)
{
GameObject b = Instantiate(bfield) as GameObject;
b.SetActive(true);
b.GetComponent<PrefabListButtoms>().title.text = label;
b.transform.SetParent(bfield.transform.parent, false);
return b;
}
public void Clear()
{
foreach(GameObject b in listToStuff)
{
Destroy(b);
}
listToStuff.Clear();
}
public void LoadButton() { Load(path, "Language" ); }
public void Load(string path, string parentNode)
{
string[] fileArray = Directory.GetFiles(path, "*.xml");
foreach (string xmlfile in fileArray)
{
GameObject blist = AddNewButtonList(xmlfile + "\n" + parentNode);
listToStuff.Add(blist);
//AddText("\n\n" + xmlfile + "\n");
//AddText("Parent-Node:" + parentNode + "\n");
//AddText("Child-Nodes:" + "\n");
linq.QueryXML(xmlfile, parentNode, blist);
}
}
}
You cant access directly because you have to find parent and then you find the desired children
so your first challenge is to find the parent of your component:
so first command is : GameObject.Find("NameofObj") or GameObject.FindWithTag("NameOfTag")
so after you just find the desired children component from this GameObject..and finally you could access to the method of script linked to child

Find reference in difference scene unity

I'm confused about finding reference GameObject but different Scene and set the onclick when difference scene, so I have GameManager who manage all but available only on the Main menu. So I decide to make Dontdestroyonload, the issue start at this, so when I play to the MainGame Scene, the field of GameManager at inspector will find, but I can't drag n drop different scene, right? That confuses me.
And if the GameManager at the MainMenu scene, the question is how to drag n drop at the onClick event, like I want pause button active or something else in the game.
]3
I tried with onLloadscene(scene s, Mode mode), but nothing happens, and here the scrip for the GameManager. :
public static GameManager gameManager;
[Header("Main Menu panels")]
public GameObject startPanel;
public GameObject settingPanel;
public GameObject levelPanel;
[Header("InGame Panels")]
#region Panel
public GameObject pausePanel;
public GameObject ObjectivePanel;
public GameObject shopPanel;
private int click = 0;
[Header("Int Tweaks")]
public int indexLevel;
public int onlevel;
public bool isPaused;
_levelSelect LevelSelect;
public static GameManager Instance { set; get; }
public int levelindexPlayerPrefs;
private void Awake()
{
if (gameManager != null)
{
Instance = this;
Destroy(gameObject);
}
else
{
DontDestroyOnLoad(gameObject);
}
}
void Start()
{
LevelSelect = FindObjectOfType<_levelSelect>();
OnStart();
onlevel = int.Parse(LevelSelect.levelIndex) + 1;
indexLevel = int.Parse(LevelSelect.levelIndex);
getPlayerData();
}
// Update is called once per frame
void Update()
{
ExitApp();
}
public void OnStart()
{
startPanel.SetActive(true);
settingPanel.SetActive(false);
levelPanel.SetActive(false);
}
#region Buttons
public void startbutton()
{
levelPanel.SetActive(true);
startPanel.SetActive(false);
settingPanel.SetActive(false);
}
public void backButtonMainMenu()
{
levelPanel.SetActive(false);
startPanel.SetActive(true);
settingPanel.SetActive(false);
}
public void settingbutton()
{
levelPanel.SetActive(false);
startPanel.SetActive(false);
settingPanel.SetActive(true);
}
public void PauseButton()
{
Time.timeScale = 0f;
pausePanel.SetActive(true);
ObjectivePanel.SetActive(false);
}
public void Resume()
{
Time.timeScale = 1f;
}
#endregion
public void ExitApp()
{
if (Input.GetKey(KeyCode.Escape))
{
click++;
StartCoroutine(ClickTime());
if (click>1)
{
print("Exit Game");
Application.Quit();
}
}
}
IEnumerator ClickTime()
{
yield return new WaitForSeconds(0.5f);
click = 0;
}
public void getPlayerData()
{
levelindexPlayerPrefs = PlayerPrefs.GetInt("LevelIndex", 0);
}
public void updateLevel(int Index)
{
if (levelindexPlayerPrefs < Index)
{
PlayerPrefs.SetInt("LevelIndex", Index);
levelindexPlayerPrefs = PlayerPrefs.GetInt("LevelIndex");
}
}
#region onloadedScenePickRefferences
private void OnEnable()
{
SceneManager.sceneLoaded += OnSceneLoaded;
}
private void OnDisable()
{
SceneManager.sceneLoaded -= OnSceneLoaded;
}
void OnSceneLoaded(Scene scene, LoadSceneMode mode)
{
pausePanel = GameObject.FindGameObjectWithTag("PausePanel");
ObjectivePanel = GameObject.FindGameObjectWithTag("ObjectivePanel");
}
#endregion
//public IEnumerator EndChapter()
//{
// updateLevel(indexLevel + 1);
// getPlayerData();
//}
Here is what I would probably do:
Have a static class for storing and sharing all your references. It doesn't have to be in any scene but simply "lives" in the assets:
public static class GlobalReferences
{
// as example just for one reference but you can implement the rest equally yourself
// here this class actually stores the reference
private static GameObject startPanel;
// A public property in order to add some logic
// other classes will always access and set the value through this property
public static GameObject StartPanel
{
get
{
// if the reference exists return it right away
if(startPanel) return startPanel;
// as a fallback try to find it
startPanel = GameObject.FindGameObjectWithTag("StartPanel");
// ofcourse it might still fail when you simply try to access it
// in a moment it doesn't exist yet
return startPanel;
}
set
{
startPanel = value;
// invoke an event to tell all listeners that the startPanel
// was just assigned
OnStartPanelReady?.Invoke();
}
}
// An event you will invoke after assigning a value making sure that
// other scripts only access this value after it has been set
// you can even directly pass the reference in
public static event Action<GameObject> OnStartPanelReady;
}
So now in your component(s) that is(are) in the new loaded scene you assign the value as early as possible (Awake). Here you can already store it via the Inspector since it is a scene reference:
public class ExampleSetter : MonoBehaviour
{
// already reference it via the Inspector
[SerializeField] private GameObject startPanel;
private void Awake()
{
// as a fallback
if(!startPanel) startPanel = GameObject.FindObjectWithTag("startPanel");
// assign it to the global class
GlobalReferences.StartPanel = startPanel;
}
}
And in other scenes that where already loaded before you add a listener so they do their stuff as soon as the other scene is ready:
public class ExampleConsumer : MonoBehaviour
{
[Header("Debug")]
[SerializeField] private GameObject startPanel;
private void Awake()
{
// Try to get the reference
startPanel = GlobalReferences.StartPanel;
// if this failed then wait until it is ready
if(!startPanel)
{
// it is save to remove callbacks even if not added yet
// makes sure a listener is always only added once
GlobalReferences.OnStartPanelReady -= OnStartPanelReady;
GlobalReferences.OnStartPanelReady += OnStartPanelReady;
}
// otherwise already do what you want
else
{
OnStartPanelReady(startPanel);
}
}
private void OnDestroy()
{
// always make sure to clean up callbacks when not needed anymore!
GlobalReferences.OnStartPanelReady -= OnStartPanelReady;
}
private void OnStartPanelReady(GameObject newStartPanel)
{
startPanel = newStartPanel;
// always make sure to clean up callbacks when not needed anymore!
GlobalReferences.OnStartPanelReady -= OnStartPanelReady;
// NOTE: It is possible that at this point it is null anyway if another
// class has set this actively to null ;)
if(startPanel)
{
// Now do something with the startPanel
}
}
}
The other way round when you need a reference in the new loaded Scene form the main scene ... it should already be set since the mainscene was loaded first and has already assigned its according references.
Now you can either go for this static class or simply implement the same logic for each reference that needs to be shared directly in an according component where you reference them via drag&drop .. it makes no difference since anyway you will use static fields and events that are not bound to any instance but the type(s) itself.

unable to see variable

stupid question,but still. I have some variable, let it be MyVariable, which gets its value in one of the methods in code-behind, in MouseClick, for example, but then value of MyVariableis used in a method in another class(I'm using different Patterns). Of course, in this class MyVariable is invisible, but what is better to do?
public partial class MainWindow : Window
{
Hero Hero1 = new Hero();
Notify newNotify = new Notify();
public MainWindow()
{
InitializeComponent();
Collections coll = new Collections();
}
private void weapons_DragLeave(object sender, DragEventArgs e)
{
Hero1.Attack = ((Weapon) listweapons.SelectedItem)._attack;
newNotify.ThrowHero().Attack = Hero1.Attack;
newNotify.Dropped += Show_Message;
newNotify.DroppedEquipment();
TextBox2.Text = newNotify.ThrowHero().Description;//this gets its value from attack
}
public void Show_Message()
{
Console.WriteLine(newNotify.ThrowHero().Description =
"Защита:" + newNotify.ThrowHero().Protection + "атака:" + newNotify.ThrowHero().Attack);
}
}
and then i have another method in another class
public class SavingInWord : IWordSaving
{
public void ExportToWord()
{
var wordApp = new Word.Application();
wordApp.Visible = false;
Word.Document doc = wordApp.Documents.Add();
doc.Select();
wordApp.Selection.TypeText("Description:" + newNotify.ThrowHero().Description); //newNotify.ThrowHero().Description) can't be seen here, but i need to have the value here which i got from weapons_DragLeave method
doc.SaveAs(#"D:...doc");
wordApp.Visible = true;
}
}
in another class:
public class Notify
{
Hero hero1 = new Hero();
public Hero ThrowHero()
{
return hero1;
}
public delegate void MyEventhandler();
public event MyEventhandler Dropped;
public void DroppedEquipment()
{
if (Dropped != null)
Dropped();
}
}
In your root-most namespace
public static class Globals
{
Notify newNotify = new Notify();
}
Then you'll be able to access it from where ever.

Categories

Resources