java从零开始开发贪吃蛇
1.游戏介绍
贪吃蛇是一款经典的益智类小游戏,是最初的翻盖机里面最常见的小游戏之一,学过编程的你有没有想过自己尝试着制作一款自己的贪吃蛇呢。
接下来我将从零开始带着大家做一款贪吃蛇小游戏。
2.了解游戏机制
图中是贪吃蛇游戏运行时的画面
我们通过控制方向键来控制蛇的上下左右,在静止的时候本质上就是一张静态图片,蛇头的朝向就是蛇的前进方向,每次吃到食物的时候,蛇的身体会变长,并且下一个食物出现在画面中的随机位置。
3.静态画面的构建
为了构建出蛇的形状和食物,我们需要寻找相应的素材图片,这里我已经找好了
每个图片的像素是25px*25px
头部分为上下左右四个方向,身体用黑色的正方形代替,食物这里没找到,用了一个苹果代替。(素材+qq1015532604)
有了素材之后我们就来先构建静态的画面吧。我们这里用的idea,当然你们也可以使用你们常用的编译器。
首先我们新建一个java项目,项目名为snake,项目下面新建两个包,com.dai.snake包下面存放我们的源代码,static包下面存放我们的素材。[外链
接下来我们新建一个主类用来初始化游戏窗口
新建一个JFrame对象,并且在初始化参数里面设置标题。
setBounds方法用来设置窗口的左上角的点坐标和窗口的大小。
jFrame.setResizable(false);
将我们的窗口设置为不可变窗口。
jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
将我们的窗口设置为点击右上角的叉可以关闭游戏进程。
jFrame.setVisible(true);
设置窗口为可以显示的窗口,接下来我们运行看一下窗口效果。
窗口设置完了之后我们还需要一个画布,用来显示游戏画面。
因此我们新建一个画板类GamePanel(该类需继承JPanel类)
继承该类之后重写父类的paintComponent方法
我们需要在我们的画板类中画蛇。
Graphics是一个画笔类,所有的绘画动作都由g这个参数来完成。
我们将背景颜色设置为黑色。
接下来我们需要将我们的Panel添加的我们的Frame当中。
我们写一个Data类来存放游戏的图片资源
先通过URL类获得图片的资源地址
通过该地址将图片资源加载到ImageIcon中,然后我们就获得了图片对象。
有了图片资源之后我们就可以来画一条静态的小蛇。
首先我们在Panel类中设置一些参数和一个初始化函数
接着我们在paintComponent里面画一条静态的小蛇
接下来我们看下效果。
emmm
这个蛇有点丑,大家可以找些更好看的图片来代替。
4.让小蛇动起来
我们的小蛇长度并不固定,因此我们可以用一个循环来动态的画蛇的身体
用if else语句来动态画小蛇的脑袋
dir设置小蛇的方向
设置监听
我们让GamePanel实现KeyListener接口并且重写三个方法来实现监听。
这里我们只使用keyPressed来实现监听
设置游戏是否开始的提示字符
默认游戏未开始
当按下的是空格,isStart取反。
接下来我们将监听事件加入到监听器中
设置定时器
将this作为参数,会重写actionPerformed参数
并且每100ms会调用这个函数
最后再启动定时器
再次启动游戏会发现小蛇可以动起来了。
接下来我们通过wasd来控制蛇的方向
直接设置dir的值即可
蛇头部的移动也需要按照动态实现
至此我们的小蛇已经可以完全按照上下左右来控制方向了。
5.小蛇吃食物
设置一个食物,位置设为屏幕的随机位置
判断是否吃到食物
吃到的话,长度加1并且重新生成食物
6.失败判断
设置参数
设置失败文字
失败的话按空格键重新开始游戏
7.积分系统
设置积分的变量score,并且在init中初始化积分为0.
画积分
吃到食物则加10分
至此,整个游戏的基本功能就已经实现了。
8.展望
游戏实现了基本功能但是仍有许多不足之处,例如游戏素材不够美观,游戏无撞墙判定,游戏中的食物单一,我们也可以改进食物使得各种食物有各种不同的功能,亦或是做成联机模式的等等。本文只是提供一个思路起到抛砖引玉的作用。
源码:
StartGame.java
package com.dai.snake;
import javax.swing.*;
public class StartGames {
public static void main(String[] args) {
//1,绘制一个静态窗口 JFrame
JFrame jFrame=new JFrame("戴通的贪吃蛇");
//设置界面大小
jFrame.setBounds(10,10,900,700);
//设置窗口可以改变大小
jFrame.setResizable(false);
jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//设置关闭事件
//2.将GamePanel添加JFrame当中
jFrame.add(new GamePanel());
jFrame.setVisible(true);//设置窗口可以显示出来
}
}
Data.java
package com.dai.snake;
import javax.swing.*;
import java.net.URL;
//存放外部数据
public class Data {
//获取图片资源
public static URL headUpUrl=Data.class.getResource("/static/up.png");
public static URL headDownUrl=Data.class.getResource("/static/down.png");
public static URL headLeftUrl=Data.class.getResource("/static/left.png");
public static URL headRightUrl=Data.class.getResource("/static/Right.png");
public static URL foodUrl=Data.class.getResource("/static/food.png");
public static URL bodyUrl=Data.class.getResource("/static/body.png");
public static ImageIcon up=new ImageIcon(headUpUrl);
public static ImageIcon down =new ImageIcon(headDownUrl);
public static ImageIcon left=new ImageIcon(headLeftUrl);
public static ImageIcon right=new ImageIcon(headRightUrl);
public static ImageIcon food=new ImageIcon(foodUrl);
public static ImageIcon body=new ImageIcon(bodyUrl);
}
GamePanel.java
package com.dai.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;
import java.util.Random;
public class GamePanel extends JPanel implements KeyListener, ActionListener {
//画板:画界面,画蛇
int length;//设置蛇的长度
int[] snakeX=new int[600];//蛇的坐标X
int[] snakeY=new int[600];//蛇的坐标Y
String dir="R";//RUDL
boolean isStart=false;
Timer timer=new Timer(100,this);//定时器
int foodX;
int foodY;
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;//设置蛇的第二节身体坐标
foodX=25+25*random.nextInt(34);
foodY=75+25*random.nextInt(24);
score = 0;
}
//Graphics:画笔
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);//清屏
this.setBackground(Color.BLACK);//设置背景的颜色为黑色
g.fillRect(25,75, 850 ,600);//画一个矩形作为游戏区域
// //画一条静态的小蛇
Data.right.paintIcon(this,g,snakeX[0],snakeY[0]);
// Data.body.paintIcon(this,g,snakeX[1],snakeY[1]);
// Data.body.paintIcon(this,g,snakeX[2],snakeY[2]);
if(dir.equals("R")){
Data.right.paintIcon(this,g,snakeX[0],snakeY[0]);
}else if(dir.equals("L")){
Data.left.paintIcon(this,g,snakeX[0],snakeY[0]);
}else if(dir.equals("U")){
Data.up.paintIcon(this,g,snakeX[0],snakeY[0]);
}else if(dir.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]);//蛇的身体长度通过length控制
}
//画积分
g.setColor(Color.GREEN);
g.setFont(new Font("微软雅黑",Font.BOLD,18));
g.drawString("长度:"+length,750,35);
g.drawString("分数"+score,750,55);
//游戏提示,是否开始
Data.food.paintIcon(this,g,foodX,foodY);
if(!isStart){
g.setColor(Color.WHITE);
g.setFont(new Font("微软雅黑",Font.BOLD,40));
g.drawString("按下空格开始游戏",300,400);
}
if(isFail){
g.setColor(Color.RED);
g.setFont(new Font("微软雅黑",Font.BOLD,40));
g.drawString("游戏失败,按下空格重新开始游戏",200,300);
}
}
@Override
public void keyTyped(KeyEvent e) {
//键盘按下弹起
}
@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_A){
dir="L";
}else if(keyCode==KeyEvent.VK_D){
dir="R";
}else if(keyCode==KeyEvent.VK_W){
dir="U";
}else if(keyCode==KeyEvent.VK_S){
dir="D";
}
}
@Override
public void keyReleased(KeyEvent e) {
//释放某个键
}
//执行定时操作,每隔100ms执行一次
@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];
}
//
if(dir.equals("R")){
snakeX[0]+=25;
//边界判断
if(snakeX[0]>850){
snakeX[0]=25;
}
}else if(dir.equals("L")){
snakeX[0]-=25;
//边界判断
if(snakeX[0]<25){
snakeX[0]=850;
}
}else if(dir.equals("U")){
snakeY[0]-=25;
//边界判断
if(snakeY[0]<75){
snakeY[0]=650;
}
}else if(dir.equals("D")){
snakeY[0]+=25;
//边界判断
if(snakeY[0]>650){
snakeY[0]=75;
}
}
if(snakeX[0]==foodX&&snakeY[0]==foodY){
length++;
score+=10;
//重新生成食物
foodX=25+25*random.nextInt(34);
foodY=75+25*random.nextInt(24);
}
//
for(int i=1;i<length;i++){
if(snakeX[0]==snakeX[i]&&snakeY[0]==snakeY[i]){
isFail=true;
}
}
//刷新游戏界面
repaint();
}
timer.start();//启动定时器
}
}