首页 > 其他 > 详细

贪吃蛇

时间:2021-05-02 16:44:23      阅读:24      评论:0      收藏:0      [点我收藏+]

运行效果图:

技术分享图片
技术分享图片
技术分享图片

一、创建项目

技术分享图片
分析目录结构:

  • StartGame为游戏的启动类
  • Data为游戏的图片资源类
  • GamePanel为游戏的具体逻辑代码类。

二、编写图片资源Data类

引入statics图片资源文件

	//头部---(1)获取图片的路径   (2)new一个ImageIcon对象,将图片放到游戏里
	public static URL headerURL = Data.class.getResource("statics/header.jpg");
	public static ImageIcon header = new ImageIcon(headerURL);

三、编写程序主启动类

新建一个窗口 JFrame 窗体类,给它一个宽和高,分别是长900,宽720的大小,给窗口设置为不可调整,setResizable()方法设置为false,并且将窗口展示出来。具体启动类的代码如下

public static void main(String[] args) {
	JFrame frame = new JFrame("Steven-贪吃蛇小游戏");// new一个JFrame 窗体对象
	frame.setResizable(false);//窗口大小不可变
	frame.setBounds(10,10,900,720);
	frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);// 设置关闭事件,游戏可以关闭
	frame.setVisible(true);//将窗口展示出来
}

四、核心类-游戏面板类

1、操作前思考

先将需要的元素定义,再将定义的元素画到游戏面板上,最终让其动起来(利用键盘和事件监听)。

2、定义属性及初始化

(1)定义属性:
游戏需要的基本元素:小蛇的长度、方向及位置,食物的位置。
利用布尔类型的属性来判断游戏是否开始及失败。
最后要让小蛇动起来,就需要有一个时间定时器Timer类。
代码如下:

	int length;//蛇的长度
	int[] snakeX = new int[600];//蛇的x坐标  
	int[] snakeY = new int[500];//蛇的y坐标 
	String dirction;//蛇的方向
	boolean isStart = false;//游戏是否开始:默认是不开始
	Timer timer = new Timer(100,this);//定时器:100毫秒执行一次
	int foodX;//食物的x坐标
	int foodY;//食物的y坐标
	Random random = new Random();
	boolean isFail = false;//游戏是否结束(失败)
	int score;//游戏的分数	

(2)初始化方法:
将定义的属性进行初始化,且游戏重新开始时,可直接调用初始化方法
代码如下:

	public void init() {
		length = 3;//初始小蛇有三节,包括小脑袋
        //初始化开始的蛇,给蛇定位
		snakeX[0] = 100;snakeY[0] = 100;//脑袋的坐标
		snakeX[1] = 75;snakeY[1] = 100;//第一个身体的坐标
		snakeX[2] = 50;snakeY[2] = 100;//第二个身体的坐标
		dirction = "R";//初始方向向右
		//把食物随机分布在界面上
		foodX = 25 + 25*random.nextInt(34);
		foodY = 75 + 25*random.nextInt(24);
		score = 0;//初始化分数为零
	}

3、绘制组件

创建GamePanel类继承JPanel类我们所有的游戏画面全部都是由画布组件来提供的,paintComponent方法这是整个游戏的核心方法,没有整个方法的话一切都是空白,这个画布上的所有图像都是依赖于paintComponent()方法的。
写入paintComponent方法,然后设置面板的背景颜色,绘制头部信息区域,食物,小蛇,游戏提示,失败判断,还有游戏区域等等全部添加上去。

	@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);//默认的游戏界面
		
		//画积分
		g.setColor(Color.yellow);
		g.setFont(new Font("微软雅黑", Font.BOLD, 18));//设置字体
		g.drawString("长度:" + length, 750, 30);
		g.drawString("分数:" + score, 750, 50);
		
		//绘制食物
		Data.food.paintIcon(this, g, foodX, foodY);
				
		//绘制小蛇
		if(dirction.equals("R")) {
			Data.right.paintIcon(this, g, snakeX[0], snakeY[0]);		
		}else if(dirction.equals("L")) {
			Data.left.paintIcon(this, g, snakeX[0], snakeY[0]);		
		}else if(dirction.equals("U")) {
			Data.up.paintIcon(this, g, snakeX[0], snakeY[0]);		
		}else if(dirction.equals("D")) {
			Data.down.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==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);
		}
	}

4、键盘监听及事件监听

小蛇的移动以及吃食物问题:我们需要引入键盘监听和事件监听的接口。

public class GamePanel extends JPanel implements KeyListener,ActionListener{}

首先判断游戏状态是否为为开始,如果是开始那么小蛇就要动,小蛇脑袋先通过键盘监听事件获得要运动的方向,再通过事件监听,实现在此方向上往前移动一个格。

	snakeX[0]+25、snakeX[0]-25、snakeY[0]+25、snakeY[0]-25

以上为小蛇头部分别向右、左、下、上移动的四种情况。
接下来就是小蛇身体,小蛇每移动一位,他的后一位就要往前移动,相当于代替前一位。

	for(int i=length-1 ; i>0 ; i--) {
		snakeX[i] = snakeX[i-1];
		snakeY[i] = snakeY[i-1];
	}

然后考虑吃食物,当蛇和头的食物坐标一样时,就算吃到了食物,吃到食物后就让长度加一。对应的分数也要增加,默认增加10分。吃到食物后食物不可能还在原地不动吧?所以这个时候要重新引入食物的随机分布,随机让食物出现在游戏界面的任意位置。

	foodX = 25 + 25*random.nextInt(34);
	foodY = 75 + 25*random.nextInt(24);

游戏结束:那结束怎么判断,当然是头和身体撞到一起就失败了嘛,同样的道理只要移动的过程中满足

snakeX[0] == snakeX[i] && snakeY[0] == snakeY[i];

就说明游戏结束。
注:小蛇不能反向移动,这是我第一次尝试出现的Bug,解决办法就是在键盘监听事件中,按键时多加一个条件,使得不能反向。例--键盘监听上转时,条件1为按上键,条件2为此时移动方向不为下:

if(keyCode == KeyEvent.VK_UP && !dirction.equals("D")) {
	dirction = "U";
}

最后加上repaint()方法,需要不断的更新页面让动画动起来。

代码如下:

	@Override
	public void actionPerformed(ActionEvent e) {
		if(isStart && isFail == false) {//如果游戏是开始状态,就让小蛇动起来
			//吃食物
			if(snakeX[0] == foodX && snakeY[0] == foodY) {
				length++;//长度加一
				score = score + 10;//分数加十
				//重新随机生成食物
				foodX = 25 + 25*random.nextInt(34);
				foodY = 75 + 25*random.nextInt(24);
			}
			
			//移动
			for(int i=length-1 ; i>0 ; i--) {//后一节移到前一节的位置
				snakeX[i] = snakeX[i-1];
				snakeY[i] = snakeY[i-1];
			}
			
			//走向
			if(dirction == "R") {
				snakeX[0] = snakeX[0]+25;
				if(snakeX[0] > 850) {snakeX[0] = 25;}//边界判断
			}else if(dirction == "L") {
				snakeX[0] = snakeX[0]-25;
				if(snakeX[0] < 25) {snakeX[0] = 850;}//边界判断
			}else if(dirction == "U") {
				snakeY[0] = snakeY[0]-25;
				if(snakeY[0] < 75) {snakeY[0] = 650;}//边界判断
			}else if(dirction == "D") {
				snakeY[0] = snakeY[0]+25;
				if(snakeY[0] > 650) {snakeY[0] = 75;}//边界判断
			}
			
			//失败判断:撞到自己就算失败
			for(int i=1 ; i<length ; i++) {
				if(snakeX[0] == snakeX[i] && snakeY[0] == snakeY[i]) {
					isFail = true;
				}
			}
			repaint();//重画页面
		}
		timer.start();//定时器开启
	}

键盘按下方法如下:

	@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 && !dirction.equals("D")) {
			dirction = "U";
		}else if(keyCode == KeyEvent.VK_DOWN && !dirction.equals("U")) {
			dirction = "D";
		}else if(keyCode == KeyEvent.VK_LEFT && !dirction.equals("R")) {
			dirction = "L";
		}else if(keyCode == KeyEvent.VK_RIGHT && !dirction.equals("L")) {
			dirction = "R";
		}
		
	}

最后的最后,我们需要把我们自己编写好的画布背景添加到主程序类中。

	frame.add(new GamePanel());

五、总代码如下:

Data 类:

package com.snake;

import java.net.URL;
import javax.swing.*;

public class Data {
	
	//头部---(1)获取图片的路径   (2)new一个ImageIcon对象,将图片放到游戏里
	public static URL headerURL = Data.class.getResource("statics/header.jpg");
	public static ImageIcon header = new ImageIcon(headerURL);
	//上下左右
	public static URL upURL = Data.class.getResource("statics/up.jpg");
	public static ImageIcon up = new ImageIcon(upURL);
	public static URL downURL = Data.class.getResource("statics/down.jpg");
	public static ImageIcon down = new ImageIcon(downURL);
	public static URL leftURL = Data.class.getResource("statics/left.jpg");
	public static ImageIcon left = new ImageIcon(leftURL);
	public static URL rightURL = Data.class.getResource("statics/right.jpg");
	public static ImageIcon right = new ImageIcon(rightURL);
	//身体
	public static URL bodyURL = Data.class.getResource("statics/body.jpg");
	public static ImageIcon body = new ImageIcon(bodyURL);
	//食物
	public static URL foodURL = Data.class.getResource("statics/food.jpg");
	public static ImageIcon food = new ImageIcon(foodURL);
}

GamePanel 类:

package com.snake;

import java.awt.*;
import javax.swing.*;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.Random;

public class GamePanel extends JPanel implements KeyListener,ActionListener{
	
	//定义蛇的数据结构
	int length;//蛇的长度
	int[] snakeX = new int[600];//蛇的x坐标  
	int[] snakeY = new int[500];//蛇的y坐标 
	String dirction;//蛇的方向
	boolean isStart = false;//游戏是否开始:默认是不开始
	Timer timer = new Timer(100,this);//定时器:100毫秒执行一次
	int foodX;//食物的x坐标
	int foodY;//食物的y坐标
	Random random = new Random();
	boolean isFail = false;//游戏是否结束(失败)
	int score;//游戏的分数	
	
