C# Unity3D - Get stuck with an sticky situation - c#

Im making a game inside Unity3D but im stuck with something what I try to find the solution on it.
Its a part of a stock system where you can store things in it like "items or materials".
the following variables / classes are as follows :
[Serializable] public class ItemStorage
{
public ExtendedList.Items item;
public List<MaterialStorage> material;
}
[Serializable] public class MaterialStorage
{
public ExtendedList.Ingots material;
public int materialCount;
}
Above are the classes.
public List<ItemStorage> itemstorage;
public List<MaterialStorage> materialstorage;
Above are the variables.
and under this are the enums what i bassicly use :
public enum Items : int
{
[Description("Helmet")]
HELMET = 0,
[Description("Chestplate")]
CHESTPLATE = 4,
[Description("Platelegs")]
PLATELEGS = 8,
[Description("Gaunlets")]
GAUNLETS = 12,
[Description("Boots")]
BOOTS = 16,
[Description("Sword")]
SWORD = 20,
[Description("Shield")]
SHIELD = 24,
[Description("Wheel")]
WHEEL = 28,
[Description("Axe")]
AXE = 32
}
public enum Ingots : int
{
[Description("Bronze Ingot")]
BRONZE = 0,
[Description("Iron Ingot")]
IRON = 5,
[Description("Steel Ingot")]
STEEL = 10,
[Description("Silver Ingot")]
SILVER = 15,
[Description("Platinum Ingot")]
PLATINUM = 20
}
but I have a code where I call it in the "Awake" to initialize the / those list(s) :
for (int i = 0; i < Enum.GetValues(typeof(ExtendedList.Ingots)).Length; i++)
{
if (GameManager.Instance.PlayerLevel >= ExtendedList.GetIngotValue(i))
{
materialstorage.Add(new MaterialStorage() { material = (ExtendedList.Ingots)ExtendedList.GetIngotValue(i) });
}
}
for (int i = 0; i < Enum.GetNames(typeof(ExtendedList.Items)).Length; i++)
{
if (GameManager.Instance.PlayerLevel >= ExtendedList.GetItemValue(i))
{
itemstorage.Add(new ItemStorage() { item = (ExtendedList.Items)ExtendedList.GetItemValue(i), material = materialstorage });
}
}
On start it initializes as perfect. So at material list i get (bronze) and in the item list I get (helmet) but when I level up I call the function. So in the material list i get (bronze, bronze) and in the item list I get (helmet, helmet) instead of (bronze) and (helmet).\
edit :
(forgot) : when the player is level 0 it only initializes bronze. but when it reaches level 5 its adding a new list.
but than instead of bronze its iron.
so i want to add a new list when the player level is equalls to the value what i give to iron, steel, silver, platinum, etc.
I hope you guys can help me with this sticky situation.
Best wishes, And have a good day.

EDIT
I was slightly mistaken in how the dictionary was being populated, the reason you are seeing "1","2" is because of the values being inserted into the dictionary, not the key. Take this line you have:
for (int i = 0; i < Enum.GetValues(typeof(ExtendedList.Ingots)).Length; i++)
The variable i will loop from 0 -> 4, because you have 5 elements in ExtendedList.Ingots. That means that when the following bit of code is run:
int key = ExtendedList.GetLevel((ExtendedList.Ingots)i);
ExtendedList.Ingots value = (ExtendedList.Ingots)i;
levelToMaterialsMap.Add(key, value);
The variable value will only ever be BRONZE,1,2,3,4. This is because when you cast an integer to an enum as in the second line above, even if it doesn't find the value in your Enum set, the cast will still work but value will now equal to i , that's just how enums works in .NET. So in the first loop, the cast is (ExtendedList.Ingots)(0) -> BRONZE because it found the value 0 in the enum set. From the second iteration onward, it doesn't find the value so you get (ExtendedList.Ingots)(1) -> 1, (ExtendedList.Ingots)(2) -> 2 and so on.

I have found the solution of my problem with ur method (Sasang)
the the console is giving now the correct key / values.
in the for loop is everything the same except the line with calculating the value line.
the code looks right now like this :
ExtendedList.Ingots value = (ExtendedList.Ingots)Enum.GetValues(typeof(ExtendedList.Ingots)).GetValue(i);

