如何使用java的WindowBuilder编写贪吃蛇小程序

思路来源

1、摘要
特别感谢上面链接的作者,让我快速理清思路。
我将该作者的代码做了不少改动,转化为我自己的思考。
我的工作如下:
1)设置游戏的等级,让贪吃蛇的运行速度随着等级的变化而改变。
2) 设置了路障,和装饰的草地,以及与贪吃蛇触碰的条件。
3) 设置了三种不同的食物,并考虑了食物和路障的摆放。
4)修复了该作者的bug,包括页面的框架,贪吃蛇移动/死亡的条件等。
5)做了不少UI的调整,包括JPanel背景色、食物、蛇的身体、提示字的位置等。

2、 代码
这份代码只是起到抛转引玉的作用,方便我们了解一个简单的小游戏是如何开发的(感兴趣的朋友可以在web上尝试贪吃蛇游戏)
首先需要根据你们的eclipse版本下载相应版本的WindowBuilder,然后新建一个JPanel部署程序;
值得注意的是,我在工程中新建(或者直接复制粘贴)了一个/pic文件夹,用来存放icon,请大家在学习的时候务必注意图片路径:
如何使用java的WindowBuilder编写贪吃蛇小程序

package Jpanel;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.Random;
import javax.swing.Timer;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JPanel;

/*
 * Part1:
 * 设计游戏图纸:画一张900*700的白色窗口
 * 在窗口上添加画布,并在画布上添加标题和游戏区域
 * 第一个问题:蛇的图片如何和画布适应,如何让图片移动
 * Part2:
    初始化放置静态的蛇:一个头、一个身体、一个尾巴
  加上开始提示:按空格键开始游戏
  让蛇动起来:监听Timer事件,平移数据(蛇的身体)
  实现游戏暂停
  实现转向功能(需要蛇头部图片的四个方向)
   Part3:
    添加食物
   吃掉食物
   添加死亡条件
   实现“重新开始”功能
   添加分数、长度和等级(有兴趣的朋友可以增加游戏时间的选项)
 */
public class SnakePanel extends JPanel implements KeyListener, ActionListener {
//初始化图片
	ImageIcon up = new ImageIcon("pic/_up.png");
	ImageIcon down = new ImageIcon("pic/_down.png");
	ImageIcon left = new ImageIcon("pic/_left.png");
	ImageIcon right = new ImageIcon("pic/_right.png");
	ImageIcon food = new ImageIcon("pic/strawberry.png");
	ImageIcon food1 = new ImageIcon("pic/apple.png");
	ImageIcon food2 = new ImageIcon("pic/orange.png");
	ImageIcon body = new ImageIcon("pic/_body.png");
	ImageIcon stone = new ImageIcon("pic/stone.png");
	ImageIcon grass = new ImageIcon("pic/grass.png");
	ImageIcon tail = new ImageIcon("pic/_tail.png");
	ImageIcon title = new ImageIcon("pic/title.jpg");

	int[] snakex = new int[751];
	int[] snakey = new int[651];

	int[] stonex = new int[31];
	int[] stoney = new int[31];
	
	int[] grassx = new int[11];
	int[] grassy = new int[11];

	Random rand = new Random();
	int foodx = rand.nextInt(34) * 25 + 25;
	int foody = rand.nextInt(24) * 25 + 75;
	
	int len = 3;// 一开始只有三节,一个头,一个身体、一个尾巴
	int score = 0;//初始化分数
	int level = 1;
	static int time = 500;//初始化延迟的时间
	int n = 0;// 控制石头
	String direction = "R";//初始化方向

	boolean isStarted = false;// 判断游戏是否开始
	boolean isFailed = false;// 判断游戏是否结束

	Timer timer = new Timer(time, this);// 控制游戏的快慢

