I'm making a wild west duelling game based on typing of the dead. You have a word to write in a certain amount of time. You win if you type out the word in time, you lose if you type it incorrectly/press the wrong button or if the time runs out.
Currently I've got everything working fine. A slight issue, however, is with how I'm dealing with displaying the letters you have to type on the screen.
Each character is stored into an array that is looped through and displayed on the screen. When the player presses the correct button, the corresponding display should turn red which it does most of the time. The times where it doesn't is when there are duplicate characters.
For example if I was typing the word 'dentist', when I type the first t, it won't turn red. However, when I get to the second t and press it, both turn red. I assume this is because I'm looping through each displayed character and checking to see if it's relevant input is being pressed and because there's two and I can only type one character at a time one is always false which 'overrides' the one that is true. I'm not sure how to implement a solution with how I'm currently dealing input so any help is appreciated!
Code:
if (Duelling)
{
if (currentWord.Count > 0 && Input.inputString == currentWord[0].ToLower())
{
print(Input.inputString);
string pressedKey = currentWord[0];
currentWord.Remove(currentWord[0]);
}
else if (Input.inputString != "" && Input.inputString != currentWord[0].ToLower())
{
DuelLost();
}
if (currentWord.Count <= 0)
{
DuelWon();
}
foreach(Transform Keypad in keyDisplay.transform)
{
//print(Keypad.Find("KeyText").GetComponent<Text>().text);
Keypad.Find("KeyText").GetComponent<Text>().color = currentWord.Contains(Keypad.Find("KeyText").GetComponent<Text>().text) ? Color.black : Color.red;
}
}
I believe the issue lies in your colour-updating logic. Contains naturally returns true if your array, well, contains the text you're looking for. Since the second T in "dentist" is still present in the array after you type the first one in, the component isn't going to change its colour. When inputting the second T, all instances of Ts are cleared from the list, and since you loop over all of your Text components all the time, both of them will become red.
No offence, but you're going about this rather... crudely. Allow me to suggest a more elegant method:
public String currentWord;
private List<Text> letterViews = new List<Text>();
private int curIndex = 0;
void Start() {
// Populate the list of views ONCE, don't look for them every single time
letterViews = ... // How you do this is entirely up to you
}
void Update() {
// ...
if (Duelling) {
// If we've gone through the whole word, we're good
if (curIndex >= currentWord.Length) DuelWon();
// Now check input:
// Note that inputString, which I've never used before, is NOT a single character, but
// you're using only its first character; I'll do the same, as your solution seems to work.
if (Input.inputString[0] == currentWord[currentIndex]) {
// If the correct character was typed, make the label red and increment index
letterViews[currentIndex].color = Color.red;
currentIndex++;
}
else DuelLost();
}
}
I daresay that this is a much simpler solution. DuelWon and DuelLost shall reset the index to 0, clear the text in all letterViews and turn them back to black, perhaps.
How to populate the list of views: you can make it public and manually link them by hand through the inspector (boring), or you can do it iteratively using Transform.GetChild(index). You've probably got enough Text views to accommodate your longest words; I recommend filling the list up with them all. You only do it once, you lose no performance by doing so, and you can re-use them for any words in your dictionary.
Related
thanks in advance for your help. I'm a hobbyist playing around with building an incremental clicker to learn various coding techniques and am trying to find a way to streamline a portion of my script. I'm going to have 80-100 different clickable images and I'd like the image to change color/turn on and off when it can/it can't be clicked to show a user when it can be interacted with. The conditions for when each can be clicked are different for every image. So far I've tried SetActive() and setting fillAmount to 0 or 1 however it's resulting in long and mostly repetitive code. Below is how I've tried both ways, could anyone recommend a way to streamline this so I don't have to write 4 lines of code for every clickable object? I've tried writing a quick program however I keep getting stuck with the conditions being different for every image.
if ((data.food >= data.hutClickFoodConsumption) && (data.energy >= data.hutClickEnergyConsumption))
hutClickYes.SetActive(true);
else
hutClickYes.SetActive(false);
if ((data.food >= data.townPlannersClickFoodConsumption) && (data.energy >= data.townPlannersClickEnergyConsumption))
townPlannersClickYes.fillAmount = 1;
else
townPlannersClickYes.fillAmount = 0;
if ((data.food >= data.hiRiseClickFoodConsumption) && (data.energy >= data.hiRiseClickEnergyConsumption))
hiRiseClickYes.fillAmount = 1;
else
hiRiseClickYes.fillAmount = 0;
With such a large number of items, I generally like to store as much information as sensible within each item. And then I'll store those items in a collection. Because of the lack of Dictionary<K, V> serialisation in Unity, it's usually a List<T> so we can use the Inspector.
An example of that for your case might look something like this:
[Serializable]
public class ItemData
{
public string Name;
public float FoodConsumption;
public float EnerguConsumption;
public GameObject ItemObject; // (see notes)
// add any extra item data here ...
}
public class Test : MonoBehaviour
{
[SerializeField] private List<ItemData> _allItems;
// Include your 'data' field or property here as well.
public void Setup ( )
{
foreach ( var i in _allItems )
i.ItemObject.SetActive (
( data.food >= i.FoodConsumption ) &&
( data.energy >= i.EnerguConsumption ) );
}
}
With this approach, you can reduce the amount of code from hundreds of lines, to half a dozen. Also, the type of the referenced object could be a GameObject (as shown), or an Image if you're using the uGUI (think Canvas), or another referenced object to suit your needs.
The result of the above code then looks like this:
Obviously, this only works if all of the conditions can be boiled down to the boolean check in the example. But, it IS possible to add further data to extend the type checking. A quick example would be adding boolean values to the item data, indicating what fields need to be checked, and/or whether a value need to be less then, equal or greater than.
My goal is to actually make a guessing game, so I created two arrays with Mysql data called answers and questions. And what I want to do is take the value from the user and if it is true, for example my first answer 'fashion' matches the guess the user entered in the textbox, I want the label to write correct and continue with the next answer and try to find the next answer
My code returns true when I enter my values in the array into the textbox, but I want them to be in order. How do you think I can use the for loop. How do you think I can use the for loop to make an ordered comparison?
for (int i=0;i<cevaplar.Count;i++)
{
string tahmin = textBox1.Text;
if(cevaplar.Contains(tahmin))
{
label1.Text = "true";
continue;
}
else
{
label1.Text = "false";
break;
}
}
}
In your code you use "cevaplar.Contains(tahmin)". With contains you're checking if tahim can be found anywhere in your array, without taking any order in account.
The solution to your problem should be quite simple. Just don't use contains in this situation but use a simple indexer to compare the elements. Try the following:
Replace:
if(cevaplar.Contains(tahmin))
{
...
}
With
if(cevaplar[i] == tahim) //here you check only if the i'th element is matching.
{
...
}
Good luck!
First off I'm quite new to both SSIS and C#, so apologies for any rookie mistakes. I am trying to muddle my way through splitting one column by a specific delimiter from an input file that will have a variable length header, and a footer.
For example, Input0Buffer has one column. The actual data is always preceded by a row starting with the phrase "STARTDATA", and is bracketed with a row starting with "ENDDATA".
The one input column contains 5 bits of data separated by | . Two of these columns I don't care about.
Basically the input file looks like this:
junkrow
headerstuff
morejunk
STARTDATA
ID1|rubbish|stuff|apple|cheese
ID2|badger|junk|pear|yoghurt
So far I have tried to get some row-by-row logic going in the C# transformer, which I think I am happy with - but I can't work out how to get it to output my split data. Code is below.
bool passedSOD;
bool passedEOD;
public void ProcessRow(Input0Buffer data)
{
string Col1, Col2, Col3;
if (data.Column0.StartsWith("ENDDATA"))
{
passedEOD = true;
}
if (passedSOD && !passedEOD)
{
var SplitData = data.Column0.Split('|');
Col1 = SplitData[0];
Col2 = SplitData[3];
Col3 = SplitData[4];
//error about Output0Buffer not existing in context
Output0Buffer.Addrow();
Output0Buffer.prodid = Col1;
Output0Buffer.fruit = Col2;
Output0Buffer.dairy = Col3;
}
if (data.Column0.StartsWith("STARTDATA"))
{
passedSOD = true;
}
}
If I change the output to asynchronous it stops the error about Output0Buffer not existing in the current context, and it runs, but gives me 0 rows output - presumably because I need it to be synchronous to work through each row as I've set this up?
Any help much appreciated.
you can shorten your code by just checking if the row contains a '|'
if(Row.Column0.Contains("|")
{
string[] cols = Row.Column0.Split('|');
Output0Buffer.AddRow();
Output0Buffer.prodid = cols[0];
Output0Buffer.fruit = cols[3];
Output0Buffer.dairy = cols[4];
}
Like Bill said. Make sure this is a transformation component and not a destination. Your options are source, transformation, and destination.
You also might want this as a different output as well. Otherwise, you will need to conditionally split out the "extra" rows.
Thanks both for for answering - it is a transformation, and thank you for the shorter way, however the header and footer are not well formatted and may contain junk characters also, so I daren't risk looking for | in rows. But I will definitely store that away for processing a better formatted file next time.
I got a reply outside this forum so I thought I should answer my own question in case any one else has a similar problem.
Note that:
it's a transform
the Output is be set to SynchronousInputID = None in the Inputs and Outputs section of the Script Transformation Editor
my input is just called Input, and contains one column called RawData
my output is called GenOutput, and has three columns
although the input file only really has 5 fields, there is a trailing | at the end of each row so this counts as 6
Setting the synchronous to None means that Output0Buffer is now recognised in context.
The code that works for me is:
bool passedSOD;
bool passedEOD;
public override void_InputProcessInputRow(InputBuffer Row)
{
if (Row.RawData.Contains("ENDDATA"))
{
passedEOD = true;
GenOutputBuffer.SetEndOfRowset();
}
//IF WE HAVE NOT PASSED THE END OF DATA, BUT HAVE PASSED THE START OF DATA, SPLIT THE ROW
if (passedSOD && !passedEOD)
{
var SplitData = Row.RawData.Split('|');
//ONLY PROCESS IF THE ROW CONTAINS THE RIGHT NUMBER OF ELEMENTS I.E. EXPECTED NUMBER OF DELIMITERS
if (SplitData.Length == 6)
{
GenOutputBuffer.AddRow();
GenOutputBuffer.prodid = SplitData[0];
GenOutputBuffer.fruit = SplitData[3];
GenOutputBuffer.dairy = SplitData[4];
}
//SILENTLY DROPPING ROWS THAT DO NOT HAVE RIGHT NUMBER OF ELEMENTS FOR NOW - COULD IMPROVE THIS LATER
}
if (Row.RawData.Contains("STARTDATA"))
{
passedSOD = true;
}
}
Now I've just got to work out how to convert one of the other fields from string to decimal, but decimal null and allow it to output a null if someone has dumped "N.A" in that field :D
I am stuck on an assignment for my Introductory Programming class, and can't get past my instructor's suggestions to generate any working code.
Assignment:
The user will type in a full name separated by spaces. When btnGetName is pressed, your program will analyze the text box and extract the first name and place it in the first name label, the middle name in the middle name label and finally the last name into the last name label (assuming they all exist).
If only one name is entered (Smith), assume it is the last name and leave first and middle names blank
If two entries are made (Joe Smith) assume it is the first and last.
If there are three entries in the text box, assume this includes first, middle and last names.
If no entries are made leave all labels empty.
If more than three entries are made give the user an error message, wipe the text box and place the cursor back into the text box.
Prevent an error from occurring using if/then or try/catch (if necessary).
Make the form look somewhat professional.
Hint:
Split the text box into an array. Use arrayname.count to determine how many entries are made (how many items in the array). Use an if/then/else to decide how to populate the name labels. This should take less than 7 lines of code in your button click event handler.
Example:
if (myarray.count==1)
{
lblLast.text=myarray[0].ToString();
//I would wipe out the contents in the other labels here
}
else if(myarray.count==2)
{
...
}
I have been stuck for hours-- probably because I am frustrated with something else, but I cannot get anything to display in my labels. From a lecture, the professor shares the Liststring... as his desired format for splitting the string into an array.
This is what I have under btnGetName right now:
private void btnGetName_Click(object sender, EventArgs e)
{
string txtFullName = "";
List<string> namesArray = txtFullName.Split(' ').ToList();
namesArray.Count();
foreach (string name in namesArray)
{
if (namesArray.Count == 1)
{
lblLast.Text = namesArray[0].ToString();
}
else if (namesArray.Count == 2)
{
lblFirst.Text = namesArray[0].ToString();
lblLast.Text = namesArray[1].ToString();
}
else if (namesArray.Count == 3)
{
lblFirst.Text = namesArray[0].ToString();
lblMiddle.Text = namesArray[1].ToString();
lblLast.Text = namesArray[2].ToString();
}
}
By looking at your example, your List<string> namesArray is empty. You are taking an empty string and splitting it, which will just give you an empty list.
You should instead be something like this, where inputText is the edit text element that a user will enter the names:
string txtFullName = inputText.Text;
List<string> namesArray = txtFullName.Split(' ').ToList();
I would also like to add that the foreach loop is redundant; you are essentially assigning your 3 labels over and over again until it reaches the end of the loop. Remove the foreach block, but keep its contents.
And one more edit, another huge redundancy is the .ToString() cast on each item in namesArray, which is by definition a list of strings. You can safely remove those extra calls.
I'm working on a portion of code that is essentially trying to reduce a list of strings down to a single string recursively.
I have an internal database built up of matching string arrays of varying length (say array lengths of 2-4).
An example input string array would be:
{"The", "dog", "ran", "away"}
And for further example, my database could be made up of string arrays in this manner:
(length 2) {{"The", "dog"},{"dog", "ran"}, {"ran", "away"}}
(length 3) {{"The", "dog", "ran"}.... and so on
So, what I am attempting to do is recursively reduce my input string array down to a single token. So ideally it would parse something like this:
1) {"The", "dog", "ran", "away"}
Say that (seq1) = {"The", "dog"} and (seq2) = {"ran", "away"}
2) { (seq1), "ran", "away"}
3) { (seq1), (seq2)}
In my sequence database I know that, for instance, seq3 = {(seq1), (seq2)}
4) { (seq3) }
So, when it is down to a single token, I'm happy and the function would end.
Here is an outline of my current program logic:
public void Tokenize(Arraylist<T> string_array, int current_size)
{
// retrieve all known sequences of length [current_size] (from global list array)
loc_sequences_by_length = sequences_by_length[current_size-min_size]; // sequences of length 2 are stored in position 0 and so on
// escape cases
if (string_array.Count == 1)
{
// finished successfully
return;
}
else if (string_array.Count < current_size)
{
// checking sequences of greater length than input string, bail
return;
}
else
{
// split input string into chunks of size [current_size] and compare to local database
// of known sequences
// (splitting code works fine)
foreach (comparison)
{
if (match_found)
{
// update input string and recall function to find other matches
string_array[found_array_position] = new_sequence;
string_array.Removerange[found_array_position+1, new_sequence.Length-1];
Tokenize(string_array, current_size)
}
}
}
// ran through unsuccessfully, increment length and try again for new sequence group
current_size++;
if (current_size > MAX_SIZE)
return;
else
Tokenize(string_array, current_size);
}
I thought it was straightforward enough, but have been getting some strange results.
Generally it appears to work, but upon further review of my output data I'm seeing some issues. Mainly, it appears to work up to a certain point...and at that point my 'curr_size' counter resets to the minimum value.
So it is called with a size of 2, then 3, then 4, then resets to 2.
My assumption was that it would run up to my predetermined max size, and then bail completely.
I tried to simplify my code as much as possible, so there are probably some simple syntax errors in transcribing. If there is any other detail that may help an eagle-eyed SO user, please let me know and I'll edit.
Thanks in advance
One bug is:
string_array[found_array_position] = new_sequence;
I don't know where this is defined, and as far as I can tell if it was defined, it is never changed.
In your if statement, when if match_found ever set to true?
Also, it appears you have an extra close brace here, but you may want the last block of code to be outside of the function:
}
}
}
It would help if you cleaned up the code, to make it easier to read. Once we get past the syntactic errors it will be easier to see what is going on, I think.
Not sure what all the issues are, but the first thing I'd do is have your "catch-all" exit block right at the beginning of your method.
public void Tokenize(Arraylist<T> string_array, int current_size)
{
if (current_size > MAX_SIZE)
return;
// Guts go here
Tokenize(string_array, ++current_size);
}
A couple things:
Your tokens are not clearly separated from your input string values. This makes it more difficult to handle, and to see what's going on.
It looks like you're writing pseudo-code:
loc_sequences_by_length is not used
found_array_position is not defined
Arraylist should be ArrayList.
etc.
Overall I agree with James' statement:
It would help if you cleaned up the
code, to make it easier to read.
-Doug