The reason you are getting [bronze, bronze] at level2 in your materialstorage list (instead of just [bronze]) is because you are adding values to your list with materialstorage.Add, it seems to me that what you really want is to initialize a new list every time that method runs, so before you iterate through your enums in the for loops, set the materialstorage and itemstorage to new lists.
Also i dont entirely understand the reason for having materialstorage list defined twice if they always appear to be the same value. You have it defined at the gamemanager level and also again in each itemStorage object. If i understand your edit statement correctly, i seems like you want a list of lists where the number of lists is equal to the number of level increments like so:
0: [[bronze]]
5: [[bronze], [bronze,iron]]
10: [[bronze], [bronze,iron], [bronze,iron,steel]]
...
In this case what you really need is a Dictionary, one which maps player level to a separate list of materials. You can initialize this at the in your main GameManager class. Where the number of keys in the dictionary equals to the number of elements in your enum, like so:
Dictionary<int, List<MaterialStorage>> levelToMaterialsMap =
new Dictionary<int, List<MaterialStorage>>();
for (int i = 0; i < Enum.GetValues(typeof(ExtendedList.Ingots)).Length; i++)
{
int key = ExtendedList.GetIngotValue(i);
List<MaterialStorage> value = new List<MaterialStorage>();
levelToMaterialsMap.add(key, value);
}
Now every time you call your looping method, check to see if the player level is one of the levels in which you need to add materials for, if it is then create a new materialstorage list, add all the materials to it, then set it into the Dictionary for that specific level key. Something like:
if(levelToMaterialMap.containsKey(GameManager.Instance.PlayerLevel))
{
List<MaterialStorage> materialstorage = new List<MaterialStorage>();
//Do all the stuff you need to add materials for this player level
//...
//Then
levelToMaterialMap[GameManager.Instance.PlayerLevel] = materialstorage;
}

Related

Ranking sums from first to third c#

I want to rank the number i added up from first to third but i cant think of a way to rank it properly since when there is a duplicate it will only show the number once and continues to the second highest
im new to the language and it would be great for someone to help me on this
Edit: Sorry i think there is a misunderstanding here my sums are in an array that is connected to the names in another array and im trying to sort it out with the same index value
Edit 2: Also i am stuck at c# 7.3 so i cant use some of the new codes
int first = Int32.MinValue;
int fs, nd, thr;
int temp = 0;
for (fs = 0; fs < hounds; fs++)
{
if (score_SUM[fs] > first)
{
first = score_SUM[fs];
temp = fs;
}
}
Console.WriteLine("\n" + "First:{1} {0}", first, houndname[temp]);
int second = Int32.MinValue;
for (nd = 0; nd < hounds; nd++)
{
if (score_SUM[nd] > second && score_SUM[nd] < first)
{
second = score_SUM[nd];
temp = nd;
}
}
Console.WriteLine("Second:{1} {0}", second, houndname[temp]);
int third = Int32.MinValue;
for (thr = 0; thr < hounds; thr++)
{
if (score_SUM[thr] > third && score_SUM[thr] < second)
{
third = score_SUM[thr];
temp = thr;
}
}
Console.WriteLine("Third:{1} {0}", third, houndname[temp]);
Console.ReadLine();
example
10 , 5 , 10 , 6, 1
The output will be like
10
6
5
But I expected
10
10
6
but i cant find a way to write a block a code for that
You're drastically over-engineering this.
If what you have is an array (or list/collection of some kind) of values then you can simply sort that array (descending in this case) and display the first three values.
For example, consider this list of values:
var hounds = new List<int> { 10, 5, 10, 6, 1 };
Then you can sort that list:
hounds = hounds.OrderByDescending(h => h).ToList();
And, either in a loop or by directly referencing the first three (if you know there will always be at least three), output them. For example:
Console.WriteLine("First:{0}", hounds[0]);
Console.WriteLine("Second:{0}", hounds[1]);
Console.WriteLine("Third:{0}", hounds[2]);
Regarding your edit...
my sums are in an array that is connected to the names in another array and im trying to sort it out with the same index value
You're doing it wrong.
Instead of trying to manually keep multiple arrays of values synchronized, maintain one array of meaningful objects. For example, consider how you define a "hound":
public class Hound
{
public int Value { get; set; }
public string Name { get; set; }
}
Create your list of hounds, not multiple lists of disconnected and unrelated values that you need to manually remember and keep synchronized. For example:
var hounds = new List<Hound>
{
new Hound { Value = 10, Name = "Fido" },
new Hound { Value = 5, Name = "Barney" },
new Hound { Value = 10, Name = "Jake" },
new Hound { Value = 6, Name = "Woof" },
new Hound { Value = 1, Name = "Dog" }
};
The rest of the process is the same. Sort the list:
hounds = hounds.OrderByDescending(h => h.Value);
And output the data:
Console.WriteLine("First:{1} {0}", hounds[0].Value, hounds[0].Name);
Console.WriteLine("Second:{1} {0}", hounds[1].Value, hounds[1].Name);
Console.WriteLine("Third:{1} {0}", hounds[2].Value, hounds[2].Name);
Overall, the main point here is that you don't need a ton of convoluted logic just to get the top 3 values in a list. Sorting is a common and well-established operation. All you need is the right data structure to be sorted.
Or, as usual, someone else has already said it better before...
"Smart data structures and dumb code works a lot better than the other way around."
Eric S. Raymond, The Cathedral & the Bazaar
Does this answer your question?
List<int> list = new() { 10, 5, 10, 6, 1 };
list.Sort((x, y) => y.CompareTo(x));
for (int i = 0; i < 3; i++)
{
Console.WriteLine(list[i]);
}
If you only want the three highest values you can also do this:
List<int> list = new() { 10, 5, 10, 6, 1 };
IEnumerable<int> highestValues = list.OrderByDescending(x => x).Take(3);
foreach (int value in highestValues)
{
Console.WriteLine(value);
}
Just change your < symbols to <= symbols. So your checks for the second and third ranks would look something like this:
// check for second
if (score_SUM[nd] > second && score_SUM[nd] <= first)
...
// check for third
if (score_SUM[thr] > third && score_SUM[thr] <= second)
...

