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.
Related
I would like to make a program which will let the user decide what program to run, by pressing certain keys. I have now come so far that some of these keys work perfectly. However I have now come to a problem that I have a difficult time to solve. Now when I press a key it activates the same thing that was activated before even though I press a different key. I believe that the issue lies in the WaitForKey()-method, but I am not sure where in there. Can you help me to locate the issue and bring in some solutions to this?
note: hasBeenPressed is a Boolean value which tells if the key has already been pressed or not and if so it should avoid activating some other functionality automatically.
public static void WaitForKey(ConsoleKey key) {
ConsoleKeyInfo keyInfo = Console.ReadKey(true);
if (keyInfo.Key == key && hasBeenPressed == false) {
hasBeenPressed = true;
return;
}
else if (keyInfo.Key == key) {
hasBeenPressed = false;
return;
}
}
I use the WaitForKey() in this context:
for (int i = 0; i < mySignalChain.Count - 1; i++) {
if (keyPress.Key == ConsoleKey.I) {
pedalsActivated(mySignalChain)
WaitForKey(ConsoleKey.I);
}
I hope this was clear otherwise I will try to elaborate on this.
Thanks in advance!
Based on the description of what you would like to achieve,
I would like to make a program which will let the user decide what program to run, by pressing certain keys.
I wrote the following Console Application that will execute a different function depending on the key you press (I, J, or K), and will keep asking for keys until the user presses a different key, in which case the program will finish.
var exit = false;
ConsoleKeyInfo keyInfo;
do
{
Console.WriteLine("Please enter a new Key");
keyInfo = Console.ReadKey();
Console.WriteLine();
switch (keyInfo.Key)
{
case ConsoleKey.I:
FunctionFor_I();
break;
case ConsoleKey.J:
FunctionFor_J();
break;
case ConsoleKey.K:
FunctionFor_K();
break;
default:
ExitProgram();
break;
}
}
while (exit == false);
void FunctionFor_I()
{
Console.WriteLine("I has been pressed");
}
void FunctionFor_J()
{
Console.WriteLine("J has been pressed");
}
void FunctionFor_K()
{
Console.WriteLine("K has been pressed");
}
void ExitProgram()
{
exit = true;
}
I tried to replicate your code first and see how it works, but I couldn't understand its purpose so I focused on your textual description of the problem. If this is not what you intended, please post the full version of your code and I'll try to reproduce it.
I have now fixed my issue (somewhat) by doing the following:
ConsoleKeyInfo keyPress = Console.ReadKey(true);
while (keyPress.Key != ConsoleKey.Q) {
switch (keyPress.Key) {
case ConsoleKey.I:
pedalsActivated(mySignalChain);
Console.WriteLine("");
WaitForKey(ConsoleKey.I);
keyPress = Console.ReadKey(true);
break;
.
.
.
}
By using my function WaitForKey() defined above I can now activate my functions while at the same time avoiding a certain key being spammed. But then I also reset the keyPress (by: keyPress = Console.Readkey(true);) so that other functions can be called with other keys - though I have to press every key twice now to do so. This works better than having to press the keys far more than twice - however it would be optimal if I would only need to press each key once. And if anyone has an idea how to do so it would be much appreciated.
I recently read an answer from this question where basicly everything gets resolved.
The answer was this. I implemented it into my code base and everything seemed to work. But when using the KEY_EVENT it only works when other keys are not pressed.
With that i mean when i press 'a' everything works fine, but when i use 'SHIFT+A' it doesnt work anymore.
I downloaded the demo project that he attached in the answer and this were the results i got.
Key press without shift or anything else
Key press with shift
The second result also applies to any other button combination. I can use CTRL or ALT and the result looks always exactly the same like the second picture. The only thing that changes is wVirtualKeyCode (Shift=16, Ctrl=17, Alt=18)
Does maybe someone have an answer why the key combinations wont work?
Here is the Start Method which does all the reading.
public static void Start()
{
if (!Run)
{
Run = true;
IntPtr handleIn = GetStdHandle(STD_INPUT_HANDLE);
new Thread(() =>
{
while (true)
{
uint numRead = 0;
INPUT_RECORD[] record = new INPUT_RECORD[1];
record[0] = new INPUT_RECORD();
ReadConsoleInput(handleIn, record, 1, ref numRead);
if (Run)
switch (record[0].EventType)
{
case INPUT_RECORD.MOUSE_EVENT:
MouseEvent?.Invoke(record[0].MouseEvent);
break;
case INPUT_RECORD.KEY_EVENT:
KeyEvent?.Invoke(record[0].KeyEvent);
break;
case INPUT_RECORD.WINDOW_BUFFER_SIZE_EVENT:
WindowBufferSizeEvent?.Invoke(record[0].WindowBufferSizeEvent);
break;
}
else
{
uint numWritten = 0;
WriteConsoleInput(handleIn, record, 1, ref numWritten);
return;
}
}
}).Start();
}
}
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 make a console application. I want the ". . ." after "Press any key to exit" to blink.
The blinking is working fine and all, but now my Console.ReadKey() is unreachable code.
I'm just wondering if there is anyway to let the Console.ReadKey() line be reached, or where I would move it to be able to let it be run?
static void Main(string[] args)
{
string blinkExit = ". . ."; //Variable for blinking periods after 'exit'
Console.Write("\n\nPress any key to exit"); //Displays a message to press any key to exit in the console
while (true)
{
WriteBlinkingText(blinkExit, 500, true);
WriteBlinkingText(blinkExit, 500, false);
}
Console.ReadKey();
}
private static void WriteBlinkingText(string text, int delay, bool visible)
{
if (visible)
Console.Write(text);
else
for (int i = 0; i < text.Length; i++)
Console.Write(" ");
Console.CursorLeft -= text.Length;
System.Threading.Thread.Sleep(delay);
}
It seems like you just want this text to blink until a key is pressed by the user. If that is the case then why not just loop until there is a key available to be read?
while (!Console.KeyAvailable)
{
WriteBlinkingText(blinkExit, 500, true);
WriteBlinkingText(blinkExit, 500, false);
}
This avoids all of the nasty issues that can come up with using multiple threads
just change your while code with this
Task.Factory.StartNew(() =>
{
while (true)
{
WriteBlinkingText(blinkExit, 500, true);
WriteBlinkingText(blinkExit, 500, false);
}
});
pressing any key will close the console
Imagine threads as workers, when you use the console application, you are working on one thread, and by using a loop which doesn't end, you are essentially stopping any other task from occurring from that point onwards because the thread is busy looping!
To solve the problem you need to place a trigger of sorts in the loop which is going to exit once a key is pressed. I've demonstrated a simple way to do this below.
Examine the code, you'll find that I've used a new class called Interlocked, the reason I used this is to avoid any thread safety issues, thread safety is a deep topic, and isn't something I want to be trying to explain in a few sentences, but the idea is that any resources being shared between two different threads, must be locked. You'll also find a lambda expression, these are all intermediate topics, and trying to learn them now is going to achieve one thing... a headache!
private static long _isKeyPressed;
private static void Main()
{
// Create a new Thread and Start it.
new Thread(() =>
{
// Variable for blinking periods after 'exit'
const string blinkExit = ". . .";
// Displays a message to press any key to exit in the console
Console.Write("\n\nPress any key to exit");
// Run until no keys are pressed.
while (Interlocked.Read(ref _isKeyPressed) == 0)
{
WriteBlinkingText(blinkExit, 500, true);
WriteBlinkingText(blinkExit, 500, false);
}
}).Start();
Console.ReadKey();
// Once a key has been pressed, increment the value of _isKeyPressed to 1.
// This will indicate to the thread running above that it should exit it's loop.
// as _isKeyPressed is no longer equal to 0.
Interlocked.Increment(ref _isKeyPressed);
}
private static void WriteBlinkingText(string text, int delay, bool visible)
{
if (visible) Console.Write(text);
else
{
for (int i = 0; i < text.Length; i++)
{
Console.Write(" ");
}
}
Console.CursorLeft -= text.Length;
Thread.Sleep(delay);
}
I want to create a console application that will display the key that is pressed on the console screen, I made this code so far:
static void Main(string[] args)
{
// this is absolutely wrong, but I hope you get what I mean
PreviewKeyDownEventArgs += new PreviewKeyDownEventArgs(keylogger);
}
private void keylogger(KeyEventArgs e)
{
Console.Write(e.KeyCode);
}
I want to know, what should I type in main so I can call that event?
For console application you can do this, the do while loop runs untill you press x
public class Program
{
public static void Main()
{
ConsoleKeyInfo keyinfo;
do
{
keyinfo = Console.ReadKey();
Console.WriteLine(keyinfo.Key + " was pressed");
}
while (keyinfo.Key != ConsoleKey.X);
}
}
This will only work if your console application has focus. If you want to gather system wide key press events you can use windows hooks
Unfortunately the Console class does not have any events defined for user input, however if you wish to output the current character which was pressed, you can do the following:
static void Main(string[] args)
{
//This will loop indefinitely
while (true)
{
/*Output the character which was pressed. This will duplicate the input, such
that if you press 'a' the output will be 'aa'. To prevent this, pass true to
the ReadKey overload*/
Console.Write(Console.ReadKey().KeyChar);
}
}
Console.ReadKey returns a ConsoleKeyInfo object, which encapsulates a lot of information about the key which was pressed.
Another solution, I used it for my text based adventure.
ConsoleKey choice;
do
{
choice = Console.ReadKey(true).Key;
switch (choice)
{
// 1 ! key
case ConsoleKey.D1:
Console.WriteLine("1. Choice");
break;
//2 # key
case ConsoleKey.D2:
Console.WriteLine("2. Choice");
break;
}
} while (choice != ConsoleKey.D1 && choice != ConsoleKey.D2);