在我们学习java的时候,为了提高我们的兴趣,我们经常会使用所学到的知识去做一些小游戏,这篇blog就介绍了一个经典而且好理解的小游戏-贪吃蛇。
一、使用知识Jframe
GUI
双向链表
线程
二、使用工具IntelliJ IDEA
jdk 1.8
三、开发过程
3.1素材准备
首先在开发之前应该准备一些素材,已备用,我主要找了一个图片以及一段优雅的音乐。
3.2 开发过程
3.2.1 创建项目首先进入idea首页 open一个你想放项目的文件夹
进入之后右键文件名 new 一个新的Directory——Snake
把准备好的素材复制到文件中
继续创建目录 src/Sanke
选中src Mark Directory as — Souces 把src添加为根目录
3.2.2 页面设计创建java Class 文件 Snake - new - java class SnakeName 接下来的时候会对这个SnakeName.java里面的代码不停完善
首先设置窗口格式packageSanke;importjavax.swing.*;/***@authorSwyee**/publicclassSnakeGameextendsJFrame{
SnakeGame(){
this.setBounds(100,50,700,500);//设置窗口大小this.setLayout(null);//更改layout以便添加组件this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//设置关闭窗口的状态this.setResizable(false);//窗口不可以改变大小this.setVisible(true);//设置焦点状态为true}
publicstaticvoidmain(String[]args){
newSnakeGame();
}}
继续创建新的文件 SnakeGridpackageSanke;importjava.awt.*;/***@authorSwyee**/publicclassSnakeGridextendsPanel{
SnakeGrid(){
this.setBounds(0,0,700,400);
this.setBackground(Color.black);设置背景颜色
}}将页面引用到SnakeGame.java中packageSanke;importjavax.swing.*;/***@authorSwyee**/publicclassSnakeGameextendsJFrame{
SnakeGridsnakeGrid=newSnakeGrid();
SnakeGame(){
this.setBounds(100,50,700,500);//设置窗口大小this.setLayout(null);//更改layout以便添加组件this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//设置关闭窗口的状态this.setResizable(false);//窗口不可以改变大小this.add(snakeGrid);
this.setVisible(true);//设置焦点状态为true}
publicstaticvoidmain(String[]args){
newSnakeGame();
}}
运行样式
设置背景图片 背景音乐
在SnakeGrid.java中增加Music方法 设置画笔 绘图packageSanke;importjava.applet.Applet;importjava.applet.AudioClip;importjava.awt.*;importjava.io.File;.MalformedURLException;.URI;.URL;importjavax.swing.ImageIcon;importjavax.swing.JOptionPane;importjavax.swing.JPanel;/***@authorSwyee**/publicclassSnakeGridextendsJPanel{
ImageIconimage=newImageIcon("Snake/sky.jpg");//图片文件地址Filef=newFile("Snake/music.wav");//音乐文件地址SnakeGrid(){
this.setBounds(0,0,700,400);
this.setBackground(Color.black);
}
/***设置画笔*@paramg*/
@Override
publicvoidpaint(Graphicsg){
super.paint(g);
image.paintIcon(this,g,0,0);//设置背景图片}
//读取音乐文件voidMusic(){
try{
URIuri=f.toURI();
URLurl=uri.toURL();
AudioClipaau=Applet.newAudioClip(url);
aau.loop();
}catch(MalformedURLExceptione){
//TODOAuto-generatedcatchblocke.printStackTrace();
}
}}
在SnakeName中调用packageSanke;importjavax.swing.*;/***@authorSwyee**/publicclassSnakeGameextendsJFrame{
SnakeGridsnakeGrid=newSnakeGrid();
SnakeGame(){
this.setBounds(100,50,700,500);//设置窗口大小this.setLayout(null);//更改layout以便添加组件this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//设置关闭窗口的状态this.setResizable(false);//窗口不可以改变大小this.add(snakeGrid);
//设置焦点snakeGrid.setFocusable(true);
snakeGrid.requestFocus();
snakeGrid.Music();//调用打开音乐的方法this.setVisible(true);//设置焦点状态为true}
publicstaticvoidmain(String[]args){
newSnakeGame();
}}
呈现
3.23 画蛇
蛇的身体将会有双向链表组成,双向链表能记录一个节点的上一个节点和下一个节点。蛇的移动其实就是节点的变化,从而达到一种移动的视觉。新建java Snake 创建节点packageSanke;importjava.awt.Graphics;publicclassSnake{
publicstaticfinalintspan=20;//间距publicstaticfinalStringup="u";
publicstaticfinalStringdown="d";
publicstaticfinalStringleft="l";
publicstaticfinalStringright="r";
classNode{
introw;
intcol;
Stringdir;//方向Nodenext;
Nodepre;
Node(introw,intcol,Stringdir){
this.row=row;
this.col=col;
this.dir=dir;
}
publicvoiddraw(Graphicsg){
g.fillOval(col*span,row*span,span,span);
}
}}画蛇
在snake里面增加draw()方法/*把蛇画出来*/
publicvoiddraw(Graphicsg){
g.setColor(Color.yellow);
for(Noden=head;n!=null;n=n.next){
n.draw(g);
g.setColor(Color.green);
}
}
在SnakeGrid.java中创建蛇Snakesnake=newSnake();//创建蛇
并在paint中调用snake.draw(g);/***设置画笔*@paramg*/
@Override
publicvoidpaint(Graphicsg){
super.paint(g);
image.paintIcon(this,g,0,0);//设置背景图片snake.draw(g);
}控制蛇的移动
在snake中增加键盘调用的方法:/*调用键盘的上下左右键head.dir记录现在操作的是什么按钮,从而更改蛇的状态向上移送时,下键失效,其他四个方向也是如此判断*/
publicvoidkeyboard(KeyEvente){
switch(e.getKeyCode()){
caseKeyEvent.VK_UP:
if(head.dir.equals(down)){
break;
}
head.dir=up;
break;
caseKeyEvent.VK_DOWN:
if(head.dir.equals(up)){
break;
}
head.dir=down;
break;
caseKeyEvent.VK_LEFT:
if(head.dir.equals(right)){
break;
}
head.dir=left;
break;
caseKeyEvent.VK_RIGHT:
if(head.dir.equals(left)){
break;
}
head.dir=right;
break;
default:
break;
}
}
增加头部的方法/*增加头部不管移动哪个方向都是在相应位置增加一个节点*/
publicvoidaddHead(){
Nodenode=null;
switch(head.dir){
case"l":
node=newNode(head.row,head.col-1,head.dir);
break;
case"r":
node=newNode(head.row,head.col+1,head.dir);
break;
case"u":
node=newNode(head.row-1,head.col,head.dir);
break;
case"d":
node=newNode(head.row+1,head.col,head.dir);
break;
default:
break;
}
node.next=head;
head.pre=node;
head=node;
}
删除尾部的方法/*删除尾部删除最后一个节点*/publicvoiddeleteTail(){
tail.pre.next=null;
tail=tail.pre;}
增加move的方法/*增加move方法一增一减,实现蛇的移动*/
publicvoidmove(){
addHead();
deleteTail();
}
在SnakeGrid中创建一个线程类,用来执行蛇的移动方法classSnakeThreadextendsThread{
@Override
publicvoidrun(){
while(true){
try{
Thread.sleep(300);//沉睡300ms用来控制蛇的移动速度}catch(InterruptedExceptione){
e.printStackTrace();
}
repaint();//每次沉睡完之后都执行一下repaint()方法,重新绘画}
}
print方法中调用remove 在SnakeGrid()创建键盘监听事件:packageSanke;importjava.applet.Applet;importjava.applet.AudioClip;importjava.awt.*;importjava.awt.event.KeyAdapter;importjava.awt.event.KeyEvent;importjava.io.File;.MalformedURLException;.URI;.URL;importjavax.swing.ImageIcon;importjavax.swing.JOptionPane;importjavax.swing.JPanel;/***@authorSwyee**/publicclassSnakeGridextendsJPanel{
Snakesnake=newSnake();//创建蛇ImageIconimage=newImageIcon("Snake/sky.jpg");//图片文件地址Filef=newFile("Snake/music.wav");//音乐文件地址SnakeThreadsnakeThread=newSnakeThread();
SnakeGrid(){
this.setBounds(0,0,700,400);
this.setBackground(Color.black);
snakeThread.start();
this.addKeyListener(newKeyAdapter(){
@Override
publicvoidkeyPressed(KeyEvente){
snake.keyboard(e);
}
});
}
/***设置画笔*@paramg*/
@Override
publicvoidpaint(Graphicsg){
super.paint(g);
image.paintIcon(this,g,0,0);//设置背景图片snake.move();//蛇移动snake.draw(g);
}
//读取音乐文件voidMusic(){
try{
URIuri=f.toURI();
URLurl=uri.toURL();
AudioClipaau=Applet.newAudioClip(url);
aau.loop();
}catch(MalformedURLExceptione){
//TODOAuto-generatedcatchblocke.printStackTrace();
}
}
classSnakeThreadextendsThread{
@Override
publicvoidrun(){
while(true){
try{
Thread.sleep(300);//沉睡300ms用来控制蛇的移动速度}catch(InterruptedExceptione){
e.printStackTrace();
}
repaint();//每次沉睡完之后都执行一下repaint()方法,重新绘画}
}}}
执行main方法可以看到可以通过键盘进行控制移动了
3.24创建蛇的食物
增加食物的实例 以及画食物的方法 反映食物坐标的方法 新建Food.javapackageSanke;importjava.awt.*;publicclassFood{
introw;
intcol;
Food(){
row=10;//创建食物的大小col=10;
}
publicvoidrepearShow(){
row=(int)(Math.random()*18);//生成随机数乘以食物的大小可以得到坐标col=(int)(Math.random()*32);
}
publicvoiddraw(Graphicsg){//把食物画出来g.setColor(Color.red);
g.fillRect(col*20,row*20,20,20);//表示坐标
}
publicRectanglegetCoordinates(){
returnnewRectangle(col*20,row*20,20,20);//获得食物的坐标
}}
修改Snake.java 增加判断蛇头位置的方法,修改午无参构造方法,改为有参构造,把food添加进来 修改move方法packageSanke;importjava.awt.*;importjava.awt.event.KeyEvent;/***@authorSwyee*/publicclassSnake{
publicstaticfinalintspan=20;//间距publicstaticfinalStringup="u";
publicstaticfinalStringdown="d";
publicstaticfinalStringleft="l";
publicstaticfinalStringright="r";
Nodebody;//蛇的身体Nodehead;//蛇的头部Nodetail;//蛇的头部Foodfood;
Snake(Foodfood){
body=newNode(5,20,left);
head=body;
tail=body;
this.food=food;
}
classNode{
introw;
intcol;
Stringdir;//方向Nodenext;
Nodepre;
Node(introw,intcol,Stringdir){
this.row=row;
this.col=col;
this.dir=dir;
}
publicvoiddraw(Graphicsg){
g.fillOval(col*span,row*span,span,span);//坐标
}
}
/*把蛇画出来*/
publicvoiddraw(Graphicsg){
g.setColor(Color.yellow);
for(Noden=head;n!=null;n=n.next){
n.draw(g);
g.setColor(Color.green);
}
}
/*调用键盘的上下左右键head.dir记录现在操作的是什么按钮,从而更改蛇的状态向上移送时,下键失效,其他四个方向也是如此判断*/
publicvoidkeyboard(KeyEvente){
switch(e.getKeyCode()){
caseKeyEvent.VK_UP:
if(head.dir.equals(down)){
break;
}
head.dir=up;
break;
caseKeyEvent.VK_DOWN:
if(head.dir.equals(up)){
break;
}
head.dir=down;
break;
caseKeyEvent.VK_LEFT:
if(head.dir.equals(right)){
break;
}
head.dir=left;
break;
caseKeyEvent.VK_RIGHT:
if(head.dir.equals(left)){
break;
}
head.dir=right;
break;
default:
break;
}
}/*增加头部*/
publicvoidaddHead(){
Nodenode=null;
switch(head.dir){
case"l":
node=newNode(head.row,head.col-1,head.dir);
break;
case"r":
node=newNode(head.row,head.col+1,head.dir);
break;
case"u":
node=newNode(head.row-1,head.col,head.dir);
break;
case"d":
node=newNode(head.row+1,head.col,head.dir);
break;
default:
break;
}
node.next=head;
head.pre=node;
head=node;
}/*删除尾部*/publicvoiddeleteTail(){
tail.pre.next=null;
tail=tail.pre;}/*增加move方法*/
publicvoidmove(){
addHead();
if(this.getSnakeRectangle().intersects(food.getCoordinates())){//当蛇头与食物重合的时候蛇吃食物食物刷新,不再删除尾巴,达到一种蛇增长的要求
food.repearShow();
}else{
deleteTail();
}
}
publicRectanglegetSnakeRectangle(){//获取蛇头的坐标
returnnewRectangle(head.col*span,head.row*span,span,span);
}}
在修改snakegrid.java 贪吃蛇的功能就基本实现了Foodfood=newFood();
Snakesnake=newSnake(food);//创建蛇ImageIconimage=newImageIcon("Snake/sky.jpg");//图片文件地址Filef=newFile("Snake/music.wav");//音乐文件地址SnakeThreadsnakeThread=newSnakeThread();
@Override
publicvoidpaint(Graphicsg){
super.paint(g);
image.paintIcon(this,g,0,0);//设置背景图片snake.move();//蛇移动snake.draw(g);
food.draw(g);
}
3.2.5增加蛇的存活状态
在Snake中增加蛇的存活状态,每一次移动都判断下是否存活,修改SnakeGrid的线程,执行时进行判断是否存活publicvoidDeadOrLive(){//超出边框范围蛇头撞到身体游戏结束if(head.row<0||head.row>rows-1||head.col<0||head.col>cols){
islive=false;
}
for(Noden=head.next;n!=null;n=n.next){
if(n.col==head.col&&n.row==head.row){
islive=false;
}
}
}
publicvoidmove(){
addHead();
if(this.getSnakeRectangle().intersects(food.getCoordinates())){//当蛇头与食物重合的时候蛇吃食物食物刷新,不再删除尾巴,达到一种蛇增长的要求
food.repearShow();
}else{
deleteTail();
}
DeadOrLive();//每移动一步都要判断一下是否存活}
classSnakeThreadextendsThread{
booleanflag=true;
@Override
publicvoidrun(){
while(Snake.islive&&flag){
try{
Thread.sleep(300);
}catch(InterruptedExceptione){
//TODOAuto-generatedcatchblocke.printStackTrace();
}
if(Snake.islive){
repaint();
}
}
if(!flag==false){
JOptionPane.showMessageDialog(SnakeGrid.this,"游戏结束");
}
}
3.2.6 增加按钮最后的时候,给这个小游戏增加几个按钮,用来实现暂停开始
新建Button.javapackageSanke;importjavax.swing.*;importjava.awt.event.ActionEvent;importjava.awt.event.ActionListener;publicclassButtonextendsJPanel{
publicstaticbooleanisMove=true;//表示运行状态
SnakeGridsnakeGrid;
Button(SnakeGridsnakeGrid){
this.snakeGrid=snakeGrid;
this.setBounds(0,400,700,100);
JButtonjb1=newJButton("暂停游戏");
JButtonjb2=newJButton("继续游戏");
JButtonjb3=newJButton("重新游戏");
this.add(jb1);
this.add(jb2);
this.add(jb3);
jb1.addActionListener(newActionListener(){
@Override
publicvoidactionPerformed(ActionEvente){
isMove=false;
}
});
jb2.addActionListener(newActionListener(){
@Override
publicvoidactionPerformed(ActionEvente){
isMove=true;
snakeGrid.setFocusable(true);
snakeGrid.requestFocus();
}
});
jb3.addActionListener(newActionListener(){
@Override
publicvoidactionPerformed(ActionEvente){//重新创建蛇等重新开始游戏snakeGrid.snakeThread.stopThread();
Foodfood=newFood();
snakeGrid.food=food;
snakeGrid.snake=newSnake(food);
Snake.islive=true;
isMove=true;
SnakeGrid.SnakeThreadst=snakeGrid.newSnakeThread();
snakeGrid.snakeThread=st;
st.start();
snakeGrid.setFocusable(true);
snakeGrid.requestFocus();
}
});
}}
再修改SnakeGrid中的threadpackageSanke;importjava.applet.Applet;importjava.applet.AudioClip;importjava.awt.*;importjava.awt.event.KeyAdapter;importjava.awt.event.KeyEvent;importjava.io.File;.MalformedURLException;.URI;.URL;importjavax.swing.ImageIcon;importjavax.swing.JOptionPane;importjavax.swing.JPanel;/***@authorSwyee**/publicclassSnakeGridextendsJPanel{
Foodfood=newFood();
Snakesnake=newSnake(food);//创建蛇ImageIconimage=newImageIcon("Snake/sky.jpg");//图片文件地址Filef=newFile("Snake/music.wav");//音乐文件地址SnakeThreadsnakeThread=newSnakeThread();
SnakeGrid(){
this.setBounds(0,0,700,400);
this.setBackground(Color.black);
snakeThread.start();
this.addKeyListener(newKeyAdapter(){
@Override
publicvoidkeyPressed(KeyEvente){
snake.keyboard(e);
}
});
}
/***设置画笔*@paramg*/
@Override
publicvoidpaint(Graphicsg){
super.paint(g);
image.paintIcon(this,g,0,0);//设置背景图片snake.move();//蛇移动snake.draw(g);
food.draw(g);
}
//读取音乐文件voidMusic(){
try{
URIuri=f.toURI();
URLurl=uri.toURL();
AudioClipaau=Applet.newAudioClip(url);
aau.loop();
}catch(MalformedURLExceptione){
//TODOAuto-generatedcatchblocke.printStackTrace();
}
}
classSnakeThreadextendsThread{
booleanflag=true;
@Override
publicvoidrun(){
while(Snake.islive&&flag){
try{
Thread.sleep(300);
}catch(InterruptedExceptione){
//TODOAuto-generatedcatchblocke.printStackTrace();
}
if(Snake.islive&&Button.isMove){
repaint();
}
}
if(!flag==false){
JOptionPane.showMessageDialog(SnakeGrid.this,"游戏结束");
}
}
publicvoidstopThread(){
flag=false;
}
}}
在主页面中把按钮添加上去packageSanke;importjavax.swing.*;/***@authorSwyee**/publicclassSnakeGameextendsJFrame{
SnakeGridsnakeGrid=newSnakeGrid();
Buttonbutton=newButton(snakeGrid);
SnakeGame(){
this.setBounds(100,50,700,500);//设置窗口大小this.setLayout(null);//更改layout以便添加组件this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//设置关闭窗口的状态this.setResizable(false);//窗口不可以改变大小this.add(snakeGrid);
this.add(button);
//设置焦点snakeGrid.setFocusable(true);
snakeGrid.requestFocus();
snakeGrid.Music();//调用打开音乐的方法this.setVisible(true);//设置焦点状态为true}
publicstaticvoidmain(String[]args){
newSnakeGame();
}}
到这里这个小游戏就全部做完了,当然也可以在其基础上增加其他功能
也可以把这个小游戏打成jar包的形式进行运行,将打好的jar包和资源文件放在同一个目录下,即可正常运行访问
四、打jar包
源码
最后附上源码链接: