这篇文章主要为大家详细介绍了Java实现贪吃蛇游戏,1小时学会贪吃蛇游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
贪吃蛇
- 帧,如果时间片足够小,就是动画,一秒30帧,60帧。很多静态的连续的静态图连起来看就是动画。
- 键盘监听
- 定时器
一、初始静态界面
1.主启动类:
- 游戏主窗口的创建,大小不可变
public class StratGame {
public static void main(String[] args) {
JFrame frame= new JFrame();
frame.setBounds(10,10,915,730);
frame.setResizable(false);//窗口大小不可变
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
//正常的游戏界面都应该在面板上
frame.add(new GamePanel());
frame.setVisible(true);
}
}
2.数据中心类:
- 存放所有静态游戏素材
package com.gong.snake;
import javax.swing.*;
import java.net.URL;
public class Data {
//使用相对路径存放素材
public static URL headerURL = Data.class.getResource("static/banner.png");
public static URL downURL = Data.class.getResource("static/down.png");
public static URL upURL = Data.class.getResource("static/up.png");
public static URL rightURL = Data.class.getResource("static/right.png");
public static URL leftURL = Data.class.getResource("static/left.png");
public static URL bodyURL = Data.class.getResource("static/body.png");
public static URL foodURL = Data.class.getResource("static/food.png");
//将上面获取的图片绘制成图标
public static ImageIcon header = new ImageIcon(headerURL);
public static ImageIcon up = new ImageIcon(upURL);
public static ImageIcon down = new ImageIcon(downURL);
public static ImageIcon right = new ImageIcon(rightURL);
public static ImageIcon left = new ImageIcon(leftURL);
public static ImageIcon body = new ImageIcon(bodyURL);
public static ImageIcon food = new ImageIcon(foodURL);
}
3.游戏面板类:
- 正常的游戏界面都应该放在面板上,于是我们新建一个游戏面板类,继承JPanel
//游戏面板
public class GamePanel extends JPanel{
//1.绘制组件函数,我们游戏中的所有东西都使用画笔来画
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);//清屏作用,不用出现闪屏
//绘制静态面板
this.setBackground(Color.WHITE);
Data.header.paintIcon(this, g, 25, 0);// 游戏头部,广告栏
g.fillRect(25, 65, 850, 600);// 默认游戏界面
}
}
二、小蛇绘制
1.新建静态小蛇
- 修改gamePanel类,并且赋予小蛇初始位置
修改GamePanel类
//游戏面板
public class GamePanel extends JPanel{
//定义蛇的数据结构
int length;//蛇的长度
int[] snakeX = new int[600];//蛇的X坐标
int[] snakeY = new int[500];//蛇的Y坐标
//构造器
public GamePanel() {
init();
}
//初始化小蛇
public void init() {
length = 3;
snakeX[0] = 100;snakeY[0] = 100;//脑袋坐标
snakeX[1] = 75;snakeY[1] = 100;//第一块身体坐标
snakeX[2] = 50;snakeY[2] = 100;//第二块身体坐标
}
//1.绘制组件函数,我们游戏中的所有东西都使用画笔来画
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);//清屏作用,不用出现闪屏
//绘制静态面板
this.setBackground(Color.WHITE);
Data.header.paintIcon(this, g, 25, 0);// 游戏头部,广告栏
g.fillRect(25, 65, 850, 600);// 默认游戏界面
//2.绘制静态小蛇
Data.right.paintIcon(this, g, snakeX[0], snakeY[0]);//绘制初始头的位置
for (int i = 1; i < length; i++) {
Data.body.paintIcon(this, g, snakeX[i], snakeY[i]);// 第一个身体的坐标
}
}
}
2.新加游戏状态
- 游戏状态,状态监听需要是实现键盘监听事件KeyListener,重写keyPressed方法,另外两个方法什么也不写
修改GamePanel类
//游戏面板
public class GamePanel extends JPanel implements KeyListener{
//定义蛇的数据结构
int length;//蛇的长度
int[] snakeX = new int[600];//蛇的X坐标
int[] snakeY = new int[500];//蛇的Y坐标
String fx;//蛇的初始方向
//游戏状态,默认不开始!
boolean isStart=false;//默认游戏是不开始!
//构造器
public GamePanel() {
init();
//获得焦点和键盘事件
this.setFocusable(true);//获得焦点事件
this.addKeyListener(this);//获得键盘监听事件
}
//初始化小蛇
public void init() {
length = 3;
snakeX[0] = 100;snakeY[0] = 100;//脑袋坐标
snakeX[1] = 75;snakeY[1] = 100;//第一块身体坐标
snakeX[2] = 50;snakeY[2] = 100;//第二块身体坐标
fx = "R";//初始化方向向右
}
//1.绘制组件函数,我们游戏中的所有东西都使用画笔来画
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);//清屏作用,不用出现闪屏
//绘制静态面板
this.setBackground(Color.WHITE);
Data.header.paintIcon(this, g, 25, 0);// 游戏头部,广告栏
g.fillRect(25, 65, 850, 600);// 默认游戏界面
//2.绘制静态小蛇
Data.right.paintIcon(this, g, snakeX[0], snakeY[0]);//绘制初始头的位置
for (int i = 1; i < length; i++) {
Data.body.paintIcon(this, g, snakeX[i], snakeY[i]);// 第一个身体的坐标
}
//3.画游戏状态
if (!isStart) {
g.setColor(Color.white);
g.setFont(new Font("微软雅黑", Font.BOLD, 40));//设置字体,第二个参数加粗
g.drawString("按下空格开始游戏!", 300, 450);
}
}
//游戏状态的键盘事件监听
@Override
public void keyPressed(KeyEvent e) {
int keyCode = e.getKeyCode();//获得键盘按键是哪一个
if (keyCode==KeyEvent.VK_SPACE){//如果按下空格,则取反游戏状态
isStart=!isStart;
repaint();
}
}
@Override
public void keyReleased(KeyEvent e) {}
@Override
public void keyTyped(KeyEvent e) {}
}
三、小蛇移动
1.定时器实现小蛇移动
- 想要小蛇动起来,即要用到定时器及时更新面板状态
- 面板实现ActionListener,重写actionPerformed方法
修改GamePanel类
//游戏面板
public class GamePanel extends JPanel implements KeyListener, ActionListener {
//定义蛇的数据结构
int length;//蛇的长度
int[] snakeX = new int[600];//蛇的X坐标
int[] snakeY = new int[500];//蛇的Y坐标
String fx;//蛇的初始方向
//游戏状态,默认不开始!
boolean isStart=false;//默认是不开始!
//定时器
Timer timer=new Timer(100,this);//监听当前类,100毫秒刷新一次
//构造器
public GamePanel() {
init();
//获得焦点和键盘事件
this.setFocusable(true);//获得焦点事件
this.addKeyListener(this);//获得键盘监听事件
timer.start();//游戏一开始就开始
}
//初始化小蛇
public void init() {
length = 3;
snakeX[0] = 100;snakeY[0] = 100;//脑袋坐标
snakeX[1] = 75;snakeY[1] = 100;//第一块身体坐标
snakeX[2] = 50;snakeY[2] = 100;//第二块身体坐标
fx = "R";//初始化方向向右
}
//1.绘制组件函数,我们游戏中的所有东西都使用画笔来画
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);//清屏作用,不用出现闪屏
//绘制静态面板
this.setBackground(Color.WHITE);
Data.header.paintIcon(this, g, 25, 0);// 游戏头部,广告栏
g.fillRect(25, 65, 850, 600);// 默认游戏界面
//2.绘制静态小蛇
Data.right.paintIcon(this, g, snakeX[0], snakeY[0]);//绘制初始头的位置
for (int i = 1; i < length; i++) {
Data.body.paintIcon(this, g, snakeX[i], snakeY[i]);// 第一个身体的坐标
}
//画游戏状态
if (!isStart) {
g.setColor(Color.white);
g.setFont(new Font("微软雅黑", Font.BOLD, 40));//设置字体
g.drawString("按下空格开始游戏!", 300, 450);
}
}
//键盘事件监听
@Override
public void keyPressed(KeyEvent e) {
int keyCode = e.getKeyCode();//获得键盘按键是哪一个
if (keyCode==KeyEvent.VK_SPACE){//如果按下空格,则取反游戏状态
isStart=!isStart;
repaint();
}
}
//事件监听----需要用到固定的时间来刷新,1s十次
@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];
}
if (fx.equals("R")){
snakeX[0]=snakeX[0]+25;
}
//边界判断
if (snakeX[0]>850){snakeX[0]=25;}
repaint();//重画页面
}
timer.start();//定时器开启!
}
@Override
public void keyReleased(KeyEvent e) {
}
@Override
public void keyTyped(KeyEvent e) {
}
}
2.小蛇方向控制
- 控制方向则是要严格计算每次坐标的大小
- 比如向上走超出边框则马上从最底再次进入游戏界面
- 例如蛇走到顶端,下一步出现在底端的坐标则是头刚好放在最下方的时刻,即为(游戏界面最低端到最顶端的距离-蛇头宽度)
修改GamePanel类
//游戏面板
public class GamePanel extends JPanel implements KeyListener, ActionListener {
//定义蛇的数据结构
int length;//蛇的长度
int[] snakeX = new int[600];//蛇的X坐标
int[] snakeY = new int[500];//蛇的Y坐标
String fx;//蛇的初始方向
//游戏状态,默认不开始!
boolean isStart=false;//默认是不开始!
//定时器
Timer timer=new Timer(100,this);//监听当前类,100毫秒刷新一次
//构造器
public GamePanel() {
init();
//获得焦点和键盘事件
this.setFocusable(true);//获得焦点事件
this.addKeyListener(this);//获得键盘监听事件
timer.start();//游戏一开始就开始
}
//初始化小蛇
public void init() {
length = 3;
snakeX[0] = 100;snakeY[0] = 100;//脑袋坐标
snakeX[1] = 75;snakeY[1] = 100;//第一块身体坐标
snakeX[2] = 50;snakeY[2] = 100;//第二块身体坐标
fx = "R";//初始化方向向右
}
//1.绘制组件函数,我们游戏中的所有东西都使用画笔来画
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);//清屏作用,不用出现闪屏
//绘制静态面板
this.setBackground(Color.WHITE);
Data.header.paintIcon(this, g, 25, 11);// 游戏头部,广告栏
g.fillRect(25, 75, 850, 600);// 默认游戏界面
//把小蛇画上去
if (fx.equals("R")) {
Data.right.paintIcon(this, g, snakeX[0], snakeY[0]);//头,需要通过方向来判断
} else if (fx.equals("up")) {
Data.up.paintIcon(this, g, snakeX[0], snakeY[0]);
} else if (fx.equals("down")) {
Data.down.paintIcon(this, g, snakeX[0], snakeY[0]);
} else if (fx.equals("L")) {
Data.left.paintIcon(this, g, snakeX[0], snakeY[0]);
}
for (int i = 1; i < length; i++) {
Data.body.paintIcon(this, g, snakeX[i], snakeY[i]);// 第一个身体的坐标
}
//画游戏状态
if (!isStart) {
g.setColor(Color.white);
g.setFont(new Font("微软雅黑", Font.BOLD, 40));//设置字体
g.drawString("按下空格开始游戏!", 300, 450);
}
}
//键盘事件监听
@Override
public void keyPressed(KeyEvent e) {
int keyCode = e.getKeyCode();//获得键盘按键是哪一个
if (keyCode==KeyEvent.VK_SPACE){//如果按下空格,则取反游戏状态
isStart=!isStart;
repaint();
}
//小蛇移动监听
if (keyCode==KeyEvent.VK_UP){
fx="up";
}else if (keyCode==KeyEvent.VK_DOWN){
fx="down";
}else if (keyCode==KeyEvent.VK_LEFT){
fx="L";
}else if (keyCode==KeyEvent.VK_RIGHT){
fx="R";
}
}
//事件监听----需要用到固定的时间来刷新,1s十次
@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];
}
//走向
if (fx.equals("R")) {
snakeX[0] = snakeX[0] + 25;
if (snakeX[0] > 850) { snakeX[0] = 25; }//边界判断
} else if (fx.equals("L")) {
snakeX[0] = snakeX[0] - 25;
if (snakeX[0] < 25) { snakeX[0] = 850; }//边界判断
} else if (fx.equals("up")) {
snakeY[0] = snakeY[0] - 25;
if (snakeY[0] < 75) { snakeY[0] = 650; }//边界判断
} else if (fx.equals("down")) {
snakeY[0] = snakeY[0] + 25;
if (snakeY[0] > 650) { snakeY[0] = 75; }//边界判断
}
repaint();//重画页面
}
timer.start();//定时器开启!
}
@Override
public void keyReleased(KeyEvent e) {
}
@Override
public void keyTyped(KeyEvent e) {
}
}
3.小蛇吃食物
- 定义食物坐标,并且需要一个random来使食物随机生成在界面上
//初始化食物的坐标
int foodX;
int foodY;
Random random=new Random();
- 在画板中,将食物画到界面上
//把食物画上去
Data.food.paintIcon(this,g,foodX,foodY);
- 在监听事件中,判定吃食物
//吃食物判断
if (snakeX[0]==foodX && snakeY[0]==foodY){
length++;//长度+1
//再次随机生成食物
//初始化食物,把食物随机分布在界面上
foodX=25+25*random.nextInt(34);
foodY=75+25*random.nextInt(24);
}
4.小蛇失败判定
- 新建游戏状态
boolean isFail= false;//默认游戏状态失败
- 绘制失败在画板上
if (isFail){
g.setColor(Color.red);
g.setFont(new Font("微软雅黑", Font.BOLD, 40));//设置字体
g.drawString("失败,按下空格重新开始!", 300, 450);
}
- 监听键盘事件判断游戏状态
if (keyCode==KeyEvent.VK_SPACE){//如果按下空格,则取反游戏状态
if (isFail){
//重新开始
isFail=false;
init();//重新开始重置游戏初始状态
}else {
isStart=!isStart;
}
repaint();
}
- 游戏事件监听,当小蛇撞到自己判断游戏失败
//失败判断,撞到自己算失败
for (int i=1;i<length;i++){
if (snakeX[0]==snakeX[i] && snakeY[0]==snakeY[i]){
isFail=true;
}
}
5.添加游戏数据面板
- 新增游戏初始分数
//初始化分数
int score;
//初始化小蛇
public void init() {
length = 3;
snakeX[0] = 100;snakeY[0] = 100;//脑袋坐标
snakeX[1] = 75;snakeY[1] = 100;//第一块身体坐标
snakeX[2] = 50;snakeY[2] = 100;//第二块身体坐标
fx = "R";//初始化方向向右
//初始化食物,把食物随机分布在界面上
foodX=25+25*random.nextInt(34);
foodY=75+25*random.nextInt(24);
//初始分数
score=0;
}
- 画分数和长度到面板
//画积分和长度
g.setColor(Color.WHITE);
g.setFont(new Font("宋体", Font.BOLD, 15));
g.drawString("长度:"+length,750,35);
g.drawString("分数:"+score,750,50);
- 事件判断吃到食物分数增加10
//吃食物判断
if (snakeX[0]==foodX && snakeY[0]==foodY){
length++;//长度+1
score=score+10;
//再次随机生成食物
//初始化食物,把食物随机分布在界面上
foodX=25+25*random.nextInt(34);
foodY=75+25*random.nextInt(24);
}
四、总结
GUI中常用的几种容器和标签方法就到这里结束了,在贪吃蛇小游戏中,几乎涵盖了所有的GUI基础,还有javaSE的部分知识
还可优化的点:
- 游戏拓展性:
- 比如可以有多种样子的食物,分别代表不同的分数
- 添加等级,到达一定分数就将定时器的时间缩短,以实现小蛇速度的提升
- 连接数据库,将游戏数据写入数据库,再次打开游戏可以继续玩上次记录
五、附录(可运行代码)
1.主启动类:
- 游戏主窗口的创建,大小不可变
package com.gong.snake;
import javax.swing.*;
import java.awt.*;
public class StartGame {
public static void main(String[] args) {
JFrame frame = new JFrame();
Container container = frame.getContentPane();
frame.setBounds(400, 400, 900, 720);
frame.setResizable(false);
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
//正常的游戏界面都应该在面板上
container.add(new GamePanel());
frame.setVisible(true);
//正常游戏界面都在上面
}
}
2.数据中心类:
- 存放所有静态游戏素材
package com.gong.snake;
import javax.swing.*;
import java.net.URL;
public class Data {
//使用相对路径存放素材
public static URL headerURL = Data.class.getResource("static/banner.png");
public static URL downURL = Data.class.getResource("static/down.png");
public static URL upURL = Data.class.getResource("static/up.png");
public static URL rightURL = Data.class.getResource("static/right.png");
public static URL leftURL = Data.class.getResource("static/left.png");
public static URL bodyURL = Data.class.getResource("static/body.png");
public static URL foodURL = Data.class.getResource("static/food.png");
//将上面获取的图片绘制成图标
public static ImageIcon header = new ImageIcon(headerURL);
public static ImageIcon up = new ImageIcon(upURL);
public static ImageIcon down = new ImageIcon(downURL);
public static ImageIcon right = new ImageIcon(rightURL);
public static ImageIcon left = new ImageIcon(leftURL);
public static ImageIcon body = new ImageIcon(bodyURL);
public static ImageIcon food = new ImageIcon(foodURL);
}
3.游戏面板类:
- 正常的游戏界面都应该放在面板上,于是我们新建一个游戏面板类,继承JPanel
package com.gong.snake;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
//游戏面板
public class GamePanel extends JPanel implements KeyListener, ActionListener {
//获取焦点和监听事件
int length;//蛇长度
int[] snakeX = new int[600];//蛇的x坐标
int[] snakeY = new int[600];//蛇的y坐标
String direction; //蛇头方向
Boolean isStar = false; //游戏状态
Boolean isFail = false; //是否失败
int foodX; //食物坐标x,y
int foodY;
int score;//分数
Timer timer = new Timer(100,this);//定时器
java.util.Random random=new java.util.Random();//随机数
//结构体
public GamePanel() {
init();
//获取焦点和监听事件
setFocusable(true);
addKeyListener(this);
}
//初始化
public void init(){
length = 3;
snakeX[0] = 100;snakeY[0]=100;//蛇头
snakeX[1] = 75;snakeY[1]=100;//第一节身体
snakeX[2] = 50;snakeY[2]=100;//第二节身体
foodX = 25 + 25*random.nextInt(34);//初始化食物位置
foodY = 75 + 25*random.nextInt(24);
direction = "right";
score = 0;
}
//绘制组件函数,我们游戏中的所有东西都使用画笔来画
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);//清屏
this.setBackground(Color.WHITE);
Data.header.paintIcon(this, g, 25, 11);//广告栏
g.fillRect(25, 75, 850,600);//游戏区域
//记分板模块
g.setColor(Color.white);
g.setFont(new Font("微软雅黑", Font.BOLD, 18));//字体字号
g.drawString("长度:"+length, 750, 30);
g.drawString("分数:"+score, 750, 55);
//设置蛇头方向
if (direction == "right"){
Data.right.paintIcon(this, g, snakeX[0], snakeY[0]);
}else if(direction == "left"){
Data.left.paintIcon(this, g, snakeX[0], snakeY[0]);
}else if(direction == "up"){
Data.up.paintIcon(this, g, snakeX[0], snakeY[0]);
}else if(direction == "down"){
Data.down.paintIcon(this, g, snakeX[0], snakeY[0]);
}
//画蛇body
for (int i = 1; i < length; i++) {
Data.body.paintIcon(this, g, snakeX[i],snakeY[i]);
}
Data.food.paintIcon(this, g, foodX, foodY);
//文字
if (isStar == false){
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);
}
}
//键盘监听
@Override
public void keyPressed(KeyEvent e) {
//获取键盘code
int keyCode = e.getKeyCode();
if (keyCode == KeyEvent.VK_SPACE){
if (isFail){
//重新开始
isFail = !isFail;
init();
}else {
isStar = !isStar;//暂停/开始
}
//重画
repaint();
}
//切换蛇头图片根据方向
if (keyCode == KeyEvent.VK_UP){
direction = "up";
}else if (keyCode == KeyEvent.VK_DOWN){
direction = "down";
}
else if (keyCode == KeyEvent.VK_LEFT){
direction = "left";
}
else if (keyCode == KeyEvent.VK_RIGHT){
direction = "right";
}
repaint();
timer.start();
}
//事件监听
@Override
public void actionPerformed(ActionEvent e) {
//使蛇移动
if (isStar && isFail == false){
for (int i = length-1; i >0; i--) {
snakeX[i] = snakeX[i-1];
snakeY[i] = snakeY[i-1];
}
if (direction == "right"){
snakeX[0] = snakeX[0]+25;
if (snakeX[0]>850) { snakeX[0]=25; }
}else if(direction == "left"){
snakeX[0] = snakeX[0]-25;
if (snakeX[0]<25) { snakeX[0]=850; }
}else if(direction == "down"){
snakeY[0] = snakeY[0]+25;
if (snakeY[0]>650) { snakeY[0]=70; }
}else if(direction == "up"){
snakeY[0] = snakeY[0]-25;
if (snakeY[0]<75) { snakeY[0]=650; }
}
//蛇吃食物操作
if (snakeX[0] == foodX && snakeY[0] == foodY){
length++;
score += 10;
foodX = 25 + 25*random.nextInt(34);
foodY = 75 + 25*random.nextInt(24);
//System.out.println(foodX+" "+foodY);
}
//游戏失败判断(头碰到身体即为失败)
for (int i = 1; i < length; i++) {
if (snakeX[0] == snakeX[i] && snakeY[0] == snakeY[i]){
isFail = true;
}
}
repaint();
}
}
@Override
public void keyReleased(KeyEvent e) {
}
@Override
public void keyTyped(KeyEvent e) {
}
}