C# 贪吃蛇
UML
面向对象的七大原则
单一职责原则
开闭原则
里氏替换原则
依赖倒转原则
迪米特法则
接口隔离原则
合成复用原则
总结
贪吃蛇小项目
BeginOrEndScene
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using 贪吃蛇.lesson1;
namespace 贪吃蛇.lesson1
{
abstract class BeginOrEndScene : IsceneUpdate
{
protected int nowSelIndex = 0;
protected string strTitle;
protected string StrOne;
public abstract void EnterJDoSomthing();
public void Update()
{
// 开始或结束游戏逻辑
//选择当前选项,监听键盘输入
Console.ForegroundColor = ConsoleColor.White;
// 显示标题
Console.SetCursorPosition(Game.w/2-strTitle.Length,5);
Console.Write(strTitle);
Console.SetCursorPosition(Game.w / 2 - StrOne.Length, 8);
Console.ForegroundColor = nowSelIndex==0?ConsoleColor.Red:ConsoleColor.White;
Console.Write(StrOne);
Console.SetCursorPosition(Game.w / 2 -4, 10);
Console.ForegroundColor = nowSelIndex == 1 ? ConsoleColor.Red : ConsoleColor.White;
Console.Write("结束游戏");
switch (Console.ReadKey(true).Key)
{
case ConsoleKey.W:
--nowSelIndex;
if (nowSelIndex <0)
{
nowSelIndex = 0;
}
break;
case ConsoleKey.A:
++nowSelIndex;
if (nowSelIndex >1)
{
nowSelIndex = 1;
}
break;
case ConsoleKey.S:
++nowSelIndex;
if (nowSelIndex > 1)
{
nowSelIndex = 1;
}
break;
case ConsoleKey.D:
--nowSelIndex;
if (nowSelIndex < 0)
{
nowSelIndex = 0;
}
break;
case ConsoleKey.J:
EnterJDoSomthing();
break;
}
// 显示下方选项
// 检测输入
}
}
}
BeginScene.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace 贪吃蛇.lesson1
{
internal class BeginScene : BeginOrEndScene
{
public BeginScene() {
strTitle = "贪吃蛇";
StrOne = "开始游戏";
}
public override void EnterJDoSomthing()
{
// 按J键做什么
if (nowSelIndex == 0)
{
Game.ChangeScene(E_SceneType.Game);
}
else {
Environment.Exit(0);
}
}
}
}
Draw
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace 贪吃蛇.lesson1
{
internal interface Draw
{
void Draw();
}
}
EndScene
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace 贪吃蛇.lesson1
{
internal class EndScene : BeginOrEndScene
{
public EndScene() {
strTitle = "结束游戏";
StrOne = "回到开始界面";
}
public override void EnterJDoSomthing()
{
// 按J键做什么
if (nowSelIndex == 0)
{
Game.ChangeScene(E_SceneType.Begin);
}
else
{
Environment.Exit(0);
}
}
}
}
Food
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using 贪吃蛇.lesson1;
namespace 贪吃蛇.lesson1
{
internal class Food : GameObject
{
public Food(Snake snake) {
RandomPos(snake);
}
public override void Draw()
{
Console.SetCursorPosition(pos.x, pos.y);
Console.ForegroundColor = ConsoleColor.DarkCyan;
Console.Write("卐");
}
// 随机位置的行为和蛇的位置有关系
public void RandomPos( Snake snake) {
// 得到蛇
// 随机位置
Random random = new Random();
int x = random.Next(2, Game.w/2-1)*2;
int y = random.Next(1, Game.h / 2 - 4);
pos = new Position(x, y);
// 得到蛇
// 如果重合
if (snake.CheckSamePos(pos))
{
RandomPos(snake);
}
}
}
}
GameObject
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace 贪吃蛇.lesson1
{
abstract class GameObject : Draw
{
// 可以继承接口后,可以把接口中的行为抽象行为
//供子类实现因为是抽象行为,所以子类中是必须去实现
// 游戏对象位置
public Position pos;
public abstract void Draw();
}
}
游戏主逻辑
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace 贪吃蛇.lesson1
{
enum E_SceneType
{
/// <summary>
/// 开始
/// </summary>
Begin,
/// <summary>
/// 游戏
/// </summary>
Game,
/// <summary>
/// 结束
/// </summary>
End
}
class Game
{
public const int w = 80;
public const int h = 40;
public static IsceneUpdate nowSecene;
public Game()
{
Console.CursorVisible = false;
Console.SetWindowSize(w, h);
Console.SetBufferSize(w, h);
ChangeScene(E_SceneType.Begin);
}
public void Start() {
while (true)
{
if (nowSecene != null)
{
nowSecene.Update();
}
}
}
public static void ChangeScene(E_SceneType type)
{
// 切场景之前把上一个场景绘制清理
Console.Clear();
switch (type)
{
case E_SceneType.Begin:
nowSecene = new BeginScene();
break;
case E_SceneType.Game:
nowSecene = new GameScene();
break;
case E_SceneType.End:
nowSecene = new EndScene();
break;
}
}
}
}
GameScene.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using 贪吃蛇.lesson1;
namespace 贪吃蛇.lesson1
{
class GameScene: IsceneUpdate
{
int updateIndex = 0;
int SpeedIndex = 0;
Map map;
Snake snake;
Food food;
public GameScene() {
map = new Map();
snake = new Snake(40,10);
food = new Food(snake);
}
public void Update() {
if (updateIndex % 100000 == 0)
{
map.Draw();
food.Draw();
snake.Move();
snake.Draw();
// 检测是否撞墙
if (snake.CheckEnd(map))
{
Game.ChangeScene(E_SceneType.End);
}
snake.CheckEatFood(food);
updateIndex = 0;
}
updateIndex += 1;
// 在控制台中检测玩家输入,让程序不被卡主
if (Console.KeyAvailable)
{
// 检测输入输出不能在间隔帧里面处理
switch (Console.ReadKey(true).Key)
{
case ConsoleKey.W:
snake.ChangDir(E_MoveDir.up);
break;
case ConsoleKey.A:
snake.ChangDir(E_MoveDir.left);
break;
case ConsoleKey.S:
snake.ChangDir(E_MoveDir.down);
break;
case ConsoleKey.D:
snake.ChangDir(E_MoveDir.right);
break;
}
}
}
//
}
}
IsceneUpdate 场景更新接口
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace 贪吃蛇.lesson1
{
// 场景更新接口
internal interface IsceneUpdate
{
void Update();
}
}
地图Map
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Linq;
using 贪吃蛇.lesson1;
namespace 贪吃蛇.lesson1
{
internal class Map:Draw
{
public Wall[] walls;
public Map() {
walls = new Wall[Game.w+(Game.h-3)*2 ];
int index = 0;
for (int i = 0; i < Game.w; i+=2)
{
walls[index] = new Wall(i,0);
index++;
}
for (int i = 0; i < Game.w; i+=2)
{
walls[index] = new Wall(i, Game.h-2);
index++;
}
for (int i = 1; i < Game.h-2; i++)
{
walls[index] = new Wall(0,i);
index++;
}
for (int i = 1; i < Game.h - 2; i++)
{
walls[index] = new Wall(Game.w-2, i);
index++;
}
}
public void Draw()
{
for (int i = 0; i < walls.Length; i++)
{
walls[i].Draw();
}
}
}
}
坐标Position
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace 贪吃蛇.lesson1
{
internal struct Position
{
public int x;
public int y;
public Position(int x, int y) {
this.x = x; this.y = y;
}
// 各个游戏对象都会去比较位置是不是重合
public static bool operator ==(Position a, Position b)
{
if (a.x == b.x && a.y == b.y) return true;
return false;
}
public static bool operator !=(Position a, Position b)
{
if (a.x == b.x && a.y == b.y) return true;
return false;
}
}
}
主接口
using System;
using 贪吃蛇.lesson1;
namespace 贪吃蛇;
class Program
{
static void Main(string[] args)
{
Game games = new Game();
games.Start();
//games.ChangeScene(E_SceneType.Game);
}
}
蛇类
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http.Headers;
using System.Security.Principal;
using System.Text;
using System.Threading.Tasks;
using 贪吃蛇.lesson1;
namespace 贪吃蛇.lesson1
{
enum E_MoveDir
{
/// <summary>
/// 上
/// </summary>
up,
/// <summary>
/// 下
/// </summary>
down,
/// <summary>
/// 左
/// </summary>
left,
/// <summary>
/// 右
/// </summary>
right
}
internal class Snake : Draw
{
SnakeBody[] bodys;
//记录蛇身体长度
int nowNum;
E_MoveDir dir;
public Snake(int x, int y)
{
bodys = new SnakeBody[10000];
bodys[0] = new SnakeBody(E_SnakeBody_Type.Head, x, y);
nowNum = 1;
dir = E_MoveDir.right;
}
public void Draw()
{
for (int i = 0; i < nowNum; i++)
{
bodys[i].Draw();
}
}
public void Move()
{
// 擦除最后一个位置
SnakeBody lastBody = bodys[nowNum - 1];
Console.SetCursorPosition(lastBody.pos.x, lastBody.pos.y);
// 擦屁股
Console.Write(" ");
// 在蛇头移动之前,从蛇尾开始,不停的让后一个位置等于前一个位置
for (int i = nowNum-1; i > 0; i--)
{
bodys[i].pos = bodys[i-1].pos;
}
switch (dir)
{
case E_MoveDir.up:
--bodys[0].pos.y;
break;
case E_MoveDir.down:
++bodys[0].pos.y;
break;
case E_MoveDir.left:
bodys[0].pos.x -= 2;
break;
case E_MoveDir.right:
bodys[0].pos.x += 2;
break;
default:
break;
}
}
// 改变方向
public void ChangDir(E_MoveDir dir)
{
// 只有头部时候可以直接左转右,右转左,上转下,下转上
//有身体时,这种情况就不能直接转
if (dir == this.dir || nowNum > 1 &&
dir == E_MoveDir.left && dir == E_MoveDir.right ||
dir == E_MoveDir.right && dir == E_MoveDir.left ||
dir == E_MoveDir.up && dir == E_MoveDir.down ||
dir == E_MoveDir.down && dir == E_MoveDir.up
)
{
return;
}
this.dir = dir;
}
// 撞墙撞身体
public bool CheckEnd(Map map)
{
for (int i = 0; i < map.walls.Length; i++)
{
if (bodys[0].pos == map.walls[i].pos)
{
return true;
}
}
for (int i = 1; i < nowNum; i++)
{
if (bodys[0].pos == bodys[i].pos)
{
return true;
}
}
return false;
}
//吃食物
// 通过传入一个位置判断这个位置是否和蛇重合
public bool CheckSamePos(Position position)
{
for (int i = 0; i < nowNum; i++)
{
if (bodys[i].pos == position)
{
return true;
}
}
return false;
}
public void CheckEatFood(Food food)
{
if (bodys[0].pos == food.pos ) {
// 吃到了就应该让食物位置随机,增加蛇的身体长度
food.RandomPos(this);
ADDBody();
}
}
// 长身体
private void ADDBody() {
SnakeBody frontBody = bodys[nowNum-1];
// 先长
bodys[nowNum] = new SnakeBody(E_SnakeBody_Type.Body,frontBody.pos.x,frontBody.pos.y);
// 加长度
++nowNum;
}
}
}
蛇身体
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using 贪吃蛇.lesson1;
namespace 贪吃蛇.lesson1
{
/// <summary>
/// 蛇身体类型
/// </summary>
enum E_SnakeBody_Type
{
/// <summary>
/// 头
/// </summary>
Head,
/// <summary>
/// 身体
/// </summary>
Body
}
internal class SnakeBody : GameObject
{
private E_SnakeBody_Type Type;
public SnakeBody( E_SnakeBody_Type type ,int x, int y ) {
//
this.Type = type;
this.pos = new Position(x,y);
}
public override void Draw()
{
Console.SetCursorPosition(pos.x,pos.y );
Console.ForegroundColor = Type == E_SnakeBody_Type.Head ? ConsoleColor.Yellow : ConsoleColor.Green;
Console.Write(Type == E_SnakeBody_Type.Head ? "●" : "◎");
}
}
}
墙
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using 贪吃蛇.lesson1;
namespace 贪吃蛇.lesson1
{
internal class Wall : GameObject
{
public Wall(int x ,int y)
{
pos = new Position(x,y);
}
public override void Draw()
{
Console.SetCursorPosition(pos.x,pos.y);
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("▆");
}
}
}