基于C#开发的简易贪吃蛇
最近一直忙着11月份软考的复习,一直没有练习C#。觉得对于一个新手,长时间不练肯定不行,所以觉得敲敲代码练练手。刚好电脑磁盘里有以前Down的C#开源游戏,于是挑中了贪吃蛇,来练练手。
贪吃蛇游戏界面如下:
为了实现贪吃蛇,一共写了四个类,分别是:
1)Bean:食物类,就是玩家要吃的那个食物,里面实现了两个方法:食物的显示与食物的消失(因为食物被吃了以后应该消失)
2)Block:蛇块类,就是组成蛇的单元,玩家吃到食物,蛇的蛇块增加一节。
3)Snake:蛇类,游戏中的主角,玩家通过上下左右四个键来控制蛇的运动。
4)Map:地图类,用于画出游戏主界面,通过定时器来控制,每个单元时间重新画一幅地图,用于动态显示
以下是代码的实现
1 //食物类 2 public class Bean 3 { 4 //用于画食物的顶端坐标 5 private Point _origin; 6 7 public Point Origin 8 { 9 get { return _origin; } 10 set { _origin = value; } 11 } 12 13 //显示食物 14 public void ShowBean(Graphics g) 15 { 16 //定义红色的画笔 17 SolidBrush brush = new SolidBrush(Color.Red); 18 //画实心矩形表示食物 19 g.FillRectangle(brush,Origin.X,Origin.Y,15,15); 20 } 21 22 public void UnShowBean(Graphics g) 23 { 24 //定义系统背景颜色的画笔 25 SolidBrush brush = new SolidBrush(Color.Silver); 26 //画实心矩形颜色为系统背景颜色,相当于食物被吃掉了 27 g.FillRectangle(brush,Origin.X,Origin.Y,15,15); 28 } 29 }
//蛇身体的每一单元,简称块 public class Block { //是否为蛇头 private bool _isHead; public bool IsHead { get { return _isHead; } set { _isHead = value; } } //蛇块的编号 private int _blockNumber; public int BlockNumber { get { return _blockNumber; } set { _blockNumber = value; } } //蛇块的左上角位置 private Point _origin; public Point Origin { get { return _origin; } set { _origin = value; } } //根据指定位置画蛇块 public void ShowBlock(Graphics g) { Bitmap bitMap; if (IsHead) { //蛇头 bitMap = new Bitmap(Image.FromFile("Blue.gif")); } else { bitMap = new Bitmap(Image.FromFile("Yellow.gif")); } g.DrawImage(bitMap,Origin.X,Origin.Y,15,15); } //消除蛇块 public void UnShowBlock(Graphics g) { SolidBrush brush = new SolidBrush(Color.Silver); g.FillRectangle(brush,Origin.X,Origin.Y,15,15); } }
1 public class Snake 2 { 3 //用于存放蛇的集合 4 private List<Block> blocksList; 5 //0-上,1-右,2-下,3-左 6 private int direction = 1; 7 //蛇头的编号,蛇的长度 8 private int headNumber; 9 //蛇头左上角坐标 10 private Point headPoint; 11 private Point mapLeft; 12 //游戏开始时,初始的蛇 13 public Snake(Point map, int count) 14 { 15 mapLeft = map; 16 Block blockSnake; 17 //定义蛇的起始位置(蛇尾) 18 Point p = new Point(map.X + 15, map.Y + 15); 19 blocksList = new List<Block>(); 20 //循环画蛇块用于填充蛇集合 21 for (int i = 0; i < count; i++) 22 { 23 //x坐标加15 24 p.X += 15; 25 //实例化蛇块 26 blockSnake = new Block(); 27 //定义蛇块的左上角位置 28 blockSnake.Origin = p; 29 //定义蛇块的编号1,2,3... 30 blockSnake.BlockNumber = i + 1; 31 if (i == count - 1) 32 { 33 //蛇头 34 //给蛇头位置赋值 35 headPoint = blockSnake.Origin; 36 blockSnake.IsHead = true; 37 } 38 blocksList.Add(blockSnake); 39 40 } 41 //蛇的长度赋值 42 headNumber = count; 43 } 44 //蛇头坐标的只读属性 45 public Point HeadPoint 46 { 47 get { return headPoint; } 48 } 49 //蛇的运动方向的属性 50 public int Direction 51 { 52 get { return direction; } 53 set { direction = value; } 54 } 55 /// <summary> 56 /// 蛇的转换方向 57 /// </summary> 58 /// <param name="pDirection">想要改变的方向</param> 59 public void TurnDirection(int pDirection) 60 { 61 switch (direction) 62 { 63 //原来向上运动 64 case 0: 65 //希望向左运动 66 if (pDirection == 3) 67 direction = 3; 68 //希望向右运动 69 else if (pDirection == 1) 70 direction = 1; 71 break; 72 //原来向右运动 73 case 1: 74 //下 75 if (pDirection == 2) 76 direction = 2; 77 //上 78 else if (pDirection == 0) 79 direction = 0; 80 break; 81 case 2: 82 if (pDirection == 1) 83 direction = 1; 84 else if (pDirection == 3) 85 direction = 3; 86 break; 87 case 3: 88 if (pDirection == 0) 89 direction = 0; 90 else if (pDirection == 2) 91 direction = 2; 92 break; 93 94 95 } 96 } 97 98 public Point getHeadPoint //只读蛇头位置属性 99 { 100 get { return headPoint; } 101 } 102 //蛇吃到食物后变长,蛇头+1 103 public void SnakeGrowth() 104 { 105 //找到蛇头的坐标 106 Point head = blocksList[blocksList.Count - 1].Origin; 107 int x = head.X; 108 int y = head.Y; 109 //判断蛇的运动方向,改变蛇头的位置 110 switch (direction) 111 { 112 case 0: 113 //向上运动 114 y -= 15; 115 break; 116 case 1: 117 x += 15; 118 break; 119 case 2: 120 y += 15; 121 break; 122 case 3: 123 x -= 15; 124 break; 125 } 126 //把原先蛇头的块变为普通块 127 blocksList[blocksList.Count - 1].IsHead = false; 128 //实例化新蛇头 129 Block headNew = new Block(); 130 headNew.IsHead = true; 131 headNew.BlockNumber = blocksList.Count + 1; 132 headNew.Origin = new Point(x,y); 133 blocksList.Add(headNew); 134 headNumber++; 135 headPoint = headNew.Origin; 136 } 137 138 //蛇向前运动(没有吃到食物的情况),蛇尾移除,蛇头移位+1 139 public void Go(Graphics g) 140 { 141 Block snakeTail = blocksList[0]; 142 //消除蛇尾块 143 snakeTail.UnShowBlock(g); 144 //集合中移除设为块 145 blocksList.RemoveAt(0); 146 foreach (var item in blocksList) 147 { 148 item.BlockNumber--; 149 } 150 //由于SnakeGrowth中仅仅使蛇头+1,但是headNumber++了。但是此值并没有改变,所以先-- 151 headNumber--; 152 SnakeGrowth(); 153 } 154 155 //画出蛇 156 public void ShowSnake(Graphics g) 157 { 158 foreach (var item in blocksList) 159 { 160 item.ShowBlock(g); 161 } 162 } 163 //隐藏蛇 164 public void UnShowSnake(Graphics g) 165 { 166 foreach (var item in blocksList) 167 { 168 item.UnShowBlock(g); 169 } 170 } 171 //重置蛇 172 public void Reset(Point map, int count) 173 { 174 Block blockSnake; 175 //定义蛇的起始位置(蛇尾) 176 Point p = new Point(mapLeft.X + 15, mapLeft.Y + 15); 177 blocksList = new List<Block>(); 178 //循环画蛇块用于填充蛇集合 179 for (int i = 0; i < count; i++) 180 { 181 //x坐标加15 182 p.X += 15; 183 //实例化蛇块 184 blockSnake = new Block(); 185 //定义蛇块的左上角位置 186 blockSnake.Origin = p; 187 //定义蛇块的编号1,2,3... 188 blockSnake.BlockNumber = i + 1; 189 if (i == count - 1) 190 { 191 //蛇头 192 //给蛇头位置赋值 193 headPoint = blockSnake.Origin; 194 blockSnake.IsHead = true; 195 } 196 blocksList.Add(blockSnake); 197 198 } 199 //蛇的长度赋值 200 headNumber = count; 201 direction = 1; 202 } 203 //是否碰到自己 204 public bool IsTouchMyself() 205 { 206 bool isTouched = false; 207 for (int i = 0; i < blocksList.Count-1; i++) 208 { 209 if (headPoint == blocksList[i].Origin) 210 { 211 isTouched = true; 212 break; 213 } 214 } 215 return isTouched; 216 } 217 }
1 public class Map 2 { 3 private Point mapLeft; 4 private static int unit = 15; 5 //定义地图长,为28个蛇块的长度 6 private readonly int length = 30 * unit; 7 //定义地图宽,为20个蛇块的宽度 8 private readonly int width = 25 * unit; 9 //定义分数 10 public int score = 0; 11 //定义蛇 12 private readonly Snake snake; 13 public bool victory = false; 14 public Snake Snake 15 { 16 get { return snake; } 17 } 18 19 Bean food; 20 public Map(Point start) 21 { 22 //把地图左上角的点的值赋值给全局变量 23 mapLeft = start; 24 //实例化蛇 25 snake = new Snake(start,5); 26 //实例化食物 27 food = new Bean(); 28 food.Origin = new Point(start.X+30,start.Y+30); 29 } 30 //显示新食物 31 public void ShowNewFood(Graphics g) 32 { 33 //消除原先食物 34 food.UnShowBean(g); 35 //产生随机位置的食物 36 food = FoodRandom(); 37 //显示食物 38 food.ShowBean(g); 39 } 40 //随机产生一个新食物 41 private Bean FoodRandom() 42 { 43 Random d=new Random (); 44 int x = d.Next(0, length / unit); 45 int y = d.Next(0, width / unit); 46 Point origin = new Point(mapLeft.X+x*15,mapLeft.Y+y*15); 47 Bean food = new Bean(); 48 food.Origin = origin; 49 return food; 50 } 51 //画地图 52 public void ShowMap(Graphics g) 53 { 54 //创建一支红笔 55 Pen pen = new Pen(Color.Blue); 56 //画出地图的框 57 g.DrawRectangle(pen,mapLeft.X,mapLeft.Y,length,width); 58 //显示食物 59 food.ShowBean(g); 60 if (CheckBean()) 61 { 62 //吃到了食物 63 //显示新食物 64 ShowNewFood(g); 65 //蛇变长 66 snake.SnakeGrowth(); 67 //分数增加 68 score += 10; 69 //if (score >= 100) 70 //{ 71 // victory = true; 72 //} 73 //显示蛇 74 snake.ShowSnake(g); 75 } 76 else 77 { 78 snake.Go(g); 79 snake.ShowSnake(g); 80 } 81 } 82 //判断是否吃到了食物 83 public bool CheckBean() 84 { 85 86 return food.Origin.Equals(snake.HeadPoint); 87 } 88 89 //检查蛇是否撞墙 90 public bool CheckSnake() 91 { 92 return !(snake.getHeadPoint.X > mapLeft.X-5 && snake.getHeadPoint.X < (mapLeft.X + length-5) && snake.getHeadPoint.Y > mapLeft.Y-5 && snake.getHeadPoint.Y < (mapLeft.Y + width -5)); 93 } 94 95 //重新开始 96 public void Reset(Graphics g) 97 { 98 snake.UnShowSnake(g); 99 snake.Reset(mapLeft,5); 100 } 101 }
1 这是窗体的代码 2 public frmMain() 3 { 4 InitializeComponent(); 5 //定义地图 6 map = new Map(new Point(50,50)); 7 BackColor = Color.Silver; 8 } 9 private readonly Map map; 10 private int gradeNum = 100; 11 private int pink; 12 private void frmMain_Load(object sender, EventArgs e) 13 { 14 SetStyle( 15 ControlStyles.OptimizedDoubleBuffer | ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint, true); 16 UpdateStyles(); 17 } 18 19 private void timer1_Tick(object sender, EventArgs e) 20 { 21 //this.BackgroundImage = Image.FromFile(pink+".jpg"); 22 开始ToolStripMenuItem.Text = "重新开始"; 23 lblScore.Text = map.score.ToString(); 24 if (map.score >= 500) 25 { 26 timer1.Enabled = false; 27 MessageBox.Show("恭喜,成功!!!"); 28 } 29 Bitmap bmp = new Bitmap(Width, Height); 30 //Image face = Image.FromFile("1.jpg"); 31 BackgroundImage = bmp; 32 Graphics g = Graphics.FromImage(bmp); 33 map.ShowMap(g); 34 if (map.CheckSnake()||map.Snake.IsTouchMyself()) 35 { 36 timer1.Enabled = false; 37 MessageBox.Show("很遗憾,失败了!!!"); 38 } 39 40 } 41 //键盘响应事件 42 private void frmMain_KeyDown(object sender, KeyEventArgs e) 43 { 44 int k, d = 0; 45 k = e.KeyValue; 46 if (k == 37) //左 47 d = 3; 48 else if (k == 40) //下 49 d = 2; 50 else if (k == 38) //上 51 d = 0; 52 else if (k == 39) //右 53 d = 1; 54 map.Snake.TurnDirection(d); 55 } 56 57 private void 开始ToolStripMenuItem_Click(object sender, EventArgs e) 58 { 59 if (lblGrade.Text == "") 60 { 61 MessageBox.Show("请先选择级别"); 62 return; 63 } 64 if (开始ToolStripMenuItem.Text == "开始") 65 { 66 开始ToolStripMenuItem.Text = "重新开始"; 67 timer1.Enabled = true; 68 } 69 else if (开始ToolStripMenuItem.Text == "重新开始") 70 { 71 开始ToolStripMenuItem.Text = "开始"; 72 继续ToolStripMenuItem.Text = "暂停"; 73 timer1.Enabled = true; 74 map.Reset(CreateGraphics()); 75 map.score = 0; 76 } 77 78 } 79 80 private void 继续ToolStripMenuItem_Click(object sender, EventArgs e) 81 { 82 if (继续ToolStripMenuItem.Text == "继续") 83 { 84 继续ToolStripMenuItem.Text = "暂停"; 85 timer1.Enabled = true; 86 } 87 else if(继续ToolStripMenuItem.Text == "暂停") 88 { 89 继续ToolStripMenuItem.Text = "继续"; 90 timer1.Enabled = false; 91 } 92 } 93 94 private void 低级ToolStripMenuItem_Click(object sender, EventArgs e) 95 { 96 gradeNum = 200; 97 lblGrade.Text = "菜鸟"; 98 timer1.Interval = gradeNum; 99 } 100 101 private void 中级ToolStripMenuItem_Click(object sender, EventArgs e) 102 { 103 gradeNum = 150; 104 lblGrade.Text = "平民"; 105 timer1.Interval = gradeNum; 106 } 107 108 private void 高级ToolStripMenuItem_Click(object sender, EventArgs e) 109 { 110 gradeNum = 100; 111 lblGrade.Text = "高手"; 112 timer1.Interval = gradeNum; 113 } 114 115 private void 大神ToolStripMenuItem_Click(object sender, EventArgs e) 116 { 117 gradeNum = 50; 118 lblGrade.Text = "大神"; 119 timer1.Interval = gradeNum; 120 } 121 122 private void 肤色1ToolStripMenuItem_Click(object sender, EventArgs e) 123 { 124 this.BackgroundImage = Image.FromFile("1.jpg"); 125 pink = 1; 126 } 127 128 private void 肤色2ToolStripMenuItem_Click(object sender, EventArgs e) 129 { 130 this.BackgroundImage = Image.FromFile("2.jpg"); 131 pink = 2; 132 } 133 }
编程过程借鉴了网路大神的帮忙。
还有一个问题,希望看到的大神可以帮忙解决:原本希望把选择级别菜单下的几个级别都注册成一个点击事件,再通过switch判断点击的级别的text属性,再作出相应操作,但是不知道在代码中怎么得到被点击的级别的对象,所以方法实现不了。
也就是说,我想实现的效果是把菜单中的几个按钮的点击事件注册成一个事件,但是在写代码的过程中,不知道怎么才能得到被点击按钮(Button clickedBtn=sender as Button 不对,但也不知道还有什么其他的方法)的对象,也就无法确定点击的是哪个按钮。
肤色的单元还没有实现,有待优化。
游戏方法:点击菜单栏中的选择级别,选择一个级别,按F2开始游戏,游戏过程中可以按F4暂停,继续游戏,每吃到一个食物加10分,达到500分成功。