I am making a program based on the console. I want to make it look as clean as possible and I want the texts to be centered in their lines.
I tried
string msg = "Hello";
Console.SetCursorPosition((Console.WindowWidth - string.Length) / 2, Console.CursorTop);
Console.WriteLine(msg);
It worked. BUT it doesn't quite solve my problem. You see, I want multiple lines to be centered. For example
string title = "--------Message-------";
Console.SetCursorPosition((Console.WindowWidth - string.Length) / 2, Console.CursorTop);
Console.WriteLine(title);
Console.WriteLine(msg);
Now the entire thing is messed up. I hope someone can give a solution to my issue. Thanks
It is not possible to create Console extension methods, but you may try something like this:
public static class ConsoleExtensions
{
public static void WriteLineCentered(string msg)
{
Console.SetCursorPosition((Console.WindowWidth - msg.Length) / 2, Console.CursorTop);
Console.WriteLine(msg);
}
}
And then use this method, when you want to center a text:
string title = "--------Message-------";
string msg = "Hello";
ConsoleExtensions.WriteLineCentered(msg);
ConsoleExtensions.WriteLineCentered(title);
Another option, use one of the following methods to start at top of screen or begin at center of screen.
public class ConsoleHelpers
{
/// <summary>
/// Center lines horizontally and vertically
/// </summary>
public static void CenterLines(params string[] lines)
{
int verticalStart = (Console.WindowHeight - lines.Length) / 2;
int verticalPosition = verticalStart;
foreach (var line in lines)
{
int horizontalStart = (Console.WindowWidth - line.Length) / 2;
Console.SetCursorPosition(horizontalStart, verticalPosition);
Console.Write(line);
++verticalPosition;
}
}
/// <summary>
/// Center lines vertically starting at top of screen
/// </summary>
public static void CenterLinesFromTop(params string[] lines)
{
int verticalPosition = 0;
foreach (var line in lines)
{
int horizontalStart = (Console.WindowWidth - line.Length) / 2;
Console.SetCursorPosition(horizontalStart, verticalPosition);
Console.Write(line);
++verticalPosition;
}
}
}
Usage ConsoleHelpers.CenterLinesFromTop("Hello world","Enjoy the ride"); and ConsoleHelpers.CenterLines("Hello world","Enjoy the ride");
You need to centre each line individually. It's helpful to make a helper function that simply prints out a line of text in the middle of the current line rather than doing it over and over manually.
Of course, that's only going to work for text that fits within a single line - but then again, multi-line text shouldn't really be centred in the first place; that's always going to be awful to look at.
Related
Is it possible to display the value of a variable at a fixed position in a C# Windows console app, to have it visible while the content of the screen otherwise would have pushed every old value upwards and out of sight, to not print the values again and again on new lines?
I know of Curses, but would like to use any standard if it exists, and learn how to do it.
Thanks for your time and help.
The Console.SetCursorPosition is the framework method required to position the caret in a specific point of your console windows. Of course setting a point then displaces all the following writes to the console starting from the new point, so, if you want to write something at precise position and then restart from the previous point then you need to handle also the position of the previous writes.
This is just an example to get you started
static void Main()
{
for (int x = 0; x < 100; x++)
{
if (x % 10 == 0)
Console.SetCursorPosition(0, 0);
else
Console.SetCursorPosition(0, x % 10);
Console.WriteLine(x);
WriteStatusText("Printing line " + x);
// Remove this comment to see it slowly
// Console.ReadLine();
}
Console.ReadLine();
}
static void WriteStatusText(string msg)
{
Console.SetCursorPosition(0, 10);
Console.WriteLine(msg);
}
Keep in mind that setting the position outside the buffer area (Console.BufferWidth and Console.BufferHeight) defined for the console will trigger an exception
I have a face model with 12 blendshapes where each blendshape is simply a list of float values between 0 (neutral facial expression) and 1 (maximum activated expression) but I am starting off with only the first 2 blendshapes; i.e. only two lists for now, say smile and skeptic looks.
My intention is to go through all the possible combinations of all the items in these two lists and make a footage (movie clip) of the facial movements, to display how all the possible combinations of blendshape values/weights look like.
So, I wrote the following to facilitate this scenario for only two blendshapes for now, and I save them to file as soon as the application closes:
public class BlendShapesVAL : MonoBehaviour
{
private List<float> _weightValues_Preset1_smile = new List<float>();
private List<float> _weightValues_Preset2_skeptic = new List<float>();
public bool _TransitionAnimation = true;
public float _TransitionAnimationSpeed = 2f;
public BlendShapesPresetController _BSPC;
private List<float> _weightsList = new List<float>();
public List<bool> _ActivationsList = new List<bool>();
public List<string> _PresetsNamesList = new List<string>();
private void Awake()
{
_weightsList.Clear();
_ActivationsList.Clear();
for (int i = 0; i < _PresetsNamesList.Count; ++i)
{
_ActivationsList.Add(false);
_weightsList.Add(0);
}
}
private void Start()
{
if (_BSPC != null)
{
// . . .
}
else
{
_BSPC = GetComponent<BlendShapesPresetController>();
}
StartCoroutine("Interpolate");
}
/// <summary>
/// Writes (i.e. saves) blendshape values to file when the application quits.
/// </summary>
///
private void OnApplicationQuit()
{
SaveBlendShapesValues(_weightValues_Preset1_smile);
SaveBlendShapesValues(_weightValues_Preset2_skeptic);
PlayerPrefs.DeleteAll();
}
/// <summary>
/// Goes thorugh all the possible combinations of blendshape weights.
/// For now, only the first two though!
/// </summary>
///
private IEnumerator Interpolate()
{
for (int i = 0; i <= 100; i++)
{
float weightValuesmile = (float)i / 100.0f;
_BSPC.SetWeight("Preset1_smile", weightValuesmile);
_weightValues_Preset1_smile.Add(weightValuesmile);
for (int j = 0; j <= 100; j++)
{
float weightValueSkeptic = (float)j / 100.0f;
_BSPC.SetWeight("Preset2_skeptic", weightValueSkeptic);
_weightValues_Preset2_skeptic.Add(weightValueSkeptic);
}
yield return null;
}
}
/// <summary>
/// Writes (i.e. saves) blendshape values to file.
/// <param name="blendShapesValuesFilePath">
/// The path to the file that will store the list of float values;
/// i.e. "Application.dataPath" plus the name of the CSV file.
/// </param>
/// <param name="values">
/// The float values that are the blendshape weights.
/// </param>
/// </summary>
///
private static void SaveBlendShapesValues(List<float> values)
{
List<string> lines = new List<string>
{
/// Add a header row for the labels.
"TimeStamp,Preset1_smile,Preset2_skeptic"
};
foreach (var value in values)
{
/// Iterate through all the elements.
lines.Add(DateTime.Now + "," + value);
}
/// Load the old counter.
int counter = PlayerPrefs.GetInt("_counter_", 0);
/// Concatenate the file name constituents and combine it with the application data path.
string fileName = string.Format("BlendShapesValues_{0}.csv", counter.ToString() );
string tempPath = Path.Combine(Application.dataPath, fileName);
try
{
File.WriteAllLines(tempPath, lines.ToArray() );
Debug.Log("Saved blendshape weight values to: " + tempPath);
/// Increment the counter.
counter++;
/// Save the current counter.
PlayerPrefs.SetInt("_counter_", counter);
PlayerPrefs.Save();
}
catch (Exception e)
{
Debug.LogWarning("Failed to save to PlayerPrefs: " + tempPath);
Debug.LogWarning("Error: " + e.Message);
}
}
}
In the Unity Editor, the blendshapes are displayed with values from 0 to 100, hence my conversion in the code, as seen in this screenshot:
The first file has 101 values (0...100 plus a top row for column labels) a snippet can be seen in this screenshot:
The second file has 10201 values. My first question is whether this method of saving the iterated values to file after the app stops is a good solution, given the large growth in the values as I add more lists (i.e. blendshapes)?
My second question is how I can slow down the iterations, because (in the first screenshot) the smile values start counting up from 0 to 100 and I can see them (the face moves slowly in a visible manner) but as that is happening, I notice that the second list (skeptic) apparently jumps to 100 immediately, so it is done so quickly that it cannot be recorded by the Win screen recorder...
Well i was not sure if this could be a comment or an answer so let me know if i am missing something.
For your first question if you are okay with those saved files being non-readable by humans i would suggest using BinaryReader. Because size of saved files would be smaller and when you want to read them back to make your clip it will read it faster. Also, considering you want to have 12 blend shapes and their combination this file can be huge.
For your second question iteration jumps to 100 because you only yield when the inner loop is completed. Therefore, in each frame 100 combinations of skeptical for 1 smile are added to the list. I would recommend using multi threading or Unity job system for such a task because it can be computationally costly with 12 blendshapes and all the combinations.
Let me know if i can help further. Good luck!
I have a console application which I'm trying to treat like a terminal. Even terminals don't go this far to make sure the format is perfect, but I have OCD with this kind of thing, and wish to do it.
Once I've typed about 10 or 20 lines into the terminal, I reach a place where my input cursor is right at the bottom of the console window, and there isn't a blank line underneath.
I just think things would go much better if there was a line below at all lines. So after my while loop, I've attempted this, but it doesn't seem to help with the problem I'm currently facing.
private static void PrintLineToBottom()
{
Console.WriteLine();
Console.SetCursorPosition(0, Console.CursorTop - 1);
}
Can anyone help here? I suppose it also needs to know when it needs to do this.
I think the only problem with your method is that it doesn't write anything to the console window. If you add an argument and write it to the console, it should work as expected.
This example uses an object with a default value of null, but you could create many overloads to mimic the actual WriteLine method. It's basically the same code that you have, except that it prints the argument to the window plus a newline character, then moves the cursor up one space. I also added an optional argument to change the number of blank lines below the last line:
private static void TerminalWriteLine(object output = null, int numBlankLinesBelow = 1)
{
// Don't allow negative numbers
numBlankLinesBelow = Math.Max(0, numBlankLinesBelow);
Console.WriteLine(output + new string('\n', numBlankLinesBelow));
Console.SetCursorPosition(Console.CursorLeft, Console.CursorTop - numBlankLinesBelow);
}
Then you would use this method in your code in place of Console.WriteLine, for example:
private static void Main()
{
for (int i = 0; i < 100; i++)
{
TerminalWriteLine(i);
}
Console.ReadLine();
TerminalWriteLine("There should be 5 blank lines below this", 5);
Console.ReadLine();
}
Output after first ReadLine():
Output after second ReadLine():
Hello there!
I had to sign up just to give your question a real and correct answer.
WriteLine() with no params, is 100% okay, and it doesn't cause issues. I'm sorry about Rufus L.
Your function shouldn't be called after the loop. It won't be called then until you break loop.
That's all I can say without more code giving context to your words.
It's always nice to*recreate your code as excerpts if you wish to keep your code private, like below.
The correct answer is the simplest. Copy and paste this, then play with it.
while (true)
{
Console.WriteLine(">");
Console.CursorTop--;
Console.CursorLeft++;
string input = Console.ReadLine();
}
At this point, it is answered.
But let's break it all down and have some fun!
Print a prompt string (">"), or nothing (), and go to next line as desired.
Console.WriteLine(">"); // Or...
Console.WriteLine(); // Blank prompt.
There are times when it is impossible to know the target position.
Console.CursorTop--; // Cursor up by 1.
Console.CursorLeft++; // Right by length of prompt, if not blank.
The '_' represents the cursor position as is.
>_ // Prompt awaiting user input.
At which point user input can be captured.
string input = Console.ReadLine();
Blurb.
I am weird because I love Command Prompt console apps the most. I have made several over the past few years, and only this month created my latest version, the omg simplest of the lot, and the absolute best functional--works 100% as Cmd.exe, yet now has front end ability to add custom commands, so by default it is Cmd.exe.
My most complex version, the previous version, had amazing custom functionality, including a Terminal function to become a literal terminal. The terminal also had the 'exit' command which snapped you back to my Control Console.
This is literally one of my greatest programming passions. As such, a page full of horrible non-answers are sighted swiftly and with lament.
Hence...
In my consoles, I always use the Write function as I haven't desired a sure blank line below.
Write also gives you more control. Let's get dynamic!
while (true)
{
string prompt = "Terminal> ";
Console.Write(prompt);
int count = 5;
for (int i = 0; i < count; i++) Console.WriteLine();
Console.CursorTop -= count;
Console.CursorLeft += prompt.Length;
string input = Console.ReadLine();
}
Now we want the prompt string to be a variable.
string prompt = "Terminal> "; // Notice the space.
Console.Write(prompt);
Here we can decide how many blank lines we want, which can also be decided externally.
int count = 5; // Or variable.
A simple for loop to give us the desired number of blank lines after each prompt.
This is why we do not prompt with WriteLine, as that would give us an extra one.
for (int i = 0; i < count; i++) Console.WriteLine();
Move the cursor up and right based on the sizes known to us.
Console.CursorTop -= count; // Blank lines we printed.
Console.CursorLeft += prompt.Length; // The string length of the prompt.
The '_' represents the cursor position as is.
Terminal> _ // Prompt awaiting user input.
At which point user input can be captured.
string input = Console.ReadLine();
Here is what my code might look like. Copy and paste this function, and have fun!
static void CommandPrompt()
{
string input = "";
string promptString = "Terminal> ";
int blankLinesCount = 1;
while (true) PromptLoop();
// Local functions are a thing.
string PromptSimple()
{
Console.Write(">");
return Console.ReadLine();
}
string Prompt()
{
Console.Write(promptString);
for (int i = 0; i < blankLinesCount; i++) Console.WriteLine();
Console.CursorTop -= blankLinesCount;
Console.CursorLeft += promptString.Length;
return Console.ReadLine();
}
void PromptLoop()
{
input = Prompt();
// Process the input.
}
}
Enjoy! ♥
I have a console application project in C# 2.0 that needs to write something to the screen in a while loop. I do not want the screen to scroll because using Console.Write or Console.Writeline method will keep displaying text on console screen incremently and thus it starts scrolling.
I want to have the string written at the same position. How can i do this?
Thanks
Use Console.SetCursorPosition to set the position. If you need to determine it first, use the Console.CursorLeft and Console.CursorTop properties.
Function to write the progress of a loop. Your loop counter can be used as the x position parameter. This prints on line 1, modify for your needs.
/// <summary>
/// Writes a string at the x position, y position = 1;
/// Tries to catch all exceptions, will not throw any exceptions.
/// </summary>
/// <param name="s">String to print usually "*" or "#"</param>
/// <param name="x">The x postion, This is modulo divided by the window.width,
/// which allows large numbers, ie feel free to call with large loop counters</param>
protected static void WriteProgress(string s, int x) {
int origRow = Console.CursorTop;
int origCol = Console.CursorLeft;
// Console.WindowWidth = 10; // this works.
int width = Console.WindowWidth;
x = x % width;
try {
Console.SetCursorPosition(x, 1);
Console.Write(s);
} catch (ArgumentOutOfRangeException e) {
} finally {
try {
Console.SetCursorPosition(origCol, origRow);
} catch (ArgumentOutOfRangeException e) {
}
}
}
I want to display a kind of banner everytime the player enters a new location. This should be at the very left border of the screen. My problem is that my current solution behaves inconsistently across resolutions. On higher resolutions the banner is positioned further to the right than needed.
I also use a piece of code to properly position the text every time it is displayed.
public class AreaName : MonoBehaviour
{
private static AreaName instance;
void Start ()
{
if (instance)
Destroy(this);
else
instance = this;
BannerBase = transform.FindChild("BannerBase").GetComponent<Image>();
NameDisplay = BannerBase.transform.FindChild("BannerText").GetComponent<Text>();
}
private Image BannerBase;
private Text NameDisplay;
public static void Display (string areatitle)
{
int width = TextWidth(instance.NameDisplay, areatitle);
instance.BannerBase.rectTransform.position = new Vector3(-305 + width, instance.BannerBase.transform.position.y, 0);
instance.NameDisplay.text = areatitle;
print(width);
}
private static int TextWidth (Text obj, string text)
{
int totalLength = 0;
Font myFont = obj.font; //chatText is my Text component
CharacterInfo characterInfo = new CharacterInfo();
char[] arr = text.ToCharArray();
foreach (char c in arr)
{
myFont.GetCharacterInfo(c, out characterInfo, obj.fontSize);
totalLength += characterInfo.advance;
}
return totalLength;
}
}
Are there any simple errors that I am overseeing? Or am I just understanding the Unity UI system wrong?
You shouldn't need to code nothing to do that.
You have put your banner at the lower left corner of the screen, with a (0,0) pivot, that's all right, but, why is it positioned at x: -320?
If you put your banner at -320 for some reason, at native resolution it probably will work as expected, but if you have activated the Canvas Resizer, at other resolutions it will be moved since is not positioned at the corner at all.