How to activate 2 gameobjects randomly?

I have this code but looks like the random is activating the same gameobject. How can I be sure that not the same will be activated? For example, this code activate 2 gameobjects but sometimes just only one, because the random picked the same gameobjects twice.
for (int i = 0; i < 2; i++)
{
int index = Random.Range(0, EyeButtonArray.Length);
EyeButtonArray[index].SetActive(true);
}
You can shuffle your container randomly and pick the first two elements.
int wanted = 2;
int[] nums = System.Linq.Enumerable.Range(0, EyeButtonArray.Length).ToArray();
System.Random rnd = new System.Random();
nums = nums.OrderBy(x => rnd.Next()).ToArray();
for(int x = 0; x < 2; ++x)
EyeButtonArray[nums[x]].SetActive(true);
Another solution using a HashSet is to keep track of the elements you have picked and keep generating until you pick a unique one.
HashSet<int> RandomElementIdx = new HashSet<int>();
int picked = 0;
int wanted = 2;
while(picked < wanted)
{
int index = Random.Range(0, EyeButtonArray.Length);
if(RandomElementIdx.Contains(index))
continue;
RandomElementIdx.Add(index);
EyeButtonArray[index].SetActive(true);
++picked;
}
The above snippet is assuming your list is greater than or equal to wanted. If it is less, it will result in an infinite loop. It is also not exactly efficient as it will continually try to spawn new numbers until it randomly hits one not used.
It sounds like what you want is
You have a list/array of objects
You want to enable the next two random ones of them
(optionally - not sure on this) you want to disable the two current ones
You could simply "shuffle" your array and then enable the two first of the now shuffled items like e.g.
using System.Linq;
...
// OPTIONAL (but I thought makes sense)
// store the currently enabled objects to also be able to disable them later
// when you enable the next pair
private GameObject[] _currentEnabled;
public void EnableTwoRandom()
{
// OPTIONAL (but I thought makes sense)
// if exists first disable the last selected two buttons
if(_currentEnabled!=null)
{
foreach(var item in _currentEnabled)
{
item.SetActive(false);
}
}
// Pick two random buttons
// This first "shuffles" the buttons and then simply takes the
// first two random elements
_currentEnabled = EyeButtonArray.OrderBy(i => Random.value).Take(2).ToArray();
// enable the new two random buttons
foreach(var item in toEnable)
{
item.SetActive(true);
}
}
See
IEnumerable.OrderBy
Random.value
IEnumerable.Take
if additionally you want to be sure that the new selected buttons are also not equal to the previous ones you could add
// Pick two random buttons
if(_currentEnabled != null)
{
// if there already exists a previous selection
// then exclude this selection from the available options first
_currentEnabled = EyeButtonArray.Except(_currentEnabled).OrderBy(i => Random.value).Take(2).ToArray();
}
else
{
_currentEnabled = EyeButtonArray.OrderBy(i => Random.value).Take(2).ToArray();
}
See
IEnumerable.Except

