基于GUI简易贪吃蛇
首先定义一个JFrame窗口,之后会在窗口上加面板用来显示游戏。
StartGame.java
import javax.swing.*;
//游戏主启动
public class StartGame {
public static void main(String[] args) {
JFrame jFrame = new JFrame();
jFrame.setBounds(300,100,900,800);//宽度和高度要按照一定比例
jFrame.setResizable(false);//设置窗口大小不可变
jFrame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
jFrame.setVisible(true);
}
}
然后创建一个GamePanel.java用来些游戏的面板,使用画笔来完善具体的游戏元素。
public class GamePanel extends JPanel{
@Override
protected void paintComponent(Graphics g){
super.paintComponent(g);
}
}
面板中首先要画出一个游戏的范围,这里设定蛇一块身体是2525大小,因此游戏区域的长和宽就需要与蛇的身体成比例。
这里设定了一个850650的游戏区域
g.fillRect(25,25,850,650);
设置一个键盘监听实现按空格键开始游戏,这里将键盘监听设置为GamePanel的内部类。
先定义一个游戏状态变量
boolean isStart = false;//游戏状态默认未开始
将开始提示用画笔画到界面上
if(!isStart){
g.setColor(Color.white);
g.setFont(new Font("微软雅黑",Font.BOLD,40));
g.drawString("按空格开始游戏",300,300);
}
键盘监听类
class GameListener extends KeyAdapter{
@Override
public void keyPressed(KeyEvent e) {
int keyCode = e.getKeyCode();//获得键盘按键是哪一个
//空格开始游戏
if (keyCode == KeyEvent.VK_SPACE) {
isStart = !isStart;//取反
repaint();
}
}
}
接下来要用画笔将蛇画出来,这里设定蛇的每一个身体都是由25*25大小的正方形组成,为了区分头与身体,头部用红色,身体用绿色,设定头部坐标(100,50),身体部分在X轴依次减25(初始状态向右)。
//定义蛇的数据结构
int length;//蛇的长度
final int size = 24;//蛇的尺寸,这里设置为24*24可以更好区分每一块身体
//设定蛇每一个身体的坐标
int[] snakeX = new int[600];
int[] snakeY = new int[600];
String direction;//初始方向
//设定一个初始化方法用于初始化数据
public void init(){
length = 3;//初始长度
snakeX[0] = 100;//头部坐标
snakeY[0] = 50;
snakeX[1] = 75;//第一个身体坐标
snakeY[1] = 50;
snakeX[2] = 50;//第二个身体坐标
snakeY[2] = 50;
direction = "R";//初始方向向右
}
实现按方向键使蛇移动
利用键盘监听获得按的是哪一个方向
if (keyCode == KeyEvent.VK_UP) {
direction = "U";
} else if (keyCode == KeyEvent.VK_DOWN) {
direction = "D";
} else if (keyCode == KeyEvent.VK_RIGHT) {
direction = "R";
} else if (keyCode == KeyEvent.VK_LEFT) {
direction = "L";
}
设置定时器定义刷新时间
Timer timer = new Timer(50,new timeListener());//50毫秒执行一次
定义一个内部类timeListener用于刷新画面实现蛇的移动
class timeListener implements ActionListener{
@Override
public void actionPerformed(ActionEvent e) {
if(isStart){
for (int i = length-1;i>0;i--){
//将后一节移动到前一节的位置
snakeX[i] = snakeX[i-1];
snakeY[i] = snakeY[i-1];
}
switch (direction) {
case "R":
snakeX[0] = snakeX[0] + 25;
break;
case "L":
snakeX[0] = snakeX[0] - 25;
break;
case "U":
snakeY[0] = snakeY[0] - 25;
break;
case "D":
snakeY[0] = snakeY[0] + 25;
break;
}
repaint();//重画页面
}
timer.start();//开启定时器
}
}
再用类似的方法在随机坐标画出一个食物。先定义食物的相关变量
int foodX;
int foodY;
Random random = new Random();
在init中添加初始化食物坐标代码
foodX = 25+25*random.nextInt(33);
foodY = 25+25*random.nextInt(23);
使用画笔将食物画出来
g.setColor(Color.yellow);
g.fillOval(foodX,foodY,25,25);
实现蛇吃食物事件,当蛇头部坐标与食物坐标重合时重新生成食物并让蛇的长度+1。
actionPerformed方法中
if (snakeX[0]==foodX && snakeY[0]==foodY){
length++;//长度增加
//重新生成食物
foodX = 25+25*random.nextInt(33);
foodY = 25+25*random.nextInt(23);
}
游戏失败的情况:
-
蛇碰到自己的身体
-
蛇碰到边界
首先定义一个失败状态变量boolean isFail = false;//游戏失败状态
将以上两种情况代码添加到timeListener类中
if(snakeX[0] > 850 || snakeX[0] < 25 || snakeY[0] > 650 || snakeY[0] < 25){ //边界判定
isFail = true;
}
for (int i = 1;i < length;i++) {//重合判定
if (snakeX[0] == snakeX[i] && snakeY[0] == snakeY[i]) {
isFail = true;
break;
}
}
画出一个失败的画面
if(isFail){
g.setColor(Color.red);
g.setFont(new Font("微软雅黑",Font.BOLD,40));
g.drawString("失败,请重新开始游戏",300,300);
}
修改开始游戏部分代码
if (keyCode == KeyEvent.VK_SPACE) {
if(isFail){
isFail = false;
init();
}else {
isStart = !isStart;//取反
}
repaint();
}
timeListener类中修改
if(isStart && !isFail)
最后在StartGame类中添加面板
jFrame.add(new GamePanel());
完成。
完整代码如下
StartGame.java
import javax.swing.*;
//游戏主启动
public class StartGame {
public static void main(String[] args) {
JFrame jFrame = new JFrame();
jFrame.add(new GamePanel());
jFrame.setBounds(300,100,900,800);//宽度和高度要按照一定比例
jFrame.setResizable(false);//设置窗口大小不可变
jFrame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
jFrame.setVisible(true);
}
}
GamePanel.java
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.Random;
//游戏面板
public class GamePanel extends JPanel {
//定义蛇的数据结构
int length;//蛇的长度
final int size = 24;//蛇的尺寸
int[] snakeX = new int[600];
int[] snakeY = new int[600];
String direction;//初始方向
//食物坐标
int foodX;
int foodY;
Random random = new Random();
boolean isStart = false;//游戏状态默认未开始
boolean isFail = false;//游戏失败状态
//定时器,100毫秒刷新一次
Timer timer = new Timer(50,new timeListener());
//构造器
public GamePanel(){
init();
//获得焦点和键盘事件
setFocusable(true);
addKeyListener(new GameListener());
}
//初始化方法
public void init(){
length = 3;//初始长度
snakeX[0] = 100;//头部坐标
snakeY[0] = 50;
snakeX[1] = 75;//第一个身体坐标
snakeY[1] = 50;
snakeX[2] = 50;//第二个身体坐标
snakeY[2] = 50;
//随机生成事物
foodX = 25+25*random.nextInt(33);
foodY = 25+25*random.nextInt(23);
direction = "R";//初始方向向右
timer.start();//游戏一开始启动定时器
}
//绘制面板,游戏中所有东西都使用此画笔
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
//绘制静态面板
// setBackground(Color.BLACK);
g.fillRect(25,25,850,650);
g.setColor(Color.red);
g.drawRect(25,25,850,650);
//画食物
g.setColor(Color.yellow);
g.fillOval(foodX,foodY,25,25);
//画蛇
g.setColor(Color.red);
g.fillRect(snakeX[0],snakeY[0],size,size);
g.setColor(Color.green);
for (int i = 1;i < length; i++) {
g.fillRect(snakeX[i],snakeY[i],size,size);
}
//画积分
g.setColor(Color.white);
g.setFont(new Font("微软雅黑",Font.BOLD,15));
g.drawString("长度:"+length,750,50);
//游戏状态
if(!isStart){
g.setColor(Color.white);
g.setFont(new Font("微软雅黑",Font.BOLD,40));
g.drawString("按空格开始游戏",300,300);
}
if(isFail){
g.setColor(Color.red);
g.setFont(new Font("微软雅黑",Font.BOLD,40));
g.drawString("失败,请重新开始游戏",300,300);
}
}
//内部类,键盘监听
class GameListener extends KeyAdapter{
@Override
public void keyPressed(KeyEvent e) {
int keyCode = e.getKeyCode();//获得键盘按键是哪一个
//空格开始游戏
if (keyCode == KeyEvent.VK_SPACE) {
if(isFail){
isFail = false;
init();
}else {
isStart = !isStart;//取反
}
repaint();
}
//移动
if (keyCode == KeyEvent.VK_UP) {
direction = "U";
} else if (keyCode == KeyEvent.VK_DOWN) {
direction = "D";
} else if (keyCode == KeyEvent.VK_RIGHT) {
direction = "R";
} else if (keyCode == KeyEvent.VK_LEFT) {
direction = "L";
}
}
}
//定时刷新
class timeListener implements ActionListener{
@Override
public void actionPerformed(ActionEvent e) {
if(isStart && !isFail){
for (int i = length-1;i>0;i--){
//将后一节移动到前一节的位置
snakeX[i] = snakeX[i-1];
snakeY[i] = snakeY[i-1];
}
switch (direction) {
case "R":
snakeX[0] = snakeX[0] + 25;
break;
case "L":
snakeX[0] = snakeX[0] - 25;
break;
case "U":
snakeY[0] = snakeY[0] - 25;
break;
case "D":
snakeY[0] = snakeY[0] + 25;
break;
}
//吃事物事件
if (snakeX[0]==foodX && snakeY[0]==foodY){
length++;//长度增加
//重新生成食物
foodX = 25+25*random.nextInt(33);
foodY = 25+25*random.nextInt(23);
}
//失败判定
if(snakeX[0] > 850 || snakeX[0] < 25 || snakeY[0] > 650 || snakeY[0] < 25){ //边界判定
isFail = true;
}
for (int i = 1;i < length;i++) {//重合判定
if (snakeX[0] == snakeX[i] && snakeY[0] == snakeY[i]) {
isFail = true;
break;
}
}
repaint();//重画页面
}
timer.start();//开启定时器
}
}
}