	public SnakePanel() {
		this.setFocusable(true);// 获取焦点
		/* 所谓焦点就是被选中的意思,或者说是“当前正在操作的组件”的意思。
		 * 如果一个组件被选中,或者正在被操作者,就是得到了焦点,而相反的,一个组件没有被选中或者失去操作,就是被转移了焦点,焦点已经到   别的组件上去了。
		 * 最明显的两个例子: 一个按钮(button)一旦被选中,就会有一个虚线框在按钮中,并且环绕着按钮的文字,一旦失去焦点,不被操作了,这个虚线框就消失了。
		 * 一个文本框(textfield)一旦被选中,就会有一个“|”在文本框里面闪动,提示可以输入信息,一旦失去或者转移焦点了,这个“|”就没有了,不闪动,
		 * 表示这个文本框你没有在操作。*/
		this.addKeyListener(this);// 监听键盘事件
		//下面都是初始化操作
		setup();
		StoneInitial();
		GrassInitial();
	}   
	public void GrassInitial() {
		Random rand = new Random();
		for(int i=0;i<11;i++) {
			grassx[i] = rand.nextInt(34) * 25 + 25;
			grassy[i] = rand.nextInt(24) * 25 + 75;
		}
	}
	public void GrassPaint(Graphics g) {
		for (int i = 0; i < 11; i++) {
			grass.paintIcon(this, g, grassx[i], grassy[i]);
		}
	}
	public void StoneInitial() {
		Random rand = new Random();
		for (int i = 0; i < 31; i++) {
			stonex[i] = rand.nextInt(34) * 25 + 25;
			stoney[i] = rand.nextInt(24) * 25 + 75;
			while ((stonex[i] == 100 && stoney[i] == 100) || (stonex[i] == 75 && stoney[i] == 100)
					|| (stonex[i] == 50 && stoney[i] == 100)) {//防止与初始化的蛇的身体碰撞,并以此为基准放置石头
				stonex[i] = rand.nextInt(34) * 25 + 25;
				stoney[i] = rand.nextInt(24) * 25 + 75;
				i = 0;
			}
		}
	}
	public void StonePaint(Graphics g) {
		for (int i = 0; i < 31; i++) {
			stone.paintIcon(this, g, stonex[i], stoney[i]); 
		}
	}

	public void Level() {//设置游戏的等级
		if (score > 5 && score <= 10 && level == 1) {
			timer.stop();//timer其实也调用了线程,我们首先要暂定之前的线程再开启新的
			time -= 40;
			level++;
			timer = new Timer(time, this);
		} else if (score > 10 && score <= 20 && level == 2) {
			timer.stop();
			time -= 40;
			level++;
			timer = new Timer(time, this);
		} else if (score > 20 && score <= 30 && level == 3) {
			timer.stop();
			time -= 50;
			level++;
			timer = new Timer(time, this);
		} else if (score > 30 && score <= 40 && level == 4) {
			timer.stop();
			time -= 50;
			level++;
			timer = new Timer(time, this);
		}
		else if (score > 40 && score <= 50 && level == 5) {
			timer.stop();
			time -= 60;
			level++;
			timer = new Timer(time, this);
		}
		else if (score > 50 && score <= 60 && level == 6) {
			timer.stop();
			time -= 60;
			level++;
			timer = new Timer(time, this);
		}
	}

	public void food() {// 要改进
		Random rand = new Random();
		for (int i = 0; i < len; i++) {
			if ((snakex[i] == foodx && snakey[i] == foody)) {
				foodx = rand.nextInt(34) * 25 + 25;// 返回一个大于等于0小于34的随机整数
				foody = rand.nextInt(24) * 25 + 75;
				i = 0;
				continue;//从头开始
			}
			for (int j = 0; j < 31; j++) {
				if ((stonex[j] == foodx && stoney[j] == foody)||Dilemma()) {
					foodx = rand.nextInt(34) * 25 + 25;
					foody = rand.nextInt(24) * 25 + 75;
					j = 0;
					i = 0;
					break;
				}
			}
		}
	}
    