C# Reading one or more integer values and assigning an object certain enum attributes based on the integers read

I am having a small problem, and I cant seem to find an answer anywhere. I will try to keep it short and clear
I have a class called exercises, which has an enum responsible for what body parts the exercise hits. Lets say we have
Chest = 1
Shoulders = 2
Arms = 4
Legs = 8
Back = 16
So far my class "exercise" has a name, and this enum. In theory, this should result in having something like "Bech press - chest, arms n/ Deadlift - back, legs" and so on.
Next I have a class Catalog, that holds all the exercises, and in that class a method, called AddExercise. Here is the code snippet:
public void AddExercise()
{
Exercise ToAdd = new Exercise();
Console.WriteLine("What is the name of the exercise?");
string name = Console.ReadLine();
ToAdd.name = name;
katalog.Add(ToAdd);
}
What I want to do know, is include "What muscle groups does the exercise hit?", and then a multichoice with mentioned muscle groups. My idea is that it would look something like this:
1. Chest
2. Shoulders
3. Arms
4. Legs
5. Back
6. That is all
As long as the person kept choosing 1-5 the exercise would acquire the enum attributes, and then quit the loop once the person chose 6, but I have no idea how to do this part. From most Ive seen about enums they are mostly used in the string format and parsing, so I have no idea how to assign attributes by reading integer. And the hardest part is assigning several of them.
Here is my enum, by the way:
[Flags]
public enum BodyParts : int
{
None = 0,
Chest = 1,
Shoulders = 2,
Biceps = 4,
Triceps = 8,
Back = 16,
Legs = 32
}
Thank you for your answers.
You can have a lookup table connecting string answer to enum values
var lookup = new Dictionary<string,BodyParts > () {
{ "chest" , BodyParts .Chest},
{ "shoulders " , BodyParts .Shoulders}
...
};
and use it like this
string name = Console.ReadLine().ToLower;
var bodypart = BodyParts.None;
lookup.TryGetValue(name, out bodypart);
katalog.Add(bodypart));
Use a CheckedListBox where the items are the instances of the enum.
var clb = new CheckedListBox();
foreach (BodyParts bp in Enum.GetValues(typeof(BodyParts)))
clb .Items.Add(bp);
Then to get the flagged combination of all checked items, just bitwise or (combine them). The result will be a bitmask that has all the selected body parts.
var result = clb.CheckedItems
.Cast<BodyParts>()
.Aggregate(BodyParts.None,
(current, bp) => current | bp);

Increment through a list on a button list

I've stored a list of colors in my program. I am after an object in my scene to one of the colors in the list. So far, I have done the followings:
if(Input.GetKeyDown(KeyCode.O))
{
for(int i = 0; i < claddingColor.Count; i++)
{
claddingMaterial.color = claddingColor[i];
}
}
This isn't working due to a reason I know (and you can probably spot) but I lack to the verbal fortitude to write it down.
As opposed to have a multiple lines of the following:
claddingMaterial.color = claddingColor[0];
Each tied to different buttons, I like a way I can emulate the above but tie it to a single button press. Thus, if I hit the 0 button 5 times, it will loop through each color stored in the list. If I hit it for a sixth time, it will go back to the first color in the list.
Could someone please help me implement this? Or point me to something that I may learn how to do it for myself?
Define LastColor property as class member:
int LastColor;
In your function use modulo
if(Input.GetKeyDown(KeyCode.O))
{
claddingMaterial.color = claddingColor[(LastColor++) % claddingColor.Count];
}
Note: Depending on the type of claddingColor use Count for a List or Length for Array.
You won't need a for loop
int lastStep = 0;
if(Input.GetKeyDown(KeyCode.O))
{
claddingMaterial.color = claddingColor[lastStep++];
if (lastStep == claddingColor.Count)
lastStep = 0;
}

