Optimizing code to easily edit - c#

In my game there is a text that outputs code based on if certain Upgrades are bought I started out coding it like this
if (UpgradeManager.Instance.HasUpgrade("basicFunction") && !UpgradeManager.Instance.HasUpgrade("basicCounter"))
{
codeString = "protected void GainUnits(){ units += " + gains + ";}";
}
else if (UpgradeManager.Instance.HasUpgrade("basicCounter") && UpgradeManager.Instance.HasUpgrade("basicFunction"))
{
codeString = "private timer = 20; private void Update(){ if(timer > 0){GainUnits();}" + "protected void GainUnits(){ units += " + gains + ";}";
}
else if (UpgradeManager.Instance.HasUpgrade("basicCounter") && !UpgradeManager.Instance.HasUpgrade("basicFunction"))
{
codeString = "private timer = 20; private void Update(){ if(timer > 0){GainUnits();}" + "units += " + gains;
}
else
{
codeString = "units += " + gains;
}
as i add more and more upgrades i realize that i have to add more and more rules on top of whats already there creating an exponential amount of work as more is added to the game
So my question is how can i optimize this to lighten the workload?

You can try that approach that can be more maintainable:
// arrange a list of bool predicates and corresponding handlers
var policies = new KeyValuePair<Func<bool>, Action>[]
{
new KeyValuePair<Func<bool>, Action>(
() => {return UpgradeManager.Instance.HasUpgrade("basicFunction") && !UpgradeManager.Instance.HasUpgrade("basicCounter");},
() => {codeString = "protected void GainUnits(){ units += " + gains + ";}";}),
new KeyValuePair<Func<bool>, Action>(
() => {return UpgradeManager.Instance.HasUpgrade("basicCounter") && UpgradeManager.Instance.HasUpgrade("basicFunction");},
() => {codeString = "private timer = 20; private void Update(){ if(timer > 0){GainUnits();}" + "protected void GainUnits(){ units += " + gains + ";}";}),
new KeyValuePair<Func<bool>, Action>(
() => {return UpgradeManager.Instance.HasUpgrade("basicCounter") && !UpgradeManager.Instance.HasUpgrade("basicFunction");},
() => {codeString = "private timer = 20; private void Update(){ if(timer > 0){GainUnits();}" + "units += " + gains;}),
new KeyValuePair<Func<bool>, Action>(
() => {return true;}, // last one should be always true
() => {codeString = "units += " + gains;}),
};
// now let iterate over the policies
foreach(var policy in policies)
{
if (policy.Key()) // evaluate predicates one-by-one
{
policy.Value();
break; // if predicated matched, do the action and break out of the loop
}
}
Depending on the logic necessary, you can employ different containers (that can give you better performance or flexibility, etc.)

else if (UpgradeManager.Instance.HasUpgrade("basicCounter") && !UpgradeManager.Instance.HasUpgrade("basicFunction"))
{
codeString = "private timer = 20; private void Update(){ if(timer > 0){GainUnits();}" + "units += " + gains;
}
Doesn't look like valid code there. I assume you want to increment it directly in the if-block, like this?
"if(timer > 0){ units += " + gains + "; }"
You can start by isolating what part of code that each flag modifies:
basicCounter: adds timer and implements Update() that increments units
basicFunction: encapsulates how units is incremented
With that in mind, it is easier to deconstruct the code:
increasing units:
GainUnits(); // if basicFunction
units += __GAINS__; // if !basicFunction
counter:
private timer = 20;
private void Update() {
if(timer > 0) {
/* use the code from <increasing units> */
}
}
core implementation:
/* use code from <counter> */ // if basicCounter
/* use code from <increasing units> */ // if !basicCounter
And, putting it back together:
string GenerateCode()
{
return string.Join("\n", GetImplementationCode());
}
IEnumerable<string> GetImplementationCode()
{
return UpgradeManager.Instance.HasUpgrade("basicCounter")
? GetCounterCode()
: GetIncreaseUnitsCode();
}
IEnumerable<string> GetCounterCode()
{
yield return "private timer = 20;";
yield return "private void Update() {";
yield return "if(timer > 0) {";
foreach (var loc in GetIncreaseUnitsCode())
{
yield return loc;
}
yield return "}";
yield return "}";
}
IEnumerable<string> GetIncreaseUnitsCode()
{
if (UpgradeManager.Instance.HasUpgrade("basicFunction"))
{
yield return "GainUnits();";
}
else
{
var gains = /* insert logics here */;
yield return $"units += {gains};";
}
}

