案例-java贪吃蛇(附源码)
创建屏幕
开始游戏的窗口,首先引入窗口,然后在窗口画布上进行添加各类动画。
JFrame frame=new JFrame("My SnakeGame");
Jframe 是个类,引入了窗口frame,该窗口的标题是:My SnakeGame
frame.setBounds(100,100,800,500);//设置零点坐标,窗口大小
参数1: 窗口的左上角点的横坐标
参数2:窗口的左上角点的纵坐标
参数3:窗口的宽度
参数4:窗口的高度
单位为像素
frame.setResizable(false); //窗口大小不可随意改变大小
这样设置后不能够随意改变窗口的大小。主要是牵扯到画面的自动扩展延伸。
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//关闭窗口即关闭程序,无后台
这样设置后关闭窗口后自动关闭程序,否则关闭窗口后,程序依旧在运行。
frame.setVisible(true); //窗口可被展示
窗口可以被看到
frame.add(new GameJPanel());//引入画板
通过这个语句引入画板,图片,操作都在画板上进行。
画板
定义我们自己的画布类必须继承自JPanel系统画板类,我们需要重写方法
protected void paintComponent(Graphics g){} //在这个方法里编写我们想要画在画板上的东西
这里的Graphics g参数是画笔g,我们可以设置画笔的颜色,字体等。
在我们重写的这个方法里先调用父类的方法 :super.paintComponent(g);
需要在画面上进行某些改变的话都要写在这个重写的方法里。
this.setBackground(Color.WHITE); //定义背景颜色
WHITE可选该为其他颜色,其他部分为固定语法法
this表示当前画布,可以理解为当前画布设置背景颜色为白色
g.setColor(Color.WHITE); //画笔颜色设置
g.setFont(new Font("宋体",Font.BOLD,40));
设置画笔的写出的颜色,字体格式,字体类型,字体大小。
g.drawString("按下空格开始",300,250);
语法:g.drawString("要打印的字体",x,y);
写入打印的字符串,打印起始点的坐标。
Date.header.paintIcon(this,g,2,0);//在画板里打印图片
//参数分别为,窗口this(本窗口),画笔g,(2,0)表示坐标距离
语法:Date.名字.paintIcon(画面,画笔,x,y);
这里的header是我们在下面的数据引入时引入的图片资源名称。
paomtIcon();指打印图片的方法。
g.fillRect(25,75,750,400); //填充窗口画面
25,75表示起始的点坐标。750,400表示从那点开始要打印的画面的宽度,高度
这部主要实现游戏窗口的背景打印。
按键监听
这里我们需要实现两个接口,第一个是键盘监听接口 KeyListener。这个接口依旧在我们自己定义的画板上进行实现,实现了这个接口我们就需要实现三个方法。
-
public void keyTyped(KeyEvent e) { //按一下 }
这个方法主要监听键盘上按键仅按一下的情况下。可以理解为按一下记一下。
-
public void keyReleased(KeyEvent e) { //弹起 }
这个方法监听的是键盘按下后弹起的过程。
-
public void keyPressed(KeyEvent e) { //一直按 }
监听长按的键盘。
这里的KeyEvent e是接受的参数,e.getKeyCode();表示得到的键盘输入情况
定义个数keyCode接受这个按键。 int keyCode=e.getKeyCode();后面都是用keyCode与按键进行比较
KeyEvent.VK_UP //表示上键
语法:KeyEvent.VK_按键; 表示这个案件 //按键部分需要全大写字母。例如LEFT
if(keyCode==KeyEvent.VK_DDWN)
{
//当按键为“下”时做什么,一般为改变一个变量(在这个监听按键的方法里改变),通过这个改变这个变量改变打印的内容(在重写的画板方法里改变打印的内容),从而实现画面里方向的改变。
}
repaint(); //重新绘制界面的方法
repaint();作用是刷新打印的界面,可以在监听按键的方法里调用,从而实现按键后画面的改变。
一般会定义个初始化变量的方法,用来实现游戏重新加载或第一次加载时的变量初始化。
定时操作
我们需要实现重写的第二个接口是定时活动操作ActionListener,会随着时间变化而变化,设置定时执行该方法的的方法体。我们需要重写方法
public void actionPerformed(ActionEvent e) {//执行定时操作
//随时间变化的操作,如想让蛇头朝一个方向走动只需要其坐标(X或Y)自增。这里面的操作都会随着我们设置的时间间隔不断重复执行。
timer.start(); //计时器开始
}
在方法的左后要加上timer.start(); //计时器开始确保计时器开始执行,即执行完第一次后不断重复执行开始
数据引入
在这之前需要先建个static包用来存放我们的资源数据,直接将所需要的照片包拷进该文件夹即可。
定义Data类,通过Date类引入我们需要使用的图片,音乐等外部资源。
语法: 修饰符 URL 地址名称=类名.class.getResource("地址"); //地址名称是自己按规则随意取的,地址是文件所在的文件夹和文件名称和类型。
public static URL headerURL=Date.class.getResource("地址");
//引入资源的地址
eg:public static URL upURL=Date.class.getResource("/static/up.png");
需要注意的是这里只是引入资源的地址,并没有说明这是什么资源类型。
声明引入资源的类型。如图片的声明。
语法:修饰符 ImageIcon 名称=new ImageIcon(地址名称);
public static ImageIcon header=new ImageIcon(headerURL);
//引入资源为图片
屏幕框架引入
package com.cyqf.snake.www;
import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
public class StartGame {
public static void main(String[] args) {
JFrame frame=new JFrame("My SnakeGame");
frame.setBounds(100,100,800,500);//设置零点坐标,窗口大小
frame.setResizable(false); //窗口大小不可随意改变
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//关闭窗口即关闭程序,无后台
frame.setVisible(true); //窗口可被展示
frame.add(new GameJPanel());//引入画板
//在我们的窗口里可以引入画板画的东西
}
}
数据引入
package com.cyqf.snake.www;
import javax.swing.*;
import java.net.URL;
public class Date { //引入数据,引入后才能被直接调用
public static URL headerURL=Date.class.getResource("/static/header.png");
//引入资源的地址
public static ImageIcon header=new ImageIcon(headerURL);
//引入资源为图片
public static URL upURL=Date.class.getResource("/static/up.png");
public static URL downURL=Date.class.getResource("/static/down.png");
public static URL leftURL=Date.class.getResource("/static/left.png");
public static URL rightURL=Date.class.getResource("/static/right.png");
public static URL foodURL=Date.class.getResource("/static/food.png");
public static URL bodyURL=Date.class.getResource("/static/body.png");
public static ImageIcon up=new ImageIcon(upURL);
public static ImageIcon down=new ImageIcon(downURL);
public static ImageIcon left=new ImageIcon(leftURL);
public static ImageIcon right=new ImageIcon(rightURL);
public static ImageIcon food=new ImageIcon(foodURL);
public static ImageIcon body=new ImageIcon(bodyURL);
}
画板
package com.cyqf.snake.www;
import com.sun.xml.internal.ws.policy.EffectiveAlternativeSelector;
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 GameJPanel extends JPanel implements KeyListener , ActionListener {
//2.Jpanel画板类,写入我们需要的东西如图片,背景颜色等,
//这里我们自己定义一个画板,继承来自系统的画板
int length=600;//定义蛇的长度
String fx;//蛇头方向
int[] snakex=new int[600]; //定义横坐标
int[] snakey=new int[500]; //定义纵坐标
boolean isstart; //开始结束变量
boolean isfail; //成功失败变量
//设置定时器
Timer timer=new Timer(100,this);//每个多少毫秒,监听哪个窗口
Random random=new Random(); //引入随机方法
int foodx; //食物x坐标
int foody; //食物y坐标
int score; //分数
GameJPanel(){ //构造器
init(); //初始化方法
this.setFocusable(true); //获取键盘的焦点,将键盘的焦点聚集在游戏上
this.addKeyListener(this); //实现类
timer.start(); //计时器引入
}
void init(){//初始化方法
length=3; //初始长度
fx="R"; //默认朝向
snakex[0]=100; //头的初始位置
snakey[0]=100;
snakex[1]=75;snakey[1]=100; //第一节身体
snakex[2]=50;snakey[2]=100; //第二节身体
isstart=false; //判断游戏是否开始
isfail=false;
foodx=75+25*random.nextInt(28); //给食物坐标随机
foody=75+25*random.nextInt(13);
score=0;
}
@Override
protected void paintComponent(Graphics g) { //重写方法,画板
super.paintComponent(g); //这里的Graphics g就是画笔
this.setBackground(Color.WHITE); //定义背景颜色
Date.header.paintIcon(this,g,2,0);//在画板里打印图片
//参数分别为,窗口this(本窗口),画笔g,坐标距离
g.fillRect(25,75,750,350); //填充窗口画面
if (fx.equals("R")){ //通过监听键盘改变fx的值,改变fx的值后打印不同画面,实现蛇头的转向
Date.right.paintIcon(this,g,snakex[0],snakey[0]);
}
else if (fx.equals("L"))
{
Date.left.paintIcon(this,g,snakex[0],snakey[0]);
}
else if (fx.equals("U"))
{
Date.up.paintIcon(this,g,snakex[0],snakey[0]);
}
else if (fx.equals("D"))
{
Date.down.paintIcon(this,g,snakex[0],snakey[0]);
}
for (int i = 1; i <length; i++) {
Date.body.paintIcon(this,g,snakex[i],snakey[i]); //初始值为3,两节身体,
// 随着长度增加,可以不断打印
}
if (isfail==false){
if (isstart==false){ //提示信息
g.setColor(Color.WHITE); //画笔颜色设置
g.setFont(new Font("宋体",Font.BOLD,40)); //字体格式,粗体,大小
g.drawString("按下空格开始",300,250);
}
}else {
g.setColor(Color.red); //设置画笔的颜色
g.setFont(new Font("宋体",Font.BOLD,40)); //
g.drawString("游戏失败按空格重新开始",250,250);
init();
}
Date.food.paintIcon(this,g,foodx,foody);
g.setColor(Color.blue);
g.setFont(new Font("微软雅黑",Font.BOLD,18));
g.drawString("分数:"+score,675,25);
g.drawString("长度:"+length,675,39);
}
@Override //按键控制监听
public void keyPressed(KeyEvent e) { //一直按
int keyCode=e.getKeyCode();
if (keyCode==KeyEvent.VK_SPACE) //接受到了空格,给状态取反,从而实现开始暂停
{
isstart=!isstart;
}
if (keyCode==KeyEvent.VK_UP) //由键盘输入的值改变fx
{
if(!fx.equals("D")) //不能回头的判断语句
fx="U";
}else if (keyCode==KeyEvent.VK_DOWN)
{
if(!fx.equals("U")) //不能回头的判断语句
fx="D";
}else if (keyCode==KeyEvent.VK_RIGHT)
{
if(!fx.equals("L")) //不能回头的判断语句
fx="R";
}else if (keyCode==KeyEvent.VK_LEFT)
{
if(!fx.equals("R")) //不能回头的判断语句
fx="L";
}
repaint(); //重新绘制界面的方法
}
@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")){ //在这里进行打印判断,由fx的值判断蛇头的打印
snakex[0]=snakex[0]+25;
if (snakex[0]>750)
{
snakex[0]=25; //超过边界后从另一边出来
}
} else if (fx.equals("L")){
snakex[0]=snakex[0]-25;
if (snakex[0]<25)
snakex[0]=750; //超过边界后从另一边出来
}else if (fx.equals("U")){
snakey[0]=snakey[0]-25;
if (snakey[0]<75)
snakey[0]=400; //超过边界后从另一边出来
}else if (fx.equals("D")){
snakey[0]=snakey[0]+25;
if (snakey[0]>400){
snakey[0]=75; //超过边界后从另一边出来
}
}
if (foodx==snakex[0]&&foody==snakey[0]) //吃到食物的哦按段
{
length++; //身体增加
score+=10; //分数增加
foodx=50+25*random.nextInt(28); //食物坐标刷新坐标随机
foody=75+25*random.nextInt(13);
}
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) { //弹起
}
}
所需要的资源:
right.png
left.png
up.png
down.png
food.png
header.png
body.png