Using an Object in Mobile App c# - c#

I'm working on a simple mobile app game using c# and Xamarin.Forms and have gotten stuck trying to use an object class called Player.
class Player
{
private int level;
public int Level
{
get { return Level; }
set { Level = value; }
}
public Player()
{ //variables initialized
this.Level = 1;}
}
The ContentPage that creates the UI also creates an instance of Player, but when any property in Player is accessed, the app crashes.
Right now I have Player player as a data member in BasePage (the ContentPage class), and the accessing of player happens when the user presses a button:
class BasePage : ContentPage
{
private Player player;
public BasePage() {
player= new Player();
//stuff...
var btnPlayer = new Button
{
Text = "YOU"
};
btnPlayer.Clicked += (o, e) =>
{
//change text in lblMessage
lblMessage.Text = "Your level: " + player.Level;
};
//stuff is added to screen, not relevant
}
How should I be creating player so it doesn't crash? I've been checking out other questions on here and I haven't been able to find what I'm looking for (I've tried binding the object as well, but I might not be doing it correctly).

This code causes an infinitely recursive loop - the public Level (capital 'L') property is returning the value of the public Level property, which causes it to call the 'get' again, repeatedly
public int Level
{
get { return Level; }
set { Level = value; }
}
instead you should use the private member variable level (small 'l')
public int Level
{
get { return level; }
set { level = value; }
}
You could also do this, which will allow C# to create the property for you without explicitly creating a private variable.
public int Level { get; set; }

Related

Setting Singleton Property definition with dynamic String

I have a singleton class called KeyManager that is used to define and change player keybinds. It needs references to Text objects that will display what the currently set Keybind is. The singleton and text objects are in different scenes so I want to set the object definitions using a separate script that is attached to the (parent of) the Text objects themselves.
Here is the Singleton in its basic implementation.
public class KeyManager : MonoBehaviour
{
public static KeyManager Instance { get; private set; }
public static Dictionary<string, KeyCode> Keybinds = new Dictionary<string, KeyCode>();
private GameObject currentKey;
//the Text objects that need references
public static Text PitchUp { get { return pitchUp; } set { pitchUp = value; } }
private static Text pitchUp;
public static Text PitchDown { get { return pitchDown; } set { pitchDown = value; } }
private static Text pitchDown;
public static Text RotateLeft { get { return rotateLeft; } set { rotateLeft = value; } }
private static Text rotateLeft;
public static Text RotateRight { get { return rotateRight; } set { rotateRight = value; } }
private static Text rotateRight;
//...irrelevant singleton logic continues
}
Here is the script attached to the parent GameObject of each Text object to be referenced (parent is a button). The GameObject name of the button matches the Singleton property names.
public class KeybindCRData : MonoBehaviour
{
string buttonName;
Text childText;
void Start()
{
buttonName = this.name; //collect name of GameObject as set in Editor as a string
childText = this.transform.GetChild(0).GetComponent<Text>(); // collect Text object that will be assigned
//set Singleton property to this objects child Text
KeyManager.-INSERT PROPERTY NAME HERE- = childText;
//KeyManager.(Text)buttonName = childText;
// ^ this doesn't work of course, but this is the idea/goal
}
}
What is needed so I can dynamically change which property of the Singleton I am referencing based on the name of the Text object? I want to be able to just slap the KeybindCRData script on each button and be done.
The alternative that I see is specifically defining the property and related button for each keybind, but with 30+ keybinds it would not be ideal to end up with 30 different scripts each tailored to only 1 button.
Thanks for any help.
Thanks for the previous replies, they show other ways to do this more efficiently.
I figured out how to answer the crux of my original question though, so here is the code if it is useful to anyone else.
In the Manager Singleton, I add this public method.
public void SetProperty(string propertyName, TextMeshProUGUI value)
{
this.GetType().GetProperty(propertyName).SetValue(this, value);
}
In the KeybindCRData script that is attached to each individual button(used to rebind), I added this method call to the Awake() function.
void Awake()
{
//collect name of GameObject as set in Editor as a string
buttonName = this.name;
// collect Text object that will be assigned
childText = this.transform.GetChild(0).GetComponent<TextMeshProUGUI>();
//set Singleton property to this objects child Text
KeyManager.Instance.SetProperty(buttonName, childText);
}
This allows me to "spell out" the Property value using a String, grabbed from the matching Button-name. Thank you everyone, cheers!
I would try to redesign the code, so that your manager does not need to know about all the different text components. To keep your manager code simple and focused, it's better to invert the flow and have all your text components subscribe to an event in the manager to get notified whenever a key binding changes.
public static class KeyBindings
{
public static event Action<string, KeyCode> Changed;
private static readonly Dictionary<string, KeyCode> bindings = new Dictionary<string, KeyCode>();
public static KeyCode Get(string name) => bindings[name];
public static void Set(string name, KeyCode key)
{
bindings[name] = key;
Changed?.Invoke(name, key);
}
}
public class KeyBindingDisplayer : MonoBehaviour
{
[SerializeField]
private string keyBindingName;
private void Reset() => keyBindingName = name;
private void OnEnable()
{
KeyBindings.Changed += OnKeyBindingsChanged;
UpdateText(KeyBindings.Get(keyBindingName));
}
private void OnDisable()
{
KeyBindings.Changed -= OnKeyBindingsChanged;
}
private void OnKeyBindingsChanged(string name, KeyCode keyCode)
{
UpdateText(keyCode);
}
private void UpdateText(KeyCode keyCode)
{
GetComponent<Text>().text = keyCode.ToString();
}
}
Instead of creating new property for each Text object, use a Dictionary<string, Text>. The Key can be the buttonName and the value can be childText.
public class KeyManager : MonoBehaviour
{
public static KeyManager Instance { get; private set; }
public static Dictionary<string, KeyCode> Keybinds = new Dictionary<string, KeyCode>();
private GameObject currentKey;
//Key: buttonName (pitchUp, pitchDown, rotateLeft.. etc )
public static Dictionary<string, Text> Texts;
}
In KeybindCRData class, set the value to the dictionary.
public class KeybindCRData: MonoBehaviour
{
string buttonName;
Text childText;
void Start()
{
buttonName = this.name;
childText = this.transform.GetChild(0).GetComponent<Text>();
//Add appropriate logic as per requirement
if(!KeyManager.Texts.ContainsKey(buttonName)) {
KeyManager.Texts.Add(buttonName, childText)
}
}
}

Trying to store values in a script that does not inherit from MonoBehaviour

I'm trying to make it so that once you click "Accept" on a UI button that it will store all the values from the quest into the following script:
[System.Serializable]
public class DataHolder
{
//Active Quest
public bool isActive;
public string title;
public string description;
public int goldReward;
public QuestGoal goal;
}
The script that stores it is here:
public class QuestGiver : MonoBehaviour
{
public Quest quest;
public DataHolder data;
public GameObject questWindow;
public Text titleText;
public Text descriptionText;
public Text goldText;
public void OpenQuestWindow()
{
Debug.Log("Quest Window Opened");
questWindow.SetActive(true);
titleText.text = quest.title;
descriptionText.text = quest.description;
goldText.text = quest.goldReward.ToString();
}
public void AcceptQuest()
{
questWindow.SetActive(false);
data.isActive = true;
data.title = quest.title;
data.description = quest.description;
data.goldReward = quest.goldReward;
}
}
and the script that is in the scene so that when a button is pressed (Tab) it will pop up a UI that will show what the current quest is, which should be stored in the first "DataHolder" script:
public class QuestChecker : MonoBehaviour
{
DataHolder data;
public GameObject questWindow;
public Text titleText;
public Text descriptionText;
public Text goldText;
void Update()
{
if (Input.GetKeyDown(KeyCode.Tab))
{
CheckQuest();
}
}
public void CheckQuest()
{
questWindow.SetActive(true);
titleText.text = data.title;
descriptionText.text = data.description;
goldText.text = data.goldReward.ToString();
}
public void CloseQuestWindow()
{
questWindow.SetActive(false);
}
}
The reason I want it stored in "DataHolder" is because I want it to be stored between scenes. I believe my main problem is not knowing how to properly reference the "DataHolder" script as Unity gives me the warning:
Assets\Scripts\QuestChecker.cs(8,13): warning CS0649: Field 'QuestChecker.data' is never assigned to, and will always have its default value null
Any help would be appreciated. I'm pretty new to programming, please let me know if this won't work for what I want it to do and need to do it another way or if you need more info. Thanks
If all you want to do is store some data between scenes, you could simply do this:
public static class DataHolder
{
//Active Quest
public static bool isActive;
public static string title;
public static string description;
public static int goldReward;
public static QuestGoal goal;
}
Then just reference it as DataHolder.isActive etc. Note that you're not actually "saving" the data anywhere (serialising). The SerializableAttribute just indicates that the item CAN be serialised. But, this WILL store data between scenes.
Is it the best way to do so? Well, if it's just some simple data you need to store between scenes, then there's nothing wrong with doing it this way. And it'll probably fit in nicely with what you've already programmed.
You're never actually creating an instance of DataHolder.
Somewhere along the line you need a new DataHolder() that is assigned to data before trying to set any of its properties.
You need to create a relationship between QuestGiver and QuestChecker
eg.
public class QuestChecker : MonoBehaviour
{
public QuestGiver giver;
}
Now drag your QuestGiver game object onto the QuestChecker.giver field in the inspector. Then you are able to access your saved data.
public void CheckQuest()
{
titleText.text = saver.data.title;
}

Creating a List of UnityActions And Assigning Them to Buttons

I am working on a tycoon game and I want to have several options on each building the player might click on. A shop should have an Eat, Invest, and Buy-off option while an office should have something like Work, Talk to Manager, etc. To do this I created one panel for the options and spawn in the number of options per building from a list of strings which instantiates a button for each string and assigns the button text to that particular string as such:
public override void Interact()
{
Debug.Log(type);
SetUpFunctions(activites);
}
public void SetUpFunctions(List<string> activs)
{
foreach (string item in activs)
{
optionsUI.AddOption(item);
}
}
The list of activities is assigned in the inspector. This method works perfectly and assigns each activity string to the button text. However, all the buttons created all have the same function assigned to them, so I decided to do the same thing with a list of methods and assign them to each button as I did with the strings:
[HideInInspector]public List<UnityAction> functions; //list of functions tied to each activity
public override void Interact()
{
Debug.Log(type);
functions.Add(Eat);
functions.Add(Work);
functions.Add(StopWork);
SetUpFunctions(activites, functions);
}
public void SetUpFunctions(List<string> activs, List<UnityAction> funcs)
{
//foreach (string item in activs)
//{
// optionsUI.AddOption(item);
//}
var col = activs.Zip(funcs, (x, y) => new { X = x, Y = y });//combine the two lists as one
foreach (var entry in col)
{
optionsUI.AddOption(col.X, col.Y);//Assign the activity name and function
}
}
public void Eat()
{
Debug.Log("Player is eating");
}
public void Work()
{
Debug.Log("Player is Working");
}
public void StopWork()
{
Debug.Log("Player Stopped Working");
}
//From the OptionsUI script
public void AddOption(string optionName, UnityAction optionFunc)
{
OptionUIItem emptyOption = Instantiate(itemProfile, scrollViewContent);
emptyOption.transform.SetParent(scrollViewContent);
emptyOption.SetupOption(optionName, optionFunc);
}
//The script assigned to each button created
public class OptionUIItem : MonoBehaviour
{
public Text text;
public Button btn;
public void SetupOption(string name, UnityAction func)
{
text.text = name;
btn.onClick.AddListener(func);
}
public void OnOptionSelect()
{
Debug.Log("Option selected");
}
}
However, when I try doing it this way I get a NullReferenceException on the first function.Add(Eat);
My question is
Is it possible or even right to do it this way?
If so how do I resolve this in order to assign each function on the list to each button created and
any other methods that might be better to get this done like maybe using a dictionary?
Thank you.
If you hide a field in the Inspector it is not automatically initialized (only serialized fields are), thus the NullReferenceException when trying to Add something to it.
You should be sure to do this already together with the declaration
public List<UnityAction> functions = new List<UnityAction>();

Child to parent class Object Reference not set to an instance of an Object C#

I am very new to inheritance in C# and my problem is Object Reference not set to an instance of an object from Parent to Child
I tried the debug log from string to parent to child and works now that im experimenting this happens now
This my parent class
public class GameManagerRevamped : MonoBehaviour
{
//Arrays Buttons
public GameObject[] gameButtons;
//Arrays ImagesSource
public Image[] ImagesSource;
//Source Images
public Sprite[] BlackImages;
//Instance Variables
public int ImagesClick =-1;
public int buttonOrder;
public string finalAnswer = "";
public string TryInheritance;
public virtual void buttonClicked()
{
ImagesSource[ImagesClick].sprite = BlackImages[ImagesClick];
gameButtons[buttonOrder].SetActive(false);
}
public virtual void firstButtonClicked()
{
ImagesClick += 1;
buttonOrder = 0;
buttonClicked();
finalAnswer += "";
Debug.Log(TryInheritance);
}
}
this my Child Class
public class FableScript : GameManagerRevamped
{
public override void buttonClicked()
{
base.buttonClicked();
}
public override void firstButtonClicked()
{
finalAnswer += "f";
TryInheritance =finalAnswer;
ImagesClick += 1;
buttonOrder = 0;
buttonClicked();
base.firstButtonClicked();
}
}
The "TryInheritance" works but the other is on not set to instance object. Point is when I click on the button the it goes to inactive state :))
It's not entirely clear from the original question where the exception is being thrown, but it does look very strange that you repeat a number of lines of firstButtonClicked in the derived (child) class before then calling the method in the base (parent) class.
When you follow the code path, for example, ImagesClick gets incremented twice (once in the derived class and then once in the parent class, the same happens with buttonClicked() and my guess (because I don't know the implementation) is that the button goes inactive because buttonClicked() is called twice.
One of the advantages of inheritance is that you do not need to repeat common code. Your derived class firstButtonClicked method might work with just:
public override void firstButtonClicked()
{
finalAnswer += "f";
TryInheritance =finalAnswer;
base.firstButtonClicked();
}

Where can I find the instance of a page-class in a windows phone app

I'm currently programming an app for windows phone 8.1. I've some page classes and one normal C# class in this project.
On the page I'm currently working on are some texboxes, comboboxes and one button.
I want to enable the button, whene the user has given input to all text- and comboboxes. So I set a variable in the C# class Variables.cs whene e.g. he entered a number which I can parse to double and selected an item in a combobox. Variables.cs looks like this:
class Variables
{
public static int iSelectedIndex = -1;
private static void SupplyParameterReady()
{
if (tbSupply1 && tbSupply2 && unitSupply1 && unitSupply2)
{
SupplyParameter.ParameterCompleted(true);
}
else
{
SupplyParameter.ParameterCompleted(false);
}
}
public static bool tbSupply1
{
get
{
return tbSupply1;
}
set
{
tbSupply1 = value;
if (value)
SupplyParameterReady();
}
}
}
Every time a variable is set true the method SupplyParameterReady() checks if all the other variables are true too.
If this is the case I want to call the method ParameterCompleted(bool) in my page class, looks like this:
public sealed partial class SupplyParameter : Page
{
...
public void ParameterCompleted(bool ready)
{
btnSupplyCalculationGo.IsEnabled = ready;
}
}
This brings the issue that ParameterCompleted(bool) isn't static. So I need to have an instance of the class SupplyParameter.
But don't want to create a new instance of it, because that brings an infinite loop between the 2 classes.
I guess there has to be already an instance, which is created when the page is loaded. But where is this instance? Or how can I call this method without an instance?
If you move things around, you could have a (non static) Variables member inside your SupplyParameter Page and pass this to the constructor. It would look like this
class Variables
{
public int iSelectedIndex = -1;
public SupplyParameter _page;
public Variables(SupplyParameter page)
{
_page = page;
}
private void SupplyParameterReady()
{
if (tbSupply1 && tbSupply2 && unitSupply1 && unitSupply2)
{
_page.ParameterCompleted(true);
}
else
{
_page.ParameterCompleted(false);
}
}
public bool tbSupply1
{
get
{
return tbSupply1;
}
set
{
tbSupply1 = value;
if (value)
SupplyParameterReady();
}
}
}
public sealed partial class SupplyParameter : Page
{
Variables _vars;
public SupplyParameter()
{
vars = new Variables(this);
}
...
public void ParameterCompleted(bool ready)
{
btnSupplyCalculationGo.IsEnabled = ready;
}
}

Categories