I'm trying to write a simple console based game engine in C# for fun. Right now what I'm trying to do is establish a simple game loop where the "game" waits for user input, updates then renders then clears the screen then repeats.
This is my code so far:
int x = 0;
bool quit = false;
Task.Run(async () =>
{
try // Catch any issues
{
while (!quit)
{
Console.WriteLine("render");
Console.WriteLine("Position of implicit object: {0}", x);
await Task.Delay(100);
Console.Clear();
}
}
catch (Exception e) { Console.WriteLine(e); }
});
while (!quit)
{
ConsoleKeyInfo keyInfo = Console.ReadKey(false);
ConsoleKey key = keyInfo.Key;
switch (key)
{
case ConsoleKey.D:
x++;
Console.WriteLine("A-key pressed");
break;
case ConsoleKey.A:
x--;
Console.WriteLine("D-key pressed");
break;
case ConsoleKey.Escape:
quit = true;
break;
}
}
But the issue is, there's a constant flicker when the console is updating. I heard Console.SetCursorPosition(0, 0); might work but when I try that, it seems that the updating stops when the user clicks the screen.
Is there another way to do it? Even if it involves changing the method of the game loop itself.
Thank you!
Please use this as a fix Console.SetCursorPosition(0, 0), add this before Console.WriteLine("render"). To fix the click issue as you have mentioned. You have to uncheck Quick Edit mode on console window. Right click on console window -> properties in edit section uncheck Quick Edit. Screenshot attached.
I want to create a program that makes keyboard event for every 10 seconds.
but once my code starts to perform,
it can't stop until it's finished.
Is there any way to stop to close the program??
Can I make it detect my keyboard action by two seconds, like detect hitting ESC to stop the program?
My code is at below.
private void StartAction()
{
int minutes = 2;
Process pr = Process.Start("notepad.exe");
for (int a = 0; a < minutes; a++)
{
for (int b = 0; b < 6; b++)
{
Thread.Sleep(2000);
Thread.Sleep(2000);
Thread.Sleep(2000);
Thread.Sleep(2000);
Thread.Sleep(2000);
SendKeys.Send("KeyBoard Action");
}
}
}
Sometimes its easier to just use a timer.
using System.Threading;
...
private static Timer _myTimer;
private static volatile bool _isCancled;
private static void Main(string[] args)
{
_myTimer = new Timer(Callback, null, 2000, 2000);
Console.ReadLine();
}
private static void Callback(object state)
{
if (_isCancled)
{
_myTimer.Change(0, 0);
return;
}
try
{
//SendKeys.Send("KeyBoard Action");
Console.WriteLine("blah");
}
catch (Exception ex)
{
//Handle Exception
}
}
or another way
Task.Run(async () =>
{
while (!_isCancled)
{
await Task.Delay(2000);
//SendKeys.Send("KeyBoard Action");
Console.WriteLine("blah");
}
});
Just for completeness, the optimal way to cancel a task is using a Cancellation Token. You can read more about them here
CancellationToken Structure
Propagates notification that operations should be canceled.
Add a condition inside the for loop, so that it will exit when requested.
if (condition)
{
break;
}
Edit: Because of the Thread.Sleep everywhere, the program won't actually be able to have a conditional change, example, if you're condition was when the user pressed f1, the condition would never become true because the Thread.Sleep would block the thread, therefore the users input would never be recognized. Try not to use so many Thread.Sleep, if any at all. If it is absolutely necessary, I advise you to use an async method, or use Timers.
I need to make it to where my code will only exit the loop when the key that is pressed, is released. I am doing this is console and will need to stay in console.
Here is an example of the code.
ConsoleKeyInfo = ki;
while (true)
{
ki = Console.ReadKey();
if (ki.Key == ConsoleKey.A)
{
Console.Write("A");
}
}
As you can see above, when the A key is pressed, it will write A in the console. If you hold down the A key, it will continuously write A.
What I'm wanting is for the console to write A ONCE until the A key is released. Then, if you press A again, it will print again.
I've thought about using "KeyUp" but I'm not able to use it on a console application. But something that would accomplish the following...
ConsoleKeyInfo = ki;
while (true)
{
ki = Console.ReadKey();
if (ki.Key == ConsoleKey.A)
{
//I know the following isn't actually code, but it's explaining what I want to happen.
do (onKeyRelease)
{
Console.Write("A");
}
}
}
Basically, I'm wanting only one thing to happen when the loop when a key is pressed until the key is released. When the key is released, the loop will start again.
Please note that I am using this idea for a Text-Based RPG game. So waiting until another key is pressed (including adding another Console.ReadKey() to the end) would not be ideal.
1st Suggestion - Have you tried "break" the loop when the console has displayed the "A"?
2nd Suggestion -
ConsoleKeyInfo = ki;
var alreadyPressed = false
while (true)
{
ki = Console.ReadKey();
if (ki.Key == ConsoleKey.A && !alreadyPressed)
{
Console.Write("A");
alreadyPressed = true;
}}`
Store last pressed key and print character only when it is different from the last pressed key.
I'm trying to figure out how I can use Console.ReadLine and a timer. My console program is designed to run a long process automatically, with this process restarting every 30 seconds after the previous process completed. I want to give the user the ability to break the auto-run by typing a command though. If I use Console.ReadLine() though, it will wait until the user enters something, whereas I want the program to continue on through a loop if nothing is entered within 30 seconds. . . Any thoughts??
For example:
RunProcess > Wait 30s for User Input. If none: Continue Loop
Thanks a lot!
You could run your timer on a separate thread. When the user enters text, store it in a variable that is accessible to both threads. When the timer ticks, see if anything is entered and continue accordingly.
Be sure to be thread safe :-)
EDIT:
You can use a System.Threading.Timer to tick every 30 seconds and in its callback method, check if the text has been set.
Don't use Console.ReadLine() but check if Console.KeyAvailable is true and then read Console.ReadKey() to check for exit condition.
Try this example code
class Program
{
static bool done;
static void Main(string[] args)
{
int count = 0;
done = false;
while (!done)
{
Thread.Sleep(2000);
count++;
Console.WriteLine("Calculation #" + count.ToString());
if (Console.KeyAvailable)
{
ConsoleKeyInfo key = Console.ReadKey();
if (key.Key == ConsoleKey.Escape)
{
done = true;
}
}
}
Console.WriteLine();
Console.WriteLine("end");
}
}
I am writing a server app and I want it to be console based. I need the user to be able to input different commands, but at the same time there is a possibility that something will be output to the console while the user is writing. This messes the buffer up. Is there any clean way of doing this?
Thanks.
I started work on a test program to show how you could divide the console into an output area and an input area, where the input area is moved down as the output area expands with more output. It's not perfect yet, but you may be able to develop it into the answer you're looking for:
static int outCol, outRow, outHeight = 10;
static void Main(string[] args)
{
bool quit = false;
System.DateTime dt = DateTime.Now;
do
{
if (Console.KeyAvailable)
{
if (Console.ReadKey(false).Key == ConsoleKey.Escape)
quit = true;
}
System.Threading.Thread.Sleep(0);
if (DateTime.Now.Subtract(dt).TotalSeconds > .1)
{
dt = DateTime.Now;
WriteOut(dt.ToString(" ss.ff"), false);
}
} while (!quit);
}
static void WriteOut(string msg, bool appendNewLine)
{
int inCol, inRow;
inCol = Console.CursorLeft;
inRow = Console.CursorTop;
int outLines = getMsgRowCount(outCol, msg) + (appendNewLine?1:0);
int outBottom = outRow + outLines;
if (outBottom > outHeight)
outBottom = outHeight;
if (inRow <= outBottom)
{
int scrollCount = outBottom - inRow + 1;
Console.MoveBufferArea(0, inRow, Console.BufferWidth, 1, 0, inRow + scrollCount);
inRow += scrollCount;
}
if (outRow + outLines > outHeight)
{
int scrollCount = outRow + outLines - outHeight;
Console.MoveBufferArea(0, scrollCount, Console.BufferWidth, outHeight - scrollCount, 0, 0);
outRow -= scrollCount;
Console.SetCursorPosition(outCol, outRow);
}
Console.SetCursorPosition(outCol, outRow);
if (appendNewLine)
Console.WriteLine(msg);
else
Console.Write(msg);
outCol = Console.CursorLeft;
outRow = Console.CursorTop;
Console.SetCursorPosition(inCol, inRow);
}
static int getMsgRowCount(int startCol, string msg)
{
string[] lines = msg.Split('\n');
int result = 0;
foreach (string line in lines)
{
result += (startCol + line.Length) / Console.BufferWidth;
startCol = 0;
}
return result + lines.Length - 1;
}
Personally i would use event handlers to managed a console that handles both input and outup at the same time, create a class ScreenManager or whatever, inside that class add a void RunProgram() mthod, create an event with handler and required variables for reading the input key "Console.ReadKey(bool).key".
static Consolekey newKey;
on your main program, creat an instance of your class "whatev you called it", then create a thread of that instances internal method, Thread coreThread = new Thread(delegate() {myinstance.myProgramMrthod()});
loop in your main until the threads up and running. while (!Thread.IsAlive) ;
then create the main program loop.
while (true)
{
}
then for safty, join your custom thread so the main program doesnt continue until the custom thread is closed/disposed.
customThread.Join();
you now have two threads running seperatly.
back to your class, create a switch inside your event handler method.
switch (newkey)
{
case Consolekey.enter
Console.WriteLine("enter pressed");
break;
ect, ect.
default:
Console.write(newkey); // writes character key that dont match above conditions to the screen.
break;
}
stick allyour logic inhere with how you want to handle keys.
How to use multiple modifier keys in C#
might be of some help.
inside your instance's method RunProgram() or whatev you choose to call it, after you've done whatever code you need to, create an infinite loop to check for key change.
while (true)
{
newKey = Console.ReadKey(true).Key;
if (newKey != oldKey)
{
KeyChange.Invoke();
}
}
this loop stores any key pressed and then checks to see if theres a new key, if true fires the event.
you now have the core of what your looking for, one string that loops askng for a new key, whilst the main loop is free to display whatever text you wish to display.
two fixable bugs with this that i can think of, one is "default" inside switch will print to console in caps or strings. and the other is any text added to the console is added at the cursor point so it adds to the text the user has just input.
hwoever i will, since i've just made it, how you have to manager the text been added to the console. again im using an event. i could use methods and functions throughout but events add move flexability to the program, i think.
okay so we want to be able to add text to the console, without it upsetting the input we enter. keeping the input at the bottom;
create a new delegate that has a signiture with a string argument, void delegate myDelegate(string Arg). then create an event with this delegate, call it newline, newinput, whatev you like.
the events handler will take a string argument (repersenting the console update text: what you want to insert into the console above the users input) it will grab the text the user has been entering into the console, store it, then print out the paramiter string onto the console, then print out the users input underneith.
personally i chose to create a static string at the top outside the method, initialise it to empty, cos its going to be frequently used and you dont want to be creating a new identifyer and then initialising the variable everytime the method is called, then dispose of it at the end of the method, only to recreate a new one again, and again.
call the string "input" or whatever.
in the default area of the keychange event handle add input +=newkey.
in the Consolekey.enter section console writline input then input = string.empty Or string = "".
in the event handler add some logic.
public void OnInsert(string Argument)
{
Console.CursorTop -= 1;
// moves the cursor to far left so new input overwrites the old.
// if arg string is longer, then print arg string then print input // string.
if (Argument.Length > input.Length)
{
Console.WriteLine(Argument);
Console.WriteLine(input);
}
else
{
// if the users input if longer than the argument text then print
// out the argument text, then print white spaces to overwrite the
// remaining input characters still displayed on screen.
for (int i = 0; i < input.Length;i++ )
{
if (i < Argument.Length)
{
Console.Write(Argument[i]);
}
else
{
Console.Write(' ');
}
}
Console.Write(Environment.NewLine);
Console.WriteLine(input);
}
}
hope this helps some of you, its not perfect, a quick put together test that works enough to be built on.
If you need to allow output to arrive while the user is typing I recommend sending the output to a new window. So, you could have one window that is used to start the application and then it spawns a thread to open a new console for input and then it continues to send any output messages to the original window. I think you will run in to too many resource locking issues if you try to keep everything in the same window.
This sort of thing becomes a somewhat simpler problem if you treat the server as a client/server application. Let the server have "n" connections to client admin applications that send commands and receive output. The client application could completely separate input and output, having one thread to handle input, and one to handle output.
The output thread could block if the input thread is in the middle of entering a line, and unblock when the line is either cancelled or committed.
I got my example working using Console.MoveBufferArea(), but note that this won't work on platforms other than Windows because the method is not implemented on those platforms.
With this example you would use Read() instead of Console.ReadLine() and Log(...) instead of Console.WriteLine(...) in your code.
class Program
{
static void Main(string[] args)
{
// Reader
new Thread(() =>
{
string line;
while ((line = Read()) != null)
{
//...
}
Environment.Exit(0);
}).Start();
// Writer
new Thread(() =>
{
while (true)
{
Thread.Sleep(1000);
Log("----------");
}
}).Start();
}
static int lastWriteCursorTop = 0;
static void Log(string message)
{
int messageLines = message.Length / Console.BufferWidth + 1;
int inputBufferLines = Console.CursorTop - lastWriteCursorTop + 1;
Console.MoveBufferArea(sourceLeft: 0, sourceTop: lastWriteCursorTop,
targetLeft: 0, targetTop: lastWriteCursorTop + messageLines,
sourceWidth: Console.BufferWidth, sourceHeight: inputBufferLines);
int cursorLeft = Console.CursorLeft;
Console.CursorLeft = 0;
Console.CursorTop -= inputBufferLines - 1;
Console.WriteLine(message);
lastWriteCursorTop = Console.CursorTop;
Console.CursorLeft = cursorLeft;
Console.CursorTop += inputBufferLines - 1;
}
static string Read()
{
Console.Write(">"); // optional
string line = Console.ReadLine();
lastWriteCursorTop = Console.CursorTop;
return line;
}
}
Have you tried calling OpenStandardInput, reading any input and resetting it's position, then writing to the output stream. Afterwards, you can call OpenStandardInput again and fill the data back into the stream.
There's no perfect way of accomplishing this, I think. What telnet does (at least the last version I used) was not print any input (just read the keystrokes) and simply print the output as it arrives. The alternative is to store any data that needs to be output to the console in a buffer, and only print it once the user has finished entering their command. (You could even timestamp the output, to make it more obvious.) I really can't see any better alternative here - you're inevitably going to run into problems using a synchronous I/O interface (i.e. the command line) together with asynchronous operations in the backend.