GUI编程自我总结
GUI编程,是一种java早期的图形化界面编程,因为编写效率、界面效果和交互逻辑不能紧跟当下时代要求,已经有些渐渐淡出大众视野
但是作为学习Java和理解一些热门框架的入门练手还是很合适的,而且因为包含的内容不多,只谈谈重点的话就只需要解释两个关键的包java.awt和java.swing
在AWT中主要包含的是一些抽象的窗口工具,重在理解容器(Container)、面板(Panel)和窗口(Frame)这些元素的关系和布局问题
而swing则是Frame的一个子类,重在掌握一些元素的使用,有弹窗(Dialog)、标签(Label)、按钮(Button)、列表(JList)和文本框(Text)等
这次总结实践做的项目就是做一个贪吃蛇小游戏,当然只是实现了简单的开始和结束功能,更多的完善和优化还有很大的空间
全文阅读和自己动手完成保守需要两小时左右,希望不会浪费读者宝贵的时间
一小时贪吃蛇小游戏
在准备动手之前你需要先准备好小蛇的身体、“食物”的图片和游戏的头部图片,考虑到小蛇的移动,你需要将头部上下左右旋转着保存一遍
相对简单,所以你可以自己制作或者寻找差不多的图片替代一下都可以,下面是我准备的图片
![预备图片](https://note.youdao.com/yws/api/personal/file/ED5B126BCB4A40459C97C21C7956BB44?method=download&shareKey=6d69f34e7583bdb4526383fd0ef5926a)
我用的IDE是IntelliJ IDEA 2019.1 x64版本,jdk版本用的13.0.2,当然任何集成开发软件都是可以的,我们就可以新建一个java项目
首先在新建的项目中导入你刚才准备的图片,尽量将图片资源与程序放在一个文件夹下,之后说我这样做的原因
下面就可以开始准备写程序了,写程序之前,你需要先把考虑到的写死的私有变量写在单独的类中,这是优秀的编程习惯
以下是我的资源文件的代码
package com.sbc.GUI.Snack;
import javax.swing.*;
import java.net.URL;
//数据中心
public class Data {
//相对路径
private static URL headURL = Data.class.getResource("Snack/head.jpg");
public static ImageIcon head = new ImageIcon(headURL);
private static URL upURL = Data.class.getResource("Snack/up.jpg");
public static ImageIcon up = new ImageIcon(upURL);
private static URL downURL = Data.class.getResource("Snack/down.jpg");
public static ImageIcon down = new ImageIcon(downURL);
private static URL rightURL = Data.class.getResource("Snack/right.jpg");
public static ImageIcon right = new ImageIcon(rightURL);
private static URL leftURL = Data.class.getResource("Snack/left.jpg");
public static ImageIcon left = new ImageIcon(leftURL);
private static URL bodyURL = Data.class.getResource("Snack/body.jpg");
public static ImageIcon body = new ImageIcon(bodyURL);
private static URL flagURL = Data.class.getResource("Snack/flag.jpg");
public static ImageIcon flag = new ImageIcon(flagURL);
}
相信你已经看到了,这里我引用的图片用的是相对路径,自己项目经验过少,查阅资料说绝对路径会影响Linux部署问题,当然这个小游戏没有影响
所以你可以用你喜欢的方式来引用图片资源,这里我引用'/'来充当根目录时,是得不到图片资源的,这也是我选择放在一个文件夹下用相对路径的原因,也许是自己记得绝对路径方式有误吧
之后再写一个类,来作为启动类,这里面就是标准的窗口写法,以下是代码
package com.sbc.GUI.Snack;
import javax.swing.*;
//启动程序
public class StartSnack {
public static void main(String[] args) {
JFrame jFrame = new JFrame();
jFrame.add(new GamePanel());
//窗口可见性
jFrame.setVisible(true);
//窗口形状不可改变
jFrame.setResizable(false);
//窗口大小
jFrame.setSize(900,720);
}
}
接下来就是主要内容了,我们要做的游戏界面都要再一个Panel中完成,首先我们要先知道游戏的思路
当然大家对于贪吃蛇的游戏规则肯定是耳熟能详了,所以我们主要解释我们的实现思路,其实游戏大概就由初始化方法、监听器、定时器和绘图方法组成
初始化方法就是将变量初始化,保证游戏一开始的界面和开始顺利,游戏的开始图画是这样的
![开始界面](https://note.youdao.com/yws/api/personal/file/E66011E60A554E9485F9E4F11AF6AAE1?method=download&shareKey=6b2843fcf451fb076c5a598e36a9b7fd)
其次是监听器,监听器主要负责监听用户操作键盘的操作,本游戏主要是监听空格和上下左右键的动作
定时器的存在是定时重新绘图,速度不能过快或者过慢,定时器的数值都可以参照我的代码中的来
绘图方法主要考虑的就是界面的布局,很多数字需要自己计算,而且绘图的顺序在吃掉“食物”时要保证蛇头在“食物”上方,当然其他优化的地方还需要你自己来完成
所以其实编程思路就是定义变量,然后绘制好,最后在监听器和定时器中做好逻辑交互即可,只要思路明确,自己很快就可以独立完成
还有积分的计算就是一个Label,自己找好位置得到自己定义的长度值就可以了,下面是代码展示
package com.sbc.GUI.Snack;
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 {
public GamePanel() {
init();
}
//定义蛇的属性
int length;
int[] snack_x = new int[600];
int[] snack_y = new int[500];
//蛇头方向
String turn = "R";
//游戏当前状态
boolean isStart = false;
boolean isFail = false;
//定时器 1000ms = 1s
Timer timer = new Timer(100, this);
//食物的坐标
int flagX;
int flagY;
Random ran = new Random();
//积分成绩
int score;
//初始化方法
public void init() {
//初始化成绩
score = 0;
//初始长度
length = 3;
//初始头部位置
snack_x[0] = 100;
snack_y[0] = 100;
//第一个身体坐标
snack_x[1] = 75;
snack_y[1] = 100;
//第二个身体坐标
snack_x[2] = 50;
snack_y[2] = 100;
turn = "R";
this.setFocusable(true);
this.addKeyListener(this);
flagX = 25 + 25 * ran.nextInt(34);
flagY = 75 + 25 * ran.nextInt(24);
timer.start();
}
//绘制面板
@Override
protected void paintComponent(Graphics g) {
//清屏
super.paintComponent(g);
//绘制静态面板
//设置背景颜色
this.setBackground(Color.white);
Data.head.paintIcon(this, g, 18, 11);
g.fillRect(18, 70, 850, 600);
//画积分
g.setColor(Color.red);
g.setFont(new Font("微软雅黑", Font.BOLD, 18));
g.drawString("长度" + length, 750, 35);
g.drawString("分数" + 10 * length, 750, 50);
//画食物目标
Data.flag.paintIcon(this, g, flagX, flagY);
//画小蛇
if (turn.equals("R")) {
Data.right.paintIcon(this, g, snack_x[0], snack_y[0]);
}
if (turn.equals("L")) {
Data.left.paintIcon(this, g, snack_x[0], snack_y[0]);
}
if (turn.equals("U")) {
Data.up.paintIcon(this, g, snack_x[0], snack_y[0]);
}
if (turn.equals("D")) {
Data.down.paintIcon(this, g, snack_x[0], snack_y[0]);
}
for (int i = 1; i <= length - 1; i++) {
Data.body.paintIcon(this, g, snack_x[i], snack_y[i]);
}
//游戏状态
if (isStart == false) {
g.setColor(Color.white);
g.setFont(new Font("微软雅黑", Font.BOLD, 40));
g.drawString("按下空格开始游戏!", 300, 300);
}
if (isFail == true) {
g.setColor(Color.red);
g.setFont(new Font("微软雅黑", Font.BOLD, 40));
g.drawString("游戏结束!", 300, 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_UP) {
turn = "U";
} else if (keyCode == KeyEvent.VK_DOWN) {
turn = "D";
} else if (keyCode == KeyEvent.VK_LEFT) {
turn = "L";
} else if (keyCode == KeyEvent.VK_RIGHT) {
turn = "R";
}
}
@Override
public void keyReleased(KeyEvent e) {
}
//定时器定时刷新
@Override
public void actionPerformed(ActionEvent e) {
if (isFail == false && isStart == true) {
//吃食物
if (snack_x[0] == flagX && snack_y[0] == flagY) {
length++;
score += 10;
flagX = 25 + 25 * ran.nextInt(34);
flagY = 75 + 25 * ran.nextInt(24);
}
if (isStart) {
//移动操作
for (int i = length - 1; i > 0; i--) {
snack_x[i] = snack_x[i - 1];
snack_y[i] = snack_y[i - 1];
}
if (turn.equals("R")) {
snack_x[0] += 25;
//边界判断
if (snack_x[0] > 850) {
snack_x[0] = 25;
}
} else if (turn.equals("L")) {
snack_x[0] -= 25;
//边界判断
if (snack_x[0] < 25) {
snack_x[0] = 850;
}
} else if (turn.equals("U")) {
snack_y[0] -= 25;
//边界判断
if (snack_y[0] < 75) {
snack_y[0] = 650;
}
} else if (turn.equals("D")) {
snack_y[0] += 25;
//边界判断
if (snack_y[0] > 650) {
snack_y[0] = 75;
}
}
}
//失败判定
for (int i = 1; i < length; i++) {
if (snack_x[0] == snack_x[i] && snack_y[0] == snack_y[i]) {
isFail = true;
}
}
repaint();
}
timer.start();
}
}
以上就是全部代码了,相信你已经自己调试和完成了,自己肯定也完了好几局了,所以你会发现你有很多可以改善的地方
比如说存档下次继续、选择闯关难度和增加障碍物,只要你想到的都可以加到上面,相信编程的我们有自己独特的想法和思路
最后附上自己较长的游戏图片,相信大家肯定有比我还优秀的,都可以在评论区展示自己的作品和成绩
![游戏结束](https://note.youdao.com/yws/api/personal/file/1FA0ED4557594022A705D82717E5D67F?method=download&shareKey=c24532129c1d733c2da078badd689f9c)
总结
通过这个小游戏,我也熟练了GUI编程的基本过程,当然在过程中也遇到了一些问题
在我继承了键盘监听器接口的时候,我只需要重写键盘按压方法的接口就可以了,但是我发现我必须要把其他两个接口方法显示出来
我度娘后才知道吗,原来一个类实现接口方法时,如果是非抽象类需要重写所有方法,否之不然
以上就是贪吃蛇小程序所有的内容了,如果存在问题或者有可以完善和改正的地方,希望大佬斧正,希望大家和我一起讨论一起进步