我在C#中制作我的第一个游戏机游戏,这是一个简单的迷宫游戏,但由于某种原因,我在屏幕上有一个荒谬的闪烁量。我尝试过使用Thread.Sleep和Console.CursorVisible = false;但无济于事。 如果您遇到卡片,请按1然后进入标题画面,这将引导您进入仍处于预alpha阶段的迷宫。如果它有所作为我使用Visual Studio 2013作为IDE。我的问题是如何摆脱迷宫部分的过度闪烁。
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Text.RegularExpressions;
using System.Threading;
class Game
{
static void Main()
{
Console.WriteLine("Select Level (Available levels: 1,2):");
Console.WriteLine("\n(\\_/)\n(o.o)\n(___)0\n");
int gameLevel = int.Parse(Console.ReadLine()); // by pressing a number the user can select different labyrinths.
// Console Height and Width
Console.BufferHeight = Console.WindowHeight = 25;
Console.BufferWidth = Console.WindowWidth = 80;
Console.OutputEncoding = System.Text.Encoding.Unicode; // Must have this + Change font to Lucida in CMD
// Reads File:
string map = File.ReadAllText(String.Format("level{0}.txt", gameLevel));
string[] mapRows = Regex.Split(map, "\r\n");
int mapSize = mapRows[0].Length;
int mapHeight = mapRows.Count() - 1;
char[,] charMap = new char[mapHeight, mapSize];
// Creates Matrix:
for (int row = 0; row < mapHeight; row++)
{
for (int col = 0; col < mapSize; col++)
{
charMap[row, col] = mapRows[row].ElementAt(col);
}
}
// Rabbit init:
string rabbitIcon = "\u0150"; // \u0150 \u014E \u00D2 \u00D3 --> alternatives
int rabbitX = 1, rabbitY = 0;
int carrotCounter = 0;
// Game Loop:
while (true)
{
DrawLabyrinth(mapHeight, mapSize, charMap);
MoveRabbit(mapHeight, mapSize, ref rabbitX, ref rabbitY, charMap);
EatCarrot(rabbitX, rabbitY, charMap,carrotCounter);
Console.SetCursorPosition(rabbitX, rabbitY);
Console.Write(rabbitIcon);
Thread.Sleep(66);
Console.CursorVisible = false;
Console.Clear();
}
}
static void EatCarrot(int rabbitX, int rabbitY, char[,] theMap,int carrotCount)
{
if (theMap[rabbitY, rabbitX] == '7' || theMap[rabbitY, rabbitX] == '8')
{
if (theMap[rabbitY, rabbitX] == '7')
{
theMap[rabbitY, rabbitX] = ' ';
theMap[rabbitY - 1, rabbitX] = ' ';
carrotCount++;
}
else if (theMap[rabbitY, rabbitX] == '8')
{
theMap[rabbitY, rabbitX] = ' ';
theMap[rabbitY + 1, rabbitX] = ' ';
carrotCount++;
}
}
}
static void MoveRabbit(int height, int width, ref int rabbitX, ref int rabbitY, char[,] theMap)
{
if (Console.KeyAvailable == true)
{
ConsoleKeyInfo pressedKey = Console.ReadKey(true);
while (Console.KeyAvailable) Console.ReadKey(true);
if (pressedKey.Key == ConsoleKey.LeftArrow || pressedKey.Key == ConsoleKey.A)
{
if (theMap[rabbitY, rabbitX - 1] == ' ' || theMap[rabbitY,rabbitX - 1 ] == '7' || theMap[rabbitY,rabbitX - 1 ] == '8')
{
rabbitX -= 1;
}
}
else if (pressedKey.Key == ConsoleKey.RightArrow || pressedKey.Key == ConsoleKey.D)
{
if (theMap[rabbitY, rabbitX + 1] == ' ' || theMap[rabbitY,rabbitX + 1 ] == '7' || theMap[rabbitY,rabbitX + 1 ] == '8')
{
rabbitX += 1;
}
}
else if (pressedKey.Key == ConsoleKey.UpArrow || pressedKey.Key == ConsoleKey.W)
{
if (theMap[rabbitY - 1, rabbitX] == ' ' || theMap[rabbitY - 1,rabbitX ] == '7' || theMap[rabbitY - 1,rabbitX ] == '8')
{
rabbitY -= 1;
}
}
else if (pressedKey.Key == ConsoleKey.DownArrow || pressedKey.Key == ConsoleKey.S)
{
if (theMap[rabbitY + 1, rabbitX] == ' ' || theMap[rabbitY + 1, rabbitX] == '7' || theMap[rabbitY + 1, rabbitX] == '8')
{
rabbitY += 1;
}
}
}
}
static void DrawLabyrinth(int height, int width, char[,] array)
{
for (int i = 0; i < height; i++)
{
for (int j = 0; j < width; j++)
{
if (array[i, j] == '1')
Console.Write("─");
else if (array[i, j] == '2')
Console.Write("│");
else if (array[i, j] == '3')
Console.Write("┌");
else if (array[i, j] == '4')
Console.Write("┐");
else if (array[i, j] == '5')
Console.Write("└");
else if (array[i, j] == '6')
Console.Write("┘");
else if (array[i, j] == '7')
{
Console.ForegroundColor = ConsoleColor.Red;
Console.Write("▼");
Console.ForegroundColor = ConsoleColor.White;
}
else if (array[i, j] == '8')
{
Console.ForegroundColor = ConsoleColor.Green;
Console.Write("\u00B8");
Console.ForegroundColor = ConsoleColor.White;
}
else if (array[i, j] == '9')
{
Console.Write("┬");
}
else if (array[i, j] == '0')
{
Console.Write("┴");
}
else if (array[i, j] == 'a')
{
Console.Write('├');
}
else if (array[i, j] == 'b')
{
Console.Write('┤');
}
else if (array[i, j] == 'c')
{
Console.Write('┼');
}
else
{
Console.Write(" ");
}
}
Console.WriteLine();
}
}
}
答案 0 :(得分:3)
您的代码最大的问题是您不断刷新屏幕,即使不需要重绘任何内容,因为用户还没有移动兔子。
如上所示,您要做的是重绘的最小量,即只有在需要重绘时重绘,然后尝试尽可能少的重绘。对于你的示例游戏,在伪代码中,这应该是这样的:
// One time actions
var maze = ReadMaze(level);
DrawMaze(maze);
DrawRabbit(rabbitX, rabbitY);
// Game loop
while ((var input = GetInput()) != Input.Quit) {
oldRabbitX = rabbitX, oldRabbitY = rabbitY;
if (MoveRabbit(input, rabbitX, rabbitY, maze)) {
EraseRabbit(oldX, oldY);
DrawRabbit(rabbitX, rabbitY);
if (IsPositionWithCarrot(rabbitX, rabbitY, maze))
// This only the erases the carrot on screen.
EatCarrot(rabbitX, rabbitY, maze);
}
}
可以找到一篇博客文章,其中包含有关构建c#控制台游戏的大量有用信息here。
因为我发现这是一个有趣的问题,所以我把你的代码重新整理了一下以匹配上面的伪代码。这消除了游戏中的所有闪烁。您可以在下面找到此尝试:
public class Game
{
const string RabbitIcon = "\u0150"; // \u0150 \u014E \u00D2 \u00D3 --> alternatives
static readonly char[] MazeChars = { '─', '│', '┌', '┐', '└', '┘', '▼', '\u00B8', '┬', '┴', '├', '┤', '┼' };
static readonly ConsoleColor MazeFgColor = ConsoleColor.DarkGray;
enum Input
{
MoveLeft,
MoveRight,
MoveUp,
MoveDown,
Quit
};
public static void Run()
{
Console.WriteLine("Select Level (Available levels: 1,2):");
Console.WriteLine("\n(\\_/)\n(o.o)\n(___)0\n");
int carrotCounter = 0;
int gameLevel = int.Parse(Console.ReadLine()); // by pressing a number the user can select different labyrinths.
// Console Height and Width
Console.WindowHeight = 25;
Console.BufferHeight = Console.WindowHeight + 1; // +1 to allow writing last character in the screen corner
Console.BufferWidth = Console.WindowWidth = 80;
Console.OutputEncoding = System.Text.Encoding.Unicode; // Must have this + Change font to Lucida in CMD
// Reads maze map
string[] mapRows = File.ReadAllLines(String.Format("game.level{0}.txt", gameLevel));
if (!mapRows.All(r => r.Length == mapRows[0].Length))
throw new InvalidDataException("Invalid map");
var charMap = mapRows.Select(r => r.ToCharArray()).ToArray();
// Draw maze & rabbit once
Console.CursorVisible = false;
DrawLabyrinth(charMap);
int rabbitX = 1, rabbitY = 1;
DrawRabbit(rabbitX, rabbitY, RabbitIcon);
// Game Loop:
Input input;
while ((input = GetInput()) != Input.Quit)
{
if (MoveRabbit(input, ref rabbitX, ref rabbitY, charMap) &&
IsPositionWithCarrot(rabbitX, rabbitY, charMap))
EatCarrot(rabbitX, rabbitY, charMap, ref carrotCounter);
}
}
static void EatCarrot(int rabbitX, int rabbitY, char[][] theMap, ref int carrotCounter)
{
// determine carrot top position.
var carrotTopY = theMap[rabbitY][rabbitX] == '7' ? rabbitY - 1 : rabbitY;
// "eat it" from the map.
theMap[carrotTopY][rabbitX] = ' ';
theMap[carrotTopY + 1][rabbitX] = ' ';
// and erase it on screen;
Console.SetCursorPosition(rabbitX, carrotTopY);
Console.Write(' ');
Console.SetCursorPosition(rabbitX, carrotTopY + 1);
Console.Write(' ');
// redraw the rabbit
carrotCounter++;
DrawRabbit(rabbitX, rabbitY, RabbitIcon);
}
static Input GetInput()
{
while (true)
{
var key = Console.ReadKey(true);
switch (key.Key)
{
case ConsoleKey.LeftArrow:
case ConsoleKey.A:
return Input.MoveLeft;
case ConsoleKey.RightArrow:
case ConsoleKey.D:
return Input.MoveRight;
case ConsoleKey.UpArrow:
case ConsoleKey.W:
return Input.MoveUp;
case ConsoleKey.DownArrow:
case ConsoleKey.S:
return Input.MoveDown;
case ConsoleKey.Q:
return Input.Quit;
default:
break;
}
}
}
static bool IsValidRabbitPosition(int x, int y, char[][] theMap)
{
return x >= 0 && x < theMap[0].Length && y >= 0 && y < theMap.Length &&
(theMap[y][x] == ' ' || IsPositionWithCarrot(x, y, theMap));
}
static bool IsPositionWithCarrot(int x, int y, char[][] theMap)
{
return theMap[y][x] == '7' || theMap[y][x] == '8';
}
static void DrawRabbit(int x, int y, string rabbitIcon)
{
Console.SetCursorPosition(x, y);
Console.ForegroundColor = ConsoleColor.DarkYellow;
Console.Write(rabbitIcon);
Console.ResetColor();
}
static bool MoveRabbit(Input direction, ref int rabbitX, ref int rabbitY, char[][] theMap)
{
int newX = rabbitX, newY = rabbitY;
switch (direction)
{
case Input.MoveLeft: newX--; break;
case Input.MoveRight: newX++; break;
case Input.MoveUp: newY--; break;
case Input.MoveDown: newY++; break;
default: return false;
}
if (IsValidRabbitPosition(newX, newY, theMap))
{
DrawRabbit(rabbitX, rabbitY, " "); // erase
rabbitX = newX;
rabbitY = newY;
DrawRabbit(rabbitX, rabbitY, RabbitIcon); // draw
return true;
}
return false;
}
static void DrawLabyrinth(char[][] theMap)
{
Console.Clear();
for (int y = 0; y < theMap.Length; y++)
{
Console.SetCursorPosition(0, y);
for (int x = 0; x < theMap[0].Length; x++)
{
var ndx = theMap[y][x] - '1';
var c = ndx >= 0 && ndx < MazeChars.Length
? MazeChars[ndx]
: ' ';
Console.ForegroundColor = IsPositionWithCarrot(x, y, theMap)
? ndx == 6 ? ConsoleColor.Red : ConsoleColor.Green
: MazeFgColor;
Console.Write(c);
Console.ResetColor();
}
}
Console.WindowTop = 0; // scroll back up.
}
}
答案 1 :(得分:1)
这是(许多)原因之一,控制台应用程序不适合实时游戏(或者其他任何动画)。如您所示,您绝对可以做到,但不断清除和重绘整个窗口将会闪烁。
因此,真正的解决方案是选择一种更好的动画技术,例如Windows Forms甚至更好的WPF。两者都可以在屏幕上移动一个元素,只重绘“脏”区域,这在减少闪烁方面是巨大的功能。
如果您打算在控制台应用程序中执行此操作,我会通过移动控制台光标,擦除旧位置并重新绘制新角色来进行自己的“脏”检查。它仍然不如真正的图形库那么高效,如果它的大小很大,你的角色可能仍会闪烁,但它会更好地 ton 。