贪吃蛇小项目

This commit is contained in:
2025-09-21 20:11:21 +08:00
parent 0a1446e7b3
commit 116b65164b
18 changed files with 695 additions and 25 deletions
+78
View File
@@ -0,0 +1,78 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace
{
class Game
{
//窗口宽高
public const int h = 20;
public const int w = 80;
//当前游戏场景
public static ISceneUpdate nowScene;
public Game()
{
Console.CursorVisible = false;
Console.SetWindowSize(w, h);
Console.SetBufferSize(w, h);
ChangeScene(E_Scene.Begin);//初始场景为开始场景
}
public void Start()
{
//游戏主循环
while (true)
{
try
{
if (nowScene != null)
{
nowScene.Update();//场景不为空就更新场景
}
}
catch
{
}
}
}
public static void ChangeScene(E_Scene type)
{
//切场景时先擦除上一次绘制的内容
Console.Clear();
switch (type)
{
case E_Scene.Begin:
nowScene = new BeginScene();
break;
case E_Scene.Game:
nowScene = new GameScene();
break;
case E_Scene.End:
nowScene = new EndScene();
break;
default:
break;
}
}
}
enum E_Scene
{
/// <summary>
/// 开始场景
/// </summary>
Begin,
/// <summary>
/// 游戏场景
/// </summary>
Game,
/// <summary>
/// 结束场景
/// </summary>
End,
}//游戏场景枚举
}
@@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace
{
/// <summary>
/// 场景更新接口
/// </summary>
interface ISceneUpdate
{
void Update()
{
}
}
}
@@ -0,0 +1,48 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace
{
abstract class BaseScene : ISceneUpdate
{
protected int nowSceneIndex = 0;
protected string title;
protected string str1;
public abstract void PressEnter();
public void Update()
{
//要完成的内容:绘制开始和结束场景,并完成切换选择功能
Console.ForegroundColor = ConsoleColor.White;
//绘制标题
Console.SetCursorPosition(Game.w / 2 - title.Length, 5);
Console.Write(title);
//绘制选项
Console.SetCursorPosition(Game.w / 2 - str1.Length, 8);
Console.ForegroundColor = nowSceneIndex == 0 ? ConsoleColor.Red : ConsoleColor.White;
Console.Write(str1);
Console.SetCursorPosition(Game.w / 2 - 4, 10);
Console.ForegroundColor = nowSceneIndex == 1 ? ConsoleColor.Red : ConsoleColor.White;
Console.Write("结束游戏");
//根据nowSceneIndex绘制wsj选中的索引
switch (Console.ReadKey(true).Key)
{
case ConsoleKey.W:
nowSceneIndex = 0;
break;
case ConsoleKey.S:
nowSceneIndex = 1;
break;
case ConsoleKey.Enter:
PressEnter();
break;
}
}
}
}
@@ -0,0 +1,31 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace
{
class BeginScene : BaseScene
{
public BeginScene()
{
title = "贪吃蛇小游戏";
str1 = "开始游戏";
}
public override void PressEnter()
{
//根据nowSceneIndex的值决定按下回车后做什么
if( nowSceneIndex == 0 )
{
//切换到开始游戏
Game.ChangeScene(E_Scene.Game);
}
else
{
//结束游戏
Environment.Exit(0);
}
}
}
}
@@ -0,0 +1,32 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading.Tasks;
namespace
{
class EndScene : BaseScene
{
public EndScene()
{
title = "游戏结束";
str1 = "重新开始";
}
public override void PressEnter()
{
//根据nowSceneIndex的值决定按下回车后做什么
if (nowSceneIndex == 0)
{
//切换到开始游戏
Game.ChangeScene(E_Scene.Begin);
}
else
{
//结束游戏
Environment.Exit(0);
}
}
}
}
@@ -0,0 +1,65 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace
{
class GameScene : ISceneUpdate
{
Map map;
Snake snake;
Food food;
public GameScene()
{
map = new Map();
snake = new Snake(40, 10);
food = new Food(snake);
}
public void Update()
{
map.Draw();//画地图
food.Draw();//画食物
snake.Move();//让蛇动起来
snake.Draw();//画蛇
if (snake.CheckEnd(map))//检测游戏是否结束
{
Game.ChangeScene(E_Scene.End);
}
snake.CheckEat(food);//检测蛇是否吃到食物
Thread.Sleep(200);//控制蛇的移动速度
//检测用户输入
if (Console.KeyAvailable)//Console.KeyAvailable的作用是:如果有按键输入才返回Ture,防止卡住
{
switch (Console.ReadKey(true).Key)
{
case ConsoleKey.W:
case ConsoleKey.UpArrow:
snake.ChangeDirection(E_MoveDirection.Up);
//向上
break;
case ConsoleKey.S:
case ConsoleKey.DownArrow:
snake.ChangeDirection(E_MoveDirection.Down);
//向下
break;
case ConsoleKey.A:
case ConsoleKey.LeftArrow:
snake.ChangeDirection(E_MoveDirection.Left);
//向左
break;
case ConsoleKey.D:
case ConsoleKey.RightArrow:
snake.ChangeDirection(E_MoveDirection.Right);
//向右
break;
default:
break;
}
}
}
}
}
@@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace
{
abstract class GameObject : IDraw
{
//当前位置
public Position pos;
//绘制对象
//可以继承接口后,把接口中的方法变成抽象方法,让子类去实现
//因为时抽象行为,所以子类必须实现
public abstract void Draw();
}
}
+13
View File
@@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace
{
interface IDraw
{
void Draw();
}
}
@@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace
{
struct Position
{
public int x;
public int y;
public Position(int x, int y)
{
this.x = x;
this.y = y;
}
//重载==和!=运算符来判断两个Position是否相等
public static bool operator == (Position p1, Position p2)
{
return p1.x == p2.x && p1.y == p2.y;
}
public static bool operator !=(Position p1, Position p2)
{
return !(p1 == p2);
}
}
}
+36
View File
@@ -0,0 +1,36 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace
{
class Food : GameObject
{
public Food(Snake snake)
{
RandomPos(snake);
}
public override void Draw()
{
Console.ForegroundColor = ConsoleColor.Cyan;
Console.SetCursorPosition(pos.x, pos.y);
Console.Write("◎");
}
public void RandomPos(Snake snake)
{
Random r = new Random();
int x = r.Next(1, Game.w / 2 - 1) * 2; //1-38的随机数
int y = r.Next(2, Game.h - 3); //1-18的随机数
pos = new Position(x, y);
//判断生成的位置是否和蛇身重合
if (snake.CheckSamePos(pos))
{
RandomPos(snake);
}
}
}
}
@@ -0,0 +1,29 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace
{
enum E_BodyType
{
Head, //蛇头
Body, //蛇身
}
class SnakeBody : GameObject
{
private E_BodyType bodyType; //身体类型
public SnakeBody(int x, int y, E_BodyType bodyType)
{
pos = new Position(x, y);
this.bodyType = bodyType;
}
public override void Draw()
{
Console.SetCursorPosition(pos.x, pos.y);
Console.ForegroundColor = bodyType == E_BodyType.Head ? ConsoleColor.Yellow : ConsoleColor.Green;
Console.Write(bodyType == E_BodyType.Head ? "●" : "○");
}
}
}
+22
View File
@@ -0,0 +1,22 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace
{
class Wall : GameObject
{
public Wall(int x, int y)
{
pos = new Position(x, y);
}
public override void Draw()
{
Console.ForegroundColor = ConsoleColor.Red;
Console.SetCursorPosition(pos.x, pos.y);
Console.Write("■");
}
}
}
+49
View File
@@ -0,0 +1,49 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace
{
class Map : IDraw
{
public Wall[] walls; //存放墙位置的数组
public Map()
{
//初始化墙
walls = new Wall[Game.w + (Game.h -3) * 2];
int index = 0;
//上边墙
for (int x = 0; x < Game.w; x+=2)
{
walls[index++] = new Wall(x, 0);
}
//下边墙
for (int x = 0; x < Game.w; x+=2)
{
walls[index++] = new Wall(x, Game.h - 2);
}
//左边墙
for (int y = 1; y < Game.h - 2; y++)
{
walls[index++] = new Wall(0, y);
}
//右边墙
for (int y = 1; y < Game.h - 2; y++)
{
walls[index++] = new Wall(Game.w - 2, y);
}
}
public void Draw()
{
for (int x = 0; x < walls.Length; x++)
{
walls[x].Draw();
}
}
}
}
+141
View File
@@ -0,0 +1,141 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace
{
enum E_MoveDirection
{
Up,
Down,
Left,
Right
}
class Snake : IDraw
{
E_MoveDirection moveDirection;//记录蛇当前的移动方向
SnakeBody[] bodies; //存放蛇身体的数组
int nowNum;//用一个变量记录蛇的长度
public Snake(int x, int y)
{
moveDirection = E_MoveDirection.Right;
bodies = new SnakeBody[200];
bodies[0] = new SnakeBody(x, y, E_BodyType.Head);
nowNum = 1;
}
public void Draw()
{
//只画出当前的蛇身
for (int i = 0; i < nowNum; i++)
{
bodies[i].Draw();
}
}
#region Lesson7
//蛇的移动
public void Move()
{
//移动前擦除前一个位置
SnakeBody lastBody = bodies[nowNum - 1];
Console.SetCursorPosition(lastBody.pos.x, lastBody.pos.y);
Console.Write(" ");
for(int i = nowNum - 1; i > 0; i--)
{
bodies[i].pos = bodies[i - 1].pos;
}
switch (moveDirection)
{
case E_MoveDirection.Up:
bodies[0].pos.y--;
break;
case E_MoveDirection.Down:
bodies[0].pos.y++;
break;
case E_MoveDirection.Left:
bodies[0].pos.x -= 2;
break;
case E_MoveDirection.Right:
bodies[0].pos.x += 2;
break;
}
}
#endregion
#region Lesson8
public void ChangeDirection(E_MoveDirection newDirection)
{
//不能直接掉头
if ( newDirection == moveDirection ||
nowNum > 1 && (moveDirection == E_MoveDirection.Right && newDirection == E_MoveDirection.Left ) ||
nowNum > 1 && (moveDirection == E_MoveDirection.Left && newDirection == E_MoveDirection.Right) ||
nowNum > 1 && (moveDirection == E_MoveDirection.Up && newDirection == E_MoveDirection.Down) ||
nowNum > 1 && (moveDirection == E_MoveDirection.Down && newDirection == E_MoveDirection.Up) )
{
return;
}
moveDirection = newDirection;
}
#endregion
#region Lesson9
//碰撞检测
public bool CheckEnd(Map map)
{
//是否和墙体位置重合
for(int i = 0; i < map.walls.Length; i++)
{
if (bodies[0].pos == map.walls[i].pos)
{
return true;
}
}
//是否和自己身体位置重合
for(int i = 1; i < nowNum; i++)
{
if (bodies[0].pos == bodies[i].pos)
{
return true;
}
}
return false;
}
#endregion
#region Lesson10
public bool CheckSamePos(Position p)
{
for(int i = 0; i < nowNum; i++)
{
if (bodies[i].pos == p)
{
return true;
}
}
return false;
}
public void CheckEat(Food food)
{
if (bodies[0].pos == food.pos)
{
//吃到了就再次随机生成食物,并且让蛇变长
food.RandomPos(this);
GrowUp();
}
}
#endregion
#region Lesson11
private void GrowUp()
{
SnakeBody lastBody = bodies[nowNum - 1];
bodies[nowNum] = new SnakeBody(lastBody.pos.x, lastBody.pos.y, E_BodyType.Body);
nowNum++;
}
#endregion
}
}
+11
View File
@@ -0,0 +1,11 @@
namespace
{
class Program
{
static void Main(string[] args)
{
Game game = new Game();
game.Start();
}
}
}
+14
View File
@@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<Folder Include="Lesson7\" />
</ItemGroup>
</Project>