Having trouble adding to a List that exists - c#

So I have a user script that contains a List inventory. Then I use a public static User user to access a single user's information throughout the game.
The problem is at some point I'm losing the link to that inventory.
In the awake function if I call Debug.Log(User.user.inventory.Count), it prints 0.
however if i try and access this inventory at any future time I get a Null Reference error as if it doesn't exist and can't add to it.
If I make the size of the list in the inspector to 1, then it somehow exists and I can add things to it forever, however then I have a dead spot at index 0.
private void AddItemToInventory(ItemObject item, Image uiSprite)
{
User.user.inventory.Add(item);
uiSprite.sprite = GenerateOrbRaritySprite(RaritySprites.raritySprites, item.rarity);
uiSprite.enabled = true;
}
It is failing on the inventory lookup, even though I can see it in the inspector fine.
My User has the following Awake() function so that it stays between scenes:
void Awake()
{
if(user == null)
{
user = this;
DontDestroyOnLoad(gameObject);
}
else if (user != this)
{
Destroy(gameObject);
}
}
Any ideas? Thank you!

Try with just one User.
private void AddItemToInventory(ItemObject item, Image uiSprite)
{
user.inventory.add(item);
uiSprite.sprite = GenerateOrbRaritySprite(RaritySprites.raritySprites, item.rarity);
uiSprite.enabled = true;
}
And since this method doesn't have access to the user the user will need passed in as a parameter.
private void AddItemToInventory(User user, ItemObject item, Image uiSprite)
{
user.inventory.add(item);
uiSprite.sprite = GenerateOrbRaritySprite(RaritySprites.raritySprites, item.rarity);
uiSprite.enabled = true;
}

Related

How to stop the execution of the code until I read a value from PlayerPref in Unity

Currently I am using Toggles to select a list of images and rather ran into an interesting problem.
I am using a Toggle Group called Radio Group and have 3 toggles under it. Each time when a toggle is selected the command
PlayerPrefs.SetInt("SaveToggleId", id);
is run. In this the id number is 0 for toggle 1, 1 for toggle 2 and so on.
So when I try to read this data the next time , the following set of code always reads 0 when used in Start and the correct value when used in Awake
toggleGroupId = PlayerPrefs.GetInt("SaveToggleId");
toggleGroupObject = GetComponent<ToggleGroup>();
SelectStartingToggle(toggleGroupId);
When I used this code in conjuction with the Debug.log() statements in various places what I found is when used in Start , it first reads from the function associated when the first toggle is selected and therby stores 0 . But when I use it in Awake it reads the right value stored in PlayerPrefs and selects the correct initial value
My explanation would be that because Awake is executed before Start , it has ample time to read from PlayerPrefs which gives the correct value. Also when I used only the number in the Start() as follows
SelectStartingToggle(3);
it correctly selected the right toggle whereas when I used PlayerPref instead of number ,it chose the wrong value.
Is my explanation correct or am I missing something?Also how to make sure the code execution is halted until the data from PlayerPref is read. Here is the full code:
public class RadioButtonSystem : MonoBehaviour
{
ToggleGroup toggleGroupObject;
private int toggleGroupId;
// Start is called before the first frame update
private void Awake()
{
toggleGroupId = PlayerPrefs.GetInt("SaveToggleId");
toggleGroupObject = GetComponent<ToggleGroup>();
Debug.Log("SaveToggleId........" + toggleGroupId);
SelectStartingToggle(toggleGroupId);
}
void Start()
{
}
// Update is called once per frame
void Update()
{
}
public void onSelectedToggle1()
{
SaveToggleId(0);
}
public void onSelectedToggle2()
{
SaveToggleId(1);
}
public void onSelectedToggle3()
{
SaveToggleId(2);
}
public void SelectStartingToggle(int id)
{
Toggle[] toggles = GetComponentsInChildren<Toggle>();
toggles[id].isOn = true;
}
public void SaveToggleId(int id)
{
PlayerPrefs.SetInt("SaveToggleId", id);
Debug.Log("SaveToggleId........saving..........." + PlayerPrefs.GetInt("SaveToggleId"));
}
/* Toggle GetSelectedToggle()
{
Toggle[] toggles = GetComponentsInChildren<Toggle>();
foreach (var t in toggles)
if (t.isOn) return t; //returns selected toggle
return null; // if nothing is selected return null
}*/
}
Playerprefs are saved upon OnApplicationQuit(). If you want to save it immediately, call PlayerPrefs.Save(). After PlayerPrefs.SetInt().
Btw, from the unity scripting api:
This function will write to disk potentially causing a small hiccup, therefore it is not recommended to call during actual gameplay.
In the edit/project settings there is a tab called script execution order, where you can set the order in which your scripts will be executed. For example, you have a "LoadManager" script, you set its priority to 1, and you set everything that relys on it to a greater number like 10.
If you fo this, nothing eill start executing until your manager script finished.

