In the file below, I use the video that you can find here to make a type-writer effect on screen. I'm having a bit of an issue with it at this time.
For whatever reason, it's cutting off the last letter whenever I use it (i.e. putting in "Hello there" types out "Hello ther"). Any ideas on why this is happening?
The programming I am using is a modified version to fit my game:
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
public class TypeWriterEffect : MonoBehaviour {
public float delay = 0.1f;
public float startDelay = 0f;
public string fullText;
public bool showGameHS;
public bool showTotalScore;
public string totalGameScore;
private string currentText = "";
// Use this for initialization
void Start () {
totalGameScore = PlayerPrefs.GetString("totalGameScore"); // total score throughout the game
if (showTotalScore)
{
fullText = TimerScript.fullScore.ToString() + "."; // a "." is added to fix this issue
}
else if (showGameHS) // the local highscore
{
fullText = totalGameScore + "."; // a "." is added to fix this issue
}
StartCoroutine(ShowText());
}
IEnumerator ShowText(){
yield return new WaitForSeconds(startDelay); // this was added on as a basic start delay
for (int i = 0; i < fullText.Length; i++){
currentText = fullText.Substring(0,i);
this.GetComponent<Text>().text = currentText;
yield return new WaitForSeconds(delay);
}
}
}
Your for loop is looping from 0 to Length - 1. Then you are using this variable to tell Substring the number of characters to return. It returns 0 characters the first time. At the end, it returns all but the last character.
You can either add 1 to the length argument, or change the bounds of your for loop:
for (int length = 1; length <= fullText.Length; length++){
currentText = fullText.Substring(0, length);
this.GetComponent<Text>().text = currentText;
yield return new WaitForSeconds(delay);
}
Related
I'm creating a rhythm game on Unity and I'm using an external program which in turn gives me 0000,1000,0100,0001 as outputs, and I'm using them to create the song levels. I've come across a problem where sometimes it prints four lines before a comma (a whole beat), and sometimes eight lines before a comma (half beats), and so on. Is there any way I can make Unity detect the amount of lines before a comma hits? Code & images below; if you need any other parts tell me, thanks !
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
[CustomEditor(typeof(NoteData))]
public class NoteDataEditor : Editor
{
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
NoteData data = target as NoteData;
if (GUILayout.Button("Parse"))
{
float x = 60f / data.BPM;
float currentTime = 0;
List<NoteData.NoteInfo> notes = new List<NoteData.NoteInfo>();
string[] allNotes = data.noteSequence.Split('\n');
foreach (string note in allNotes)
{
if (note.Trim() == ",") continue;
currentTime += x;
if (note.Trim() == "0000") continue;
NoteData.NoteColor colorToAdd = NoteData.NoteColor.Red;
if (note.Trim() == "1000")
{
colorToAdd = NoteData.NoteColor.Green;
}
else if (note.Trim() == "0100")
{
colorToAdd = NoteData.NoteColor.Blue;
}
else if (note.Trim() == "0010")
{
colorToAdd = NoteData.NoteColor.Red;
//colorToAdd = (NoteData.NoteColor) Random.Range(0, 3);
}
notes.Add(new NoteData.NoteInfo(){
color = colorToAdd,
timeStamp = currentTime
});
}
data.notes = notes.ToArray();
}
}
}
https://i.stack.imgur.com/zDjSP.png
I managed to find a solution by adding a line counter which puts the counter to 0 whenever a comma is seen, and adds +1 to the counter everytime there isn't a comma until one pops up.
I'm making a simple game in C# where the player fights until they have no more health:
public void FightLoop(int currentHealth) {
bool death = false;
do{
hit = false;
if(currentHealth == 0){
death = true;
}
turn = turn + 1;
string turnRecord = turn.ToString();
string firstPlayerChoiceString = GetPlayerInput();
string secondPlayerChoiceString = GetRandomWeapon ();
string gameResult = DetermineWinner(firstPlayerChoiceString, secondPlayerChoiceString, currentHealth, turnRecord);
if(hit == true){
currentHealth = currentHealth - 1;
}
FightRecord();
AddRecord(firstPlayerChoiceString, secondPlayerChoiceString, gameResult, turnRecord, turn);
}while (death == false);
DisplayFightHistory ();
}
At the end of each fight, a summary of the whole thing should be shown (array with turns, attacks and results).
public void FightRecord(int recordSize = 100)
{
try
{
fightRecordSize = recordSize;
fightRecord = new string[fightRecordSize, 4];
}
catch (OverflowException e)
{
System.Console.WriteLine("OverflowException during FightRecord initialization: \"{0}\"\nrecordSize given was [{1}]\nSetting recordSize to 10", e.Message, recordSize);
fightRecordSize = 100;
fightRecord = new string[fightRecordSize, 4];
}
fightRecordCurrentSize = fightRecordCurrentSize++;
}
public void AddRecord(string playerOneChoice, string playerTwoChoice, string gameResult, string turnRecord, int turn)
{
// Insert the record data
fightRecord[fightRecordCurrentIndex, 0] = playerOneChoice;
fightRecord[fightRecordCurrentIndex, 1] = playerTwoChoice;
fightRecord[fightRecordCurrentIndex, 2] = gameResult;
fightRecord[fightRecordCurrentIndex, 3] = turnRecord;
// Increment the fight index counter and current history size
fightRecordCurrentIndex = (fightRecordCurrentIndex + 1) % fightRecordSize;
if (fightRecordCurrentSize < fightRecordSize)
{
fightRecordCurrentSize++;
}
}
public void DisplayFightHistory () {
System.Console.WriteLine ("\nPodsumowanie:");
for (int i = 0; i < fightRecordCurrentSize; i++){
System.Console.WriteLine ("Tura #{0}:\t{1}\t-\t{2},\t{3} {4}",
fightRecord[i,3], fightRecord[i,0], fightRecord[i,1], fightRecord[i,2], i);
}
}
However, every time I get only the last value, like this:
Summary:
Turn #: - ,
Turn #: - ,
Turn #: - ,
Turn #: - ,
Turn #: - ,
Turn #: - ,
Turn #8: Sword - Spear, You lose
By following the whole process of creating the array, it seems like the values are being put in it correctly at first, but with each new turn, only the last value remains.
Does anyone have any idea why it's happening?
Use the loop variable as the index.
Change
fightRecord[displayRecordIndex,3], fightRecord[displayRecordIndex,0], fightRecord[displayRecordIndex,1], fightRecord[displayRecordIndex,2], displayRecordIndex);
To
fightRecord[i,3], fightRecord[i,0], fightRecord[i,1], fightRecord[i,2], i);
I started my project where i wanted to read out PDF documents.
The problem here is in the usual ExtractionStrategy it outputs as:
Input (example.pdf)
Looks like:
Text text2 text3 text4
iText7 outputs this as:
Text text2 text3 text4
The problem here is that i need to know there is a big part between them.
In the official GitHub i found the Extraction class i used.
https://github.com/itext/itext7-dotnet/blob/develop/itext/itext.kernel/itext/kernel/pdf/canvas/parser/listener/LocationTextExtractionStrategy.cs
I know i can override a method like this.
public class ExampleName: LocationTextExtractionStrategy
{
public virtual String GetResultantText(TextRenderInfo info)
{
The problem there is
The class doesn't contain any of the values, and I'm not quite sure how to intercept these.
Basicly in the GetResultantText void i want to override this part:
if (IsChunkAtWordBoundary(chunk, lastChunk) && !StartsWithSpace(chunk.text) && !EndsWithSpace(lastChunk.text
)) {
sb.Append(' ');
}
To this (where the ' ' became '¥')
if (IsChunkAtWordBoundary(chunk, lastChunk) && !StartsWithSpace(chunk.text) && !EndsWithSpace(lastChunk.text
)) {
sb.Append('¥');
}
I just want that character to be another character so i know if its space or white space.
Below is my old method i used which did exactly that but that was in iTextSharp and now i use iText7.
class FixedStrategy : ITextExtractionStrategy
{
private Vector lastStart;
private Vector lastEnd;
//Store each line individually. A SortedDictionary will automatically shuffle things around based on the key
private SortedDictionary<int, StringBuilder> results = new SortedDictionary<int, StringBuilder>();
//Constructor and some methods that aren't used
public virtual void BeginTextBlock() {
}
public virtual void EndTextBlock() { }
public virtual void RenderImage(ImageRenderInfo renderInfo) { }
//Convert our lines into a giant block of text
public virtual String GetResultantText()
{
//Buffer
StringBuilder buf = new StringBuilder();
//Loop through each line (which is already sorted top to bottom)
foreach (var s in results)
{
//Append to the buffer
buf.AppendLine(s.Value.ToString());
}
return buf.ToString();
}
public virtual void RenderText(TextRenderInfo renderInfo)
{
bool firstRender = results.Count == 0;
LineSegment segment = renderInfo.GetBaseline();
Vector start = segment.GetStartPoint();
Vector end = segment.GetEndPoint();
//Use the Y value of the bottom left corner of the text for the key
int currentLineKey = (int)start[1];
if (!firstRender)
{
Vector x0 = start;
Vector x1 = lastStart;
Vector x2 = lastEnd;
float dist = (x2.Subtract(x1)).Cross((x1.Subtract(x0))).LengthSquared / x2.Subtract(x1).LengthSquared;
float sameLineThreshold = 1f;
//If we've detected that we're still on the same
if (dist <= sameLineThreshold)
{
//Use the previous Y coordinate
currentLineKey = (int)lastStart[1];
}
}
//Hack: PDFs start with zero at the bottom so our keys will be upside down. Using negative keys cheats this.
currentLineKey = currentLineKey * -1;
//If this line hasn't been used before add a new line to our collection
if (!results.ContainsKey(currentLineKey))
{
results.Add(currentLineKey, new StringBuilder());
}
//Insert a space between blocks of text if it appears there should be
if (!firstRender && //First pass never needs a leading space
results[currentLineKey].Length != 0 && //Don't append a space to the begining of a line
//!results[currentLineKey].ToString().EndsWith("¥") && //Don't append if the current buffer ends in a space already
renderInfo.GetText().Length > 0 && //Don't append if the new next is empty
!renderInfo.GetText().StartsWith(" "))
{ //Don't append if the new text starts with a space
//Calculate the distance between the two blocks
float spacing = lastEnd.Subtract(start).Length;
//If it "looks" like it should be a space
if (spacing > renderInfo.GetSingleSpaceWidth() / 2f)
{
//Add a space
results[currentLineKey].Append("¥");
}
/*
else if (spacing > renderInfo.GetSingleSpaceWidth() / 10f)
{
//Add a space
results[currentLineKey].Append("¶¶");
}
*/
}
//Add the text to the line in our collection
results[currentLineKey].Append(renderInfo.GetText());
lastStart = start;
lastEnd = end;
}
}
I am a beginner C# programmer in general, attempting to create a Japanese falling word typing game in Unity where the word / letter shown on screen will be in Hiragana, but the requested input is in Romaji (alphabetical) letters.
Currently, I am stuck in a rut attempting to figure out how to generate a random number once per words.Add (word) is executed. For instance, when a Word object is created, generate a random number. Then, that random number is used on classes that depend on it, such as getWord_Hiragana, and getWord_Romaji. Most typing games existing on the internet are shown only a single object (English), so I couldn't find what I need.
// WordManager.cs
public class WordManager : MonoBehaviour {
public List<Word> words;
public WordSpawner wordSpawner;
public void AddWord ()
{
Word word = new Word (WordGenerator.GetWord_Romaji(), wordSpawner.SpawnWord());
words.Add (word);
}
}
// WordGenerator.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class WordGenerator : MonoBehaviour {
public static string[] wordList_Hiragana = { "あ", "い", "う", "え", "お" };
public static string[] wordList_Katakana = { "ア", "イ", "ウ", "エ", "オ" };
public static string[] wordList_Romaji = { "a", "i", "u", "e", "o" };
public static int GetIndex ()
{
int index = Random.Range (0, wordList_Romaji.Length - 1); // Get Random number which has the same index for Hiragana, Katakana, and Romaji arrays
Debug.Log ("Index #" + index + ": " + wordList_Hiragana[index] + " " + wordList_Katakana[index] + " " + wordList_Romaji[index]); // Debug Log
return index; // Returns the result of the random as a guidance.
}
public static string GetWord_Hiragana () // A function to return the result of GetIndex as Hiragana word to be used on WordManager and in turn, displays that Hiragana.
{
int index = GetIndex ();
string getWord_Hiragana = wordList_Hiragana [index];
return getWord_Hiragana;
}
public static string GetWord_Romaji ()
{
int index = GetIndex ();
string getWord_Romaji = wordList_Romaji [index];
return getWord_Romaji;
}
}
// Word.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[System.Serializable]
public class Word {
public string word;
private int typeIndex; // Checks for current letter
WordDisplay display;
public Word (string _word, WordDisplay _display) // Displays the word as Hiragana / Katakana
{
word = _word;
display = _display;
display.SetWord (word);
}
public char GetNextLetter ()
{
return word[typeIndex]; // Gets the next letter of the Romaji array
}
public void TypeLetter ()
{
typeIndex++;
}
public bool WordTyped ()
{
bool wordTyped = (typeIndex >= word.Length); // Checks if the whole word has been typed
if (wordTyped)
{
display.RemoveWord (); // Remove the whole object on screen
}
return wordTyped;
}
}
The expected result is for GetIndex to roll a random number once per Word object. When getWord_Romaji is executed, it grabs the return value of GetIndex. The same goes when getWord_Hiragana is executed. Right now, GetIndex is executed twice and generates a random number twice in each Word object, causing the word that appeared on Debug to be different compared to what appears on the game screen. How do I make this work?
If the code above is not enough to solve the problem, the project is posted here.
I would change your Word class constructor to the below:
public class Word
{
public string word;
private int typeIndex; // Checks for current letter
private int wordIndex; // index of word used from WordGenerator
WordDisplay display;
public Word(WordDisplay _display) // Displays the word as Hiragana / Katakana
{
wordIndex = WordGenerator.GetIndex(); // now it will only be called ONCE per instance
word = _WordGenerator.wordList_Romaji[wordIndex];
// you can get the equivalent? letters similarly...with something like:
word = _WordGenerator.wordList_Hiragana[wordIndex];
display = _display;
display.SetWord(word);
}
// ... other existing code ...
}
Note that I've added another field to store the index selected for that particular word instance.
Without seeing the implementation of Word I'ld say from your example it actually does call GetIndex() only exactly once ... namely for GetWord_Romaji() ...
Anyway a quick simple fix could be
private static int randomizedIndex;
public static void NewRandomIndex()
{
randomizedIndex = Random.Range (0, wordList_Romaji.Length - 1);
Debug.Log("Index #" + randomizedIndex + ": " + wordList_Hiragana[randomizedIndex] + " " + wordList_Katakana[randomizedIndex] + " " + wordList_Romaji[randomizedIndex]);
}
public static string GetWord_Hiragana()
{
return wordList_Hiragana[randomizedIndex];
}
public static string GetWord_Romaji ()
{
return wordList_Romaji[randomizedIndex];
}
and call it
public void AddWord ()
{
WordGenerator.NewRandomIndex();
Word word = new Word(WordGenerator.GetWord_Romaji(), wordSpawner.SpawnWord());
words.Add (word);
}
If you rather want to be able to later "translate" the words between those arrays then you should in Word instead of retrieved string rather store the used randomizedIndex and get the string value for it "on the fly".
I am trying to make a rudimentary, very simple animation function using a simple manager. The issue is that, as of right now, when the user clicks "Go" on the main form, it will display the first image then jump to the last image.
I have also tried to insert a delay which delays the thing going from 1 to the last frame but it just jumps to the last frame. I'm utilizing several PNG files in order to make this thing work instead of trying to use a "sprite sheet" which would be a larger pain in the arse I would imageine...
Here's the code for the AnimationManager class...
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Drawing;
namespace Animation
{
public class AnimationManager
{
public PictureBox AnimationBox;
public float widthX, heightY;
public string ImageDirectory, ImageName;
public int frame, maxFrames;
public int speed; // in milliseconds
public bool isLooping;
public AnimationManager()
{
AnimationBox = new PictureBox();
widthX = 0;
heightY = 0;
ImageName = null;
frame = 1;
speed = 0;
maxFrames = 0;
isLooping = false;
}
public async void CycleThrough(string ImageDirectory, int FPMS, int maxFrames, bool isLooping)
{
for (int i = frame; i < maxFrames + 1; i++)
{
ImageName = ImageDirectory + #"\" + frame.ToString() + ".png";
AnimationBox.Image = Image.FromFile(ImageName);
AnimationBox.Refresh();
await Task.Delay(FPMS);
switch (isLooping)
{
case false:
default:
{
frame = maxFrames;
break;
}
case true:
{
frame = 1;
break;
}
}
}
}
}
}
That's as far as I was able to get. If anyone has any insight or can point me in the right direction to get this to work, that would be awesome.
You had some problems in your code so i write it again and i comment out where you had problem
public async void CycleThrough(string ImageDirectory, int FPS, int maxFrames, bool isLooping)
{
for (int i = frame; i < maxFrames + 1; i++) // frame and maxFrames must not change. counter is i
{
ImageName = ImageDirectory + #"\" + i.ToString() + ".png"; // Get the i-th png using counter.
AnimationBox.Image = Image.FromFile(ImageName);
AnimationBox.Refresh();
await Task.Delay(TimeSpan.FromSeconds(1/FPS)); // delay in seconds.
if(isLooping && i == maxframes) // if must Loop and counter is in last iteration then start over
{
i = frame - 1; // set the counter to the first frame
}
}
}
While waiting for the answer, I went ahead and attempted to do it myself... :) Thank you for your answers and comments...
This was my final solution...
private async void CycleThrough(string ImageDirectory, int FPMS, int maxFrames, bool isLooping)
{
this.speed = FPMS;
for (int i = frame; i < maxFrames + 1; i++)
{
ImageName = ImageDirectory + #"\" + i.ToString() + ".png";
Console.WriteLine(frame.ToString());
AnimationBox.Refresh();
AnimationBox.Image = Image.FromFile(ImageName);
await Task.Delay(FPMS);
if (isLooping)
{
if (i >= maxFrames) i = 1;
}
}
}
I made an elementary mistake and didn't use "i" that was setup. Instead I manipulated a bunch of other things that had no bearing on the loop's intent itself.
I agree with the person above - I didn't use a switch in my solution. It didn't make sense since I only have one conditional that would reset the loop (manipulating i back to 1) if isLooping was true.
This works beautifully so I'll offer this solution to anyone who wants a rudimentary animator for their code :)
Thanks all for your answers and time.