一、引入
涉及技能:
- 循环、分支
- 方法的抽取
- 数组的使用
- 面向对象
- 继承,子类方法的重写
- 接口,接口的实现
- GUI(图像化界面编程)
GUI中的组件:
7.1 窗口
7.2 弹窗
7.3 面板
7.4 文本框
7.5 列表框
7.6 按钮
7.7 图片
7.8 交互的事件:监听事件(鼠标事件,键盘事件)
GUI技术,不流行了!!!界面太简单粗糙!!
已经被淘汰了,为什么还学?
- 提起兴趣
- 分层思想
- 锻炼监听器的思想
- 举一反三(蛇的图片换成其他图片,食物的图片换成其他图片)
- 工作中用到
注意:
学的是 思想!!!
二、原理图
小蛇素材图像素为25*25,因此在对应xy轴上的数值也以25为步长。
三、进入正题
1.打开IDEA工具,新建一个模块module。
2.选择java,点next。
3.取一个名字叫TestSnake,然后点击finish。
4.打开新建好的TestSnake,在src下新建一个文件夹,取名为images。
5.将所有的素材图片复制到images文件夹下。
四、将图片加载到程序中
1.在src下再新建一个文件夹com.game,在这个文件夹下新建一个类Images,用于获取图片。
import javax.swing.*;
import java.net.URL;
/*用来获取游戏中所有涉及的图片*/
public class Images {
/*面向对象思想:将图片进行封装,封装为一个对象,在程序中通过操纵这个对象来操纵图片*/
/*将图片的路径封装为一个对象:*/
// class获取字节码
// getResource获取图片路径
// 在菜单Build下选中第二个Build Module,重写构建一下,生成对应的字节码信息
// 在out文件夹下找图片路径
// '/'指代相对路径,相对file:/C:/Users/ASUS/Desktop/贪吃蛇小游戏/out/production/TestSnake/而言
public static URL bodyURL=Images.class.getResource("/images/body.png");
/*将图片封装为程序中一个对象:*/
// ImageIcon构造器,可以直接把url传进去
public static ImageIcon bodyImg=new ImageIcon(bodyURL);
/*同理,将剩下的图片都封装:*/
public static URL downURL=Images.class.getResource("/images/down.png");
public static ImageIcon downImg=new ImageIcon(downURL);
public static URL foodURL=Images.class.getResource("/images/food.png");
public static ImageIcon foodImg=new ImageIcon(foodURL);
public static URL headerURL=Images.class.getResource("/images/header.png");
public static ImageIcon headerImg=new ImageIcon(headerURL);
public static URL leftURL=Images.class.getResource("/images/left.png");
public static ImageIcon leftImg=new ImageIcon(leftURL);
public static URL rightURL=Images.class.getResource("/images/right.png");
public static ImageIcon rightImg=new ImageIcon(rightURL);
public static URL upURL=Images.class.getResource("/images/up.png");
public static ImageIcon upImg=new ImageIcon(upURL);
}
3.关于代码注释中的“在菜单Build下选中第二个Build Module,重写构建一下,生成对应的字节码信息”、“ 在out文件夹下找图片路径”的操作,具体步骤如下:
五、创建窗体
1.在com.game文件夹下新建一个类,叫StartGame。
2.将以下代码写入StartGame类中。
package com.game;
import javax.swing.*;
import java.awt.*;
public class StartGame {
public static void main(String[] args) {
/*1.创建一个窗体:*/
JFrame jf=new JFrame();
/*2.给窗体设置一个标题:*/
jf.setTitle("贪吃蛇小游戏 by:cym");
/*3.设置窗体弹出的坐标,对应窗体的宽和高:*/
// 获取屏幕的宽、高
int width = Toolkit.getDefaultToolkit().getScreenSize().width;
int height = Toolkit.getDefaultToolkit().getScreenSize().height;
jf.setBounds((width-800)/2,(height-800)/2,800,800);
/*4.设置窗体大小不可调:*/
jf.setResizable(false);
/*5.关闭窗口的同时,程序随之关闭:*/
jf.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
/*默认情况下窗体隐藏,需要进行显现(最好放在最后):*/
jf.setVisible(true);
}
}
3.计算窗体在屏幕居中的方法如下:
六、创建面板
1.新建一个类GamePanel。
2.将以下代码写入GamePanel类中。
package com.game;
import javax.swing.*;
import java.awt.*;
/*面板*/
//继承JPanel才具备面板的功能,才成为一个面板
public class GamePanel extends JPanel {
/*此方法特殊,就相当于图形版的main方法,自动调用*/
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
/*1.填充背景颜色:*/
// 颜色由rgb(red green blue)组成
// 可通过点击左边行号旁的色块进行颜色修改
this.setBackground(new Color(243, 239, 190, 255));
/*2.画头部的图片header.png:*/
// Images.headerImg:调用图片类中的header.png图片
// paintIcon()方法将图片在面板中绘制出来
// this:当前面板
// g:使用的画笔
// x、y:对应坐标轴
Images.headerImg.paintIcon(this, g, 15, 10);
/*3.画一个矩形:*/
// 先调节画笔颜色:
g.setColor(new Color(195, 205, 246));
// fillRect填充矩形
g.fillRect(15, 70, 750, 670);
}
}
3.在StartGame类中添加第6 步创建面板,将面板添加到窗体中。
package com.game;
import javax.swing.*;
import java.awt.*;
/*窗体*/
public class StartGame {
public static void main(String[] args) {
/*1.创建一个窗体:*/
JFrame jf = new JFrame();
/*2.给窗体设置一个标题:*/
jf.setTitle("贪吃蛇小游戏 by:cym");
/*3.设置窗体弹出的坐标,对应窗体的宽和高:*/
// 获取屏幕的宽、高
int width = Toolkit.getDefaultToolkit().getScreenSize().width;
int height = Toolkit.getDefaultToolkit().getScreenSize().height;
jf.setBounds((width - 800) / 2, (height - 800) / 2, 800, 800);
/*4.设置窗体大小不可调:*/
jf.setResizable(false);
/*5.关闭窗口的同时,程序随之关闭:*/
jf.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
/*6.创建面板:*/
GamePanel gp=new GamePanel();
// 将面板放入窗体中:
jf.add(gp);
/*默认情况下窗体隐藏,需要进行显现(最好放在最后):*/
jf.setVisible(true);
}
}
七、画静态小蛇
1.以这个图为例,做为一个默认小蛇,一个头,两节身子。
2.在面板类中的paintComponent()方法外部,写入以下代码。
/*定义两个数组:
* 一个数组,专门存储蛇的x轴坐标
* 一个数组,专门存储蛇的y轴坐标
* */
int[] snakeX=new int[200];
int[] snakeY=new int[200];
/*定义蛇的长度:*/
int length;
/*自定义一个初始化方法,初始化小蛇的坐标:*/
public void init(){
/*初始化蛇的长度:*/
length=3;
/*初始化蛇头坐标:*/
snakeX[0]=175;
snakeY[0]=275;
/*初始化第一节身子坐标:*/
snakeX[1]=150;
snakeY[1]=275;
/*初始化第二节身子坐标:*/
snakeX[2]=125;
snakeY[2]=275;
}
/*构造器,调用一下小蛇默认方法init(),进行面板初始化的时候就默认了蛇的位置:*/
public GamePanel(){
init();
}
3.在面板类中的paintComponent()方法内部,写第4步画小蛇,详见以下代码。
/*4.画小蛇:*/
// 画蛇头:
Images.rightImg.paintIcon(this,g,snakeX[0],snakeY[0]);
画第一节身子:
// Images.bodyImg.paintIcon(this,g,snakeX[1],snakeY[1]);
画第二节身子:
// Images.bodyImg.paintIcon(this,g,snakeX[2],snakeY[2]);
// 用循环来画蛇身子:
for(int i=1;i<length;i++){
Images.bodyImg.paintIcon(this,g,snakeX[i],snakeY[i]);
}
八、将蛇头按照行走方向改变
1.定义蛇头的方向,代码如下。
/*定义两个数组:
* 一个数组,专门存储蛇的x轴坐标
* 一个数组,专门存储蛇的y轴坐标
* */
int[] snakeX=new int[200];
int[] snakeY=new int[200];
/*定义蛇的长度:*/
int length;
/*定义蛇的行走方向:*/
String direction;
/*自定义一个初始化方法,初始化小蛇的坐标:*/
public void init(){
/*初始化蛇的长度:*/
length=3;
/*初始化蛇头坐标:*/
snakeX[0]=175;
snakeY[0]=275;
/*初始化第一节身子坐标:*/
snakeX[1]=150;
snakeY[1]=275;
/*初始化第二节身子坐标:*/
snakeX[2]=125;
snakeY[2]=275;
/*初始化蛇头的方向为右:*/
direction="R";//上U 下D 左L 右R
}
2.根据不同方向画蛇头,代码如下。
/*4.画小蛇:*/
// 画蛇头:
if("R".equals(direction)){
Images.rightImg.paintIcon(this,g,snakeX[0],snakeY[0]);
}
if("L".equals(direction)){
Images.leftImg.paintIcon(this,g,snakeX[0],snakeY[0]);
}
if("U".equals(direction)){
Images.upImg.paintIcon(this,g,snakeX[0],snakeY[0]);
}
if("D".equals(direction)){
Images.downImg.paintIcon(this,g,snakeX[0],snakeY[0]);
}
九、加入监听,空格来控制游戏是否开始
具体代码如下:
/*游戏只有两个状态:开始、暂停*/
boolean isStart=false;//默认游戏为暂停状态
/*构造器,调用一下小蛇默认方法init(),进行面板初始化的时候就默认了蛇的位置:*/
public GamePanel(){
init();
// 将焦点定位在当前操作的面板上
this.setFocusable(true);
// 加入监听:
// addKeyListener表示加入键盘监听
this.addKeyListener(new KeyAdapter() {//涉及到适配器模式
// 监听键盘按下操作
@Override
public void keyPressed(KeyEvent e) {
super.keyPressed(e);
int keyCode = e.getKeyCode();
// System.out.println(keyCode);//空格对应32
if (keyCode==32){
// System.out.println("点击了空格");
isStart=!isStart;//点击了空格,将isStart变成相反的状态
repaint();//重绘,调用paintComponent方法
}
}
});
}
/*5.如果游戏是暂停的,界面中间有一个提示语*/
if(isStart==false){
/*画一个文字:*/
// 画笔设置颜色
g.setColor(new Color(250, 3, 3));
// 设置文字的字体、加粗、大小
g.setFont(new Font("微软雅黑",Font.BOLD,40));
// 画文字:
// drawString()方法,画字符串
// 文字内容、xy轴坐标
g.drawString("点击空格开始游戏",250,330);
}
十、小蛇向右动起来
具体代码如下:
/*加入一个定时器:*/
Timer timer;
/*构造器,调用一下小蛇默认方法init(),进行面板初始化的时候就默认了蛇的位置:*/
public GamePanel(){
init();
// 将焦点定位在当前操作的面板上
this.setFocusable(true);
// 加入监听:
// addKeyListener表示加入键盘监听
this.addKeyListener(new KeyAdapter() {//涉及到适配器模式
// 监听键盘按下操作
@Override
public void keyPressed(KeyEvent e) {
super.keyPressed(e);
int keyCode = e.getKeyCode();
// System.out.println(keyCode);//空格对应32
if (keyCode==32){
// System.out.println("点击了空格");
isStart=!isStart;//点击了空格,将isStart变成相反的状态
repaint();//重绘,调用paintComponent方法
}
}
});
/*对定时器做初始化:*/
// 延迟毫秒数、一个ActionListener类对象
timer=new Timer(100, new ActionListener() {//创建ActionListener对象,一个匿名内部类
// ActionListener是 事件监听
// 相当于每100ms监听你是否发生一个动作
// 具体动作放入actionPerformed中
@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];
}
// 动头的位置
snakeX[0]+=25;
// 防止蛇超出边界:
if(snakeX[0]>750){
snakeX[0]=25;
}
repaint();//重绘,根据新的蛇头蛇身的位置画
}
}
});
// 启动计时器
timer.start();
}
十一、小蛇上下左右动起来
代码如下:
/*构造器,调用一下小蛇默认方法init(),进行面板初始化的时候就默认了蛇的位置:*/
public GamePanel(){
init();
// 将焦点定位在当前操作的面板上
this.setFocusable(true);
// 加入监听:
// addKeyListener表示加入键盘监听
this.addKeyListener(new KeyAdapter() {//涉及到适配器模式
// 监听键盘按下操作
@Override
public void keyPressed(KeyEvent e) {
super.keyPressed(e);
int keyCode = e.getKeyCode();
// System.out.println(keyCode);//空格对应32
/*监听空格:*/
if (keyCode==KeyEvent.VK_SPACE){//将空格32封装起来
// System.out.println("点击了空格");
isStart=!isStart;//点击了空格,将isStart变成相反的状态
repaint();//重绘,调用paintComponent方法
}
/*监听向上箭头:*/
if (keyCode==KeyEvent.VK_UP){
direction="U";
}
/*监听向下箭头:*/
if (keyCode==KeyEvent.VK_DOWN){
direction="D";
}
/*监听向左箭头:*/
if (keyCode==KeyEvent.VK_LEFT){
direction="L";
}
/*监听向右箭头:*/
if (keyCode==KeyEvent.VK_RIGHT){
direction="R";
}
}
});
/*对定时器做初始化:*/
// 延迟毫秒数、一个ActionListener类对象
timer=new Timer(100, new ActionListener() {//创建ActionListener对象,一个匿名内部类
// ActionListener是 事件监听
// 相当于每100ms监听你是否发生一个动作
// 具体动作放入actionPerformed中
@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("R".equals(direction)){
snakeX[0]+=25;
}
if("L".equals(direction)){
snakeX[0]-=25;
}
if("U".equals(direction)){
snakeY[0]-=25;
}
if("D".equals(direction)){
snakeY[0]+=25;
}
// 防止蛇超出边界:
if(snakeX[0]>745){
snakeX[0]=25;
}
if (snakeX[0]<25) {
snakeX[0]=745;
}
if(snakeY[0]<70){
snakeY[0]=700;
}
if(snakeY[0]>700){
snakeY[0]=70;
}
repaint();//重绘,根据新的蛇头蛇身的位置画
}
}
});
// 启动计时器
timer.start();
}
十二、画食物、吃食物
1.画食物代码:
/*定义食物的xy轴坐标*/
int foodX,foodY;
/*自定义一个初始化方法,初始化小蛇的坐标:*/
public void init(){
/*初始化蛇的长度:*/
length=3;
/*初始化蛇头坐标:*/
snakeX[0]=175;
snakeY[0]=275;
/*初始化第一节身子坐标:*/
snakeX[1]=150;
snakeY[1]=275;
/*初始化第二节身子坐标:*/
snakeX[2]=125;
snakeY[2]=275;
/*初始化蛇头的方向为右:*/
direction="R";//上U 下D 左L 右R
/*初始化食物的坐标:*/
foodX=300;
foodY=200;
}
/*6.画食物:*/ Images.foodImg.paintIcon(this,g,foodX,foodY);
2.吃食物代码:
/*对定时器做初始化:*/
// 延迟毫秒数、一个ActionListener类对象
timer=new Timer(100, new ActionListener() {//创建ActionListener对象,一个匿名内部类
// ActionListener是 事件监听
// 相当于每100ms监听你是否发生一个动作
// 具体动作放入actionPerformed中
@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("R".equals(direction)){
snakeX[0]+=25;
}
if("L".equals(direction)){
snakeX[0]-=25;
}
if("U".equals(direction)){
snakeY[0]-=25;
}
if("D".equals(direction)){
snakeY[0]+=25;
}
// 防止蛇超出边界:
if(snakeX[0]>725){
snakeX[0]=25;
}
if (snakeX[0]<25) {
snakeX[0]=725;
}
if(snakeY[0]<75){
snakeY[0]=700;
}
if(snakeY[0]>700){
snakeY[0]=75;
}
/*监测碰撞的动作,也就是吃食物的动作:*/
// 如果蛇头坐标与食物坐标一样时,即吃到食物
if (snakeX[0]==foodX&&snakeY[0]==foodY) {
// 蛇长度加一
length++;
// 食物坐标改变,随机生成坐标,随机数必须是25的倍数,x轴【25,725】,y轴【75,700】
// 方法一:
// math.random()-->[0.0,1.0)
// math.random()*29-->[0.0,29.0)
// (int)(math.random()*29)-->[0,28]
// (int)(math.random()*29)+1-->[1,29]
foodX=((int)(Math.random()*29)+1)*25;//[1,29]*25=[25,725]
// 方法二:
// [75,700]-->[3,28]*25
// [3,28]-->[0,25]+3
// [0,25]
// new Random().nextInt(26)-->[0,26)-->[0,25]
foodY=((new Random().nextInt(26))+3)*25;//[3,28]*25=[75,700]
}
repaint();//重绘,根据新的蛇头蛇身的位置画
}
}
});
// 启动计时器
timer.start();
}
十三、画积分
代码如下:
/*定义一个积分:*/
int score;
/*自定义一个初始化方法,初始化小蛇的坐标:*/
public void init(){
/*初始化蛇的长度:*/
length=3;
/*初始化蛇头坐标:*/
snakeX[0]=175;
snakeY[0]=275;
/*初始化第一节身子坐标:*/
snakeX[1]=150;
snakeY[1]=275;
/*初始化第二节身子坐标:*/
snakeX[2]=125;
snakeY[2]=275;
/*初始化蛇头的方向为右:*/
direction="R";//上U 下D 左L 右R
/*初始化食物的坐标:*/
foodX=300;
foodY=200;
/*初始化积分:*/
score=0;
}
/*监测碰撞的动作,也就是吃食物的动作:*/
// 如果蛇头坐标与食物坐标一样时,即吃到食物
if (snakeX[0]==foodX&&snakeY[0]==foodY) {
// 蛇长度加一
length++;
// 食物坐标改变,随机生成坐标,随机数必须是25的倍数,x轴【25,725】,y轴【75,700】
// 方法一:
// math.random()-->[0.0,1.0)
// math.random()*29-->[0.0,29.0)
// (int)(math.random()*29)-->[0,28]
// (int)(math.random()*29)+1-->[1,29]
foodX=((int)(Math.random()*29)+1)*25;//[1,29]*25=[25,725]
// 方法二:
// [75,700]-->[3,28]*25
// [3,28]-->[0,25]+3
// [0,25]
// new Random().nextInt(26)-->[0,26)-->[0,25]
foodY=((new Random().nextInt(26))+3)*25;//[3,28]*25=[75,700]
/*每吃到一次食物,积分加10*/
score+=10;
}
repaint();//重绘,根据新的蛇头蛇身的位置画
}
}
});
/*7.画积分(画文字):*/
g.setColor(new Color(246, 245, 245));
g.setFont(new Font("微软雅黑",Font.BOLD,20));
g.drawString("积分:"+score,280,40);
十四、死亡判定
/*初始化积分:*/
score=0;
@Override
public void keyPressed(KeyEvent e) {
super.keyPressed(e);
int keyCode = e.getKeyCode();
// System.out.println(keyCode);//空格对应32
/*监听空格:*/
if (keyCode==KeyEvent.VK_SPACE){//将空格32封装起来
if(isDie){
// 全部恢复到初始化状态
init();
isDie=false;
}else{
isStart=!isStart;
repaint();//重绘
}
}
@Override
public void actionPerformed(ActionEvent e) {
if(isStart&&isDie==false){//游戏开始状态、小蛇活着的状态下才动
// 由最后一节身子往前一节身子上动
for(int i=length-1;i>0;i--){
snakeX[i]=snakeX[i-1];
snakeY[i]=snakeY[i-1];
}
/*死亡判定:*/
for(int i=1;i<length;i++){
// 如果蛇头的坐标和身子的任意一节坐标一样
if (snakeX[0]==snakeX[i]&&snakeY[0]==snakeY[i]) {
// 判定为死亡状态
isDie=true;
}
/*8.画死亡状态:*/
if(isDie){
g.setColor(new Color(246, 11, 11));
g.setFont(new Font("微软雅黑",Font.BOLD,35));
g.drawString("小蛇死亡!游戏停止,按下空格重新开始游戏",50,330);
十五、游戏打包
在对方电脑有jre的情况下,可以将程序打包发给对方。
1.选择File菜单下的项目结构Project Structure
2.点击Artifacts,再点击左上角的“+”号,JAR包下选择from modules with dependencies从项目中将所有依赖选上
3.在Main Class中选择游戏进入的入口,即main方法所在的类。
4.此时已经生成了一个jar文件,点击Apply运行,再OK即可。
5.在菜单栏Build下选择Build Artifacts,再点击Build,就会生成jar包。
6.生成好的jar包,可以在out文件夹下,找到刚生成好的jar包,右击选择Show inExplorer,就可以打开jar包所在位置
7.直接双击jar包,即可运行。
源代码
Images类:
package com.game;
import javax.swing.*;
import java.net.URL;
/*用来获取游戏中所有涉及的图片*/
public class Images {
/*面向对象思想:将图片进行封装,封装为一个对象,在程序中通过操纵这个对象来操纵图片*/
/*将图片的路径封装为一个对象:*/
// class获取字节码
// getResource获取图片路径
// 在菜单Build下选中第二个Build Module,重写构建一下,生成对应的字节码信息
// 在out文件夹下找图片路径
// '/'指代相对路径,相对file:/C:/Users/ASUS/Desktop/贪吃蛇小游戏/out/production/TestSnake/而言
public static URL bodyURL=Images.class.getResource("/images/body.png");
/*将图片封装为程序中一个对象:*/
// ImageIcon构造器,可以直接把url传进去
public static ImageIcon bodyImg=new ImageIcon(bodyURL);
/*同理,将剩下的图片都封装:*/
public static URL downURL=Images.class.getResource("/images/down.png");
public static ImageIcon downImg=new ImageIcon(downURL);
public static URL foodURL=Images.class.getResource("/images/food.png");
public static ImageIcon foodImg=new ImageIcon(foodURL);
public static URL headerURL=Images.class.getResource("/images/header.png");
public static ImageIcon headerImg=new ImageIcon(headerURL);
public static URL leftURL=Images.class.getResource("/images/left.png");
public static ImageIcon leftImg=new ImageIcon(leftURL);
public static URL rightURL=Images.class.getResource("/images/right.png");
public static ImageIcon rightImg=new ImageIcon(rightURL);
public static URL upURL=Images.class.getResource("/images/up.png");
public static ImageIcon upImg=new ImageIcon(upURL);
}
StartGame类:
package com.game;
import javax.swing.*;
import java.awt.*;
public class StartGame {
public static void main(String[] args) {
/*1.创建一个窗体:*/
JFrame jf = new JFrame();
/*2.给窗体设置一个标题:*/
jf.setTitle("贪吃蛇小游戏 by:cym");
/*3.设置窗体弹出的坐标,对应窗体的宽和高:*/
// 获取屏幕的宽、高
int width = Toolkit.getDefaultToolkit().getScreenSize().width;
int height = Toolkit.getDefaultToolkit().getScreenSize().height;
jf.setBounds((width - 800) / 2, (height - 800) / 2, 800, 800);
/*4.设置窗体大小不可调:*/
jf.setResizable(false);
/*5.关闭窗口的同时,程序随之关闭:*/
jf.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
/*6.创建面板:*/
GamePanel gp = new GamePanel();
// 将面板放入窗体中:
jf.add(gp);
/*默认情况下窗体隐藏,需要进行显现(最好放在最后):*/
jf.setVisible(true);
}
}
GamePanel类:
package com.game;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.Random;
/*面板*/
//继承JPanel才具备面板的功能,才成为一个面板
public class GamePanel extends JPanel {
/*定义两个数组:
* 一个数组,专门存储蛇的x轴坐标
* 一个数组,专门存储蛇的y轴坐标
* */
int[] snakeX = new int[200];
int[] snakeY = new int[200];
/*定义蛇的长度:*/
int length;
/*定义蛇的行走方向:*/
String direction;
/*游戏只有两个状态:开始、暂停*/
boolean isStart = false;//默认游戏为暂停状态
/*加入一个定时器:*/
Timer timer;
/*定义食物的xy轴坐标*/
int foodX, foodY;
/*定义一个积分:*/
int score;
/*加入一个变量,判断小蛇生死状态:*/
boolean isDie = false;//默认小蛇为活着的状态
/*自定义一个初始化方法,初始化小蛇的坐标:*/
public void init() {
/*初始化蛇的长度:*/
length = 3;
/*初始化蛇头坐标:*/
snakeX[0] = 175;
snakeY[0] = 275;
/*初始化第一节身子坐标:*/
snakeX[1] = 150;
snakeY[1] = 275;
/*初始化第二节身子坐标:*/
snakeX[2] = 125;
snakeY[2] = 275;
/*初始化蛇头的方向为右:*/
direction = "R";//上U 下D 左L 右R
/*初始化食物的坐标:*/
foodX = 300;
foodY = 200;
/*初始化积分:*/
score = 0;
}
/*构造器,调用一下小蛇默认方法init(),进行面板初始化的时候就默认了蛇的位置:*/
public GamePanel() {
init();
// 将焦点定位在当前操作的面板上
this.setFocusable(true);
// 加入监听:
// addKeyListener表示加入键盘监听
this.addKeyListener(new KeyAdapter() {//涉及到适配器模式
// 监听键盘按下操作
@Override
public void keyPressed(KeyEvent e) {
super.keyPressed(e);
int keyCode = e.getKeyCode();
// System.out.println(keyCode);//空格对应32
/*监听空格:*/
if (keyCode == KeyEvent.VK_SPACE) {//将空格32封装起来
if (isDie) {
// 全部恢复到初始化状态
init();
isDie = false;
} else {
isStart = !isStart;
repaint();//重绘
}
}
/*监听向上箭头:*/
if (keyCode == KeyEvent.VK_UP) {
direction = "U";
}
/*监听向下箭头:*/
if (keyCode == KeyEvent.VK_DOWN) {
direction = "D";
}
/*监听向左箭头:*/
if (keyCode == KeyEvent.VK_LEFT) {
direction = "L";
}
/*监听向右箭头:*/
if (keyCode == KeyEvent.VK_RIGHT) {
direction = "R";
}
}
});
/*对定时器做初始化:*/
// 延迟毫秒数、一个ActionListener类对象
timer = new Timer(100, new ActionListener() {//创建ActionListener对象,一个匿名内部类
// ActionListener是 事件监听
// 相当于每100ms监听你是否发生一个动作
// 具体动作放入actionPerformed中
@Override
public void actionPerformed(ActionEvent e) {
if (isStart && isDie == false) {//游戏开始状态、小蛇活着的状态下才动
// 由最后一节身子往前一节身子上动
for (int i = length - 1; i > 0; i--) {
snakeX[i] = snakeX[i - 1];
snakeY[i] = snakeY[i - 1];
}
// 动头的位置
if ("R".equals(direction)) {
snakeX[0] += 25;
}
if ("L".equals(direction)) {
snakeX[0] -= 25;
}
if ("U".equals(direction)) {
snakeY[0] -= 25;
}
if ("D".equals(direction)) {
snakeY[0] += 25;
}
// 防止蛇超出边界:
if (snakeX[0] > 725) {
snakeX[0] = 25;
}
if (snakeX[0] < 25) {
snakeX[0] = 725;
}
if (snakeY[0] < 75) {
snakeY[0] = 700;
}
if (snakeY[0] > 700) {
snakeY[0] = 75;
}
/*监测碰撞的动作,也就是吃食物的动作:*/
// 如果蛇头坐标与食物坐标一样时,即吃到食物
if (snakeX[0] == foodX && snakeY[0] == foodY) {
// 蛇长度加一
length++;
// 食物坐标改变,随机生成坐标,随机数必须是25的倍数,x轴【25,725】,y轴【75,700】
// 方法一:
// math.random()-->[0.0,1.0)
// math.random()*29-->[0.0,29.0)
// (int)(math.random()*29)-->[0,28]
// (int)(math.random()*29)+1-->[1,29]
foodX = ((int) (Math.random() * 29) + 1) * 25;//[1,29]*25=[25,725]
// 方法二:
// [75,700]-->[3,28]*25
// [3,28]-->[0,25]+3
// [0,25]
// new Random().nextInt(26)-->[0,26)-->[0,25]
foodY = ((new Random().nextInt(26)) + 3) * 25;//[3,28]*25=[75,700]
/*每吃到一次食物,积分加10*/
score += 10;
}
/*死亡判定:*/
for (int i = 1; i < length; i++) {
// 如果蛇头的坐标和身子的任意一节坐标一样
if (snakeX[0] == snakeX[i] && snakeY[0] == snakeY[i]) {
// 判定为死亡状态
isDie = true;
}
}
repaint();//重绘,根据新的蛇头蛇身的位置画
}
}
});
// 启动计时器
timer.start();
}
/*此方法特殊,就相当于图形版的main方法,自动调用*/
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
/*1.填充背景颜色:*/
// 颜色由rgb(red green blue)组成
// 可通过点击左边行号旁的色块进行颜色修改
this.setBackground(new Color(243, 239, 190, 255));
/*2.画头部的图片header.png:*/
// Images.headerImg:调用图片类中的header.png图片
// paintIcon()方法将图片在面板中绘制出来
// this:当前面板
// g:使用的画笔
// x、y:对应坐标轴
Images.headerImg.paintIcon(this, g, 15, 10);
/*3.画一个矩形:*/
// 先调节画笔颜色:
g.setColor(new Color(205, 228, 246));
// fillRect填充矩形
g.fillRect(15, 70, 750, 670);
/*4.画小蛇:*/
// 画蛇头:
if ("R".equals(direction)) {
Images.rightImg.paintIcon(this, g, snakeX[0], snakeY[0]);
}
if ("L".equals(direction)) {
Images.leftImg.paintIcon(this, g, snakeX[0], snakeY[0]);
}
if ("U".equals(direction)) {
Images.upImg.paintIcon(this, g, snakeX[0], snakeY[0]);
}
if ("D".equals(direction)) {
Images.downImg.paintIcon(this, g, snakeX[0], snakeY[0]);
}
画第一节身子:
// Images.bodyImg.paintIcon(this,g,snakeX[1],snakeY[1]);
画第二节身子:
// Images.bodyImg.paintIcon(this,g,snakeX[2],snakeY[2]);
// 用循环来画蛇身子:
for (int i = 1; i < length; i++) {
Images.bodyImg.paintIcon(this, g, snakeX[i], snakeY[i]);
}
/*5.如果游戏是暂停的,界面中间有一个提示语*/
if (isStart == false) {
/*画一个文字:*/
// 画笔设置颜色
g.setColor(new Color(222, 13, 245));
// 设置文字的字体、加粗、大小
g.setFont(new Font("微软雅黑", Font.BOLD, 40));
// 画文字:
// drawString()方法,画字符串
// 文字内容、xy轴坐标
g.drawString("点击空格开始游戏", 250, 330);
}
/*6.画食物:*/
Images.foodImg.paintIcon(this, g, foodX, foodY);
/*7.画积分(画文字):*/
g.setColor(new Color(246, 245, 245));
g.setFont(new Font("微软雅黑", Font.BOLD, 20));
g.drawString("积分:" + score, 280, 40);
/*8.画死亡状态:*/
if (isDie) {
g.setColor(new Color(246, 11, 11));
g.setFont(new Font("微软雅黑", Font.BOLD, 35));
g.drawString("小蛇死亡!游戏停止,按下空格重新开始游戏", 50, 330);
}
}
}
贪吃蛇小游戏b站原视频:
https://www.bilibili.com/video/BV1w5411g73U?share_source=copy_web
贪吃蛇图片素材:
链接:https://pan.baidu.com/s/1B5aIR-luqF94Y-8Lhwck0g?pwd=5gj9
提取码:5gj9
解压缩密码:456654
我的jar包:
链接:https://pan.baidu.com/s/1Zk31sOPTr_WCidZUd2Jl9A?pwd=4h7d
提取码:4h7d
若双击jar包不能够直接运行的解决办法:
指路我的博客:http://t.csdn.cn/1QALL
文章出处登录后可见!