Related

How to have an Coroutine Act like an Update

I have been told I should not be using Updates when I don't need to use them. I am not sure when to not use them, but I am trying to figure it out.
I want to have an event, "Wave", run for 20 seconds and then switch to a random "Wave" and run for another 20 seconds and keep repeating this cycle.
Game Manager:
void Start()
{
StartFireWaves();
}
public void StartFireWaves()
{
StartCoroutine(wm.SelectWave());
}
Wave Manager:
public IEnumerator SelectWave()
{
float waitFor;
float diff = 0;
diff = nextWaveAT - Time.time;
if (diff <= 0f)
{
SpawnFlames();
waitFor = Random.Range(15, 20);
}
nextWaveAT = nextWaveAT + waitFor;
yield return new WaitForSeconds(waitFor);
}
SpawnFlames:
void SpawnFlames()
{
float val = Random.value;
if (val < .25f)
{
Wave1();
}
if (val >= .25f && val < .5f)
{
Wave2();
}
if (val >= .5f && val < .75f)
{
Wave3();
}
else
{
Wave4();
}
}
I am assuming this is how it should work, but it doesn't seem to keep "updating" or running. It just runs once and then nothing happens.
Use Coroutine with multiple nested while loops to do this. The first while loop is to make it run forever until stopRunningWaves() function is called. The second while loop is used to run the wave for x amount of time then jumps back to the first while loop.
bool keepRunningWaves = false;
IEnumerator startingRunningWaves(float second = 20)
{
if (keepRunningWaves)
{
yield break;
}
keepRunningWaves = true;
while (keepRunningWaves)
{
float timer = 0;
int randWave = Random.Range(1, 5);
while (timer < second)
{
if (!keepRunningWaves)
{
yield break;
}
if (randWave == 1)
{
Debug.Log("Running Wave 1");
Wave1();
}
else if (randWave == 2)
{
Debug.Log("Running Wave 2");
Wave2();
}
else if (randWave == 3)
{
Debug.Log("Running Wave 3");
Wave3();
}
else if (randWave == 4)
{
Debug.Log("Running Wave 4");
Wave4();
}
timer += Time.deltaTime;
yield return null;
}
//Reset Timer for next run
timer = 0;
yield return null;
}
keepRunningWaves = false;
}
void stopRunningWaves()
{
keepRunningWaves = false;
}
To test it, use 3 seconds to do it so that you will save your time:
StartCoroutine(startingRunningWaves(3));

Waiting for web call to finish before continuing code in Unity3d

I've read many articles but I don't seem to get it. I have this code working now because I hardcode a 3 second delay so there is enough time for the web call to finish so when the score is displayed there is data. But what I really want is to have the web call finish and THEN display the score. Help ?
IEnumerator Start()
{
client = new MobileServiceClient(_appUrl, _appKey);
table = client.GetTable<Highscore>("Highscores");
yield return StartCoroutine(ReadItems());
DisplayScores();
}
void Update()
{
}
public void btn_GoBack()
{
Application.LoadLevel("StartScene");
}
private void OnReadItemsCompleted(IRestResponse<List<Highscore>> response)
{
if (response.StatusCode == HttpStatusCode.OK)
{
Debug.Log("OnReadItemsCompleted data: " + response.Content);
List<Highscore> items = response.Data;
Debug.Log("Read items count: " + items.Count);
Scores = items;
}
else
{
Debug.Log("Error Status:" + response.StatusCode + " Uri: " + response.ResponseUri);
}
}
private IEnumerator ReadItems()
{
table.Read<Highscore>(OnReadItemsCompleted);
yield return new WaitForSeconds(3);
}
private void DisplayScores()
{
txtHighScores.text = "";
int numberOfScores = Math.Min(Scores.Count, 5);
for (int i = 0; i < numberOfScores; i++)
{
string name = Scores[i].username.ToString();
string score = Scores[i].score.ToString();
txtHighScores.text += (i + 1).ToString() + ". " +
" - " + name + "\r\n" +
score.ToString().PadLeft(4, '0');
}
}
It is important to pass a parameter with Startcorutine method,
Otherwise it gives error.
So try my fix in your code.
IEnumerator Start()
{
client = new MobileServiceClient(_appUrl, _appKey);
table = client.GetTable<Highscore>("Highscores");
yield return StartCoroutine(ReadItems(2.0f)); //delay
DisplayScores();
}
IEnumerator ReadItems(float delay)
{
yield return new WaitForSeconds(delay);
table.Read<Highscore>(OnReadItemsCompleted);
}
Remove the method call for "DisplayScores()" from Start()
And insert DisplayScores() as the last line of method callback OnReadItemsCompleted
And then you should remove the line yield return new WaitForSeconds(3);

