写在前面:
??????? 有一天突发奇想,如果我只用算法实现基本逻辑,让控制台输出图像,不用任何组件,只用一门编程语言,能否实现一个可以人机交互的小游戏,抱着试一试的想法,博主用c#控制台程序复原了贪吃蛇玩法的小游戏。
目录
一、可视化界面
1.创建地图
?二、贪吃蛇基本逻辑
1.蛇的定义
2.控制移动
?3.创建食物
?三、大功告成
一、可视化界面
????????众所周知,控制台程序通常没有可视化的界面,只是通过字符串来显示或者监控程序,在使用控制台写程序时,我们只关心数据,不在乎界面。但是,如果我们偏偏反其道而行之,用控制台可视化形式将游戏界面动态反馈给玩家,该怎么办呢。
????????在c#中,我们用Console.WriteLine()在控制台打印一行数据,如果我们将它嵌套在一个循环里面,那么它会不停的在控制台一行一行的打印数据。此时,对于输出的某一行来说,可以看作动画里面的一帧。(动画是因为通过连续播放许多帧静止的画面,使眼睛产生连续动作的错觉。其实说白了就是人的视觉停留造成了动画会动。)
????????明白了这一点,我们写一个while循环,循环打印一段代码,并且,每次循环结束,让代码停顿0.5秒,使得人眼能跟上动态的帧率。实现一个石头从高处慢慢降落的过程。
代码演示:
int stonex = 10;//石头x初始值
int stoney = 0;//石头y初始值
while (true)
{
for (int y = 0; y < 20; y++)
{
for (int x = 0; x < 20; x++)
{
if (y == 0||y==19)
{
Console.Write("-");
continue;
}
if (x == 0 || x == 19)
{
Console.Write("|");
continue;
}
if (x == stonex && y == stoney)
{
Console.Write("0");//输出石头
continue;
}
Console.Write(" ");
}
Console.WriteLine();
}
stoney++;//石头y值+1
System.Threading.Thread.Sleep(500);//停顿0.5秒
}
1.创建地图
明白原理之后,我们只要定义一个二维数组,用来存储地图的数据。再定义一个地图显示的方法,按照预定好的规则循环输出就好了。
public static void ShowMap(int[,] map)
{
for (int y = 0; y < map.GetLength(0); y++)
{
for (int x = 0; x < map.GetLength(1); x++)
{
if (x == 0 || x == map.GetLength(1) - 1)
{
Console.Write('|');
continue;
}
if (y == 0 || y == map.GetLength(0) - 1)
{
Console.Write('-');
continue;
}
if (map[y, x] == 0)
{
Console.Write(" ");
continue;
}
if (map[y, x] == 1)
{
Console.Write(0);
continue;
}
if (map[y, x] == 6)
{
Console.Write(0);
continue;
}
}
Console.WriteLine();
}
}
}
?二、贪吃蛇基本逻辑
??????? 玩家操控一条蛇,上下左右控制蛇的方向,寻找吃的东西,每吃一口就能得到一定的积分,而且蛇的身子会越吃越长,身子越长玩的难度就越大,不能碰墙,不能咬到自己的身体,更不能咬自己的尾巴。
1.蛇的定义
首先定义一个“蛇“类,我们可以把蛇看成若干个节点,对于每个节点来说,都有一个前节点和尾节点,同时还有一个当前节点的位置。、
public class Snake
{
private Direction dir;//当前位置
public Snake qianqu;//前节点
public Snake houji;//后节点
//构造函数
public Snake(Direction dir)
{
Dir = dir;
}
public Direction Dir { get => dir; set => dir = value; }
}
2.控制移动
把蛇按节点分开来看之后,我们其实只用操作头节点,给定一个方向,头节点向该方向移动,而对于除了头节点以外的每个节点,我们要做的是,将后一个节点位置移动到前一个节点位置。这样,就实现了贪吃蛇的移动。
public static void Move(Direction dir,ref Snake snake,ref int[,] map)
{
if (snake.houji != null) {
Move(dir,ref snake.houji,ref map);
}
if (snake.qianqu == null)
{
snake.Dir.X += dir.X;
snake.Dir.Y += dir.Y;
}
snake.Dir.X = snake.qianqu.Dir.X;
snake.Dir.Y = snake.qianqu.Dir.Y;
}
移动方法定义好以后,我们只用传入一个方向,就可以改变蛇移动的方向,但控制台程序是单线程的程序,程序要怎么知道什么时候接收参数改变方向呢,这时候就用到多线程,第一个线程就是主函数,用来运行游戏的主要逻辑,第二个程序用来接收用户的输入,当用户按下按键之后,立马改变蛇的方向。
public static void TancsMain()
{
direction = new Direction();
ThreadStart start = new ThreadStart(run);
Thread startThread = new Thread(start);
startThread.Start();
while (true)
{
ConsoleKeyInfo x = Console.ReadKey();
if (x != null)
{
switch (x.KeyChar)
{
case 's':
if (direction.X != -1)
{
direction.Y = 0;
direction.X = 1;
}
break;
case 'w':
if (direction.X != 1)
{
direction.Y = 0;
direction.X = -1;
}
break;
case 'a':
if (direction.Y != 1)
{
direction.Y = -1;
direction.X = 0;
}
break;
case 'd':
if (direction.Y != -1)
{
direction.Y = 1;
direction.X = 0;
}
break;
default:
break;
}
}
}
}
?3.创建食物
用随机数随机创建一个食物位置,当蛇吃到食物时,创建一个节点。
public static void CreatFood(ref int[,] map)
{
for (int y = 0; y < map.GetLength(0); y++)
{
for (int x = 0; x < map.GetLength(1); x++)
{
if (map[y, x] == 6)
{
return;
}
}
}
Random random = new Random();
random.Next(1, 19);
map[random.Next(1, 19), random.Next(1, 39)] = 6;
}
?
?三、大功告成
到此、通过控制台实现贪吃蛇的小游戏就结束啦,博主也只是初学者,在枯燥的学习当中给自己找点乐子,如果看完了本篇文章,有兴趣的小伙伴也可以尝试做一个玩玩,实现的方法有很多,也不局限于我的思路。完整代码附上:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
namespace ConsoleApp1
{
class Tancs
{
public static Direction direction;
public static void TancsMain()
{
direction = new Direction();
ThreadStart start = new ThreadStart(run);
Thread startThread = new Thread(start);
startThread.Start();
while (true)
{
ConsoleKeyInfo x = Console.ReadKey();
if (x != null)
{
switch (x.KeyChar)
{
case 's':
if (direction.X != -1)
{
direction.Y = 0;
direction.X = 1;
}
break;
case 'w':
if (direction.X != 1)
{
direction.Y = 0;
direction.X = -1;
}
break;
case 'a':
if (direction.Y != 1)
{
direction.Y = -1;
direction.X = 0;
}
break;
case 'd':
if (direction.Y != -1)
{
direction.Y = 1;
direction.X = 0;
}
break;
default:
break;
}
}
}
}
public static void CreatFood(ref int[,] map)
{
for (int y = 0; y < map.GetLength(0); y++)
{
for (int x = 0; x < map.GetLength(1); x++)
{
if (map[y, x] == 6)
{
return;
}
}
}
Random random = new Random();
random.Next(1, 19);
map[random.Next(1, 19), random.Next(1, 39)] = 6;
}
public static void run()
{
direction.X = 1;
direction.Y = 0;
int[,] map = new int[20, 40];
ShowMap(map);
Console.ReadLine();
Snake snake = new Snake(new Direction());
snake.Dir.X = 3;
snake.Dir.Y = 8;
Snake snake2 = new Snake(new Direction());
snake2.Dir.X = 2;
snake2.Dir.Y = 8;
Snake snake3 = new Snake(new Direction());
snake2.Dir.X = 1;
snake2.Dir.Y = 8;
Snake snake4 = new Snake(new Direction());
snake2.Dir.X = 0;
snake2.Dir.Y = 8;
snake.houji = snake2;
snake2.qianqu = snake;
snake2.houji = snake3;
snake3.qianqu = snake2;
snake3.houji = snake4;
snake4.qianqu = snake3;
while (true)
{
Move(direction,ref snake,ref map);
ClearMap(ref map);
CreatFood(ref map);
ChangeMap(snake, ref map);
ShowMap(map);
System.Threading.Thread.Sleep(500);
}
}
public static void Move(Direction dir,ref Snake snake,ref int[,] map)
{
if (snake.houji != null) {
Move(dir,ref snake.houji,ref map);
}
if (snake.qianqu == null)
{
snake.Dir.X += dir.X;
snake.Dir.Y += dir.Y;
if (map[snake.Dir.X, snake.Dir.Y] == 6)
{
map[snake.Dir.X, snake.Dir.Y] = 0;
Snake temp = snake;
while (temp.houji != null)
{
temp = temp.houji;
}
Snake snake2 = new Snake(new Direction());
temp.houji = snake2;
snake2.qianqu = temp;
snake2.Dir.X = temp.Dir.X;
snake2.Dir.Y = temp.Dir.Y;
}
return;
}
snake.Dir.X = snake.qianqu.Dir.X;
snake.Dir.Y = snake.qianqu.Dir.Y;
}
public static void ChangeMap(Snake snake,ref int[,] map)
{
map[snake.Dir.X, snake.Dir.Y] = 1;
if (snake.houji != null)
{
ChangeMap(snake.houji, ref map);
}
}
public static void ClearMap(ref int[,] map)
{
for (int y = 0; y < map.GetLength(0); y++)
{
for (int x = 0; x < map.GetLength(1); x++)
{
if (map[y, x] != 6)
{
map[y, x] = 0;
}
}
}
}
public static void ShowMap(int[,] map)
{
for (int y = 0; y < map.GetLength(0); y++)
{
for (int x = 0; x < map.GetLength(1); x++)
{
if (x == 0 || x == map.GetLength(1) - 1)
{
Console.Write('|');
continue;
}
if (y == 0 || y == map.GetLength(0) - 1)
{
Console.Write('-');
continue;
}
if (map[y, x] == 0)
{
Console.Write(" ");
continue;
}
if (map[y, x] == 1)
{
Console.Write(0);
continue;
}
if (map[y, x] == 6)
{
Console.Write(0);
continue;
}
}
Console.WriteLine();
}
}
}
public class Snake
{
private Direction dir;//当前位置
public Snake qianqu;//前节点
public Snake houji;//后节点
//构造函数
public Snake(Direction dir)
{
Dir = dir;
}
public Direction Dir { get => dir; set => dir = value; }
}
public class Direction
{
private int x;
private int y;
public int X { get => x; set => x = value; }
public int Y { get => y; set => y = value; }
}
}
?
|