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.
Related
I'm new to threading, and the examples I see online construct threads by passing a void function. However, I want to speed up computation time by returning integer values in multiple threads. Here's the relevant part of my code:
public int CalculateLineWins5(List<List<Symbol>> _screen)
{
int wins = 0;
foreach (List<int> line in lines)
{
for (int i = 0; i < reelsNumber; i++)
{
lineSymbols[i] = _screen[i][line[i]];
}
//evaluate the win per line
wins += CalculateWinsInLine5(lineSymbols);
}
return wins;
}
public int CalculateWinsInLine5(List<Symbol> _symbolsOnLine)
{
// this function assumes there could be wilds on reel 1 (and there are only 5 reels)
int wildMultiplier = 1;
int win = 0;
Symbol symbol = _symbolsOnLine[0];
if (symbol.IsPayTableSymbol)
{
int oak = 0;
int oakWild = 0;
if (symbol.IsWild)
{
symbol = _symbolsOnLine[1];
if (symbol.IsWild)
{
oakWild++;
symbol = _symbolsOnLine[2];
if (symbol.IsWild)
{
oakWild++;
symbol = _symbolsOnLine[3];
if (symbol.IsWild)
{
oakWild++;
symbol = _symbolsOnLine[4];
if (symbol.IsWild)
{
oakWild++;
}
}
}
}
}
if (symbol.IsPayTableSymbol)
{
if (_symbolsOnLine[1].Index == symbol.Index || _symbolsOnLine[1].IsWild)
{
oak++;
if (_symbolsOnLine[1].IsWild) wildMultiplier *= _symbolsOnLine[1].Multiplier;
if (_symbolsOnLine[2].Index == symbol.Index || _symbolsOnLine[2].IsWild)
{
oak++;
if (_symbolsOnLine[2].IsWild) wildMultiplier *= _symbolsOnLine[2].Multiplier;
if (_symbolsOnLine[3].Index == symbol.Index || _symbolsOnLine[3].IsWild)
{
oak++;
if (_symbolsOnLine[3].IsWild) wildMultiplier *= _symbolsOnLine[3].Multiplier;
if (_symbolsOnLine[4].Index == symbol.Index || _symbolsOnLine[4].IsWild)
{
oak++;
if (_symbolsOnLine[4].IsWild) wildMultiplier *= _symbolsOnLine[4].Multiplier;
}
}
}
}
win += Mathf.Max(payTable[symbol.Index][oak], payTable[0][oakWild]) * wildMultiplier;
}
else
{
win += payTable[0][oakWild] * wildMultiplier;
}
}
return win;
}
Essentially, I want to run the computations within the foreach loop above in parallel. Typically that's going to be 40 to 50 iterations of the CalculateWinsInLine5 method, so I think I'd have to create 40 to 50 threads that are computed in parallel and each return an integer value. I just don't know how. Any help or feedback on how to approach this is welcome.
Edit: My project runs random simulations of slot machine payouts. In each simulation the program goes through a large number of iterations where it generates a random set of reel stops which generate a matrix of symbols (the screen). In each iteration, the screen pay is evaluated using the CalculateLineWins5 method.
This is actually a Unity project, and interestingly enough, I already tried using the Burst compiler which makes the simulations 1.5x slower, just like Parallel.ForEach.
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);
}
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 have a quiz game and at the end of the game I want to display a leaderboard of the top 10 people with their names and score for example: Ioue 500.
Right now I'm adding score every time user scores, and I'm checking the high score. However I'm not sure how to do the leaderboard. can someone guide me?
This script is where the user registers, it's a different scene.
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
using System.Linq;
using System.Text.RegularExpressions;
using System.IO;
using System;
public class Registration : MonoBehaviour
{
public Text name;
public static Registration _instace;
private void Awake()
{
_instace = this;
}
/// <summary>
/// Performs data validation and registers user
/// </summary>
public void Register()
{
string data;
string currentUser = string.Empty;
string userName;
data = GetData(name);
if (data == null || string.IsNullOrEmpty(data))
{
Recolor(name, errorColor);
return;
}
else
{
userName = data;
currentUser += data + ";";
}
string previousDirectory = Directory.GetCurrentDirectory();
Directory.SetCurrentDirectory(Application.persistentDataPath);
if (File.Exists("Users.csv"))
{
string[] users = File.ReadAllLines("Users.csv");
foreach (string user in users)
{
string[] parts = user.Split(";".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
}
}
File.AppendAllText("Users.csv", currentUser + Environment.NewLine);
Directory.SetCurrentDirectory(previousDirectory);
SceneManager.LoadScene(nextScene);
}
// Gets input data from control group provided
string GetData(Graphic group)
{
InputField inputField = group.GetComponentInChildren<InputField>();
if (inputField != null)
{
return inputField.text;
}
return null;
}
// Gets input data from control group provided
string GetDataD(Graphic group)
{
Dropdown inputField = group.GetComponentInChildren<Dropdown>();
if (inputField != null)
{
return inputField.captionText.text;
}
return null;
}
}
Where i add score is when player get's the right answer
if (isCorrect)
{
theAnswerIsCorrect = true;
playerScore += 20;
scoreDisplayText.text = "Score: " + playerScore.ToString();
}
This is where I compare the old and new score:
public void submitNewPlayerScore(int newScore)
{
if (newScore > playerProgress.highestScore)
{
playerProgress.highestScore = newScore;
SavePlayerProgress();
}
}
Now what I need is I will have many users playing, I just want to display the top 10 users with their names and high scores. Thank you!
Okay first thing : Why do you even need registration when it's offline?
You can omit the registration and just ask for a player name after quiz ends ( if you want to make offline highscores ).
Since you already have kind of highestScore you can just order it using Linq. I assume your code has access to all players ( because it's in csv file ) so I can recommend doing something like this :
List<Player> playerList = new List<Player>();
foreach(string user in File.ReadAllLines("Users.csv"))
{
playersList.Add(Player.Parse(user)); // If you have such method...
}
Now since you have all Players in one list you can use Linq to order it and take only x to display :
int x = 10; // how many records to take
IEnumerable<Player> highestX = playerList.OrderBy(player => player.highestScore).Take(x);
highestX is now holding 10 records with the highest score. You can just iterate through them and display details like highestX.ElementAt(0).Name or something like this.
I need to create a class (or classes, if need be) that loads levels randomly every time the user clicks on the "Next Button" and once all levels have been loaded, we stop loading and close the application. I got the code set up but I am still not getting the result I am looking for which is:
User clicks on a button.
Load a random level
That levels gets stored in an array list
Once the user is done with that level he/she presses the "Load Next Level" button
Load the next random level
But first, we check if the random level is not the same as before.
If it's not, then we repeat steps 2-5, else we go to step 8
If the levels have all been visited then we quit the application
The problem I am having is that my game loads the same level every time I hit play and it doesn't go to the next scene after I am done with the current one. This is what I have so far:
using UnityEngine;
using System.Collections;
[ExecuteInEditMode]
public class SceneManager : MonoBehaviour
{
public static bool userClickedNextButton; //This flag is raised by the other classes that have the GUI button logic
protected const int MAX = 2;
private ArrayList scenesWereAlreadyLoaded = new ArrayList();
void Update()
{
if (userClickedNextButton)
{
//by default the game starts at 0 so I want to be able to
//randomly call the next two scenes in my game. There will
//be more levels but for now I am just testing two
int sceneToLoad = Random.Range(1, 2);
if (!scenesWereAlreadyLoaded.Contains(sceneToLoad))
{
scenesWereAlreadyLoaded.Add(sceneToLoad);
Application.LoadLevel(sceneToLoad);
}
userClickedNextButton = false;
}
if (scenesWereAlreadyLoaded.Count > MAX) { Application.Quit(); }
}
}
You create a list with your level numbers, then remove the currently loaded level. You repeat that until the list is empty.
Also don't use ArrayList, it's very old and deprecated type, from the ages before .NET/Mono had Generic support. Better use a Generic List<T>, which is type safe and faster than ArrayList.
using UnityEngine;
using System.Collections;
using System.Linq;
using System.Collections;
using System.Collections.Generic;
[ExecuteInEditMode]
public class SceneManager : MonoBehaviour
{
// not best idea to have it static
public static bool userClickedNextButton;
protected const int MAX = 50;
private List<int> scenes = new List<int>();
void Start() {
// Initialize the list with levels
scenes = new List<int>(Enumerable.Range(1,MAX)); // This creates a list with values from 1 to 50
}
void Update()
{
if (userClickedNextButton)
{
if(scenes.Count == 0) {
// No scenes left, quit
Application.Quit();
}
// Get a random index from the list of remaining level
int randomIndex = Random.Range(0, scenes.Count);
int level = scenes[randomIndex];
scenes.RemoveAt(randomIndex); // Removes the level from the list
Application.LoadLevel(level);
userClickedNextButton = false;
}
}
}
As per the Unity3D documentation (http://docs.unity3d.com/Documentation/ScriptReference/Random.Range.html), range returns an integer between min (included) and max (excluded), so, in your case, Random.Range(1,2) will always returns 1.
Try with this
int = sceneToLoad = Random.Range(1,3)
There is an easier way to do so. You can prepare an array with random unique numbers. When you want to load a new level just increment the index of the array.
Here's a code that can help:(Attach this script to an empty gameobject in your first scene)
using UnityEngine;
using System.Collections;
public class MoveToRandomScene : MonoBehaviour {
public Texture nextButtonTexture; // set this in the inspector
public static int[] arrScenes;
public static int index;
void Start () {
index = 0;
arrScenes = GenerateUniqueRandom (10, 0, 10); //assuming you have 10 levels starting from 0.
}
void OnGUI {
// Load the first element of the array
if (GUI.Button(new Rect (0,0,Screen.width/4,Screen.height/4),nextButtonTexture))
{
int level = arrScenes [0] ;
Application.LoadLevel (level);
}
}
//Generate unique numbers (levels) in an array
public int[] GenerateUniqueRandom(int amount, int min, int max)
{
int[] arr = new int[amount];
for (int i = 0; i < amount; i++)
{
bool done = false;
while (!done)
{
int num = Random.Range(min, max);
int j = 0;
for (j = 0; j < i; j++)
{
if (num == arr[j])
{
break;
}
}
if (j == i)
{
arr[i] = num;
done = true;
}
}
}
return arr;
}
}
For the other scenes you just need to create this script and attach it to an empty game object whenever you want to load a new random scene:
void OnGUI {
if (GUI.Button(new Rect (0,0,Screen.width/4,Screen.height/4),nextButtonTexture))
{
if (MoveToRandomScene.index == 9) {
// Load again your first level
Application.LoadLevel(0);
}
// else you continue loading random levels
else {
MoveToRandomScene.index++;
int level = MoveToRandomScene.arrScenes[MoveToRandomScene.index];
Application.LoadLevel(level);
}
}
}