前言
一觉睡醒,发现自己要励志做个健美男,下决心从减脂开始,不能久坐台中,脑中计划出500条减脂计划,本着有想法就实践,抱着凡是重在参与不及结果的脑子,首先写个贪吃啥,立证自己的减脂决心。
项目教程完全小白化,大神不用瞄了,本项目有源码下载。
目录
一、准备工作
1.1、创建控制台应用
打开vs2022,创建一个控制台应用,如图
1.2、创建类
项目中右键,弹出框后选择创建一个类
类名就叫 tanchisha,如果你创建得类有其他内容,都清空,直接复制下面的内容
using System.Data;
static class Tanchisha
{
}
1.3、声明全部变量
//蛇体字符
static string snakeString = "o";
//墙字符
static string wallString = "█";
//食物字符
static string foodString = "*";
//行
static int row = 20;
//列
static int column = 50;
//地图二维坐标
static List<List<int>> mapList = new List<List<int>>();
//食物坐标
static List<int> foodPoint = new List<int>();
//蛇点集合
static List<List<int>> snakePointsList = new List<List<int>>();
//移动速度
static int moveBasicSpd = 100;
//移动速度
static int moveSpdLV = 2;
//是否死亡
static bool isLive = true;
//十进制计数(点阵字体,因每个格子表示一个数字的特性,如果不自己设计十进制计数,会出现计数错乱显示的问题)
static List<int> movecountList = new List<int>() { 0, 0, 0, 0 };
static List<int> eatcountList = new List<int>() { 0, 0, 0, 0 };
上面的方块是长方形(string wallString = "█";),显示在控制台很难看,没关系,接着要调整控制台的字体显示
1.4、点阵字体
右键控制台
选择点阵字体,因为点阵字体是实实在在的正方形矩阵,这样可以把它看成像素点。这样整个游戏视觉会规正很多。
也是这样原因,数字的显示要自己写十进制计数
(点阵字体,因每个格子显示一个数字的特性,如果不自己设计十进制计数,会出现计数错乱显示的问题)
二、主要功能函数
2.1、蛇的操作
创建按键枚举,枚举分类,方便判断
enum Dirction
{
D_up,
S_down,
A_left,
D_right
}
按键判断
//键盘操作
static void KeyCtrl()
{
while (true)
{
switch (Console.ReadKey().Key)
{
case ConsoleKey.UpArrow:
SnakeDirction(Dirction.S_down, Dirction.D_up);
break;
case ConsoleKey.DownArrow:
SnakeDirction(Dirction.D_up, Dirction.S_down);
break;
case ConsoleKey.LeftArrow:
SnakeDirction(Dirction.D_right, Dirction.A_left);
break;
case ConsoleKey.RightArrow:
SnakeDirction(Dirction.A_left, Dirction.D_right);
break;
default:
break;
}
}
}
禁止蛇掉头
static void SnakeDirction(Dirction d1, Dirction d2)
{
curDirction = curDirction != d1 ? d2 : d1;
}
2.2、地图和围墙
0 为可以行动区间,1 为不可行动区间。
static void CreateMap()
{
for (int i = 0; i < row; i++)
{
List<int> temp = new List<int>();
for (int j = 0; j < column; j++)
{
if (i % (row - 1) == 0 || j % (column - 1) == 0)
temp.Add(1);
else
temp.Add(0);
}
mapList.Add(temp);
}
}
i % (row - 1) i 等于 0 或者 i 等于row - 1时,表示左右的围墙
j % (column - 1) j 等于 0 或者 i 等于column - 1时,表示上下的围墙
这样上下左右都被墙裹上了。
//清屏幕
Console.Clear();
// 光标不可见
Console.CursorVisible = false;
for (int i = 0; i < row; i++)
{
for (int j = 0; j < column; j++)
{
if (mapList[i][j] == 0) continue;
SetString(ConsoleColor.Red, j, i, wallString);
}
}
2.3、生成食物
生成食物要确保两点
- 食物不能生成在围墙上
- 食物不能生成在蛇的身上
static void CreateFood()
{
//先将蛇占用的格子取出
List<List<int>> tempList = new List<List<int>>(mapList);
for (int i = 0; i < mapList.Count; i++)
{
for (int j = 0; j < mapList[i].Count; j++)
{
List<int> ijlist = new List<int>() { i, j };
foreach (List<int> innerList in snakePointsList)
{
if (innerList.SequenceEqual(ijlist))
{
tempList[i][j] = 1;
break;
}
}
}
}
//再随机取出非1的地图二维序列,该点阵坐标是食物的生成坐标
var tempElements = tempList.Select((sublist, rowIndex) => new { sublist, rowIndex })
.SelectMany(x => x.sublist.Select((value, colIndex) => new { x.rowIndex, colIndex, value }))
.Where(x => x.value != 1);
var randomElement = tempElements.ElementAt(new Random().Next(tempElements.Count()));
int x = randomElement.colIndex; // 行索引
int y = randomElement.rowIndex; // 列索引
foodPoint = new List<int> { x, y };
SetString(ConsoleColor.Yellow, foodPoint[0], foodPoint[1], foodString);
}
分别对应的方法是:
- 在整个二维地图数组中,先将蛇占用的地图坐标临时赋值为1,这样得到一个临时新地图二维数据
- 再随机取出元素值非1的临时地图二维序列,该点阵坐标是食物的生成坐标
2.4、吃到食物
static void EatFood(int x, int y)
{
if (x == foodPoint[1] && y == foodPoint[0])
{
snakePointsList.Add(new List<int> { snakePointsList[snakePointsList.Count - 1][1], snakePointsList[snakePointsList.Count - 1][0] });
DecimalismNum(eatcountList, "吃食物:", ConsoleColor.Yellow, 15, row + 1, 24);
CreateFood();
}
}
吃到食物,将食物坐标加入到蛇的坐标中
2.5、吃到自己
static void HitSelf(int x, int y)
{
if (snakePointsList.Where(point => point[0] == x && point[1] == y).Any())
{
isLive = false;
Console.SetCursorPosition(column / 2 - 4, row / 2 - 2);
Console.Write("游戏结束");
}
}
吃到自己后,在荧幕中央显示结束语,并且将 isLive = false;
isLive 负责跳出游戏的进程,终止游戏进度。
同理,蛇撞到墙效果一样。
2.6、撞到墙
static void HitWall(int x, int y)
{
if (x >= row || y >= column || x < 0 || y < 0)
{
isLive = false;
Console.SetCursorPosition(column / 2 - 4, row / 2 - 2);
Console.Write("游戏结束");
}
}
2.7、控制蛇
操作键盘的上下左右,控制蛇,蛇按照方向进行坐标位移,并获取唯一点阵坐标加入到蛇坐标,同时清除蛇的后坐标,刷新画面。
static void CreateSnake(int x, int y)
{
string snkStr = string.Empty;
Task.Run(() =>
{
snakePointsList.Add(new List<int>() { x, y });
while (true)
{
switch (curDirction)
{
case Dirction.D_right:
y++;
break;
case Dirction.A_left:
y--;
break;
case Dirction.S_down:
x++;
break;
case Dirction.D_up:
x--;
break;
}
HitSelf(x, y);
HitWall(x, y);
if (isLive == false) break;//不把isLive放到while循环条件,放这里可以蛇死亡后跳出循环,不会出现延迟效果
DecimalismNum(movecountList, "步数:", ConsoleColor.Yellow, 1, row + 1, 8);
Refesh(x, y);
Thread.Sleep(moveBasicSpd * moveSpdLV);
//Task.Delay(moveBasicSpd * moveSpdLV).Wait(); // 不要用Task.Delay
EatFood(x, y);
}
});
}
2.8、十进制显示
前面说了,点阵字体显示数字占据一个格子,多位数计数显示会出现错乱,所以写一个十进制,计数到千位。如果想计数到更多,自己添加。
static void DecimalismNum(List<int> list, string title, ConsoleColor color, int tx, int ty,int nx)
{
list[0] += 1;
if (list[0] % 10 == 0)
{
list[0] = 0;
list[1] += 1;
if (list[1] % 10 == 0)
{
list[1] = 0;
list[2] += 1;
if (list[2] % 10 == 0)
{
list[2] = 0;
list[3] += 1;
}
}
}
SetString(color, tx, ty, title);
Console.SetCursorPosition(nx, row + 1);
Console.Write(list[3].ToString("#"));
Console.SetCursorPosition(nx + 1, row + 1);
Console.Write(list[2].ToString(list[3] > 0 ? list[1].ToString() : "#"));
Console.SetCursorPosition(nx + 2, row + 1);
Console.Write(list[1].ToString(list[2] > 0 ? list[1].ToString() : "#"));
Console.SetCursorPosition(nx + 3, row + 1);
Console.Write(list[0].ToString());
}
这样就能解决,步数,分数等计数问题了。
三、运行图示
四、总结
- Task.Run(() => while (true) { 内容 } }); 是游戏的动画功能模块
- Thread.Sleep() 和 Task.Delay 都有延时执行的功能,但在项目中Thread.Sleep效果更好
- 很多奇特的,特殊的字符,控制台并不会显示。控制台只适合熟悉编程思路。
- 点阵字体8x8是目前唯一的正方形格子,只能显示一个字符,最好二进制自己写
写着写着就这么多了,可能不是特别全,不介意费时就看看吧。有时间还会接着更新。