16.代码汇总
16.1 主入口
using System;
using 贪食蛇.Lesson1;
namespace 贪食蛇
{
/// <summary>
/// 游戏入口
/// </summary>
class Program
{
static void Main(string[] args)
{
//new出一个游戏类
Game game = new Game();
//开始游戏
game.Start();
}
}
}
16.2 游戏类
using System;
using System.Collections.Generic;
using System.Text;
using 贪食蛇.Lesson2;
namespace 贪食蛇.Lesson1
{
/// <summary>
/// 场景类型枚举
/// </summary>
enum E_SceneType
{
/// <summary>
/// 开始场景
/// </summary>
Begin,
/// <summary>
/// 游戏场景
/// </summary>
Game,
/// <summary>
/// 结束场景
/// </summary>
End,
}
/// <summary>
/// 游戏类
/// </summary>
class Game
{
//游戏窗口宽高
public const int w = 80;
public const int h = 20;
//游戏帧更新接口变量 用来标记 当前选中的场景
public static ISceneUpdate nowScene;
//构造函数
//初始化控制台和场景
public Game()
{
//初始化控制台
Console.CursorVisible = false;
Console.SetWindowSize(w, h);
Console.SetBufferSize(w, h);
//初始化场景
ChangeScene(E_SceneType.Begin);
}
//游戏开始的方法
public void Start()
{
//游戏主循环 主要负责 游戏场景逻辑的更新
while (true)
{
//判断当前游戏场景不为空 就更新
if( nowScene != null )
{
nowScene.Update();
}
}
}
//场景切换方法
public static void ChangeScene(E_SceneType type)
{
//切场景之前 应该把上一个场景的绘制内容擦掉
Console.Clear();
//根据场景枚举new出新的对应的场景类
switch (type)
{
case E_SceneType.Begin:
nowScene = new BeginScene();
break;
case E_SceneType.Game:
nowScene = new GameScene();
break;
case E_SceneType.End:
nowScene = new EndScene();
break;
}
}
}
}
16.3 游戏帧更新接口
using System;
using System.Collections.Generic;
using System.Text;
namespace 贪食蛇.Lesson1
{
/// <summary>
/// 游戏帧更新接口
/// </summary>
interface ISceneUpdate
{
/// <summary>
/// 更新方法
/// </summary>
void Update();
}
}
16.4 开始场景和结束场景基类
using System;
using System.Collections.Generic;
using System.Text;
using 贪食蛇.Lesson1;
namespace 贪食蛇.Lesson2
{
//开始场景和结束场景基类 是抽象类 继承游戏帧更新接口
abstract class BeginOrEndBaseScene : ISceneUpdate
{
//标题名字
protected string strTitle;
//选项索引 当前选择的是选项几 从0开始
protected int nowSelectIndex = 0;
//选项0的名字
protected string strZero;
//选项1的名字
protected const string strOne = "结束游戏";
//抽象方法 按j键进入不同选项后 做的事情
public abstract void EnterJDoSomthing();
//实现开始场景和结束场景基类里的更新方法
public void Update()
{
//开始和结束场景的 游戏逻辑
//选择当前的选项 然后 监听 键盘输入 wsj
//设置字体颜色
Console.ForegroundColor = ConsoleColor.White;
//设置标题显示位置 打印标题
Console.SetCursorPosition(Game.w / 2 - strTitle.Length, 5);
Console.Write(strTitle);
//设置选项0和选项1显示位置 打印选项0和选项1 根据选项索引设置选项颜色
Console.SetCursorPosition(Game.w / 2 - strZero.Length, 8);
Console.ForegroundColor = nowSelectIndex == 0 ? ConsoleColor.Red : ConsoleColor.White;
Console.Write(strZero);
Console.SetCursorPosition(Game.w / 2 - 4, 10);
Console.ForegroundColor = nowSelectIndex == 1 ? ConsoleColor.Red : ConsoleColor.White;
Console.Write(strOne);
//检测输入 ws上下切换当前选项 j确认进入当前选项
switch(Console.ReadKey(true).Key)
{
case ConsoleKey.W:
--nowSelectIndex;
if( nowSelectIndex < 0 )
{
nowSelectIndex = 0;
}
break;
case ConsoleKey.S:
++nowSelectIndex;
if (nowSelectIndex > 1)
{
nowSelectIndex = 1;
}
break;
case ConsoleKey.J:
EnterJDoSomthing();
break;
}
}
}
}
16.5 开始场景类
using System;
using System.Collections.Generic;
using System.Text;
using 贪食蛇.Lesson1;
namespace 贪食蛇.Lesson2
{
//开始场景类 继承开始场景和结束场景基类
class BeginScene : BeginOrEndBaseScene
{
//开始场景类构造函数
public BeginScene()
{
strTitle = "贪食蛇";
strZero = "开始游戏";
}
//按J键做什么的逻辑
public override void EnterJDoSomthing()
{
//选项0是开始游戏
if (nowSelectIndex == 0)
{
//进入游戏场景
Game.ChangeScene(E_SceneType.Game);
}
//选项1是结束游戏
else
{
Environment.Exit(0);
}
}
}
}
16.6 结束场景类
using System;
using System.Collections.Generic;
using System.Text;
using 贪食蛇.Lesson1;
namespace 贪食蛇.Lesson2
{
//结束场景类 继承开始场景和结束场景基类
class EndScene : BeginOrEndBaseScene
{
//结束场景类构造函数
public EndScene()
{
strTitle = "结束游戏";
strZero = "回到开始界面";
}
//按J键做什么的逻辑
public override void EnterJDoSomthing()
{
//选项0是回到开始界面
if (nowSelectIndex == 0)
{
Game.ChangeScene(E_SceneType.Begin);
}
//选项1是结束游戏
else
{
Environment.Exit(0);
}
}
}
}
16.7 游戏场景类
using System;
using System.Collections.Generic;
using System.Text;
using 贪食蛇.Lesson1;
using 贪食蛇.Lesson4;
using 贪食蛇.Lesson5;
using 贪食蛇.Lesson6;
namespace 贪食蛇.Lesson2
{
//游戏场景类 继承游戏帧更新接口
class GameScene : ISceneUpdate
{
//地图类变量 管理所有墙壁
Map map;
//蛇类变量 管理蛇相关
Snake snake;
//食物类变量
Food food;
//while循环次数 判断当前while循环进了多少次
int whileLoopTimes = 0;
//游戏场景类构造函数
public GameScene()
{
//初始化地图类变量
map = new Map();
//初始化一个蛇
snake = new Snake(40, 10);
//初始化一个食物
food = new Food(snake);
}
//实现游戏场景里的更新方法 在游戏类中的start方法中的while循环中调用
public void Update()
{
//while循环运行的太快 每次进一次while 绘制一次的话 游戏速度太快 要降速
//用while循环次数来判断 每当进入while循环10000次后 再进行一次绘制
//可以理解为每10000帧更新一次
if (whileLoopTimes % 10000 == 0)
{
//绘制地图类
map.Draw();
//绘制食物
food.Draw();
//蛇移动 先移动再绘制蛇
snake.Move();
//绘制蛇类
snake.Draw();
//检测是否撞墙或撞身体
if(snake.CheckEnd(map))
{
//切换为结束场景
Game.ChangeScene(E_SceneType.End);
}
//判断是否吃到食物
snake.CheckEatFood(food);
//实际update了 重写开始计数
whileLoopTimes = 0;
}
//每进一次while循环加一
++whileLoopTimes;
//检测输入输出 不能再间隔帧(就是while循环一万次)里面去处理 应该每次都检测 这样才准确 否则会有延迟
//假如检测玩家输入用 Console.ReadKey(true).Key 程序会卡住等待输入
//因为Console.ReadKey(true).Key会一直等
//想在控制台中 检测玩家输入 让程序不被检测卡住
//使用 Console.KeyAvailable
//判断 有没有键盘输入 如果有 才为true 否则一直false 就不会被卡住
if ( Console.KeyAvailable )
{
//根据输入
switch (Console.ReadKey(true).Key)
{
case ConsoleKey.W:
snake.ChangeDir(E_MoveDir.Up);
break;
case ConsoleKey.A:
snake.ChangeDir(E_MoveDir.Left);
break;
case ConsoleKey.S:
snake.ChangeDir(E_MoveDir.Down);
break;
case ConsoleKey.D:
snake.ChangeDir(E_MoveDir.Right);
break;
}
}
}
}
}
16.8 绘制接口
using System;
using System.Collections.Generic;
using System.Text;
namespace 贪食蛇.Lesson3
{
// 绘制接口
interface IDraw
{
// 绘制方法
void Draw();
}
}
16.9 位置结构体
using System;
using System.Collections.Generic;
using System.Text;
namespace 贪食蛇.Lesson3
{
//位置结构体
struct Position
{
//位置信息xy坐标
public int x;
public int y;
//位置构造函数 必须初始化所有变量
public Position(int x, int y)
{
this.x = x;
this.y = y;
}
//贪食蛇中 肯定是存在 位置的比较
//各个游戏对象 都会去比较位置是不是重合
//运算符重载 重载==和!=运算符
public static bool operator ==(Position p1, Position p2)
{
if( p1.x == p2.x && p1.y == p2.y)
{
return true;
}
return false;
}
public static bool operator !=(Position p1, Position p2)
{
if (p1.x == p2.x && p1.y == p2.y)
{
return false;
}
return true;
}
}
}
16.10 游戏对象基类
using System;
using System.Collections.Generic;
using System.Text;
namespace 贪食蛇.Lesson3
{
//游戏对象基类 是抽象类 继承绘制接口
abstract class GameObject : IDraw
{
//游戏对象位置
public Position pos;
//可以继承接口后 把接口中的行为 编程 抽象行为
//供子类去实现 因为是抽象行为 所以子类中是必须去实现
public abstract void Draw();
}
}
16.11 食物类
using System;
using System.Collections.Generic;
using System.Text;
using 贪食蛇.Lesson1;
using 贪食蛇.Lesson3;
using 贪食蛇.Lesson6;
namespace 贪食蛇.Lesson4
{
//食物类 继承游戏对象类
class Food : GameObject
{
//食物类构造函数
public Food(Snake snake)
{
//食物的位置不能和蛇重合 要有一定规则的随机出食物的位置
RandomPos(snake);
}
//实现食物的绘制方法
public override void Draw()
{
//设置颜色和输出位置 输出一个食物符号
Console.SetCursorPosition(pos.x, pos.y);
Console.ForegroundColor = ConsoleColor.Cyan;
Console.Write("¤");
}
//随机位置的行为 行为 和蛇的位置 有关系 有了蛇再来考虑
public void RandomPos(Snake snake)
{
//这个函数的目标 最终还是要设置食物的位置的
//只是不能和蛇重合
//随机位置 在墙内
Random r = new Random();
int x = r.Next(2, Game.w / 2 - 1) * 2;
int y = r.Next(1, Game.h - 4);
//设置食物位置
pos = new Position(x, y);
//得到蛇 如果重合 就会进if语句 递归的重新设置食物的位置
if(snake.CheckSamePos(pos))
{
RandomPos(snake);
}
}
}
}
16.12 蛇身子类
using System;
using System.Collections.Generic;
using System.Text;
using 贪食蛇.Lesson3;
namespace 贪食蛇.Lesson4
{
/// <summary>
/// 蛇身体类型
/// </summary>
enum E_SnakeBody_Type
{
/// <summary>
/// 头
/// </summary>
Head,
/// <summary>
/// 身体
/// </summary>
Body,
}
//蛇身子类 继承游戏对象类
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 ? "●" : "◎");
}
}
}
16.13 地图墙壁类
using System;
using System.Collections.Generic;
using System.Text;
using 贪食蛇.Lesson3;
namespace 贪食蛇.Lesson4
{
//地图墙壁类 继承游戏对象类
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("■");
}
}
}
16.14 地图类
using System;
using System.Collections.Generic;
using System.Text;
using 贪食蛇.Lesson1;
using 贪食蛇.Lesson3;
using 贪食蛇.Lesson4;
namespace 贪食蛇.Lesson5
{
//地图类 继承绘制接口
class Map : IDraw
{
//墙壁数组
public Wall[] walls;
//地图类构造函数 初始化墙壁数组 new出中的每一堵墙
public Map()
{
//new出墙壁数组
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();
}
}
}
}
16.15 蛇类
using System;
using System.Collections.Generic;
using System.Text;
using 贪食蛇.Lesson3;
using 贪食蛇.Lesson4;
using 贪食蛇.Lesson5;
namespace 贪食蛇.Lesson6
{
#region Lesson7 蛇移动
/// <summary>
/// 蛇的移动方向
/// </summary>
enum E_MoveDir
{
/// <summary>
/// 上
/// </summary>
Up,
/// <summary>
/// 下
/// </summary>
Down,
/// <summary>
/// 左
/// </summary>
Left,
/// <summary>
/// 右
/// </summary>
Right,
}
#endregion
//蛇类 继承绘制接口
class Snake : IDraw
{
//蛇的身体数组
SnakeBody[] bodys;
//当前蛇的长度
int snakeBodysLength;
//当前移动的方向
E_MoveDir dir;
//蛇类构造函数
public Snake(int x, int y)
{
//粗暴的 申明200个空间 游戏中 基本不会出现蛇长度达到200个身体
bodys = new SnakeBody[200];
//声明蛇头
bodys[0] = new SnakeBody(E_SnakeBody_Type.Head, x, y);
//初始时只有蛇头 初始化为1
snakeBodysLength = 1;
//初始化默认移动的方向向右
dir = E_MoveDir.Right;
}
//蛇类绘制方法
public void Draw()
{
//遍历蛇身体数组 逐个绘制蛇身体
for (int i = 0; i < snakeBodysLength; i++)
{
bodys[i].Draw();
}
}
#region Lesson7 蛇的移动
//蛇移动方法
public void Move()
{
//蛇移动前 擦除蛇尾 也就是最后一个位置 否则有残影
SnakeBody lastBody = bodys[snakeBodysLength - 1];
Console.SetCursorPosition(lastBody.pos.x, lastBody.pos.y);
Console.Write(" ");//擦屁股
#region Lesson11 长身体后 蛇身体移动
//在蛇头移动之前 从蛇尾开始 不停的 让后一个的位置 等于前一个的位置
//遍历蛇身体
for (int i = snakeBodysLength - 1; i > 0; i--)
{
bodys[i].pos = bodys[i - 1].pos;
}
#endregion
//蛇头移动
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;
}
}
#endregion
#region Lesson8 改变蛇的方向
//蛇改变移动方向方法
public void ChangeDir(E_MoveDir dir)
{
//蛇只有头部的时候 可以直接左转右 右转左 上转下 下转上
//蛇有身体时 这种情况就不能直接转
if(
//假如传进来的方向就等于现在的方向
dir == this.dir
||
//或者 蛇长度大于0且想要直接180度转向
(
snakeBodysLength > 1
&&
(this.dir == E_MoveDir.Left && dir == E_MoveDir.Right ||
this.dir == E_MoveDir.Right && dir == E_MoveDir.Left ||
this.dir == E_MoveDir.Up && dir == E_MoveDir.Down ||
this.dir == E_MoveDir.Down && dir == E_MoveDir.Up)
)
)
{
//直接return
return;
}
//只要没有return 就记录外面传入的方向 之后就会按照这个方向去移动
this.dir = dir;
}
#endregion
#region Lesson9 撞墙撞身体结束逻辑
//检测蛇是否撞墙撞身体后 结束游戏
//在update中每次绘制完检测 要传入地图
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 < snakeBodysLength; i++)
{
if(bodys[0].pos == bodys[i].pos)
{
return true;
}
}
return false;
}
#endregion
#region Lesson10 吃食物相关
//判断是否和蛇重合方法
//通过传入一个位置 来判断这个位置 是不是和蛇重合
public bool CheckSamePos(Position p)
{
for (int i = 0; i < snakeBodysLength; i++)
{
if(bodys[i].pos == p)
{
return true;
}
}
return false;
}
//蛇吃食物方法
public void CheckEatFood(Food food)
{
//判断蛇
if( bodys[0].pos == food.pos )
{
//吃到了 就应该让食物 位置再随机 增加蛇身体的长度
food.RandomPos(this);
//长身体
AddBody();
}
}
#endregion
#region Lesson11 长身体
//蛇长身体方法
private void AddBody()
{
//获得当前蛇尾的蛇身子类变量
SnakeBody frontBody = bodys[snakeBodysLength - 1];
//吃完食物后 先长出身体 但是先不画出来 下一帧再画
bodys[snakeBodysLength] = new SnakeBody(E_SnakeBody_Type.Body, frontBody.pos.x, frontBody.pos.y);
//增加蛇身体的总长度
++snakeBodysLength;
}
#endregion
}
}
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com