基于JAVA的贪吃蛇小游戏开发
开发了 一款基于JAVA的贪吃蛇小游戏,大家可以拿来在正式着手项目的时候拿这个练练手,话不多说,
成果图:
1.游戏面板初始化时如上,按空格键开始游戏
2.在游戏过程中,通过吃面板上随机生成的食物来增加自身长度,可以通过按空格键来暂停游戏。
3.当贪吃蛇撞击到自身时,则判定游戏失败,按空格初始化游戏数据重新开始。
步骤一:贪吃蛇的界面绘制
启动类
首先是编写游戏的启动类,这里定义的start类需要继承JFrame父类,在通常的游戏开发时,包括项目开发,在编写启动类时,不应该放入其他不相关的代码,其唯一功能就是启动项目的入口。
public class Start extends JFrame{
public static void main(String args[]){
JFrame jFrame=new JFrame("贪吃蛇游戏");
jFrame.setVisible(true);//可见
jFrame.setBounds(300,300,920,700);
jFrame.setResizible(false);//不可拖动
jFrame.setDefaultCloseOperation(WindowConstans.EXIT_ON_CLOSE);//可关闭
jFrame.add(new PanelGame());//添加自定义面板
}
}
Data类
在编写完启动类后,需要图片的素材包括(贪吃蛇的各个方向的头部,身体部分,食物)以及一个存放这些图片数据的Data类,将这些素材转化为ImageIcon图标,这里利用的时static帆方法,目的是更加快捷的访问到存储的图片素材。
public class Data{
public static URL bodyUrl=Data.class.getResource("//素材的路径");
public static ImageIcon body=new ImageIcon(bodyUrl);
public static URL foodUrl=Data.class.getResource("//素材的路径");
public static ImageIcon food=new ImageIcon(foodUrl);
public static URL foodUrl=Data.class.getResource("//素材的路径");
public static ImageIcon food=new ImageIcon(foodUrl);
.
.
.
.
.
}
PanelGame操作类
在游戏的开发时,用的最多的就是面板类,所以它需要继承的是JPanel父类
,且需要覆写里面的paintComponent方法,其主要功能是利用画笔添加多个需要的组件。
publi class PanelGame extends Jpanel{
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);//清屏
this.setBackground(Color.White);//面板颜色
g.fillRect(25,75,850,600);//面板大小
Data.paintIcon(this,g,25,11);//将头部图标插入
}
}
步骤二:绘制静态的贪吃蛇
在定义静态贪吃蛇的时候,需要在PanelGame类里定义一些变量,以及在init()方法中时初始化的这些变量。
int length;//长度
String direction;//默认头部方向
int snake_X=new int[500];//贪吃蛇的左标范围
int snake_Y=new int[500];
boolean isStart=false;//默认游戏状态
public PanelGame(){//构造方法
init();
}
public void init(){//初始化游戏数据
length=3;
direction="Right";
snake_X[0]=100;snake_Y[0]=100;//头部坐标
snake_X[1]=100;snake_Y[1]=100;//第一个身体坐标
snake_X[2]=100;snake_Y[2]=100;//第二个身体坐标
}
利用继承覆写的paintComponent方法再将贪吃蛇添加到面板上并设置游戏默认状态时的数据
//贪吃蛇头部图片需要通过方向来匹配
if(direction.equals("Right")){
Data.right.paintIcon(this,g,snake_X[0],snake_Y[0]);
}else if(direction.equals("Left")){
Data.left.paintIcon(this,g,snake_X[0],snake_Y[0]);
else if(direction.equals("Up")){
Data.up.paintIcon(this,g,snake_X[0],snake_Y[0]);
else if(direction.equals("Down")){
Data.down.paintIcon(this,g,snake_X[0],snake_Y[0]);
}
for(int i=1;i<length;i++){
Data.body.paintIcon(this,g,snake_X[i],snake_Y[i]);
}
if(!isstart){//暂停下的初始化数据
g.setBackground(Color.White);
g.setFont(new Font("微软雅黑",Font.bold,40));
g.drawString("按空格键开始游戏",250,300);
}
步骤三:让贪吃蛇动起来
让静态的贪吃蛇变成动态首先要实现keyListener的接口,在必须覆写的三个方法中选择使用keyPressed方法实现功能
public void keyPressed(KeyEvent e){
int keyCode =e.getKeyCode();//匹配键盘中按下的哪个键
if(keyCode==KeyEvent.VK_SPACE){//若按下的是空格键
isStart=!isStart;//取反
repaint();//特别注意:repaint()一定要选择含有5个参数(tm,x,y,width,height)的的方法
}
}
若想要监听事件被开启,需要在PanelGame的构造器中添加获得监听事件的相关方法
this.setFocusable(true);//获得焦点事件
this.addKeyListener(this);//添加当前键盘监听事件
步骤四:键盘控制贪吃蛇移动
实现键盘控制贪吃蛇的移动首先要实现ActionListener的接口,会自动覆写actionPerformed方法,之后先创建一个定时器
Timer timer=new Timer(1000,this);//以毫秒为单位
在覆写的actionPerformed()方法中,编写通过键盘控制贪吃蛇方向的代码以及在边界情况下,贪吃蛇的移动情况
if(isStart){//游戏启动时
for(int i=length-1;i>0;i--){//无论方向如何变换,身体始终移向前一个身体的位置,例如:shake_X[2]=snake_X[1];
snake_X[i]=snake_X[i-1];
snake_Y[i]=snake_Y[i-1];
}
if(direction.equals("Right")){
snake_X[0]+=25;//头部移动
if(snake_X[0]>850){snake_X[0]=25;}//当超出边界的情况下
}
if(direction.equals("Left")){
snake_X[0]-=25;
if(snake_X[0]<25){snake_X[0]=850;}
}
if(direction.equals("Up")){
snake_Y[0]-=25;
if(snake_Y[0]<75){snake_Y[0]=650;}
}
if(direction.equals("Down")){
snake_Y[0]+=25;
if(snake_Y[0]>650){snake_Y[0]=75;}
}
repaint();//刷新页面
}
在之前覆写的keyPressed()的方法中,添加相关关于监听通过方向来控制贪吃蛇移动的事件
if (keyCode==KeyEvent.VK_UP){//匹配到向上的按键
direction="Up";//方向改为向上
}
if(keyCode==KeyEvent.VK_DOWN){
direction="Down";
}
if(keyCode==KeyEvent.VK_LEFT){
direction="Left";
}
if(keyCode==KeyEvent.VK_RIGHT){
direction="Right";
}
最后在PanelGame构造器中和actionPerformed()方法中同时启动定时器,否则无法开启定时器功能。
timer.start();//启动定时器
步骤五:绘制食物并判定
接下来就是简单的绘制贪吃蛇所需要的食物,其过程非常简单。首先需要在定义变量的地方定义食物的坐标,这里要用到java.util中的Random()随机生成数的方法来在面板上随机生成食物的坐标。
int food_X;//定义食物的变量
int food_Y;
Random random=new Random();//随机函数
其次,init()方法中初始化食物的坐标,
food_X=25+25*random.nextInt(34);//初始化食物坐标,且不得超过边界外
food_Y=25+25*random.nextInt(24);
在if(isStart)的判定里,添加贪吃蛇吃食物增加自身长度的判定
if(snake_X[0]==food_X&&snake_Y[0]==food_Y){//若头部坐标与食物坐标重合,则表示为吃到食物
length++;
food_X=25+25*random.nextInt(34);//重新生成下一个食物
food_Y=25+25*random.nextInt(24);
}
步骤六:添加失败判定和积分系统
到目前为止,整个小游戏缺少最重要的就是失败的判定系统,当贪吃蛇撞击到自己的身体时,则判定为游戏失败,首先,先定义变量,默认状态为游戏不失败状态。
isFail=false;//默认状态为游戏不失败
在paintComponent()方法中,用画笔添加相关内容:
if (isFail){//游戏失败时的提示
g.setColor(Color.red);
g.setFont(new Font("微软雅黑",Font.BOLD,40));
g.drawString("游戏失败,按下空格键重新开始",250,300);
}
在游戏开始和游戏失败后都需要按空格键来开启或重新开启游戏,所以就需要修改keyPressed()的判定,如下:
if(isFail){
//游戏失败,重新开始
isFail=false;
init();//调用初始化方法
}else{
isStart=!isStart;//取反
}
repaint();
并且isFail==false添加到游戏启动的判定中
if (isStart && isFail==false){//游戏启动时
在游戏启动的判定中,添加游戏失败时的判定:
for(int i=0;i<length;i++){
if(snake_X[0]==snake_X[i]&&snake_Y[0]==snake_Y[i]){//贪吃蛇撞击到自己的身体时,则判定为游戏失败
isFail=true;
}
}
最后添加积分系统:
- 定义变量名称:
int score;
- 初始化变量:
score=0;
- 添加到画板当中:
g.setColor(Color.black); g.setFont(new Font("微软雅黑",Font.BOLD,20)); g.drawString("分数"+score,750,50); g.drawString("长度"+length,750,30);
- 贪吃蛇吃到食物,积分自增:
score++;
为了防止以上的内容有理不清顺序的小伙伴,特地把整个PanelGame类的代码放在下面,给大家参考:
package wang;
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;
import java.util.Random;
public class PanelGame extends JPanel implements KeyListener, ActionListener {
int length;//贪吃蛇长度
int score;//分数
String direction;//默认方向
int[] snake_X=new int[500];//贪吃蛇的坐标
int[] snake_Y=new int[500];
int food_X;//食物的坐标
int food_Y;
Random random=new Random();//随机数
boolean isStart=false;//默认状态
boolean isFail=false;//默认是不失败的状态
Timer timer=new Timer(100,this);//创建一个定时器
public PanelGame(){
init();
this.setFocusable(true);//获得焦点事件
this.addKeyListener(this);//添加当前键盘监听事件
timer.start();//在游戏开始时就启动定时器
}
//初始化游戏数据
public void init(){
length=3;
score=0;
direction="Right";
snake_X[0]=100;snake_Y[0]=100;//贪吃蛇的头部坐标
snake_X[1]=75;snake_Y[1]=100;//贪吃蛇的第一个身体坐标
snake_X[2]=50;snake_Y[2]=100;//贪吃蛇的第二个身体坐标
food_X=25+25*random.nextInt(34);//在面板内随机生成食物的坐标
food_Y=75+25*random.nextInt(24);
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);//清屏
this.setBackground(Color.white);
g.fillRect(25,75,850,600);//设置面板大小
Data.header.paintIcon(this,g,25,11);//将头部广告添加进去
Data.food.paintIcon(this,g,food_X,food_Y);//生成食物
//添加积分与长度栏
g.setColor(Color.black);
g.setFont(new Font("微软雅黑",Font.BOLD,20));
g.drawString("分数"+score,750,50);
g.drawString("长度"+length,750,30);
//贪吃蛇头部图片匹配
if (direction.equals("Right")){
Data.right.paintIcon(this,g,snake_X[0],snake_Y[0]);
}
else if (direction.equals("Left")){
Data.left.paintIcon(this,g,snake_X[0],snake_Y[0]);
}
else if (direction.equals("Up")){
Data.up.paintIcon(this,g,snake_X[0],snake_Y[0]);
}
else if (direction.equals("Down")){
Data.down.paintIcon(this,g,snake_X[0],snake_Y[0]);
}
//贪吃蛇身体坐标
for (int i = 1; i <length ; i++) {
Data.body.paintIcon(this,g,snake_X[i],snake_Y[i]);
}
//暂停状态下的初始化数据
if (!isStart){
g.setColor(Color.WHITE);
g.setFont(new Font("微软雅黑",Font.BOLD,40));
g.drawString("按下空格键开始游戏",250,300);
}
//失败时的游戏提示
if (isFail){
g.setColor(Color.red);
g.setFont(new Font("微软雅黑",Font.BOLD,40));
g.drawString("游戏失败,按下空格键重新开始",250,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){//匹配向上的按键
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";
}
}
@Override
public void actionPerformed(ActionEvent e) {
if (isStart && isFail==false){//游戏启动时
if (snake_X[0]==food_X&&snake_Y[0]==food_Y){//当贪吃蛇头部坐标与食物坐标重合,则长度自增,食物再次随机生成
length++;
score++;
food_X=25+25*random.nextInt(34);
food_Y=75+25*random.nextInt(24);
}
for (int i = length-1; i >0 ; i--) {//无论移动方向如何,后一节身体移动到前一节位置,snake[1]=snake[0]
snake_X[i]=snake_X[i-1];
snake_Y[i]=snake_Y[i-1];
}
if (direction.equals("Right")){
snake_X[0]=snake_X[0]+25;//头部移动
if (snake_X[0]>850){snake_X[0]=25;}//边界设置:若头部超过边界外,则重置到左边
}
else if (direction.equals("Left")){
snake_X[0]=snake_X[0]-25;
if (snake_X[0]<25){snake_X[0]=850;}
}
else if (direction.equals("Up")){
snake_Y[0]=snake_Y[0]-25;
if (snake_Y[0]<75){snake_Y[0]=650;}
}
else if (direction.equals("Down")){
snake_Y[0]=snake_Y[0]+25;
if (snake_Y[0]>650){snake_Y[0]=75;}
}
//判定游戏失败时的条件:当贪吃蛇头部与身体部分的坐标相重合时
for (int i = 1; i <length ; i++) {
if (snake_X[0]==snake_X[i]&&snake_Y[0]==snake_Y[i]){
isFail=true;
}
}
repaint();//刷新页面
}
timer.start();//启动定时器
}
@Override
public void keyTyped(KeyEvent e) {
}
@Override
public void keyReleased(KeyEvent e) {
}
}
到这我们这个游戏就算是大功告成了,但是游戏仍然有很多不足的地方,例如:在撞到边界的时候,也会判定游戏失败,在长度达到一定程度后,贪吃蛇移动速度加快等等,这些就交由给大家完成吧。