是我第 22 喜欢的贪吃蛇时间 1759239 金益恒
稍微翻了一下以前学 java 时做的练习, 看到了贪吃蛇的代码. 不是很长, 也有改进空间, 刚好可以拿来用, 此篇博文诞生, 下面就是心路历程和修改思路:
First: 这是贪吃蛇的界面, 我们先设置窗口的大小和行列数, 给到标题 "贪吃蛇" 和出现位置, 面子工程第一步, 当然这些参数可以随意一点, 这里强迫症发作就把数值保持一致.
另: 为方便读者阅读已修改绝大多数的变量名 (比如说 SnakeFrame 原来就叫 SheGeZi... 现规范格式, 进行统一)
备注总结彩蛋之类的在篇尾, 嗯
- public class SnakeFrame exten
- ds Frame{
- public static final int BLOCK_WIDTH = 15 ;
- public static final int BLOCK_HEIGHT = 15 ;
- public static final int ROW = 40;
- public static final int COL = 40;
- public static void main(String[] args) {
- new SnakeFrame().launch();
- }
- public void launch(){
- this.setTitle("贪恰蛇");
- this.setSize(ROW*BLOCK_HEIGHT, COL*BLOCK_WIDTH);
- this.setLocation(300, 300);
- this.addWindowListener(new WindowAdapter() {
- @Override
- public void windowClosing(WindowEvent e) {
- System.exit(0);
- }
- });
- this.setResizable(false);
- this.setVisible(true);
- }
- }
下面我们来画网格线以方便玩家进行定位
起初为了醒目是红线网格, 现在看来红线其实并不好, 还起到了反效果, 故这里修改成了灰色网格, 同上进行了变量名修改, 往后再不赘述 (不全改都跑不通我干嘛要作死好麻烦...)
- @Override
- public void paint(Graphics g) {
- Color c = g.getColor();
- g.setColor(Color.GRAY);
- for(int i = 0;i<ROW;i++){
- g.drawLine(0, i*BLOCK_HEIGHT, COL*BLOCK_WIDTH,i*BLOCK_HEIGHT );
- }
- for(int i=0;i<COL;i++){
- g.drawLine(i*BLOCK_WIDTH, 0 , i*BLOCK_WIDTH ,ROW*BLOCK_HEIGHT);
- }
- g.setColor(c);
- }
Part3: 用构造方法和 Draw 方法把蛇画出来, Node 类表示蛇的节点, 这部分除了修改变量名之外做好统一规范之外, 没做修改
关于 Direction, 这是个 enum
- public class Node {
- private static final int BLOCK_WIDTH = SnakeFrame.BLOCK_WIDTH;
- private static final int BLOCK_HEIGHT = SnakeFrame.BLOCK_HEIGHT;
- private int row;
- private int col;
- private Direction dir ;
- private Node pre;
- private Node next;
- public Node(int row, int col, Direction dir) {
- this.row = row;
- this.col = col;
- this.dir = dir;
- }
- public void draw(Graphics g){
- Color c = g.getColor();
- g.setColor(Color.BLACK);
- g.fillRect(col*BLOCK_WIDTH, row*BLOCK_HEIGHT, BLOCK_WIDTH, BLOCK_HEIGHT);
- g.setColor(c);
- }
- }
- public class Node {
- private static final int BLOCK_WIDTH = SnakeFrame.BLOCK_WIDTH;
- private static final int BLOCK_HEIGHT = SnakeFrame.BLOCK_HEIGHT;
- private int row;
- private int col;
- private Direction dir ;
- private Node pre;
- private Node next;
- public Node(int row, int col, Direction dir) {
- this.row = row;
- this.col = col;
- this.dir = dir;
- }
- public void draw(Graphics g){
- Color c = g.getColor();
- g.setColor(Color.BLACK);
- g.fillRect(col*BLOCK_WIDTH, row*BLOCK_HEIGHT, BLOCK_WIDTH, BLOCK_HEIGHT);
- g.setColor(c);
- }
- }
- public enum Direction {
- L,U,R,D
- }
再就是怎么让蛇动起来并恰东西:
首先想到的是这样: 其移动是通过在头部添加一个单元格, 在尾部删除一个单元格, 那么:
就在 Snake 类中添加一个 keyPressed 方法
当然, 这主要是根据键盘的上下左右键来确定蛇头结点的方向, 然后用 move 方法再根据头结点的方向来在头部加一个格子.
- public void keyPressed(KeyEvent e) {
- int key = e.getKeyCode();
- switch(key){
- case KeyEvent.VK_LEFT :
- if(head.dir!=Direction.R){
- head.dir = Direction.L;
- }
- break;
- case KeyEvent.VK_UP :
- if(head.dir!=Direction.D){
- head.dir = Direction.U;
- }
- break;
- case KeyEvent.VK_RIGHT :
- if(head.dir!=Direction.L){
- head.dir = Direction.R;
- }
- break;
- case KeyEvent.VK_DOWN :
- if(head.dir!=Direction.U){
- head.dir = Direction.D;
- }
- break;
- }
- }
- public void move() {
- addNodeInHead();
- deleteNodeInTail();
- }
- private void deleteNodeInTail() {
- Node node = tail.pre;
- tail = null;
- node.next = null;
- tail = node;
- }
- private void addNodeInHead() {
- Node node = null;
- switch(head.dir){
- case L:
- node = new Node(head.row,head.col-1,head.dir);
- break;
- case U:
- node = new Node(head.row-1,head.col,head.dir);
- break;
- case R:
- node = new Node(head.row,head.col+1,head.dir);
- break;
- case D:
- node = new Node(head.row+1,head.col,head.dir);
- break;
- }
- node.next = head;
- head.pre = node;
- head = node;
- }
- public void draw(Graphics g){
- if(head==null){
- return ;
- }
- move();
- for(Node node = head;node!=null;node = node.next){
- node.draw(g);
- }
- }
关于蛇吃的蛋:
getRect 方法: 用于碰撞检测
reAppear 方法: 用于重新产生蛋的方法
- public class Egg {
- private int row;
- private int col;
- private static final int BLOCK_WIDTH = SnakeFrame.BLOCK_WIDTH;
- private static final int BLOCK_HEIGHT = SnakeFrame.BLOCK_HEIGHT;
- private static final Random r = new Random();
- private Color color = Color.RED;
- public Egg(int row, int col) {
- this.row = row;
- this.col = col;
- }
- public Egg() {
- this((r.nextInt(SnakeFrame.ROW-2))+2,(r.nextInt(SnakeFrame.COL-2))+2);
- }
- public void reAppear(){
- this.row = (r.nextInt(SnakeFrame.ROW-2))+2;
- this.col = (r.nextInt(SnakeFrame.COL-2))+2;
- }
- public void draw(Graphics g){
- Color c= g.getColor();
- g.setColor(color);
- g.fillOval(col*BLOCK_WIDTH, row*BLOCK_HEIGHT, BLOCK_WIDTH, BLOCK_HEIGHT);
- g.setColor(c);
- if(color==Color.RED){
- color = Color.BLUE;
- }
- else{
- color = Color.RED;
- }
- }
- public Rectangle getRect(){
- return new Rectangle(col*BLOCK_WIDTH, row*BLOCK_HEIGHT, BLOCK_WIDTH, BLOCK_HEIGHT);
- }
- }
判断蛇是否吃到了蛋:
吃到了, 则加长
- public Rectangle getRect(){
- return new Rectangle(head.col*BLOCK_WIDTH, head.row*BLOCK_HEIGHT, BLOCK_WIDTH, BLOCK_HEIGHT);
- }
- public boolean eatEgg(Egg egg){
- if(this.getRect().intersects(egg.getRect())){
- addNodeInHead();
- egg.reAppear();
- return true;
- }
- else{
- return false;
- }
- }
接下来是增量开发时间:
贪吃蛇还能加什么呢, 坦白来说没什么好加的了
于是我们就给他加了个边框...
加了个双缓冲....
又加了个控制画面....
Finally! 下面是贴代码 time:
- private class MyPaintThread implements Runnable{
- @Override
- public void run() {
- while(true){
- repaint();
- try {
- Thread.sleep(50);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
- }
- private Image offScreenImage = null;
- @Override
- public void update(Graphics g) {
- if(offScreenImage==null){
- offScreenImage = this.createImage(ROW*BLOCK_HEIGHT, COL*BLOCK_WIDTH);
- }
- Graphics offg = offScreenImage.getGraphics();
- paint(offg);
- g.drawImage(offScreenImage, 0, 0, null);
- }
- private void checkDead() {
- if(head.row<2||head.row>SnakeFrame.ROW||head.col<0||head.col>SnakeFrame.COL){
- this.sf.gameOver();
- }
- for(Node node =head.next;node!=null;node = node.next){
- if(head.row==node.row&&head.col == node.col){
- this.sf.gameOver();
- }
- }
- }
- private boolean b_gameOver = false;
- public void gameOver(){
- b_gameOver = true;
- }
- @Override
- public void update(Graphics g) {
- if(b_gameOver){
- g.drawString("游戏结束!!!", ROW/2*BLOCK_HEIGHT, COL/2*BLOCK_WIDTH);
- }
- }
这篇博文的重难点在:
看懂自己之前写的是个什么玩意儿
修改一切不规范的变量名力求向正儿八经的程序靠拢
贪吃蛇的增量开发实在有限, 想了一阵也只能从边角料入手
Bug 来说其实是没有的, 这个真没有 (修改变量名之后搞得跑不起来的那种不算, 已经莫得了)
那么
GAME OVER~~~~ 撒花撒花撒花~~~~~~ 游戏结束~~~~~~~~
就这么长了毕竟介于 "不那么简单也不难" 的代码貌似也就这个了呢...
简要对代码做了写解释 (因为莫得备注)
那为什么没备注呢 (因为当时根本没想到要写备注)
至少了解到了备注的重要性, 嗯
以上.
来源: http://www.bubuko.com/infodetail-2976525.html