	public boolean Dilemma() {//食物的放置可能出现困境
		for(int i=0;i<29;i++) {//以下是全排列
			for(int j=i+1;j<30;j++) {
				for(int k=j+1;k<31;k++) {
					if ((stonex[i]+25 == foodx && stonex[j]-25 == foodx && stoney[k]-25==foody)||
							(stonex[i]+25 == foodx && stonex[j]-25 == foodx && stoney[k]+25==foody)) {
						return true;
					}
				}
			}
		}
		return false;
	}
	
	public void setup() {// 游戏初始化
		isStarted = false;
		isFailed = false;
		len = 3;
		score = 0;
		level=1;
		n=0;
		time=500;
		direction = "R";
		snakex[0] = 100;
		snakex[1] = 75;
		snakex[2] = 50;
		snakey[0] = 100;
		snakey[1] = 100;
		snakey[2] = 100;
		GrassInitial();//草和石头放置的初始化
		StoneInitial();
		timer.start();
	}

	@Override
	public void keyPressed(KeyEvent e) {//控制键盘的上下左右
		int KeyCode = e.getKeyCode();
		if (isStarted) {
			if (KeyCode == KeyEvent.VK_SPACE) {
				if (isFailed) {
					setup();
				} else {
					isStarted = !isStarted;// 暂停和开始
				}
			} else if (KeyCode == KeyEvent.VK_UP && direction != "D") {
				direction = "U";
			} else if (KeyCode == KeyEvent.VK_DOWN && direction != "U") {
				direction = "D";
			} else if (KeyCode == KeyEvent.VK_RIGHT && direction != "L") {
				direction = "R";
			} else if (KeyCode == KeyEvent.VK_LEFT && direction != "R") {
				direction = "L";
			}
		} else {
			if (KeyCode == KeyEvent.VK_SPACE) {
				if (isFailed) {
					setup();
				} else {
					isStarted = !isStarted;// 暂停和开始
				}
			}
		}
	}
	public void paint(Graphics g) {// Graphics 画笔
		g.setColor(Color.orange); // 设置画布背景颜色
		title.paintIcon(this, g, 25, 11);// 放置主题图片,从(25,11)处为title的左上角,放置图片
		g.fillRect(25, 75, 850, 600); // 用画笔设置游戏方框

		//画草
		GrassPaint(g);
		// 画石头
		StonePaint(g);

		// 画蛇头(注意判断蛇头的方向)
		if (direction.equals("R"))
			right.paintIcon(this, g, snakex[0], snakey[0]);
		else if (direction.equals("L"))
			left.paintIcon(this, g, snakex[0], snakey[0]);
		else if (direction.equals("U"))
			up.paintIcon(this, g, snakex[0], snakey[0]);
		else if (direction.equals("D"))
			down.paintIcon(this, g, snakex[0], snakey[0]);

		// 画蛇的身体
		for (int i = 1; i < len-1; i++)
			body.paintIcon(this, g, snakex[i], snakey[i]);
		
		//画蛇尾
		tail.paintIcon(this, g, snakex[len-1],snakey[len-1]);

		// 判断如果游戏没开始,需要显示的字段
		if (!isStarted) {
			g.setColor(Color.WHITE);
			g.setFont(new Font("arial", Font.BOLD, 28));
			g.drawString("Press Space to start / pause", 250, 320);
		}
		// 判断如果游戏失败,需要显示的字段
		if (isFailed) {
			timer.stop();
			g.setColor(Color.WHITE);
			g.setFont(new Font("arial", Font.BOLD, 28));
			g.drawString("Game Over ! Press space to restart", 250, 320);
		}
		// 显示食物
		food();
		if(foodx % 3 == 0)
		    food.paintIcon(this, g, foodx, foody);
		else if((foodx+1) % 3==0)
			food1.paintIcon(this, g, foodx, foody);
		else
			food2.paintIcon(this, g, foodx, foody);
		
		// 设置分数和蛇的长度,以及等级,我最高设置了等级七
		g.setColor(Color.WHITE);
		g.setFont(new Font("arial", Font.PLAIN, 15));
		g.drawString("Score : " + score, 650, 37);
		g.drawString("SnakeLength :" + len, 650, 57);
		g.drawString("Level :" + level, 725, 37);
	}

