Got into a problem and i can't seem to find the issue. I am currently making a war based game where players are able to buy certain buildings and place them wherever they want. When they choose the spot they like it starts building. After specific time passes it finishes building and it appears in the spot.
THE PROBLEM: I can't figure out how to give the building time to buildings if there are more than one of them building. I tried arrays, but it came to nothing.
THE CODE: The code is kind of hard to read. But if it helps, here you go:
public void BuyBuilding(string whichBuilding)
{
if (whichBuilding == "CIV")
{
if (CIVQUANT < MaxCivQuant && decidedNotToBuy == false && howManyAreBuilding < 4)
{
if (ELEM.Money >= 10000) //IF ENOUGH MONEY
{
howManyAreBuilding++;
whichBuildBought[howManyAreBuilding] = whichBuilding;
}
^ Here i made a method that activates whenever a player presses a button, it checks which building it is "CIV" is one of them, i removes 10k from the player's currency (Not shown here for now)and adds a "CIV" value to a whichBuildBought string array. Then we go to update where the game is checking if any building was bought:
private void Update()
{
if (boughtBuildingS == true && whichBuildBought[howManyAreBuilding] == "CIV")
{
ElapsedTimeCalculator(howManyAreBuilding);
if (elapsedTime[howManyAreBuilding] == time.OnDayChanged)
{
elapsedTime[howManyAreBuilding] = 0;
boughtBuildingTime[howManyAreBuilding] = 0;
ElapsedTimeCalculatorPassed = false;
howManyAreBuilding--;
}
}
^ Here i activate an ElapsedTimeCalculator method shown below, Time.OnDayChanged is a day counter, i use it to check if the day the player bought the building matches the day it's supposed to finish building by giving boughtBuildingTime specific array value a current day value and then giving elapsedTime array that value plus how many days its supposed to be building. In the update method if the elapsedTime of that specific building in the array is done it just finished building and some extra things happen that i just removed for now.
public void ElapsedTimeCalculator(int i)
{
if (ElapsedTimeCalculatorPassed == false && whichBuildBought[i] == "CIV")
{
boughtBuildingTime[i] = time.OnDayChanged;
elapsedTime[i] = boughtBuildingTime[i] + 20;
ElapsedTimeCalculatorPassed = true;
}
Thanks in advance for helping! Would really appreciate it.
Related
I have been googling for two days now, but can't figure this out and it seems to be basic.
Within the void OnTick(), I would like to create a variable int a;. Lets say it starts out with no value int a;, then I test condition if a is NULL or || equals 1 like this if (a == NULL || a == 1) which should always return true the first time the if statement runs due to NULL. I then assign a value to the variable a = 0;, so now a should equal 0.
This should trigger the else if(a == 0) the next time OnTick() is called, at this point I assign a = 1; resulting in the if being triggered next time round, etc and infinitum, constantly checking and assigning values switching between 0 and 1.
void OnTick()
int a;
if (PositionsTotal() < 1)
{
if(a == NULL || a == 1)
{
a = 0;
}
else if(a == 0)
{
a = 1;
}
}
I do not know what is going on, but during Testing in Strategy tester, I am getting a long string of numbers which switches between negatives and positives always with the same number -123456789 or 123456789, always 9 digits long.
Or during Debugging from Meta Editor. A random positive 3 digit number which never changes!
So far I have had both 303 and 597.
Beyond frustrating and most likely answered somewhere else.
Just not sure how to phrase the search term.
Thanks for you help.
I think the problem is that you declare your variable a inside the method OnTick, so it starts with an arbitrary value every time you call this method. Try declaring it outside this method.
I have a list of player numbers(int list), I also have a number of laps (int laps). I have to enter the number of the player into a text box each time the player completes a lap. If valid, the player number entered into the text box will be sent to a list. However, each player number cannot be entered more than the number of laps.
I'm new to windows forms so I've only used lists to solve the problem, I've seen people use dictionary to solve similar issues but I'm not quite sure how to incorporate that to solve this issue.
private void textBox2_TextChanged(object sender, EventArgs e)
{
bool result;
int RunnerNumberInOrder;
repeat2 = (getLaps2 * rows2);
textBox2.Focus();
//ReadPlayerNumber is the list that contains the list of the runners in the race.
//PlayerNumberInOrder int list is a global list and it is the list that the runners number order gets sent to if valid
result = int.TryParse(textBox2.Text, out RunnerNumberInOrder); //checks if the value entered is an integer
if (result == false || !ReadRunnerNumbers2.Contains(RunnerNumberInOrder)) //if the value entered is not an integer an error message will pop up and ask to enter a valid number
{
label3.Text = "Please enter a valid player number from the list";
}
else
{
if (RunnerOrder.Contains(RunnerNumberInOrder))
{
count++;
}
if (count == getLaps2)
{
count = 0;
label3.Text = "max laps for this runner has been entered";
invalid.Add(RunnerNumberInOrder); //invalid is an int list that I used to send the numbers of the runners that have completed all laps.
textBox2.Clear();
return;
}
}
if (invalid.Contains(RunnerNumberInOrder))
{
MessageBox.Show("Max number of laps for this runner has been entered");
textBox2.Clear();
}
if (result == true && ReadRunnerNumbers2.Contains(RunnerNumberInOrder) && !invalid.Contains(RunnerNumberInOrder))
{
RunnerOrder.Add(RunnerNumberInOrder); //if number entered is valid, the it is added to the global variable
PlayerNumbersEntered++;
label8.Text = RunnerNumberInOrder.ToString();//counts how many valid car numbers have been enterted
textBox2.Clear();
}
}
The code I have kinda works but it sometimes only allows one number 2 times when the number of laps is 3 and sometimes will allow more than the laps entered. I want an error message to pop up when the player's number equals to the number of laps and the number shouldn't be allowed any further. I also have to use a text_change. Cheers for the help in advance.
The main issue I see in your code I believe is the count variable which appears to be a global variable but you seems to use it like is count for specific RunnerNumbers.
The way I see it you could solve this by either creating a class of Runners that would have a number ID and the Count or, even simpler, to just get the count of that runner on the go as follows:
specificRunnerCount = RunnerOrder.Where(x => x == RunnerNumberInOrder).Count()
With this you could modify your code to something like this:
int specificRunnerCount = RunnerOrder.Where(x => x == RunnerNumberInOrder).Count();
if ((specificRunnerCount + 1) == getLaps2) // +1 because we haven't added this number to the count yet.
{
label3.Text = "max laps for this runner has been entered";
invalid.Add(RunnerNumberInOrder);
textBox2.Clear();
return;
}
// Continue code as is
Also, from your code, the part of code where you add a value to the invalid list: invalid.Add(RunnerNumberInOrder);, you use a return statement which ends the function. Due to this I don't see the need for you to check if the invalid list contains the runner number in if (invalid.Contains(RunnerNumberInOrder))
Hope this helps you.
// 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.
I am trying to figure out a way to get the Computer player to respond to my moves basically by seeing "This spot is taken, I should see if another is free and take it".
So far, I'm not making any improvements (been like 5 hours). I want the computer to realize if a certain button (which it chose at random) is taken, it should consider another choice. Not sure where the if/else should actually go or where/what I should put in for it to try another location.
Here's the snippet of code with comments on my idea (likely wrong placement where I want to do things):
if (c.Enabled == true) //if the button is free
{
if ((c.Name == "btn" + Convert.ToString(RandomGenerator.GenRand(1, 9)) )) //if a specific button is free
{
if ((c.Text != "X")) //if its empty
{
//do this
c.Text = "O"; //O will be inside the button
c.Enabled = false; //button can no long be used
CheckComputerWinner(); //check if it finishes task
return;
}
else //if it is an X
{
//try a different button choice instead
//see if that button is empty
//do stuff
//else
//... repeat until all buttons are checked if free
}
}
}
My question is simply: How can I fix this and understand what is going on? Or do it more efficiently?
You could create an array with those buttons so you don't have to check names:
Button[9] gameField;
Creating a [3,3] array might be more intuitive, but for this case a plain array is easier to work with.
Then, you could count how many of them are free:
int freeCount = gameField.Count(b => b.Text != "X");
If you want to randomly select one of the free ones, generate a random number in the range 0 - (freeCount - 1) and select the appropriate button:
int offset = RandomGenerator.GenRand(0, freeCount - 1);
Button target = gameField.Where(b => b.Text != "X").Skip(offset).FirstOrDefault();
if (target != null) {
// check it
}
The extension method Where filters your buttons to only return the free ones. Skip will skip the specified number of elements (for random selection) and FirstOrDefault returns the first element of the resulting sequence (or null if there is none).
Note: You might want to check for some cases before randomly selecting a field to make your AI a bit more ambitious:
Has the computer player two O in a row with the third field being free? If so, select that one.
Has the human player two X in a row with the third field being free? If so, select that one.
There are tricks to exploit that strategy and there are also better heuristics, but i'll leave that to you.
Your looking for a while loop here. You can modify your code as follows:
Button c;
// here you look for a Button within the Controls of your Form. It stops when an Enabled Button with Text != "X" is found
do
{
c = this.Controls.Find("btn" + Convert.ToString(RandomGenerator.GenRand(1, 9)), true).FirstOrDefault() as Button;
} while (c == null || !c.Enabled || c.Text == "X");
c.Text = "O"; //O will be inside the button
c.Enabled = false; //button can no long be used
CheckComputerWinner(); //check if it finishes task
return;
I have just few days to show a demo about a Music Player in WPF and i have a trouble that i can not work out right now .
I need know how change a location folder meantime the Music Player is running ,i have 3 location folder:
D/: Morning;
D/: Afternoon;
D/: Night;
in each folder there are songs of different genre.
This music player will be used everyday from the 8am to 10pm with no stop ,so when run the application in morning(8am) it will download automatically the folder"Morning" but when the timeOfDay is Midday it will change location folder and so pass in the folder"Afternoon"(about 6p would pass to the folder "Night") and at the point i get stuck i don't know how work out this step,i don't know how organise my code to make the Music Player change location folder and download new songs in automatic way.
Please do you have some idea to illuminate my mind and go on to finish this Demo?
Sorry for my confusion;
Thanks a lot
Honsa has the right idea, but this is a slightly cleaner implementation:
public static string GetFolderForTime(DateTime time)
{
if (time.Hour > 8 && time.Hour < 10)
return #"D:\Morning\";
if (time.Hour > 10 && time.Hour < 18)
return #"D:\Afternoon\";
return #"D:\Night\";
}
That way you can pass in a time different from the current one if you need to, although normally you would use DateTime.Now.
Also, note that the name of the function describes what it does.
static public string GetWorkingFolder()
{
if(System.DateTime.Now.Hour > 1 && System.DateTime.Now.Hour < 12)
return #"D:\Morning";
else if (System.DateTime.Now.Hour > 11 && System.DateTime.Now.Hour < 18)
return #"D:\Afternoon";
else
return #"D:\Evening";
}
would return a differential string dependant on the current pc time (which can of course change) this could then be parsed into a directory or directly used in whatever load method is picking up the various 'tunes'
Try this. Every time a song ends, before it loads a new one, simply call a function that does this:
// The string returned is the path
public string TimeOfDay()
{
// How you define
if(System.DateTime.Now.Hour >= 8 && System.DateTime.Now.Hour < 10)
return #"D:\Morning\";
else if(System.DateTime.Now.Hour >= 10 && System.DateTime.Now.Hour < 18)
return #"D:\Afternoon\";
else
return #"D:\Night\";
}
If the path returned is differen than the one you already have, then you change, and play songs from the new path.