预备知识:
- 帧:如果时间片够小,就是动画,一秒30帧。连起来是动画,拆开就是静态图片。
- 键盘监听
- 定时器Timer
分三个类文件来写: - 启动游戏类StartGame
- 功能实现类GamePanel
- 存放素材类Data
启动游戏类
import javax.swing.JFrame;
public class StartGame {
public StartGame() {
}
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setBounds(10, 10, 900, 720);
frame.setResizable(false);
frame.setVisible(true);
frame.setDefaultCloseOperation(3);
frame.add(new GamePanel()); // 开启游戏
frame.setResizable(false);
}
}
功能实现类
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.Random;
import javax.swing.JPanel;
import javax.swing.Timer;
public class GamePanel extends JPanel implements KeyListener, ActionListener {
int length;
int[] snakeX = new int[600];
int[] snakeY = new int[500]; //大小要足够存储所有坐标
String fx; //键盘输入内容判断
int foodx; //食物的坐标
int foody;
Random random = new Random();
int score; //积分
boolean isStart = false; //游戏开始判定
boolean isFail = false; //游戏失败判定
Timer timer = new Timer(100, this); //定时器初始化 100ms一次
public GamePanel() {
this.init();
this.setFocusable(true); //加入焦点事件
this.addKeyListener(this); //加入键盘监听事件
}
public void init() {
this.length = 3;
this.snakeX[0] = 100;
this.snakeY[0] = 100; //蛇脑袋的初始坐标
this.snakeX[1] = 75;
this.snakeY[1] = 100; //蛇第一节身体的初始坐标
this.snakeX[2] = 50;
this.snakeY[2] = 100; //蛇第二节身体的初始坐标
this.fx = "R"; //右转
this.timer.start(); //开启计时器
this.foodx = 25 + 25 * this.random.nextInt(34); //食物坐标初始化
this.foody = 75 + 25 * this.random.nextInt(24);
this.score = 0; 积分初始化
}
protected void paintComponent(Graphics g) {
super.paintComponent(g); //定义画笔,画全部元素
Data.header.paintIcon(this, g, 25, 11); //从Data类中取得蛇脑袋图片
this.setBackground(Color.WHITE); //设置面板的白色背景
g.fillRect(25, 75, 850, 600); //画一个矩形作为蛇运动区域
g.setColor(Color.WHITE);
g.setFont(new Font("微软雅黑", 1, 18)); //设置界面中的长度和积分栏的字符串属性
g.drawString("长度:" + this.length, 750, 35);
g.drawString("分数::" + this.score, 750, 50);
Data.food.paintIcon(this, g, this.foodx, this.foody);//取得食物图片,放置在坐标上
//键盘输入事件判断
if (this.fx.equals("R")) {
Data.right.paintIcon(this, g, this.snakeX[0], this.snakeY[0]);
} else if (this.fx.equals("L")) {
Data.left.paintIcon(this, g, this.snakeX[0], this.snakeY[0]);
} else if (this.fx.equals("U")) {
Data.up.paintIcon(this, g, this.snakeX[0], this.snakeY[0]);
} else if (this.fx.equals("D")) {
Data.down.paintIcon(this, g, this.snakeX[0], this.snakeY[0]);
}
//取得蛇身体图片绘制在坐标上
for(int i = 1; i < this.length; ++i) {
Data.body.paintIcon(this, g, this.snakeX[i], this.snakeY[i]);
}
//游戏开始的判断
if (!this.isStart) {
g.setColor(Color.WHITE);
g.setFont(new Font("微软雅黑", 1, 40));
g.drawString("按下空格,开始游戏!", 300, 300);
} //游戏开始前,显示字符串
if (this.isFail) {
g.setColor(Color.RED);
g.setFont(new Font("微软雅黑", 1, 40));
g.drawString("游戏失败!按下空格重新开始!", 300, 300);
} //游戏失败后,显示字符串
}
//键盘监听方法
public void keyPressed(KeyEvent e) {
int code = e.getKeyCode();
//游戏失败和重启
if (code == 32) {
if (this.isFail) {
this.isFail = false;
this.init();
} else {
this.isStart = !this.isStart;
}
}
this.repaint();
//键盘输入控制方向
if (code == 38) {
this.fx = "U";
} else if (code == 40) {
this.fx = "D";
} else if (code == 39) {
this.fx = "R";
} else if (code == 37) {
this.fx = "L";
}
}
//动作事件监听方法
public void actionPerformed(ActionEvent e) {
//蛇吃食物的监听和操作
if (this.isStart && !this.isFail) {
if (this.snakeX[0] == this.foodx && this.snakeY[0] == this.foody) {
++this.length;
this.score += 10;
this.foodx = 25 + 25 * this.random.nextInt(34);
this.foody = 75 + 25 * this.random.nextInt(24);
}
int i;
for(i = this.length - 1; i > 0; --i) {
this.snakeX[i] = this.snakeX[i - 1];
this.snakeY[i] = this.snakeY[i - 1];
}
//蛇向各个方向移动的动态实现
if (this.fx == "R") {
this.snakeX[0] += 25;
if (this.snakeX[0] > 850) {
this.snakeX[0] = 25;
}
} else if (this.fx == "L") {
this.snakeX[0] -= 25;
if (this.snakeX[0] < 25) {
this.snakeX[0] = 850;
}
} else if (this.fx == "D") {
this.snakeY[0] += 25;
if (this.snakeY[0] > 650) {
this.snakeY[0] = 75;
}
} else if (this.fx == "U") {
this.snakeY[0] -= 25;
if (this.snakeY[0] < 75) {
this.snakeY[0] = 650;
}
}
//游戏失败判断
for(i = 1; i < this.length; ++i) {
if (this.snakeX[0] == this.snakeX[i] && this.snakeY[0] == this.snakeY[i]) {
this.isFail = true;
}
}
this.repaint();
}
}
//下面的两个方法是用不到的,为保证完整性需要保留
public void keyReleased(KeyEvent e) {
}
public void keyTyped(KeyEvent e) {
}
}
Data类
public class Data {
public static URL headerURL = Data.class.getResource("statics/header.png");
public static ImageIcon header;
public static URL upURL;
public static URL downURL;
public static URL rightURL;
public static URL leftURL;
public static ImageIcon up;
public static ImageIcon down;
public static ImageIcon right;
public static ImageIcon left;
public static URL bodyURL;
public static URL foodURL;
public static ImageIcon body;
public static ImageIcon food;
public Data() {
}
static {
header = new ImageIcon(headerURL);
upURL = Data.class.getResource("statics/up.png");
downURL = Data.class.getResource("statics/down.png");
rightURL = Data.class.getResource("statics/right.png");
leftURL = Data.class.getResource("statics/left.png");
up = new ImageIcon(upURL);
down = new ImageIcon(downURL);
right = new ImageIcon(rightURL);
left = new ImageIcon(leftURL);
bodyURL = Data.class.getResource("statics/body.png");
foodURL = Data.class.getResource("statics/food.png");
body = new ImageIcon(bodyURL);
food = new ImageIcon(foodURL);
}
}
需要注意,所有的图片都存放在statics文件夹中,statics文件夹需要放在这三个类同层的文件夹下。