I'll show you the list that will be used in all the examples.public enum Direction
{
Right,
Down,
Left,
Up,
Stop
}
To begin with, your task can be accomplished without additional flows. There are certain limitations in this regard, but this is the best way to begin with, if you're just beginning to learn to write a code.class Program
{
static void Main(string[] args)
{
ConsoleKey key = default;
Direction direction = Direction.Right;
while (true)
{
Thread.Sleep(1000);
if (Console.KeyAvailable)
key = Console.ReadKey(true).Key;
switch (key)
{
case ConsoleKey.DownArrow:
direction = Direction.Down;
break;
case ConsoleKey.UpArrow:
direction = Direction.Up;
break;
case ConsoleKey.RightArrow:
direction = Direction.Right;
break;
case ConsoleKey.LeftArrow:
direction = Direction.Left;
break;
case ConsoleKey.Escape:
direction = Direction.Stop;
break;
}
if (direction == Direction.Stop)
break;
Console.WriteLine(direction);
}
}
}
Actually, the whole snake move is ready. For Console.WriteLine(direction) challenge your method of replenishing the gambling logic, giving him the same directionand ready.Now there's a lot of options. The multi-point of C# has long evolved, starting from the oldest way to make flows.class Program
{
private static Direction direction = Direction.Right;
static void Main(string[] args)
{
Thread thread = new Thread(Loop);
thread.Start();
while (true)
{
switch (Console.ReadKey(true).Key)
{
case ConsoleKey.DownArrow:
direction = Direction.Down;
break;
case ConsoleKey.UpArrow:
direction = Direction.Up;
break;
case ConsoleKey.RightArrow:
direction = Direction.Right;
break;
case ConsoleKey.LeftArrow:
direction = Direction.Left;
break;
case ConsoleKey.Escape:
direction = Direction.Stop;
break;
}
if (direction == Direction.Stop)
break;
}
Console.WriteLine("Exit program");
}
private static void Loop()
{
while (true)
{
Thread.Sleep(1000);
if (direction == Direction.Stop)
break;
Console.WriteLine(direction);
}
Console.WriteLine("Exit thread");
}
}
Works exactly the same way as the first example. Please note that when you're pressed, Esc First the console will be written Exit programAnd then Exit thread♪ It doesn't cause problems until the flow is background. Thread.IsBackground = trueas the exit application only kills background flows. The main flows should be completed on their own, or the annex would not end. The underwater stone here is precisely that the annex can hang in the system ' s processes until you kill it if at least one major flow is not well completed.By the way, flows can be launched in a more modern way using a pool of flows. That's what class says. System.Threading.Tasks.Task♪I mean lines.Thread thread = new Thread(Loop);
thread.Start();
Can be replaced byTask.Run(() => Loop());
But all the flows from the pool are background, that is, when the waiting for the end of the background flow is out, it will just be killed, and that's another problem. We want our entire code to be normal. In other words Task.Run It only adds to the situation.I'll just point out the difference between the first and second example. Second example: wrong♪ The point is, the reading cycle from the keyboard is not over until you press Esc. And if you close the window of the consoles, you'll get that in the ocne of the Output in Visual Studio.The program '[5712] ConsoleApp1.exe' has exited with code -1073741510 (0xc000013a).
This means that the annex has been enforced Ctrl+C Exit♪ If the application is properly completed, this line looks like it.The program '[8224] ConsoleApp1.exe' has exited with code 0 (0x0).
But this's a mistake to avoid (in any case, at this point, I don't know how) it's only possible to deal with the problem of abnormal withdrawal from the annex. I mean, make sure Main Completed before the annexes close.I mean, you have to go back to the way you use it. Console.KeyAvailable♪ Otherwise Console.ReadKey It's gonna be "weeping" until the keyboard is pressed, and this is a problem without a question. Console.KeyAvailable Consolidation https://ru.stackoverflow.com/a/1201951/373567 ♪ As shown above, no additional flow is required under this approach.Back to where we started, only now we're making a normal end to the application when the window of the console is closed. To track that the annex code is over, I will use the Visual Studio Console. This is an opportunity for a special class Debug♪Let's go.using System.Diagnostics;
That's the code.class Program
{
private static Direction direction = Direction.Right;
static void Main(string[] args)
{
AppDomain.CurrentDomain.ProcessExit += new EventHandler(CurrentDomain_ProcessExit);
ConsoleKey key = default;
while (true)
{
Thread.Sleep(1000);
if (Console.KeyAvailable)
key = Console.ReadKey(true).Key;
switch (key)
{
case ConsoleKey.DownArrow:
direction = Direction.Down;
break;
case ConsoleKey.UpArrow:
direction = Direction.Up;
break;
case ConsoleKey.RightArrow:
direction = Direction.Right;
break;
case ConsoleKey.LeftArrow:
direction = Direction.Left;
break;
case ConsoleKey.Escape:
direction = Direction.Stop;
break;
}
if (direction == Direction.Stop)
break;
Console.WriteLine(direction);
}
Debug.WriteLine("Program exit");
}
private static void CurrentDomain_ProcessExit(object sender, EventArgs e)
{
direction = Direction.Stop;
Thread.Sleep(1000);
}
}
Window Output Visual Studio now looks like this.Program exit
The program '[5188] ConsoleApp1.exe' has exited with code -1073741510 (0xc000013a).
I mean, the app is going to be fine. But there's a problem, pay attention to it. Thread.Sleepwhich I use to wait to complete the main flow of the method Main♪ The thing is, Thread.Sleepas well Console.ReadKey I just can't stop it. All of this stuff is that the application window is closed with a delay of 1 second. It's not beautiful and it looks like a cover-up. Fortunately, that can be avoided.There's a convenient alternative. Thread.Sleepwhich can be interrupted... Task.Delay (with help) CancellationToken) But... Task.Delay Asynchronous, followed by asynchronous programming with keywords async/await♪ From the beginning of the post, I've been leading you to this.class Program
{
private static Direction direction = Direction.Right;
private static readonly CancellationTokenSource cts = new CancellationTokenSource();
static async Task Main(string[] args)
{
AppDomain.CurrentDomain.ProcessExit += new EventHandler(CurrentDomain_ProcessExit);
ConsoleKey key = default;
try
{
while (true)
{
await Task.Delay(1000, cts.Token);
if (Console.KeyAvailable)
{
while (Console.KeyAvailable)
{
key = Console.ReadKey(true).Key;
}
}
switch (key)
{
case ConsoleKey.DownArrow:
direction = Direction.Down;
break;
case ConsoleKey.UpArrow:
direction = Direction.Up;
break;
case ConsoleKey.RightArrow:
direction = Direction.Right;
break;
case ConsoleKey.LeftArrow:
direction = Direction.Left;
break;
case ConsoleKey.Escape:
direction = Direction.Stop;
break;
}
if (direction == Direction.Stop)
break;
Console.WriteLine(direction);
}
}
catch (OperationCanceledException) { }
Debug.WriteLine("Program exit");
}
static void CurrentDomain_ProcessExit(object sender, EventArgs e)
{
cts.Cancel();
}
}
Now the window closes immediately, with the inscription in the sweet window. Program exit Still present. The system has been processed with the forced completion of the application.Now as I wrote https://ru.stackoverflow.com/a/1222168/373567 I want Zmeika to react to the clavicle press immediately, not with a delay until a second. To do this, we need to do more than once a second. For example, 10 times a second, and I want the snake to move just once a second, or any other appropriate speed that doesn't depend on the frequency of the keyboard survey. This is where asynchronous turns into completeness and shows its essence.class Program
{
private static Direction direction = Direction.Right;
private static readonly CancellationTokenSource cts = new CancellationTokenSource();
private const int speedDelay = 1000;
private static bool addDelay;
private static Direction Direction
{
get => direction;
set
{
if (value != direction)
{
direction = value;
addDelay = true;
Update();
}
}
}
static async Task Main(string[] args)
{
AppDomain.CurrentDomain.ProcessExit += new EventHandler(CurrentDomain_ProcessExit);
ConsoleKey key = default;
Task snakeTask = SnakeLoop(cts.Token);
try
{
while (true)
{
await Task.Delay(100, cts.Token);
while (Console.KeyAvailable)
{
key = Console.ReadKey(true).Key;
}
switch (key)
{
case ConsoleKey.DownArrow:
Direction = Direction.Down;
break;
case ConsoleKey.UpArrow:
Direction = Direction.Up;
break;
case ConsoleKey.RightArrow:
Direction = Direction.Right;
break;
case ConsoleKey.LeftArrow:
Direction = Direction.Left;
break;
case ConsoleKey.Escape:
Direction = Direction.Stop;
break;
}
if (Direction == Direction.Stop)
break;
}
cts.Cancel();
await snakeTask;
}
catch (OperationCanceledException) { }
Debug.WriteLine("Program exit");
}
private static async Task SnakeLoop(CancellationToken token)
{
while (true)
{
await Task.Delay(speedDelay, token);
if (addDelay)
{
addDelay = false;
await Task.Delay(speedDelay / 2, token);
}
Update();
}
}
private static void Update()
{
Console.WriteLine(Direction);
}
static void CurrentDomain_ProcessExit(object sender, EventArgs e)
{
cts.Cancel();
}
}
Now what's going to happen. That's it. I don't argue, it's better to make sure that after the button strikes, the next movement's irration goes straight through. speedDelaythat would make the snake's behavior smoother.By the way, I added a characteristic. Directionit just contains the logic of an immediate reaction to the clavicle.All you have to do is implement the method. Update()♪ Not yet done here, there's no reversal ban. I mean, if the snake crawled down, it's gotta be impossible to crawl right up, same for left and right. Do it yourself or look at the example for WPF, which I referred to above.In the processor, by the way. CurrentDomain_ProcessExit You can do something useful. For example, to record the number of points taken by the player on the disk or to keep the full state of the game at all, so that after the application is re-launched, everything starts at the same place where the annex was closed earlier.Yeah, by the way, read this-- https://docs.microsoft.com/ru-ru/dotnet/csharp/async ♪