Agora.io - When other user leave channel and rejoin, the video stream of the other user hanged/stopped updating

I tried to do a video streaming application with Unity and I have successfully connect two user and are able to video chat & sharescreen. The problem is now when the other/second user press leave channel and rejoin again, first user will see the second user screen just stopped at the last frame. Only way to deal with it right now is to exit Unity play mode and re-enter again. Below are the code:
void LoadEngine()
{
print("Loading Engine...");
if (engine != null)
{
print("Engine already exists. Please unload it first!");
return;
}
engine = IRtcEngine.GetEngine(appId);
engine.SetLogFilter(LOG_FILTER.DEBUG);
}
void UnloadEngine()
{
print("Unloading Engine...");
if (engine != null)
{
IRtcEngine.Destroy();
engine = null;
}
}
void EnableVideo(bool yes)
{
engine.DisableAudio();
if (yes)
{
engine.EnableVideo();
engine.EnableVideoObserver();
}
else
{
engine.DisableVideo();
engine.DisableVideoObserver();
}
}
private void JoinChannel()
{
print("Joining Channel...");
EnableVideo(true);
// add our callback to handle Agora events
engine.OnJoinChannelSuccess += OnJoinChannelSuccess;
engine.OnUserJoined += OnUserJoined;
engine.OnUserOffline += OnUserLeave;
engine.OnLeaveChannel += OnLeaveChannel;
button_Join.onClick.RemoveListener(JoinChannel);
button_Join.onClick.AddListener(LeaveChannel);
button_Join.GetComponentInChildren<Text>().text = "Leave Channel";
if (string.IsNullOrEmpty(channelName))
{
return;
}
engine.JoinChannel(channelName, null, 0);
}
private void LeaveChannel()
{
print("Leaving Channel...");
button_Join.onClick.RemoveListener(LeaveChannel);
button_Join.onClick.AddListener(JoinChannel);
button_Join.GetComponentInChildren<Text>().text = "Join Channel";
playerOne.Clear();
playerTwo.Clear();
engine.LeaveChannel();
EnableVideo(false);
engine.OnJoinChannelSuccess -= OnJoinChannelSuccess;
engine.OnUserJoined -= OnUserJoined;
engine.OnUserOffline -= OnUserLeave;
engine.OnLeaveChannel -= OnLeaveChannel;
}
private void OnJoinChannelSuccess(string channelName, uint uid, int elapsed)
{
print("Joined with uid " + uid);
myId = uid;
playerOne.Set(0);
}
private void OnUserJoined(uint uid, int elapsed)
{
string userJoinedMessage = string.Format("onUserJoined callback uid {0} with elapsed {1}", uid, elapsed);
print(userJoinedMessage);
playerTwo.Set(uid);
}
private void OnUserLeave(uint uid, USER_OFFLINE_REASON reason)
{
string userLeaveMessage = string.Format("onUserOffline callback uid {0} with reason {1}", uid, reason);
print(userLeaveMessage);
playerTwo.Clear();
}
private void OnLeaveChannel(RtcStats stats)
{
playerTwo.Clear();
}
private void OnApplicationQuit()
{
UnloadEngine();
}
Is there anything I missed out in the code?
Edit
I suspect this is Agora API bugs where the video surface will not continue to stream the video when the UID was being changed in the runtime. This explained when the same user leave and rejoin, he will get different uid everytime he rejoin. The video surface will need to set UID to another number which causes the video surface stopped streaming.
Solution
I solved it using unity instantiate to real time instantiate the video surface when connected to channel and destroy the video surface game object when leaving a channel.
first of all, great name! ;)
I'm glad that you were able to work out a solution.
Another technique you can try, is calling:
yourVideoSurface.SetForUser(yourNewUID);
It looks like you're already implicitly doing that in your playerOne.Set(0) call, however I'd figure I'd mention it just in case.
I had the same issue in Unity. The tmp was returning -1. In the Update loop of VideoSurface.cs if this occurs, the update loop stops.
if (tmpi == -1)
return;
I agree with Jeremy, the Agora VideoSurface doesn't support changing users. I set the UID on the VideoSurface manually and it fixed the issue. I also agree that the sdk implies that it would change- which is misleading and frustrating.
I believe the issue may occur because VideoSurface.cs sets uint uid = mUid; in the update loop, so it is set to the default ID when the Unity scene starts then errors out when videoSurface.SetForUser(uid); is called by TestHelloUnityVideo when a user joins.

