I have implemented a list system which records the users name, the total amount of points which they have scores and the amount of time it took to do this. Im now having problems with trying to display the list "players" from another class. So within my HighScoresMenu form when trying to display the content of the players list within a the list box, the list box only displays "Game.Player".
Is there any possible solutions to this issue. Any Help would be appreciated.
public partial class MainMenu : Form
{
public static List<Player> GetPlayers(float newScore, float newPoints,
string PlayersName)
{
var players = new List<Player>();
var newPlayer = new Player
{
Name = PlayersName,
Points = newPoints,
Timer = newScore
};
players.Add(newPlayer);
var TopTenLevel1 = players.OrderByDescending(x => x.Timer).Take(10);
return players;
}
}
public partial class HighScoresMenu : Form
{
foreach (var players in MainMenu.GetPlayers(newScore, newPoints,
PlayersName))
{
ListBoxLevel1.Items.Add(players);
}
}
Ok, so to begin, I might not understand your question correctly. What I get from it is that your list view is displaying the name of the object, hence the "Game.Player". So you would have to change the following:
ListBoxLevel1.Items.Add(players);
to something like the following:
ListBoxLevel1.Items.Add($"Player: {players.Name}, Score: {players.Points}");
I'm not sure what your list box is taking as a parameter in the Items.Add(); method, so this answer is assuming it's a string.
I had this problem also few years ago: The function GetPlayers is static, that means that the content (value) or the variable isn't accessible from other classes.
You need to remove 'static' and initiate the MainMenu in the HighScoresMenu class like this:
//MainMenu
public static List<Player> GetPlayers(float newScore, float newPoints, string PlayersName)
//HighScoreMenu
MainMenu main = new MainMenu();
menu.GetPlayers(newScore, newPoints, PlayersName);
I hope this will fix the problem (removing keyword 'static' from the function and initiating the MainMenu class)
I would glad to hear if the current situation is solved or not. :) I hope I did remember it good.
Edit
After I took a second look you are foreach looping it as var player.
But loop it as Player player. Because you putted in in the other class as Player object inside a list of Players. Now you are retrieving it as a var.**
So the final conclusion:
public partial class HighScoresMenu : Form
{
foreach (Player players in MainMenu.GetPlayers(newScore, newPoints,
PlayersName))
{
ListBoxLevel1.Items.Add(players.getName());
}
}
I am curious let me know :D
Edit 2
Also letting you know in case of you never know. You are always making just one Player so there isn't actually a list needed and this piece of code:
var TopTenLevel1 = players.OrderByDescending(x => x.Timer).Take(10);
This will order it but you aren't doing anything with it (var 'TopTenLevel1'). Just letting you know :) You could save the ordered list by returning it (overwriting variable also possible).
Related
I am trying to make a dropdown that allows you to select different ways to display time. I got it all working except I need to find a way to save and load the option the player selected.
private void Start() {
timeDropdown.options.Clear();
List < string > items = new() {
"",
DateTime.Now.ToString(),
DateTime.Now.ToLongDateString(),
DateTime.Now.ToLongTimeString()
};
foreach(var item in items) {
timeDropdown.options.Add(new TMP_Dropdown.OptionData() {
text = item
});
}
DropdownItemSelected(timeDropdown);
timeDropdown.onValueChanged.AddListener(delegate {
DropdownItemSelected(timeDropdown);
});
}
void DropdownItemSelected(TMP_Dropdown timeDropdown) {
int index = timeDropdown.value;
timeText.text = timeDropdown.options[index].text;
}
I think you would use an int to save it but I don't know how to do that. Thank you.
You could use unity's player prefs system below is a link to documentation on how it works.
Unity player prefs documentaion
Alternatively you could create your serialisation system here's a Brackey's video which shows how to make a basic class serialisations system
YouTube tutorial
For what your trying to do the fastest and quickest approach would be to just use player prefs for saving and loading the int value.
So I created a method to populate the inventory system I have created in Unity, however I can't seem to figure out a way to order them in alphabetical order. Each button is created as a child of the button holder panel which is a game object variable. This method is called whenever the player picks something up.
private void Populate_Inventory ( )
{
Button btn_CurrentItem;
int int_Count;
for ( int_Count = int_PreviousCount; int_Count < strList_Inventory.Count; int_Count++ )
{
btn_CurrentItem = Instantiate ( btn_Item, pnl_ButtonHolder.transform );
btn_CurrentItem.name = strList_Inventory [ int_Count ];
btn_CurrentItem.GetComponentInChildren<Text> ( ).text = strList_Inventory [ int_Count ];
btn_CurrentItem.gameObject.AddComponent<Inventory_Item> ( );
btn_CurrentItem.GetComponent<Inventory_Item> ( ).str_Name = cls_Pickup.str_PickupName;
btn_CurrentItem.GetComponent<Inventory_Item> ( ).str_Information = cls_Pickup.str_PickupInformation;
int_PreviousCount = int_Count;
}
int_PreviousCount++;
}
If anyone can help it would be very appreciated.
Jonathan Palmer
The primary issue with this approach is that you're adding single buttons at a time whenever you pick up an item, and those buttons are created as children of the pnl_ButtonHolder object, but your method has no knowledge of previous buttons that have been created.
Option 1 (Bad)
You can use the GetComponentsInChildren<Button>() method on the pnl_ButtonHolder object to get all of the button components that have been previously created once you've added a new button, and then sort your buttons according to their name.
This isn't ideal as GetComponentsInChildren<>() is an expensive method, and doesn't make use of the idea of an Inventory very well.
Option 2 (Good)
Create an Inventory class that manages your actual inventory, including sorting items. It might look something like this:
public class Inventory_Button : MonoBehaviour
{
public Button button = default;
public void Initialize(Intenvory_Item item)
{
button.name = item.name;
// Other work here.
}
}
public class Inventory : MonoBehaviour
{
public GameObject inventoryItemPrefab = default;
public Transform inventoryParent = default;
private List<Inventory_Item> _inventory = new List<Inventory_Item>();
private List<Inventory_Button> _inventoryButtons = new List<Inventory_Button>();
public void AddItem(Intenvory_Item item)
{
_inventory.Add(item);
Inventory_Button button = GameObject.Instantiate(inventoryItemPrefab, inventoryParent).GetComponent<Inventory_Button>();
button.Initialize(item);
_inventoryButtons.Add(button);
_inventoryButtons.Sort((x, y) => x.name.CompareTo(y.name));
}
public void RemoveItem(Inventory_Item item)
{
// Do work to remove the item.
}
}
This kind of setup is useful for several reasons:
Your inventory management is now handled by a single class rather than a collection of objects. This makes it easy to interact with.
It will make removing items much easier in the future. With your current implementation, it looks like you'd have a difficult time removing an item from the player's inventory.
It separates responsibility between an Inventory class, an Inventory_Item class, and an Inventory_Button class, each of which stands on its own and works together.
A few final notes:
I've left out some details in the two classes above. You should fill them in to fit the needs of your game.
You can be more efficient with sorting by using something like Insertion Sort. This solution will get the job done.
Using prefixes for naming your variables can be quite confusing to people trying to read your code. I recommend you check out a style guide. There's a great one here.
I need to instantiate an object at runtime, where the number of objects are based on a txt file (number of lines). All objects need to be clickable (onClick() event) and when they are pressed a tooltip must appear. Tooltips are specific ( Object1 -> Tooltip1, Object2 -> Tooltip2). Tooltips are just a Panel and they consist in some other Panels and Buttons. One of these create a new Panel. Also these Panel are specific (Tooltip1 -> Panel1 and so on). I create a prefab for each of three objects.
So, Object1 - onClick() -> Tooltip1 - onClick() -> Panel1. At runtime.
How can I keep reference of an object create at runtime?
My first solution:
Create a Empty object and assign a script to it with a public variable (Object prefab).
Instantiate Object prefab:
for (int i = 0; i < numberOfObject; i++)
{
var instance = Instantiate(m_ObjectPrefab);
instance.name = "Object_" + m_nameObject[i];
Instantiate one Tooltip for each Object:
var instancePanelTooltip = Instantiate(m_panelTooltipPrefab, m_canvas.transform);
instancePanelTooltip.name = "Panel_Tooltip_" + m_nameObject[i];
Instantiate one Panel for each Tooltip:
var instancePanel = Instantiate(m_panelPrefab, m_canvas.transform);
instancePanel.name = "Panel_" + m_nameObject[i];
instancePanel.SetActive(false);
instancePanelTooltip.SetActive(false);
Add event handler for Object
DetectClickOnObject scriptDetectClickPanelTooltip = instance.AddComponent<DetectClickOnObject>();
scriptDetectClickPanelTooltip.SetTooltip(instancePanelTooltip);
Add event handler for button on Tooltip
DetectClickOnObject scriptDetectClickPanel = buttonOpenPanel.AddComponent<DetectClickOnObject>();
scriptDetectClickPanel.SetPanel(instancePanel);
}
Issue with this solution:
I will instantiate 3 object (Object, Tooltip, Panel) for each line of file. For Object it is okay, but it is not with Tooltip and Panel, because only one Tooltip is active among all (same for Panel).
I just avoid the problem of reference because I create in the same place all objects (one for each element), but what can I do if I need to get access to Tooltip2 or Panel3 without a reference (I am trying to avoid Find and similar).
Conclusion of the first solution: Solution is working, but I think there is a better way of doing that (avoid create so much object and keep reference in the right way).
My second solution (guidelines):
I am trying to create a class to keep reference of all object create at runtime.
I want to create an instance of Object for each lines, but I want just one Tooltip and Panel for all Object and changing properties according to Object clicked. So Object is create at runtime, but Tooltip and Panel are already in the scene but not active.
I need a Register Event Manager to add onClick() event at runtime on Objects and it need to handle properties to be set on Tooltip and Panel based on clicked Object.
Problems with second solution: Referring to 1) I tried to follow that, but I ended up with nothing. I am lost between singleton, static and something else. Referring to 2) I think it can be easy, I just need to cut away some of the first solution. Referring to 3) I can not do more if I do not have a class reference manager.
What I am looking for:
Is first solution so bad? If I look at code I am disgusted by it, it is far away from elegance (or something similar).
Can you suggest me how can I keep track of reference create at runtime with a Reference Manager? And how use it?
#Behnam Sattar suggestion:
As DataModell class,
public class DataModelPOI
{
public string m_namePOI { get; private set; }
public string m_locationPOI { get; private set; }
public Vector2d m_positionPOI { get; private set; }
public GameObject m_gameObject_POI;
public GameObject m_gameObjectTooltip;
public GameObject m_gameObjectPanel;
public DataModelPOI(string namePOI, string locationPOI, Vector2d positionPOI)
{
this.m_namePOI = namePOI;
this.m_locationPOI = locationPOI;
this.m_positionPOI = positionPOI;
}
}
As DataManager,
public class POIManager : MonoBehaviour
{
List<DataModelPOI> dataCollectionPOI = new List<DataModelPOI>();
void Start()
{
ReadFile();
SpawnPOI();
}
void Update()
{
int count = dataCollectionPOI.Count;
for (int i = 0; i < count; i++)
{
UpdatePOIPosition();
}
}
void ReadFile()
{
TakeDataFromFile();
for (int i = 0; i < ength; i++)
{
DataModelPOI dataPOI = new DataModelPOI(m_namePoi[i], m_namePoi[i], _locations[i]);
dataCollectionPOI.Add(dataPOI);
}
}
private void SpawnPOI()
{
for (int i = 0; i < dataCollectionPOI.Count; i++)
{
DataModelPOI dataPOI = dataCollectionPOI[i];
var instance = Instantiate(m_POIPrefab);
instance.name = "POI_" + m_namePoi[i];
dataPOI.m_gameObject_POI = instance;
dataPOI.m_gameObjectTooltip = m_panelTooltipPOI;
dataPOI.m_gameObjectPanel = m_panelPOI;
}
}
Now I need to register Event associate to GameObject instantiate before. I want to do that in my EventManager. How can I point to dataCollectionPOI in EventManager class created and feeded in DataManager? Thanks for your time.
Based on my understanding your question is mostly a design question. In first part of this answer, I'm giving you a suggestion for doing the design and keeping a reference to your objects. In second part I'll give you some tips regarding performance.
[I'm using RootObject instead of Object to refer you the main GameObject you create.]
Design
Let's break down our needs and then come up with a solution for each.
First we want to read some text file and then get some data from that. This data will be used later for creation of GameObjects. For now let's just focus on the data itself.
What we want here is a manager class which reads the file for us and stores the data in some form. We access this manager later and ask for our data in order to create the GameObjects.
This manager class stores our data in a collection of data objects [notice here we're talking about plain objects and not Unity's GameObjects]. You need to design this data class based on each line of text you have. Optionally, you can also keep the references to GameObjects here too.
Assume you're reading three string values from each line, named ValueOne, ValueTwo, and ValueThree, and you want to keep reference to three GameObjects called RootObject, ToolTip and Panel. For such purpose you can define following class:
public class DataModel {
// Values read from text file.
public string valueOne { get; private set; }
public string valueTwo { get; private set; }
public string valueThree { get; private set; }
// Placeholders for GameObjecs created at runtime.
public GameObject rootObject;
public GameObject tooltipObject;
public GameObject panelObject;
public DataModel(string valueOne, string valueTwo, string valueThree){
this.valueOne = valueOne;
this.valueTwo = valueTwo;
this.valueThree = valueThree;
}
}
Then in your manager class you can create a collection (a List for example) for holding your data. Your manager class should read the text file at some point and populate this list with instances of DataModel. It will be something like this:
public class DataManager {
List<DataModel> dataCollection = new List<DataModel>();
public void ReadFile() {
// Here you need to read the file and get the values you need.
// The actual code should be different from what I'm putting here.
foreach(string line in lines) {
// You get valueOne, valueTwo and valueThree
// from each line and maybe prepare them
// (maybe you need conversion from string to int)
DataModel data = new DataModel(valueOne, valueTwo, valueThree);
dataCollection.Add(data);
}
}
}
After you call the method on manager to read the data, you'll have your data prepared for you whenever you want to use it.
It's time to create objects based on the data and save the reference.
for (int i = 0; i++; i <= manager.dataCollection.Count) {
DataModel data = manager.dataCollection[i];
data.rootObject = instantiate() // You instantiate the root GameObject here.
data.tooltip = instantiate() // You instantiate the tooltop GameObject here.
data.panel = instantiate() // You instantiate the panel GameObject here.
}
Done. Now you have a manager class which has reference to all the data and also GameObjects which are created based on this data.
Performance
It might not be a good idea to do this all in runtime as it might cause frame drops in your game. If this is a problem you can try object pooling. If you search for that you should be able to find great tutorials on how to perform object pooling.
If you ended up not using object pooling, you can still remedy any performance drops by instantiating only one GameObject per frame. This can be done using Coroutines. You just need to do a yield return new WaitForEndOfFrame() in your loop of instantiation.
Final Note
Keep in mind that this all a suggestion and I don't think there is one single best answer to your question. Make sure you try to understand what tools are at your disposal and try them all before deciding on one solution. :)
You're basically on the right track, but you can separate (or 'objectify' ;) what scripts keep what references. Keep scripts that manage the internal workings of your prefabs in those prefabs.
A singleton-like pattern for the Tooltips is definitely the way to go. For where you can't use a singleton, such as in the clickable objects, use something more akin to dependency injection (to save on Finds) and pre-stored reference:
var obj = Instantiate(...);
obj.GetComponent<SomeManagerScript>.SomeDependency = SingletonInstance;
obj.GetComponent<StoredReferenceScript>.TextPanel.Text = "Some Text";
As to events, you can handle them a couple of ways. One, each clickable manages its own bindings/functionality; or two, each clickable notifies a global manager that it was clicked on, and the manager determines the actions to take.
Basically you need to create an object at Runtime and set the data for that specific object.
here is the simplest way to do that
first of all create scripts first one can Create an object at runtime and second one is your data setter script that attach with your prefab
so, when ever you create an object at runtime you can pick a reference of the dataSetter and set the specific data.
Hope below given code may useful to you.
GameObject object = Instantiate(gameObject,parent)
DataSetter s = object.GetComponent<DataSetter>().toolTip = "YourTooltip";
in DataSetter script tool tip is a string variable.
you can also declare text variable and assign a text to that.
I'm making a windows forms application and I'm trying to improve my code so I'd like a little bit of advice/help.
I have a list with data at lets say my first form that I add data to from a database. I then pass around one persons details from that list rather than the whole list. My Problem is that I don't think I'm passing around data correctly and I'd like to do it correctly.
Example of what I'm doing (accountholders is of type object?):
Form 2 =I want to pass the data into this form:
private AccountHolders person;
public void SetThePersonsValue(AccountHolders inputAccountHolder)
{
person = inputAccountHolder;
}
Form 1 = I'm taking data from this form, I want to update the list that in this form later on:
WithdrawNoReceipt withdrawNoReceipt = new WithdrawNoReceipt();
withdrawNoReceipt.SetThePersonsValue(person);
withdrawNoReceipt.ShowDialog();
So I want to use a get/set property (?) to do this but I'm not sure how to do so.. Ideally I want the person I'm passing around to be a pointer so the originally list gets updated so I can write the data back to the database without passing the data back to the first form.
If you need any more information please let me know! Thanks in advance.
Are you referring to a property? because then you need to add the following code
private AccountHolders person;
public AccountHolders Person
{
get{return person;}
set{person = value;}
}
the property points to the private member person. Ideally you add a PropertyChanged event in the property so your form knows something has been updated?
EDIT:
For filling the property value, value is of type AccountHolders
public void SetThePersonsValue(AccountHolders inputAccountHolder)
{
//Set the value
Person = inputAccountHolder;
}
WithdrawNoReceipt withdrawNoReceipt = new WithdrawNoReceipt();
//get the value
WithdrawNoReceipt.SetThePersonsValue(Person);
WithdrawNoReceipt.ShowDialog();
while debugging you will now see you person object changes whenever you update your Person object
I have been working on trying to understand ArrayList but I have run into a problem with the final part of my search function.
I have an array list set up to take data in from a StreamReader. The data consists of numerous properties to do with teams, such as their name and the image path to their logo.
I have then created a search that scans through the array list for a specific piece of string input through a textbox by the user.
I have then created an if statement to open up a new form if that search was returned true.
I would like the new form to load up each property of the team according to the data searched for. E.g if I searched for "Liverpool" it would then come up with the manager name, stadium name as well as the searched for name in the new form. I do not know how to do this.
The only tool that I can really think of to do this is the load.event procedure but I can not find a lot of information about linking it to an array list.
private void btn_Search_Click(object sender, EventArgs e)
{
foreach (Object obj in allTeams)
{
Team = (Team)obj;
if (team.teamName.ToUpper() == nameToMatch.ToUpper()) //case insensitive search.
{
FormTeam frmTeam = new FormTeam(Team); //windows form that displays team info.
frmTeam.Visible = true;
break;
}
}
}
above is my search function. (I have not used List<T> because it was required that everything must be stored in an ArrayList.)
Is what I am trying to achieve possible? And if so how?
Also, you may want to use Linq on your ArrayList
foreach(var team in allTeams.OfType<Team>())
{
if(team.TeamName.Equals(nameToMatch, StringComparison.InvariantCultureIgnoreCase))
{
frmTeam = new FormTeam(Team);
Aplication.Run(frmTeam); // or frmTeam.Show();
break;
}
}
Inside the Constructor of your TeamForm class you simply assign all Values from the Team- Object to the fields on the Form.
public FormTeam (Team team)
{
teamName.Text = team.TeamName; // label or something
teamPlayerCount.text = team.PlayerCount.ToString();
...
}
If you need to search on a string such as the teamName, it would be better to use something like a Dictionary<string,Team> rather than an Arraylist. Then you'd be able to do
Team t = dic[nameToMatch.ToUpper()];
where dic is the instance of Dictionary<string,Team> that contains all your teams.
First, you're missing a variable name there. I think you meant
foreach (Object obj in allTeams)
{
Team t = (Team)obj;
if (t.teamName.ToUpper() == nameToMatch.ToUpper()) //case insensitive search.
{
FormTeam frmTeam = new FormTeam(t); //windows form that displays team info.
frmTeam.Visible = true;
break;
}
}
But why not used a typed generic List, not have to do all this silly casting.
But your question is "how can I pass what I searched to the new form", correct? I'd change the constructor for FormTeam to something like FormTeam(Team t, string nameToMatch) and save the value locally in TeamForm so you can do highlighting or whatever.