	//构造器
	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;//第二个身体的坐标
		dirction = "R";//初始方向向右
		//把食物随机分布在界面上
		foodX = 25 + 25*random.nextInt(34);
		foodY = 75 + 25*random.nextInt(24);
		score = 0;//初始化分数为零
	}
	
	//绘制面板,我们游戏中的所有东西,都使用这个画笔来画
	@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);//默认的游戏界面
		
		//画积分
		g.setColor(Color.yellow);
		g.setFont(new Font("微软雅黑", Font.BOLD, 18));//设置字体
		g.drawString("长度:" + length, 750, 30);
		g.drawString("分数:" + score, 750, 50);
		
		//绘制食物
		Data.food.paintIcon(this, g, foodX, foodY);
				
		//绘制小蛇
		if(dirction.equals("R")) {
			Data.right.paintIcon(this, g, snakeX[0], snakeY[0]);		
		}else if(dirction.equals("L")) {
			Data.left.paintIcon(this, g, snakeX[0], snakeY[0]);		
		}else if(dirction.equals("U")) {
			Data.up.paintIcon(this, g, snakeX[0], snakeY[0]);		
		}else if(dirction.equals("D")) {
			Data.down.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==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) {
		int keyCode = e.getKeyCode();//获得键盘按键是哪一个
		if(keyCode == KeyEvent.VK_SPACE) {//如果按下的是空格键
			if(isFail) {//如果游戏失败,重新开始
				isFail = false;
				init();//重新初始化
			}else {//否则,暂停游戏
				isStart = !isStart;//取反
			}
			repaint();
		}
		//键盘控制小蛇转向(第二个条件为:不能反向移动)
		if(keyCode == KeyEvent.VK_UP && !dirction.equals("D")) {
			dirction = "U";
		}else if(keyCode == KeyEvent.VK_DOWN && !dirction.equals("U")) {
			dirction = "D";
		}else if(keyCode == KeyEvent.VK_LEFT && !dirction.equals("R")) {
			dirction = "L";
		}else if(keyCode == KeyEvent.VK_RIGHT && !dirction.equals("L")) {
			dirction = "R";
		}
		
	}
	
	//事件监听---需要通过固定事件来刷新,1秒10次
	@Override
	public void actionPerformed(ActionEvent e) {
		if(isStart && isFail == false) {//如果游戏是开始状态,就让小蛇动起来
			//吃食物
			if(snakeX[0] == foodX && snakeY[0] == foodY) {
				length++;//长度加一
				score = score + 10;//分数加十
				//重新随机生成食物
				foodX = 25 + 25*random.nextInt(34);
				foodY = 75 + 25*random.nextInt(24);
			}
			
			//移动
			for(int i=length-1 ; i>0 ; i--) {//后一节移到前一节的位置
				snakeX[i] = snakeX[i-1];
				snakeY[i] = snakeY[i-1];
			}
			
			//走向
			if(dirction == "R") {
				snakeX[0] = snakeX[0]+25;
				if(snakeX[0] > 850) {snakeX[0] = 25;}//边界判断
			}else if(dirction == "L") {
				snakeX[0] = snakeX[0]-25;
				if(snakeX[0] < 25) {snakeX[0] = 850;}//边界判断
			}else if(dirction == "U") {
				snakeY[0] = snakeY[0]-25;
				if(snakeY[0] < 75) {snakeY[0] = 650;}//边界判断
			}else if(dirction == "D") {
				snakeY[0] = snakeY[0]+25;
				if(snakeY[0] > 650) {snakeY[0] = 75;}//边界判断
			}
			
			//失败判断:撞到自己就算失败
			for(int i=1 ; i<length ; i++) {
				if(snakeX[0] == snakeX[i] && snakeY[0] == snakeY[i]) {
					isFail = true;
				}
			}
			repaint();//重画页面
		}
		timer.start();//定时器开启
	}
	
	@Override
	public void keyTyped(KeyEvent e) {}
	@Override
	public void keyReleased(KeyEvent e) {}

}

StartGame 主启动类:

package com.snake;

import javax.swing.*;

public class StartGame {
	public static void main(String[] args) {
		JFrame frame = new JFrame("Steven-贪吃蛇小游戏");// new一个JFrame 窗体对象
		frame.setResizable(false);//窗口大小不可变
		frame.setBounds(10,10,900,720);
		frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);// 设置关闭事件,游戏可以关闭
		frame.add(new GamePanel()); //通常游戏界面都应该在面板上
		frame.setVisible(true);//将窗口展示出来
	}
}

六、写在最后

作为一个外行人,对我来说还是蛮有挑战性的,不过庆幸的是终于给它做出来了!!
真的很考验自己的逻辑思维能力,希望自己能在自己喜欢的事业上坚持下去,并且越走越远!
感谢大家的支持!

贪吃蛇

原文:https://www.cnblogs.com/StevenPark/p/14725216.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!