Increment through a list on a button list - c#

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;
}

Related

How to iterate backwards (with pauses for input) through a set of randomly generated numbers?

// EDIT:
This is not a duplicate to: "When should I use a List vs a LinkedList". Check the answer I've provided below.
LinkedList might be useful though if someone wants to insert some positions at a specific place after we have a properly ordered List (in that case - LinkedList) already - how to make one, check my answer below. //
How would you iterate backwards (with pauses for player input) through a set of randomly generated numbers?
I'm trying to build a turn-based game. The order of actions is determined by a result of something like that:
int randomPosition = Random.Range(1,31) + someModifier;
// please note that someModifier can be a negative number!
// There is no foreseeable min or max someModifier.
// Let's assume we can't set limits.
I have a List of KeyValue pairs already containing names of objects and their corresponding randomPosition. // Update: Values of it are already sorted by a custom Sort() function, from highest to lowest.
// A list of KeyValue pairs containing names of objects and their corresponding randomPosition.
public List<KeyValuePair<string, int>> turnOrder = new List<KeyValuePair<string, int>> ();
// GameObject names are taken from a list of player and AI GameObjects.
List <GameObject> listOfCombatants = new List<GameObjects>();
// Each GameObject name is taken from listOfCombatants list.
listOfCombatants[i].name;
I thought, maybe let's add player and AI GameObjects to a list on Index positions equal to each of their randomPosition. Unfortunately, a Generic List can't have "gaps" in it. So we can't create it, let alone iterate it backwards.
Also, I'm not sure how we'd stop a for loop to wait for player input. I have a button, pressing which will perfom an action - switch state and run some functions.
Button combat_action_button;
combat_action_button.onClick.AddListener (AttackButton);
// When player takes his turn, so in TurnState.PLAYER_ACTION:
public void AttackButton() {
switch(actionState) {
case PlayerAction.ATTACK:
Debug.Log (actionState);
// Do something here - run function, etc. Then...
currentState = TurnState.ENEMY_ACTION;
break;
}
To make things worse, I've read that "pausing" a while loop isn't good for performance. That it's better to take player input out of loops.
So maybe I should create more for loops, iterate a new loop from position to position until the last GameObject acted, and use some delegates/events, as some things players/AI can do are in different scripts (classes).
This is not a real-time project, so we can't base anything on time (other than potential max turn time).
Another thing is, we don't know how many GameObjects will take turns.
But maybe there's some collection type that can store GameObjects with gaps between their index positions and iterate a loop from highest to lowest with no problem?...
I want to make it as simple as possible.
For the issue of simultaneously having user input and looping, I recommend looking into background workers or threading/tasks. This should facilitate your problem with simultaneously doing two things at once.
For your list problem I personally prefer the lists as well which is why I would designate each "gap" with a specific character like - 1, and when referencing the data ignore the - 1. To ignore the gaps I recommend LINQ queries but if you don't want to use LINQ that should not be a problem.
EDIT*
I know very little about Unity but from what people have been saying it sounds like running multiple threads is or can be an issue. I looked into this issue and it sounds like you just cannot call the unity api from a background thread. So basically, as long as you do not reference the unity api from the background thread, you should be ok. With that said, you may possibly need/want to make a call to the api inside of the background worker. To do this you need to either invoke the call before or after the background worker thread. I am pretty sure there is also a simultaneous invocation from the main thread by setting the workers step property to true.
I've decided to share my own solutions as I think I've developped some accurate answers to my questions.
Solution #1. First, declare an array, 2 variables and 1 function:
int[] arrayOrderedCombatants;
int min = 100000;
int max;
public void SomePlayerAction() {
Debug.Log ("This is some player or AI action.");
}
public void IterateThroughTurnOrderPositions() {
for (int i=max; i >= min; i--) {
if (arrayOrderedCombatants [i] >= 0 && arrayOrderedCombatants [i] >= min) {
Debug.Log ("This is an existing position in turn order: " + arrayOrderedCombatants [i]);
foreach (var position in turnOrder) {
if (position.Value == arrayOrderedCombatants [i]) {
max = (arrayOrderedCombatants [i] - 1);
goto ExitLoop;
}
}
}
}
ExitLoop:
SomePlayerAction ();
}
Then, for our testing purposes let's use an Update() method triggered by Input.GetKeyDown:
if (Input.GetKeyDown (KeyCode.O)) {
arrayOrderedCombatants = new int[turnOrder[0].Value + 1];
Debug.Log ("This is arrayOrderedCombatants Length: " + arrayOrderedCombatants.Length);
foreach (var number in turnOrder) {
if (number.Value < min)
min = number.Value;
}
Debug.Log ("This is min result in random combat order: " + min);
for (int i=0; i < arrayOrderedCombatants.Length; i++)
arrayOrderedCombatants[i] = min -1;
foreach (var combatant in turnOrder) {
if (combatant.Value >= 0) {
arrayOrderedCombatants [combatant.Value] = combatant.Value;
}
}
max = turnOrder [0].Value;
while (max >= min)
IterateThroughTurnOrderPositions ();
}
The above code answers my question. Unfortunately, problems with this solution may be two. First - you can't have a negative index position. So if someModifier makes randomPosition go below 0, it won't work. Second problem - if you have more than 1 occurance of any value from randomPosition, it will be added to the arrayOrderedCombatants only once. So it will be iterated once too.
But that's obvious - you can't have more than one value occupying an int type Arrays' index position.
So I will provide you a better solution. It's a different approach, but works like it should.
Solution #2. First, declare a list of GameObjects:
List<GameObject> orderedCombatants = new List<GameObject> ();
Then, in Update() method:
if (Input.GetKeyDown (KeyCode.I)) {
orderedCombatants.Clear ();
foreach (var combatant in initiative) {
Debug.Log (combatant);
for (int i=0; i < listOfCombatants.Count; i++) {
if (listOfCombatants[i].name.Contains(combatant.Key)) {
orderedCombatants.Add(listOfCombatants[i]);
}
}
}
foreach (var combatant in orderedCombatants) {
Debug.Log (combatant.name);
}
}
The above creates a new list of GameObjects already set in the right order. Now you can iterate through it, easily access each GameObject and perform any actions you need.

passing a result(double type variable) from a method as an element of an array

Background -
I am new to programming in C# and am trying to code a custom indicator. On a trading platform that uses C#. A histogram with
positive Y axis = reqAskArraySum
negative Y axis = reqBidArraySum
The inbuilt compiler shows no errors. However the desired results do not seem to show up.
I know/it is possible there are some platform specific initialization problems i.e. code that I have not entered yet correctly.
Question -
The question here is with regards to the code posted below , which is a part of the whole code.
I would like to know whether the posted code satisfies the below objectives.
The objective here is to get a 'number' using a method.
Then only accept selected 'numbers' into an array. The selection/filtration is done by an 'If' statement
Any help and pointers would be highly appreciated.
int i=0;
double iAskSize = GetLevel2Size(ASK,0); //GetLevel2Size(ASK,0) is the method that helps me to retrieve the ASK side 'numbers'//
double iBidSize = GetLevel2Size(BID,0); //GetLevel2Size(BID,0) is the method that helps me to retrieve the BID side 'numbers' //
if(iAskSize>=AskTH_value) //the number should be >= user defined AskTH_value, this is the filtration of the Ask Side 'numbers'//
I am trying to get the filtered iAskSize 'numbers' into the array
reqAskSize, I believe there is a problem here. However I am not sure
{
double[] reqAskSize = new double[1000];
double reqAskArraySum = 0;
for(i=0;i<reqAskSize.Length;i++)
{
reqAskArraySum = reqAskArraySum + reqAskSize[i];
}
SetValue(0,reqAskArraySum);
}
if(iBidSize>=BidTH_value) **//the number should be >= user defined BidTH_value,this is the filtration of the Bid Side 'numbers'//**
I am trying to get the filtered iBidSize 'numbers' into the array
reqBidSize, I believe there is a problem here. However I am not sure
{
double[] reqBidSize = new double[1000];
double reqBidArraySum = 0;
for(i=0;i<reqBidSize.Length;i++)
{
reqBidArraySum = reqBidArraySum + reqBidSize[i];
}
SetValue(1,-reqBidArraySum);
}
If you are trying to add selected numbers into an array, once a number has been selected (through a conditional like if/else), the number should be set in the array.
For example
int[] array = new int[6];
for(int val = 0; val < 6; val++)
{
if(array[val] > 0)
array[val] = 6;
}
Also to obtain a numeral, a return statement makes clear if anything is got.
You should try and see where a return statement may belong within your code.

Clearing a list using a for loop

I'm making a Black Jack game, and at the start of every new round I need to clear the list of cards that represents the Player's and the Dealer's hands. I used this to do so:
public void ClearPlayerHand()
{
for (int i = 0; i < PlayerHand.Count; ++i)
{
PlayerHand.Remove(PlayerHand[i]);
}
}
Problem is I always seem to be left with one card left in the list, or I receive an out of bounds error, no matter how I change the value of i, what is the best method of removing all the elements from the PlayerHand?
If your collection PlayerHand implements ICollection<T> you can just call the .Clear() method.
A common implementation of this interface is List<T>.
If you do want to clear a List<T> via a for loop, you should use a reverse for loop. The reason for this is that as you remove an item from the list, it will shift all the index's down one, and you could easy run into index out of bounds exceptions.
An example of this would be:
for (int i = PlayerHand.Count - 1; i >= 0; i--)
{
PlayerHand.RemoveAt(i);
}
The other answers are right: use Clear.
But, if you wanted to do this with a loop and Remove calls, here's how you would do it:
for(int i = PlayerHand.Count - 1; i >= 0; i--)
{
PlayerHand.RemoveAt(i);
}
Reversing the direction of the iteration is the real trick.
This is the best/easiest way to do it.
PlayerHand.Clear();
Reason for out of bounds
As for why you are receiving the out of bounds exception, it's happening because you're removing elements from the list but continually counting up. You would want the last operation to remove i = 0 but it keeps counting.
Say PlayerHand has 3 items in it, the following occurs:
i = 0
remove PlayerHand[0] (it now contains 2 elements)
i = 1
remove PlayerHand[1] (it now contains 1 element)
i = 2
remove PlayerHand[2] (this throws an exception as only PlayerHand[0] exists)
Normally you would count backwards in this case:
for (int i = PlayerHand.Count - 1; i >= 0; i--)
Alternatively, you can consider using data binding and then you should update the ItemSource, instead of directly manipulating the listbox or listview items.
List<T> SomeSource=...
PlayHand.ItemSource=SomeSource;
SomeSource.Clear();
Another suggested approach beside Clear method, you can also use RemoveAll to either remove all or part of list
// Remove all items
PlayerHand.RemoveAll(x => true);
// Remove part of list
PlayerHand.RemoveAll(x => ConditionMethod(x));

Iterating over an array using index and assigning values depending on index

I was facing this problem earlier today, and since I could not find a satisfactory solution, I decided to change my class design, and have seperate properties such as Tag 1, Tag 2, Tag 3 etc.
My main problem is the fact that I need to bind a grid to an object that contains a list among other properties and I need to show each item in the list as a separate column which I am unable to do. Hence I am resorting to declaring variables separately. Original question is here...
Now, I'm facing one of the most common design problem that probably every programmer has at some point of time. Here is the code to demonstrate it,
for (int i = 0; i < tags.Length; ++i) // Length not known here.
{
if(i==0){
tag1 = tags[0];
}
else if(i == 1){
tag2 = tags[1];
}
else if(i == 2){
tag3 = tags[2];
}
....
}
Here tags is a string array.
I was wondering if there is a more elegant way to do this. Another thing to note is that the efficiency of this loop decreases as it progresses, since with more iterations it has to check more conditions. If we could remove a condition after it had become true once it would speed up each iteration since we know that each condition will become true only once in all the iterations
Moved answer about DataGridView and using ComponentModel to the correct question:
Displaying a list of object containing a list in a grid view
Briefing
The DataGridView controll supports the ComponentModel namespace so that you can create classes that appear to have properties that don't exist. It is the same mechanism the PropertyGrid uses.
The sample code is in this answer of that question:
https://stackoverflow.com/a/13078735/195417
OLD ANSWER
This was my previous answer, when I didn't realize the real question was about the DataGridView control.
Isn't this the same as setting the values directly:
this.tag1 = tags[0];
this.tag2 = tags[1];
this.tag3 = tags[2];
EDIT: as you sayd you don't know how many variables will be needed, then you need only one, and that is a list:
var list = new List<string>();
for (int i = 0; i < tags.Length; ++i)
{
list.add(tags[i]);
}
If all you want is to copy all values, you can even do this:
var list = new List<string>(tags);
Tell me whether this is what you want or not... maybe I have misunderstood the question.
The whole loop is pointless. But unless the tags array length is always going to be the same, you have to be sure not to go out of bounds...
if(tags.Length >= 1) this.tag1 = tags[0];
if(tags.Length >= 2) this.tag2 = tags[1];
if(tags.Length >= 3) this.tag3 = tags[2];
if(tags.Length >= 4) this.tag4 = tags[3];
if(tags.Length >= 5) this.tag5 = tags[4];
... so on for however many this.tag# you have.
This is essentially the same:
for(int index = 0; index < tags.Length[]; index++){
switch(index){
case 0:
tag1 = tags[0];
break;
// And so on
}
}

Append int to end of string or textbox name in a For Loop C#

I have a C# application in which there are several textboxes with the same name except for a number on the end which starts at 1 and goes to 19. I was hoping to use a for loop to dynamically add values to these text boxes by using an arraylist. There will be situations where there will not be 19 items in the arrayList so some text boxes will be unfilled. Here is my sample code for what I am trying to do. Is this possible to do?
for (int count = 0; count < dogList.Count; count++)
{
regUKCNumTextBox[count+1].Text=(dogList[count].Attributes["id"].Value.ToString());
}
So you've got a collection of text boxes that are to be filled out top-to-bottom? Then yes, a collection of TextBox seems appropriate.
If you stick your TextBox references in an array or a List<TextBox> -- I wouldn't use an ArrayList as it's considered deprecated in favor of List<T> -- then yes, you can do that:
TextBox[] regUKCNumTextBox = new []
{
yourTextBoxA,
yourTextBoxB,
...
};
Then yes your logic is possible, you can also query the control by it's name, though that would be heavier at runtime - so it's a tradeoff. Yes, in this solution you must set up a collection to hold your text box references, but it will be more performant.
Try this:
(By the way I am assuming you use WinForms)
for (int count = 0; count < dogList.Count; count++)
{
object foundTextBox = this.Controls.Find("nameofTextBoxes" + [count+1]);
if (foundTextBox != null)
{
if (foundTextBox is TextBox)
{
((TextBox)foundTextBox).Text=(dogList[count].Attributes["id"].Value.ToString());
}
}
}
With this code you are trying to find a Control form your Forms Controls collection. Then you have to make sure the control is of the TextBox type. When it is; cast it to a TextBox and do what you want with it. In this case; assign a value to the Text property.
It would be more efficient to keep a collection of your TextBoxes like in the solution offered by James Michael Hare
Yikes; something doesn't seem quite right with the overall design there; but looking past that, here's a quick stab at some pseudo code that might work:
for (int count = 0; count < dogList.Count; count++)
{
var stringName = string.Format("myTextBoxName{0}", count);
var ctrl = FindControl(stringName);
if(ctrl == null) continue;
ctrl.Text = dogList[count];
}

Categories