(C#) Instantiated Under Conditions, Else Reuse

I'm extremely new to programming so bear with me.
public void clickWood()
{
//Check If The Player Has Gotten Wood Previously
if(_hasWood == false)
{
GameObject newItem = Instantiate(wood_prefab) as GameObject;
//Initial Displayed Name
newItem.GetComponent<ButtonSetter>().setName("WOOD: ");
//Starts with 0 Wood, set to 1
newItem.GetComponent<ButtonSetter>().item_count += 1;
newItem.transform.SetParent(GameObject.Find("Content").transform,false);
//Got their first wood, should run else from now on
_hasWood = true;
}
else
{
//SEE CONTINUED POST
}
}
So, within the else statement, I want to basically say,
They got their first wood, so we created a panel to display information about the wood they have. Now I want to say, since we have already instantiated the displayPanel, we just want to work from that and adjust the object's variable that controls the integer part of "Wood: 0"
This should be
newItem.GetComponent<ButtonSetter>().item_count
But if I try accessing that within the else statement as shown:
newItem.GetComponent<ButtonSetter>().item_count += 1;
It tells me that newItem does not exist within the current context.
How can I Instantiate something under certain conditions, then after the condition was met, take the instantiated object and work with its script/variables?
Visual representation of my issue
I think you need to restructure your code a little bit.
So here's how I would do it
public class Item : MonoBehaviour {
public string itemName;
public int count;
}
I will create a base class Item for wood, iron or whatever is there in your game.
Next, I will Create a Handler Script, that keeps track of all Items that are clicked/not Clicked
Handler.cs
public class Handler : MonoBehaviour {
public Item itemPrefab;
public Dictionary<string, Item> displayItems= new Dictionary<string, Item>();
public void OnWoodClicked()
{
OnItemClicked("wood");
}
public void OnIronClicked()
{
OnItemClicked("iron");
}
private void OnItemClicked(string itemKey)
{
if (displayItems.ContainsKey(itemKey))
{
displayItems[itemKey].count++;
}
else
{
Item item = Instantiate(itemPrefab);
item.count = 1;
item.itemName=itemKey;
displayItems.Add(itemKey, item);
}
}
}
So To keep track of created items, I have created a dictionary public Dictionary<string, Item> displayItems;
In this script OnItemClicked(string itemKey) Method will check If Item of this type is already created or not. (By checking if that key exists)
If that item is not created then we will Instantiate new Item (Prefab of display Item you want to show) and add that to the dictionary according to its key.
But if it already exists, then simply access that object as displayItems[itemKey] as you like
So for example, if you click on Wood, you will get access to wood display item as displayItems["wood"].
I hope this helps.
Just place a comment if you want me to make it clearer.
As it's currently written, newItem only lives in the "IF" clause of the code, so the "ELSE" clause can't see it.
If you assign it to a class-level field (like it appears you have _hasWood assigned at the class level) then you will be able to access it in either the "IF" or "ELSE" blocks, and maintain the count for the life of the object "clickWood()" is called from.
private bool _hasWood;
private GameObject _woodItem;
public void clickWood()
{
//Check If The Player Has Gotten Wood Previously
if(_hasWood == false)
{
_woodItem= Instantiate(wood_prefab) as GameObject;
//Initial Displayed Name
_woodItem.GetComponent<ButtonSetter>().setName("WOOD: ");
//Starts with 0 Wood, set to 1
_woodItem.GetComponent<ButtonSetter>().item_count += 1;
_woodItem.transform.SetParent(GameObject.Find("Content").transform,false);
//Got their first wood, should run else from now on
_hasWood = true;
}
else
{
_woodItem.GetComponent<ButtonSetter>().item_count += 1;
}
}

Variable values gets deleted after a new scene is loaded

Short explenation, of what I'm trying to do:
I'm working on a project in Unity. In the first scene I have a UI canvas with 8 Input Fields, for the user to type in up to 8 names. When the user is done typing in the amount of names he/she wants, he/she presses a 'done' button, and all of the names are added in correct order to a list and a new scene is loaded. Then the game begins, and the game goes through the list in chronic order, and gives tasks to the person whos turn it is.
The problem:
The names are added correctly to the list. But when the scene changes, the list empties itself, and all the string variables are deleted. I can't figure out why.
public string namePlayer1, namePlayer2, namePlayer3;
public bool player1IsPlaying, player2IsPlaying, player3IsPlaying = false;
List<string> playerList;
public void getInput1(string player1Name)
{
if (player1Name == "")
{
player1IsPlaying = false;
}
else
{
namePlayer1 = player1Name;
player1IsPlaying = true;
}
}
This code is executed by an 'on end edit' event, when the user has finished typing in a name in the UI Input Field. When the user has typed in the names he/she wants, he/she presses the 'done' button, and this code is executed:
public void PutNamesInList()
{
if (player1IsPlaying)
{
playerList.Add(namePlayer1);
}
if (player2IsPlaying)
{
playerList.Add(namePlayer2);
}
if (player3IsPlaying)
{
playerList.Add(namePlayer3);
}
startGame();
}
Now when a new scene is loaded, all the variables are empty.
Any help is appreciated.
When loading a scene (if not in an additive way) it will destroy everything before loading the new scene. If you don't want a GameObject (and its properties) to be destroyed automatically, use this: https://docs.unity3d.com/ScriptReference/Object.DontDestroyOnLoad.html
If you are not willing to use Object.DontDestroyOnLoad You can also use PlayerPrefs which means
Stores and accesses player preferences between game sessions.

save and load Listbox Items locally and pass them to other pages

I am currently working on Windows Store App in c#.
Now,
I am having a list box 'Listbox1' which gets its items on a button click event from a text box 'tasks', and have selected Items delete property on other button click event.
private void add_Click(object sender, RoutedEventArgs e)
{
string t;
t = tasks.Text;
if (t != "")
{
Listbox1.Items.Add(t);
}
else
{
var a = new MessageDialog("Please Enter the Task First");
a.Commands.Add(new UICommand("Ok"));
a.ShowAsync();
}
tasks.Text = "";
}
private void del_Click(object sender, RoutedEventArgs e)
{
for (int p = 0; p < Listbox1.SelectedItems.Count; p++)
{
Listbox1.Items.Remove(Listbox1.SelectedItems[p].ToString());
p--;
}
}
Now I want to save this list into local application storage, after user complete the changes (on a button click event perhaps).
And also to send all Listbox Items to another page(s).
I am not much a coder, I design things.
Please guide me by sample or reference.
Thank you in advance :)
If you have already stored the data to local storage, you could just read it in the OnNavigatedTo override of the other page. Otherwise, use the navigation parameter: http://social.msdn.microsoft.com/Forums/windowsapps/en-US/8cb42356-82bc-4d77-9bbc-ae186990cfd5/passing-parameters-during-navigation-in-windows-8
Edit: I am not sure whether you also need some information about local storage. This is easy: Windows.Storage.ApplicationData.Current.LocalSettings has a property called Values, which is a Dictionary you can write your settings to. Have a look at http://msdn.microsoft.com/en-us/library/windows/apps/hh700361.aspx
Edit: Try something like this code to store your list.
// Try to get the old stuff from local storage.
object oldData = null;
ApplicationDataContainer settings = ApplicationData.Current.LocalSettings;
bool isFound = settings.Values.TryGetValue("List", out oldData);
// Save a list to local storage. (You cannot store the list directly, because it is not
// serialisable, so we use the detours via an array.)
List<string> newData = new List<string>(new string[] { "test", "blah", "blubb" });
settings.Values["List"] = newData.ToArray();
// Test whether the saved list contains the expected data.
Debug.Assert(!isFound || Enumerable.SequenceEqual((string[]) oldData, newData));
Note, this is only demo code for testing - it does not make real sense...
Edit: One advice: Do not persist the list in your click handlers as this will become extremely slow as the list grows. I would load and save the list in the Navigation handlers, i.e. add something like
protected override void OnNavigatedTo(NavigationEventArgs e) {
base.OnNavigatedTo(e);
if (this.ListBox1.ItemsSource == null) {
object list;
if (ApplicationData.Current.LocalSettings.Values.TryGetValue("List", out list)) {
this.ListBox1.ItemsSource = new List<string>((string[]) list);
} else {
this.ListBox1.ItemsSource = new List<string>();
}
}
}
protected override void OnNavigatedFrom(NavigationEventArgs e) {
if (this.ListBox1.ItemsSource != null) {
ApplicationData.Current.LocalSettings.Values["List"] = this.ListBox1.ItemsSource.ToArray();
}
base.OnNavigatedFrom(e);
}
Here is very nice simple example on SQLite DataBase Use in winRT app Development. Look at it and you will know how you can store your Data on the Local Machine. I learned Basic code from this example.
http://blogs.msdn.com/b/robertgreen/archive/2012/11/13/using-sqlite-in-windows-store-apps.aspx
Now, for ease of navigation let me suggest you a flow for this portion of your app.
take one ObservableCollection<> of string and store values of
that textBox into this ObservationCollection with onClick() and then
refer that ObservableCollection<String> to the ItemsList of the
listBox.
now at the time you need to send your Data to the next page, make one parameterised constructor of next page and pass that ObservableCollection<String> as it's parameter.
Now you can access those Data in your constructor and can use as however you want.
Hope this will help..

Categories

Resources