Find Next function in Windows Store App

I'm trying to make 'find/find next' function in my windows store application.
Word which I want to search and select is in textBox named 'tboxFind'.
Textbox 'EditorWindow' contains all my text.
My function works good only if there is one line of text in 'editorWindow'.
Otherwise, selection is moved forwards by number of new lines.
How to fix it?
Is there any simple way to create find next function?
private void btnFind_Click(object sender, Windows.UI.Xaml.RoutedEventArgs e)
{
if ((tmpPos) == pos && tmpWord == tboxFind.Text && !String.IsNullOrEmpty(editorWindow.Text))
{
string tmpString = editorWindow.Text.Substring(pos + tboxFind.Text.Length);
tmpPos = tmpString.ToLower().IndexOf(tboxFind.Text.ToLower());
if (tmpPos != -1)
{
editorWindow.Focus(Windows.UI.Xaml.FocusState.Keyboard);
editorWindow.SelectionStart = pos + tmpPos + tboxFind.Text.Length;
editorWindow.SelectionLength = tboxFind.Text.Length;
pos = pos + tmpPos + tboxFind.Text.Length;
}
}
tmpWord = tboxFind.Text;
tmpPos = pos;
}
// EDIT:
I found a different way to create that function. Here is my code:
private void btnFind_Click(object sender, Windows.UI.Xaml.RoutedEventArgs e)
{
numOfNewLines = 0;
pos = (tmpWord == tboxFind.Text) ? editorWindow.Text.ToLower().IndexOf(tboxFind.Text, pos + tboxFind.Text.Length)
: editorWindow.Text.ToLower().IndexOf(tboxFind.Text);
if (pos != -1)
{
foreach (char s in editorWindow.Text.Substring(0, pos))
{
if (s == '\n')
{
numOfNewLines++;
}
}
pos -= numOfNewLines;
editorWindow.Focus(Windows.UI.Xaml.FocusState.Keyboard);
//tmpPos = editorWindow.Text.ToLower().IndexOf(tboxFind.Text);
editorWindow.Select(pos, tboxFind.Text.Length);
pos += numOfNewLines;
}
tmpWord = tboxFind.Text;
}
I'm not 100% sure what's wrong with your code because I can't fully replicate it, but consider the following SSCCE in a basic Windows application:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
protected override void OnShown(EventArgs e)
{
base.OnShown(e);
foreach (var i in FindIndicies("text"))
{
this.textBox1.SelectionStart = i;
this.textBox1.SelectionLength = "text".Length;
var result = MessageBox.Show(
"Move to the next index?",
"Next?",
MessageBoxButtons.YesNo);
if (result == System.Windows.Forms.DialogResult.No) { break; }
}
}
private List<int> FindIndicies(string textToFind)
{
var indicies = new List<int>();
var offset = 0;
var i = 0;
while ((i = this.textBox1.Text.IndexOf(
textToFind,
offset,
StringComparison.CurrentCultureIgnoreCase)) > 0)
{
indicies.Add(i);
offset = (i + textToFind.Length);
}
return indicies;
}
}
given that textBox1 has a set text value of:
Here is a set of text
and I'm going to find the word text
Even when there are multiple lines of text.
It finds each index properly, and selects them properly.
I would consider finding all indicies up front with the method I wrote and then simply iterate through them on demand. In my case I'm using a message box to determine when I want to move to the next index, but you'll use something different.

How do you run a synchronous timer in C#?