	@Override
	public void actionPerformed(ActionEvent e) {
		// 1.再定义一个闹钟
		Level();
		timer.start();
		// 2.移动
		if (isStarted && !isFailed) {
			// 移动身体
			for (int i = len; i > 0; i--) {
				snakex[i] = snakex[i - 1];
				snakey[i] = snakey[i - 1];
			}
			// 移动头
			if (direction.equals("R")) {
				snakex[0] = snakex[0] + 25;
				if (snakex[0] > 850)
					snakex[0] = 25;
			} else if (direction.equals("L")) {
				snakex[0] = snakex[0] - 25;
				if (snakex[0] < 25)
					snakex[0] = 850;
			} else if (direction.equals("U")) {
				snakey[0] = snakey[0] - 25;
				if (snakey[0] < 75)
					snakey[0] = 650;
			} else if (direction.equals("D")) {
				snakey[0] = snakey[0] + 25;
				if (snakey[0] > 650) // 底下应该是650
					snakey[0] = 75;
			}
			if (snakex[0] == foodx && snakey[0] == foody) {
				len++;
				score++;
				foodx = rand.nextInt(34) * 25 + 25;
				foody = rand.nextInt(24) * 25 + 75;
			}
			for (int i = 1; i < len; i++) {
				if (snakex[0] == snakex[i] && snakey[0] == snakey[i]) {
					isFailed = true;
				}
			}
			for (int i = 0; i < len; i++) {
				for (int j = 1; j < 31; j++) {
					if (stonex[j] == snakex[i] && stoney[j] == snakey[i]) {
						isFailed = true;
					}
				}
			}
		}
		// 3. repaint()
		repaint();
	}

	@Override
	public void keyTyped(KeyEvent e) {
		// TODO Auto-generated method stub
	}

	@Override
	public void keyReleased(KeyEvent e) {
		// TODO Auto-generated method stub
	}

	public static void main(String[] args) {
		JFrame frame = new JFrame(); // 创建一个游戏界面的框架
		frame.setBounds(0, 0, 900, 720); // 设置框架的大小
		
		java.awt.Toolkit toolkit = java.awt.Toolkit.getDefaultToolkit();//下面的代码是让框架在屏幕正中间
		Dimension screenSize = toolkit.getScreenSize();
		double screenWidth = screenSize.getWidth();
		double screenHeight = screenSize.getHeight();
		frame.setLocation((int) (screenWidth - frame.getWidth()) / 2, (int) (screenHeight - frame.getHeight()) / 2);

		frame.setResizable(false); // 设置框架大小为不能改变
		// 点击关闭按钮 关闭游戏界面
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

		SnakePanel panel = new SnakePanel(); // 添加画布
		frame.add(panel); // 刚添加时画布是空的看不到

		frame.setVisible(true); // 允许显示游戏界面
	}
}

特别注意:调用repaint()这个回调函数的时候,paint(Graphics g)就被调用了。方法在线程里控制,一般都要不断刷屏来显示画面,我们不用理会。

3、运行图片:
如何使用java的WindowBuilder编写贪吃蛇小程序
如何使用java的WindowBuilder编写贪吃蛇小程序

4、icon素材:
链接:[百度网盘图片下载地址] https://pan.baidu.com/s/1UlfcIzKBysqUv4cG89VHfg 提取码:bl42

5、寄语:
希望大家共同进步,如有不足,希望可以指正!转载请注明出处,谢谢!!!

上一篇:JavaGUI编程之贪吃蛇小游戏原码


下一篇:Java一个简单的贪吃蛇