练手WPF(四)——贪吃蛇小游戏的简易实现(下)
程序员文章站
2022-04-29 18:49:06
八、生成新的单节蛇身我们这里先说说游戏小原理好了,游戏运行后,通过计时器事件不断生成新的单节蛇身类SnakeNode,添加到List中的0位置,原来的蛇头变成了第二节。该节新蛇头的坐标通过蛇头前进方向Direct进行判断,即如果原蛇头往左运行,则新蛇头在原蛇头位置的左一格生成,其他方向以此类推。最后 ......
八、生成新的单节蛇身
我们这里先说说游戏小原理好了,游戏运行后,通过计时器事件不断生成新的单节蛇身类snakenode,添加到list中的0位置,原来的蛇头变成了第二节。该节新蛇头的坐标通过蛇头前进方向direct进行判断,即如果原蛇头往左运行,则新蛇头在原蛇头位置的左一格生成,其他方向以此类推。最后将该节添加到snakenodes列表中,并将相应图形添加到游戏区。
private void gennewsnakenode() { snakenode snakenode = null; switch (direct) { case direction.up: snakenode = new snakenode(new point(snakenodes[snakehead]._pos.x, snakenodes[snakehead]._pos.y - 1)); break; case direction.down: snakenode = new snakenode(new point(snakenodes[snakehead]._pos.x, snakenodes[snakehead]._pos.y + 1)); break; case direction.left: snakenode = new snakenode(new point(snakenodes[snakehead]._pos.x - 1, snakenodes[snakehead]._pos.y)); break; case direction.right: snakenode = new snakenode(new point(snakenodes[snakehead]._pos.x + 1, snakenodes[snakehead]._pos.y)); break; } if (snakenode != null) { snakenodes.insert(0, snakenode); mycanvas.children.add(snakenodes[0]._rect); } }
九、随机水果位置
注意该新位置不得与蛇身任何一节重叠,如果重叠则另外生成新位置,直到成功为止。
private point setfruittorandompos() { bool flag = true; point pos = new point(); while (flag) { flag = false; pos = new point(rnd.next(0, cellwidth), rnd.next(0, cellheight)); foreach (var node in snakenodes) { if (pos.x == node._pos.x && pos.y == node._pos.y) { flag = true; break; } } } return pos; }
十、碰撞检测(蛇头与水果)
因为只有蛇头会最先触碰到水果,所以只需判断蛇头坐标与水果坐标是否相同即可。如果撞到水果,则将水果随机生成新位置;如果没有碰到,则删除蛇尾一节(因为之后会通过计时器会生成新一节蛇头,这样就会保存蛇身长度,感觉位置前进了一格)。
private void checkcollision() { if (snakenodes[snakehead]._pos.x == fruit._pos.x && snakenodes[snakehead]._pos.y == fruit._pos.y) { fruit.setpostion(setfruittorandompos()); } else { if (mycanvas.children.contains(snakenodes[snakenodes.count - 1]._rect)) mycanvas.children.remove(snakenodes[snakenodes.count - 1]._rect); snakenodes.removeat(snakenodes.count - 1); } }
十一、判断是否游戏结束
很简单,看看蛇头是否触碰游戏区四边或蛇身的任何一节,如果是则返回true。
注意蛇身循环是从1开始的,如果从0开始你知道会出现什么情况。
private bool isgameover() { if (snakenodes[snakehead]._pos.x == -1 || snakenodes[snakehead]._pos.x == cellwidth || snakenodes[snakehead]._pos.y == -1 || snakenodes[snakehead]._pos.y == cellheight) { return true; } foreach (var node in snakenodes) { if (node == snakenodes[snakehead]) continue; if (node._pos.x == snakenodes[snakehead]._pos.x && node._pos.y == snakenodes[snakehead]._pos.y) { return true; } } return false; }
十三、删除游戏区的所有蛇身节
这个会在重新开始游戏时使用到:
private void removesnakenodeall() { for (int i = 0; i < snakenodes.count; i++) { if (mycanvas.children.contains(snakenodes[i]._rect)) { mycanvas.children.remove(snakenodes[i]._rect); } } }
顺便再来一发删除游戏区的水果:
private void removefruit() { if (fruit == null) { return; } if (mycanvas.children.contains(fruit._ellipse)) { mycanvas.children.remove(fruit._ellipse); } }
十四、按键操作
通过方向键,设置direct值即可。
private void mycanvas_previewkeydown(object sender, keyeventargs e) { switch (e.key) { case key.left: if (direct != direction.right) { direct = direction.left; } break; case key.right: if (direct != direction.left) { direct = direction.right; } break; case key.up: if (direct != direction.down) { direct = direction.up; } break; case key.down: if (direct != direction.up) { direct = direction.down; } break; case key.escape: application.current.shutdown(); break; case key.space: if (currgamestate == gamestate.none) return; if (currgamestate == gamestate.pause) { currgamestate = gamestate.gameing; timer.start(); menucontrol_pause.header = "暂停"; } else if (currgamestate == gamestate.gameing) { currgamestate = gamestate.pause; timer.stop(); menucontrol_pause.header = "继续"; } break; } }
十四、定义开始游戏方法
先删除界面中的蛇身和水果(保留底部暗格),随机位置生成水果和三节蛇身。
private void startgame() { removesnakenodeall(); removefruit(); int startx = rnd.next(5, cellwidth - 6); int starty = rnd.next(5, cellheight - 6); direct = direction.right; fruit = new fruit(setfruittorandompos(), mycanvas); snakenodes = new list<snakenode>(); snakenodes.add(new snakenode(new point(startx, starty))); gennewsnakenode(); gennewsnakenode(); }
十五、开始游戏菜单
调用startgame()方法,并设定计时器开始。
private void menufile_newgame_click(object sender, routedeventargs e) { startgame(); timer.start(); currgamestate = gamestate.gameing; menucontrol_pause.header = "暂停"; }
十六、暂停与继续
直接上代码,没有要说的了。
private void menucontrol_pause_click(object sender, routedeventargs e) { if (currgamestate == gamestate.gameing) { currgamestate = gamestate.pause; timer.stop(); menucontrol_pause.header = "继续"; } else if (currgamestate == gamestate.pause) { currgamestate = gamestate.gameing; timer.start(); menucontrol_pause.header = "暂停"; } }
十七、退出游戏
private void menufile_exit_click(object sender, routedeventargs e) { application.current.shutdown(); }
十八、其他菜单功能
自己实现好了
private void menuhelp_about_click(object sender, routedeventargs e) { }