Array sorting efficiency... Beginner need advice

I'll start by saying I am very much a beginner programmer, this is essentially my first real project outside of using learning material. I've been making a 'Simon Says' style game (the game where you repeat the pattern generated by the computer) using C# and XNA, the actual game is complete and working fine but while creating it, I wanted to also create a 'top 10' scoreboard. The scoreboard would record player name, level (how many 'rounds' they've completed) and combo (how many buttons presses they got correct), the scoreboard would then be sorted by combo score. This led me to XML, the first time using it, and I eventually got to the point of having an XML file that recorded the top 10 scores. The XML file is managed within a scoreboard class, which is also responsible for adding new scores and sorting scores. Which gets me to the point... I'd like some feedback on the way I've gone about sorting the score list and how I could have done it better, I have no other way to gain feedback =(. I know .NET features Array.Sort() but I wasn't too sure of how to use it as it's not just a single array that needs to be sorted. When a new score needs to be entered into the scoreboard, the player name and level also have to be added. These are stored within an 'array of arrays' (10 = for 'top 10' scores)
scoreboardComboData = new int[10]; // Combo
scoreboardTextData = new string[2][];
scoreboardTextData[0] = new string[10]; // Name
scoreboardTextData[1] = new string[10]; // Level as string
The scoreboard class works as follows:
- Checks to see if 'scoreboard.xml' exists, if not it creates it
- Initialises above arrays and adds any player data from scoreboard.xml, from previous run
- when AddScore(name, level, combo) is called the sort begins
- Another method can also be called that populates the XML file with above array data
The sort checks to see if the new score (combo) is less than or equal to any recorded scores within the scoreboardComboData array (if it's greater than a score, it moves onto the next element). If so, it moves all scores below the score it is less than or equal to down one element, essentially removing the last score and then places the new score within the element below the score it is less than or equal to. If the score is greater than all recorded scores, it moves all scores down one and inserts the new score within the first element. If it's the only score, it simply adds it to the first element. When a new score is added, the Name and Level data is also added to their relevant arrays, in the same way. What a tongue twister. Below is the AddScore method, I've added comments in the hope that it makes things clearer O_o. You can get the actual source file HERE. Below the method is an example of the quickest way to add a score to follow through with a debugger.
public static void AddScore(string name, string level, int combo)
{
// If the scoreboard has not yet been filled, this adds another 'active'
// array element each time a new score is added. The actual array size is
// defined within PopulateScoreBoard() (set to 10 - for 'top 10'
if (totalScores < scoreboardComboData.Length)
totalScores++;
// Does the scoreboard even need sorting?
if (totalScores > 1)
{
for (int i = totalScores - 1; i > - 1; i--)
{
// Check to see if score (combo) is greater than score stored in
// array
if (combo > scoreboardComboData[i] && i != 0)
{
// If so continue to next element
continue;
}
// Check to see if score (combo) is less or equal to element 'i'
// score && that the element is not the last in the
// array, if so the score does not need to be added to the scoreboard
else if (combo <= scoreboardComboData[i] && i != scoreboardComboData.Length - 1)
{
// If the score is lower than element 'i' and greater than the last
// element within the array, it needs to be added to the scoreboard. This is achieved
// by moving each element under element 'i' down an element. The new score is then inserted
// into the array under element 'i'
for (int j = totalScores - 1; j > i; j--)
{
// Name and level data are moved down in their relevant arrays
scoreboardTextData[0][j] = scoreboardTextData[0][j - 1];
scoreboardTextData[1][j] = scoreboardTextData[1][j - 1];
// Score (combo) data is moved down in relevant array
scoreboardComboData[j] = scoreboardComboData[j - 1];
}
// The new Name, level and score (combo) data is inserted into the relevant array under element 'i'
scoreboardTextData[0][i + 1] = name;
scoreboardTextData[1][i + 1] = level;
scoreboardComboData[i + 1] = combo;
break;
}
// If the method gets the this point, it means that the score is greater than all scores within
// the array and therefore cannot be added in the above way. As it is not less than any score within
// the array.
else if (i == 0)
{
// All Names, levels and scores are moved down within their relevant arrays
for (int j = totalScores - 1; j != 0; j--)
{
scoreboardTextData[0][j] = scoreboardTextData[0][j - 1];
scoreboardTextData[1][j] = scoreboardTextData[1][j - 1];
scoreboardComboData[j] = scoreboardComboData[j - 1];
}
// The new number 1 top name, level and score, are added into the first element
// within each of their relevant arrays.
scoreboardTextData[0][0] = name;
scoreboardTextData[1][0] = level;
scoreboardComboData[0] = combo;
break;
}
// If the methods get to this point, the combo score is not high enough
// to be on the top10 score list and therefore needs to break
break;
}
}
// As totalScores < 1, the current score is the first to be added. Therefore no checks need to be made
// and the Name, Level and combo data can be entered directly into the first element of their relevant
// array.
else
{
scoreboardTextData[0][0] = name;
scoreboardTextData[1][0] = level;
scoreboardComboData[0] = combo;
}
}
}
Example for adding score:
private static void Initialize()
{
scoreboardDoc = new XmlDocument();
if (!File.Exists("Scoreboard.xml"))
GenerateXML("Scoreboard.xml");
PopulateScoreBoard("Scoreboard.xml");
// ADD TEST SCORES HERE!
AddScore("EXAMPLE", "10", 100);
AddScore("EXAMPLE2", "24", 999);
PopulateXML("Scoreboard.xml");
}
In it's current state the source file is just used for testing, initialize is called within main and PopulateScoreBoard handles the majority of other initialising, so nothing else is needed, except to add a test score.
I thank you for your time!
As a programming tip, you should replace the 3 different arrays with a single array of Score Objects, each of these objects would have all 3 properties you mentioned (Name, Level and Score). You can then create a comparator-like function (based on the score attribute) for it which can be used to sort it via the Arrays.Sort() method.
If you want to keep you current 3 arrays then you can look up sorting algorithms here: http://en.wikipedia.org/wiki/Sorting_algorithm#Comparison_of_algorithms and just make it so that any changes are done to the 3 arrays simultaneously instead of one by one (since you keep the data in them synchronized by index).
You can consider placing your user name, level, and score in a separate class (let's call it ScoreBoardEntry) that inherits from IComparable and then write a comparator. This will allow you to use the built in sort function of List and will be a neater solution than just writing your own.
Your code should look something like this:
1) Create/load a List < ScoreBoardEntry> with all the previous scores
2) push the new score into it
3) Sort List
4) Print only first 10 entries
I wrote some code for you. I've been learning some new-ish type C# stuff like linq, IEnumerable, and yield and kind of wanted to put them together, so hopefully it helps.
you might like the snippet editor i used - linqpad - it's good for learning/experimenting
void Main()
{
string [] playerNames = {"Aaron", "Rick", "Josie"};
var sim = new GameSimulator(playerNames, 1000000, 5000);
var scores = sim.GetScores(5);
var sorted = scores.OrderBy(c=>c.Score).Reverse();
var top = sorted.Take(2);
// uncomment if using linq pad
//scores.Dump("scores");
//sorted.Dump("sorted");
//top.Dump("top");
}
class GameSimulator
{
public GameSimulator(string [] playerNames, int maxScore, int maxLevel)
{
this.playerNames = playerNames;
this.maxScore = maxScore;
this.maxLevel = maxLevel;
}
private string [] playerNames;
private int maxScore;
private int maxLevel;
public IEnumerable<ScoreBoardEntry> GetScores(int numGames)
{
for (int i = 0; i < numGames; i++)
{
string randomName = playerNames[random.Next(0, playerNames.Length-1)];
int randomScore = random.Next(1, maxScore);
int randomLevel = random.Next(1, maxLevel);
yield return new ScoreBoardEntry(randomName, randomLevel, randomScore);
}
}
static private Random random = new Random();
}
class ScoreBoardEntry
{
public ScoreBoardEntry(string playerName, int levenNumber, int score)
{
PlayerName = playerName;
LevelNumber = levenNumber;
Score = score;
}
public string PlayerName { set; get; }
public int LevelNumber { set; get; }
public int Score { set; get; }
}

Categories