I am writing an app which uses a timer to display a countdown on screen to when some event happens. I want to reuse the timer, as it would be handy for a few things in the app, so I specify the words I want to wrap round the timer. For example, the following function call:
CountdownTimer(90, "You have ", " until the computer reboots");
would show:
You have 1 minute 30 seconds until the computer reboots
and then count down.
I am using the following code:
private void CountdownTimer(int Duration, string Prefix, string Suffix)
{
Countdown = new DispatcherTimer();
Countdown.Tick += new EventHandler(Countdown_Tick);
Countdown.Interval = new TimeSpan(0, 0, 1);
CountdownTime = Duration;
CountdownPrefix = Prefix;
CountdownSuffix = Suffix;
Countdown.Start();
}
private void Countdown_Tick(object sender, EventArgs e)
{
CountdownTime--;
if (CountdownTime > 0)
{
int seconds = CountdownTime % 60;
int minutes = CountdownTime / 60;
Timer.Content = CountdownPrefix;
if (minutes != 0)
{
Timer.Content = Timer.Content + minutes.ToString() + #" minute";
if (minutes != 1) { Timer.Content = Timer.Content + #"s"; }
Timer.Content = Timer.Content + " ";
}
if (seconds != 0)
{
Timer.Content = Timer.Content + seconds.ToString() + #" second";
if (seconds != 1) { Timer.Content = Timer.Content + #"s"; }
}
Timer.Content = Timer.Content + CountdownSuffix;
}
else
{
Countdown.Stop();
}
}
How do I make this run synchronously? For example I would want the following to wait 90 seconds and then reboot:
CountdownTimer(90, "You have ", " until the computer reboots");
ExitWindowsEx(2,0)
Whereas it calls the reboot immediately at present.
Any pointers would be most welcome!
Thanks,
Ben
Personally, I'd recommend having a callback happen at the end of your CountdownTimer - perhaps taking an Action as an argument, such that when it completed it'd be called.
private Action onCompleted;
private void CountdownTimer(int Duration, string Prefix, string Suffix, Action callback)
{
Countdown = new DispatcherTimer();
Countdown.Tick += new EventHandler(Countdown_Tick);
Countdown.Interval = new TimeSpan(0, 0, 1);
CountdownTime = Duration;
CountdownPrefix = Prefix;
CountdownSuffix = Suffix;
Countdown.Start();
this.onCompleted = callback;
}
...
else
{
Countdown.Stop();
Action temp = this.onCompleted; // thread-safe test for null delegates
if (temp != null)
{
temp();
}
}
Then you could just change your usage to:
CountdownTimer(90, "You have ", " until the computer reboots",
() => ExitWindowsEx(2,0));
You could use an AutoResetEvent:
System.Threading.AutoResetEvent _countdownFinishedEvent
= new AutoResetEvent(false);
Add this at the end of CountdownTimer:
_countdownFinishedEvent.WaitOne();
And add this inside the else of Countdown_Tick just after Countdown.Stop():
_countdownFinishedEvent.Set();

Console animations

I just want to know how to create simple animations like blinking, moving stuffs on C# console applications. Is there any special method for this?
Traditional Console Spinner:
static void Main(string[] args)
{
ConsoleSpiner spin = new ConsoleSpiner();
Console.Write("Working....");
while (true)
{
spin.Turn();
}
}
public class ConsoleSpiner
{
int counter;
public ConsoleSpiner()
{
counter = 0;
}
public void Turn()
{
counter++;
switch (counter % 4)
{
case 0: Console.Write("/"); break;
case 1: Console.Write("-"); break;
case 2: Console.Write("\\"); break;
case 3: Console.Write("|"); break;
}
Console.SetCursorPosition(Console.CursorLeft - 1, Console.CursorTop);
}
}
Here is my spinner. The purpose of it is to have a program do some work while the spinner displays to the user that something is actually happening:
public class Spinner : IDisposable
{
private const string Sequence = #"/-\|";
private int counter = 0;
private readonly int left;
private readonly int top;
private readonly int delay;
private bool active;
private readonly Thread thread;
public Spinner(int left, int top, int delay = 100)
{
this.left = left;
this.top = top;
this.delay = delay;
thread = new Thread(Spin);
}
public void Start()
{
active = true;
if (!thread.IsAlive)
thread.Start();
}
public void Stop()
{
active = false;
Draw(' ');
}
private void Spin()
{
while (active)
{
Turn();
Thread.Sleep(delay);
}
}
private void Draw(char c)
{
Console.SetCursorPosition(left, top);
Console.ForegroundColor = ConsoleColor.Green;
Console.Write(c);
}
private void Turn()
{
Draw(Sequence[++counter % Sequence.Length]);
}
public void Dispose()
{
Stop();
}
}
And you use the class like this:
using (var spinner = new Spinner(10, 10))
{
spinner.Start();
// Do your work here instead of sleeping...
Thread.Sleep(10000);
}
Yes, there are quite a few methods for this.
In particular, you may want to look at the following Console methods:
SetCursorPosition (you can move the cursor around, and overwrite elements)
MoveBufferArea (copy/paste over the top of regions)
ForegroundColor and BackgroundColor (change coloring)
Great work with the ConsoleSpinner and the sequence implementation. Thanks for the code. I thought about sharing my customized approach.
public class ConsoleSpinner
{
static string[,] sequence = null;
public int Delay { get; set; } = 200;
int totalSequences = 0;
int counter;
public ConsoleSpinner()
{
counter = 0;
sequence = new string[,] {
{ "/", "-", "\\", "|" },
{ ".", "o", "0", "o" },
{ "+", "x","+","x" },
{ "V", "<", "^", ">" },
{ ". ", ".. ", "... ", "...." },
{ "=> ", "==> ", "===> ", "====>" },
// ADD YOUR OWN CREATIVE SEQUENCE HERE IF YOU LIKE
};
totalSequences = sequence.GetLength(0);
}
/// <summary>
///
/// </summary>
/// <param name="sequenceCode"> 0 | 1 | 2 |3 | 4 | 5 </param>
public void Turn(string displayMsg = "", int sequenceCode = 0)
{
counter++;
Thread.Sleep(Delay);
sequenceCode = sequenceCode > totalSequences - 1 ? 0 : sequenceCode;
int counterValue = counter % 4;
string fullMessage = displayMsg + sequence[sequenceCode, counterValue];
int msglength = fullMessage.Length;
Console.Write(fullMessage);
Console.SetCursorPosition(Console.CursorLeft - msglength, Console.CursorTop);
}
}
Implementation:
ConsoleSpinner spinner = new ConsoleSpinner();
spinner.Delay = 300;
while (true)
{
spinner.Turn(displayMsg: "Working ",sequenceCode:5);
}
Output:
You'll need to use Console.ForegroundColor, Console.BackgroundColor and Console.SetCursorPosition(int, int)
EDIT: For inspiration, Let's Dance
Just saw this and a few other threads about it and love this kind of stuff! I took Tuukka's nice piece of code and improved it a bit so the class can easily be set to just about any spin sequence. I'll probably add some accessors and an overloaded constructor to polish it off and put it in the ol' toolbox. Fun stuff!
class ConsoleSpinner
{
int counter;
string[] sequence;
public ConsoleSpinner()
{
counter = 0;
sequence = new string[] { "/", "-", "\\", "|" };
sequence = new string[] { ".", "o", "0", "o"};
sequence = new string[] { "+", "x" };
sequence = new string[] { "V", "<", "^", ">" };
sequence = new string[] { ". ", ".. ", "... ", "...." };
}
public void Turn()
{
counter++;
if (counter >= sequence.Length)
counter = 0;
Console.Write(sequence[counter]);
Console.SetCursorPosition(Console.CursorLeft - sequence[counter].Length, Console.CursorTop);
}
}
I took the anwser from #ThisGuy and modified it a bit, but I noticed, that sometimes the row is not cleared as it should.
static void Main(string[] args)
{
Console.WriteLine("1");
var tddf = XConsole.BusyIndicator("test 0 trtg fdfdfvdgd 4343", () =>
{
return "";
});
Console.WriteLine("2");
var tdd = XConsole.BusyIndicator("test 0 trtg fdfdfvdgd", () =>
{
Thread.Sleep(1);
return "";
});
Console.WriteLine("3");
var t = XConsole.BusyIndicator("test 1 trtg vdgd", () =>
{
Thread.Sleep(1000);
return "";
});
Console.WriteLine("4");
var xt = XConsole.BusyIndicator("test 2", () =>
{
Thread.Sleep(2000);
return "";
});
var xtx = XConsole.BusyIndicator("test 2 csds fsd fdsf ds s", () =>
{
Thread.Sleep(2000);
return "";
});
Console.WriteLine("5");
Thread.Sleep(4000);
}
Spinner class:
public class Spinner : IDisposable
{
private const string Sequence1 = #"/-\|";
private const string Sequence3 = #".o0o";
private const string Sequence2 = #"<^>v";
private const string Sequence4 = #"#■.";
private const string Sequence5 = #"▄▀";
private const string Sequence = #"└┘┐┌";
private string BusyMessage = "";
private int counter = 0;
private readonly int delay;
private bool active;
private readonly Thread thread;
public Spinner(int delay = 200)
{
this.delay = delay;
thread = new Thread(Spin);
}
public void Start()
{
active = true;
Console.CursorVisible = false;
if (!thread.IsAlive)
{
thread.Start();
}
}
public void Start(string busyMessage)
{
BusyMessage = busyMessage;
Start();
}
public void Stop()
{
active = false;
Console.CursorVisible = true;
ClearCurrentConsoleLine();
BusyMessage = "";
}
private static void ClearCurrentConsoleLine()
{
int currentLineCursor = Console.CursorTop;
Console.SetCursorPosition(0, Console.CursorTop);
Console.Write(new string(' ', Console.WindowWidth));
Console.SetCursorPosition(0, currentLineCursor);
}
private void Spin()
{
while (active)
{
Turn();
Thread.Sleep(delay);
}
}
/// <summary>
/// Draws the busy indicator
/// </summary>
/// <param name="c">if empty char, then clear screen</param>
private void Draw(char c)
{
int left = Console.CursorLeft;
int top = Console.CursorTop;
Console.Write('[');
Console.ForegroundColor = ConsoleColor.Green;
Console.Write(c);
Console.ForegroundColor = ConsoleColor.Gray;
Console.Write(']');
if (!string.IsNullOrEmpty(BusyMessage))
{
Console.Write(" " + BusyMessage);
}
//reset cursor position
Console.SetCursorPosition(left, top);
}
private void Turn()
{
Draw(Sequence[++counter % Sequence.Length]);
}
public void Dispose()
{
Stop();
}
}
My console class:
public static class XConsole {
public static T BusyIndicator<T>(Func<T> action)
{
T result;
using (var spinner = new Spinner())
{
spinner.Start();
result = action();
spinner.Stop();
}
return result;
}
public static T BusyIndicator<T>(string content, Func<T> action)
{
T result;
using (var spinner = new Spinner())
{
spinner.Start(content);
result = action();
spinner.Stop();
}
return result;
}
}
Why do I see this result sometimes?
1
2
3 st 0 trtg fdfdfvdgd ]
4
[└] test 2 csds fsd fdsf ds s
Looks like the Dispose didn't trigger? Or a new Task alredy started?
It should look like this:
1
2
3
4
[└] test 2 csds fsd fdsf ds s
UPDATE:
I added ClearCurrentConsoleLine(); and changed the Draw method, this fixed the issue.
i'm not TOO sure i know exactly what you're on about. but i'll give it a shot.
I think the biggest and best way to cause the "blinking" affect (or what i think the blinking affect is) is to use a carriage return.
The best way to explain it to you is to show you the Foo Bar experiment.
start a new project, and in your Main function, try this.
Console.WriteLine("Foo/nBar");
The output will look like this
Foo
Bar
But if you use a carriage return.
Console.WriteLine("Foo/rBar");
The output will look like this
Bar
The reason is that Foo is written, then the carriage return takes you BACK to the start of the line, then Bar is written. All you ever see is Bar.
This can be helpful for "Moving" things on one line, instead of rewriting the same things again on multiple lines.
A way to do progression would be to use Console.Write();
Try this.
Console.Write("Loading");
for(int i = 0; i < 10; i++)
{
Thread.Sleep(1000);
Console.Write(".");
}
The output should be
Loading
Followed by a Fullstop every second for 10 seconds.
If you combine the Carriage return with the Console.Write(); function you can write multiple things on a single line, clear the line and write something else, or indeed, the same thing just moved slightly. (This would of course need more than i have shown you, like recording where the "object" you are controlling is situated. If you would like a short example i would be happy to do one, just comment and ask me for it :)
Edit:
I noticed people mentioning colour, which i forgot. If you were doing animation i guess colour would be a must.
ForegroundColor and BackgroundColor are where it's at.
note that ForegroundColor will apply to the next characters written to the console, it will not completely recolour the Console.
/Edit
I hope this helps,
Skintkingle ;)
I thought I'd chime in with my version of the previously listed code. Here it is:
class ConsoleSpinner
{
bool increment = true,
loop = false;
int counter = 0;
int delay;
string[] sequence;
public ConsoleSpinner(string sSequence = "dots", int iDelay = 100, bool bLoop = false)
{
delay = iDelay;
if (sSequence == "dots")
{
sequence = new string[] { ". ", ".. ", "... ", "...." };
loop = true;
}
else if (sSequence == "slashes")
sequence = new string[] { "/", "-", "\\", "|" };
else if (sSequence == "circles")
sequence = new string[] { ".", "o", "0", "o" };
else if (sSequence == "crosses")
sequence = new string[] { "+", "x" };
else if (sSequence == "arrows")
sequence = new string[] { "V", "<", "^", ">" };
}
public void Turn()
{
if (loop)
{
if (counter >= sequence.Length - 1)
increment = false;
if (counter <= 0)
increment = true;
if (increment)
counter++;
else if (!increment)
counter--;
}
else
{
counter++;
if (counter >= sequence.Length)
counter = 0;
}
Console.Write(sequence[counter]);
Console.SetCursorPosition(Console.CursorLeft - sequence[counter].Length, Console.CursorTop);
System.Threading.Thread.Sleep(delay);
}
}
Adds delay (unfortunately through Thread.Sleep() but, hey), the ability to loop the animation forwards and backwards (starts to reverse when it hits the end), and general improvements overall. Enjoy!
This would be my prefered method:
public sealed class Spinner
{
private static Lazy<Spinner> lazy =
new Lazy<Spinner>(()=> new Spinner());
public static void Reset()
{
lazy = new Lazy<Spinner>(()=> new Spinner());
}
public static Spinner Instance { get { return lazy.Value; }}
private readonly int _consoleX;
private readonly int _consoleY;
private readonly char[] _frames = { '|', '/', '-', '\\' };
private int _current;
private Spinner()
{
_current = 0;
_consoleX = Console.CursorLeft;
_consoleY = Console.CursorTop;
}
public void Update()
{
Console.Write(_frames[_current]);
Console.SetCursorPosition(_consoleX, _consoleY);
if (++_current >= _frames.Length)
_current = 0;
}
}
Call Spinner.Instance.Update() to start the spinner at the current position of the console. Any consecutive call will render the next frame at the same position.
Call Spinner.Reset() if you want to write more text and then add a new spinner at a new location.
This is the way i solved it, sure it could/can be shorter but, Im just not that smart yet :-)
void cc() { Console.Clear(); }
void cr() { Console.ReadKey(); }
byte Sleeptimer = 90;
void sleepy() { System.Threading.Thread.Sleep(Sleeptimer); }
string[] Loading = { #"-- ", #"\ ", #"| ", #"/ ", "Loading", " complete!" };
for (byte i = 0; i <= 15; i++)
{
cc();
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine(Loading[0] + Loading[4]);
sleepy();
cc();
Console.WriteLine(Loading[1] + Loading[4]);
sleepy();
cc();
Console.WriteLine(Loading[2] + Loading[4]);
sleepy();
cc();
Console.WriteLine(Loading[3] + Loading[4]);
sleepy();
cc();
if (i == 15) {
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine(Loading[4] + Loading[5]);
cc();
Console.ForegroundColor = ConsoleColor.Cyan;
Next();
}
}
/*
Now i feel even more noob while reading your code.
I'm a newbie in programing.
Guess this way would work if i just incfement the index of each Loading down below right?
Learned too much today, forgot how this exactly works, to increment or to change the index i'd lile to acces
Console.WriteLine(Loading[2] + Loading[4]);
sleepy();
cc();
*/
This is animation with cancellationToken
private static void ClearCurrentConsoleLine()
{
int currentLineCursor = Console.CursorTop;
Console.SetCursorPosition(0, Console.CursorTop);
Console.Write(new string(' ', Console.WindowWidth));
Console.SetCursorPosition(0, currentLineCursor);
}
public static async Task Start(CancellationToken ct)
{
await Task.Run(() =>
{
Thread.Sleep(2000);
Console.WriteLine();
string text = "Executing";
for (int i = 0; i <= 3; i++)
{
if (ct.IsCancellationRequested)
{
Console.SetCursorPosition(0, Console.CursorTop - 1);
ClearCurrentConsoleLine();
ct.ThrowIfCancellationRequested();
}
Thread.Sleep(1000);
Console.SetCursorPosition(0, Console.CursorTop - 1);
ClearCurrentConsoleLine();
Console.WriteLine(text);
if (i == 3)
{
text = "Executing";
i = 0;
}
text += ".";
}
});
}```

Categories