实现效果
代码架构
主要分为三个类:
- StartGame(游戏启动类:用于设置JFrame布局属性);
- GamePanel(游戏面板类:定义数据、初始化方法、画笔、监听事件);
- Data(素材类:用于获取素材路径)
实现代码
StartGame.class
package com.huang.Snake;
import javax.swing.*;
//游戏主启动类
public class StartGame {
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setVisible(true);
frame.setResizable(false); //设置界面不可拉伸缩放
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.setBounds(10,10,900,720);
//游戏界面放在面板上
frame.add(new GamePanel());
}
}
GamePanel.class
package com.huang.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坐标 25*25
int[] snakeY = new int[500]; //蛇的y坐标 25*25
String fx; //蛇的方向
//游戏状态判断
boolean isStart = false; //设置游戏当前状态(开始/暂停),默认为停止
boolean isFail = false; //设置游戏结果状态(是否失败),默认为false
int score; //游戏成绩
//定义食物的数据
int foodX;
int foodY;
Random random = new Random(); //产生随机数
//定时器
Timer timer = new Timer(100,this); //100毫秒执行一次
//构造器
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; //第二节身体
fx = "R";
//以上:将蛇画在主体界面的第三行、第二列(向右)
//食物随机放置在界面上
foodX = 25+25*random.nextInt(34); //850÷25=34
foodY = 75+25*random.nextInt(24); //600÷25=24
//初始化积分
score = 0;
}
//绘制面板
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g); //清屏
this.setBackground(Color.WHITE); //大面板
//绘制静态面板
Data.header.paintIcon(this,g,25,11); //头部广告栏
g.fillRect(25,75,850,600); //主体界面(黑色部分)
//画积分
g.setColor(Color.WHITE);
g.setFont(new Font("微软雅黑",Font.BOLD,18));
g.drawString("长度 "+length,750,30);
g.drawString("积分 "+score,750,55);
//画食物
Data.food.paintIcon(this,g,foodX,foodY);
//画蛇
if(fx.equals("R")){
Data.right.paintIcon(this,g,snakeX[0],snakeY[0]);
}else if(fx.equals("L")){
Data.left.paintIcon(this,g,snakeX[0],snakeY[0]);
}else if(fx.equals("U")){
Data.up.paintIcon(this,g,snakeX[0],snakeY[0]);
}else if(fx.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]); //身体坐标
}
//游戏开始状态(中间白色大字)
if(isStart==false){
g.setColor(Color.WHITE);
g.setFont(new Font("微软雅黑",Font.BOLD,40));
g.drawString("按下空格开始游戏",300,300);
}
//游戏结束状态(中间红色大字)
if(isFail){
g.setColor(Color.RED);
g.setFont(new Font("微软雅黑",Font.BOLD,40));
g.drawString("游戏失败,按下空格重新开始",250,300);
}
}
//键盘监听事件
@Override
public void keyPressed(KeyEvent e) {
int keyCode = e.getKeyCode(); //获取键盘按键
if(keyCode == KeyEvent.VK_SPACE){ //如果是空格,先判断是游戏开始时还是游戏结束时
if(isFail==true){ //如果游戏失败isFail==true
isFail = false; //重定义isFail的值[重新开始]
}else{ //如果isFail==false,即按下空格时为游戏开始时
isStart = !isStart; //进行取反操作[开始/停止]
}
repaint();
}
//小蛇移动
// if(keyCode == KeyEvent.VK_UP){
// fx = "U";
// }else if(keyCode == KeyEvent.VK_DOWN){
// fx = "D";
// }else if(keyCode == KeyEvent.VK_LEFT){
// fx = "L";
// }else if(keyCode == KeyEvent.VK_RIGHT){
// fx = "R";
// }
if(keyCode == KeyEvent.VK_UP && !fx.equals("D")){ //控制小蛇移动并设置不能回头
fx = "U";
}else if(keyCode == KeyEvent.VK_DOWN && !fx.equals("U")){
fx = "D";
}else if(keyCode == KeyEvent.VK_LEFT && !fx.equals("R")){
fx = "L";
}else if(keyCode == KeyEvent.VK_RIGHT && !fx.equals("L")){
fx = "R";
}
}
//事件监听:ActionListener(用来写定时器)
@Override
public void actionPerformed(ActionEvent e) {
if(isStart && isFail==false){ //如果游戏为开始状态
//身体移动
for (int i = length-1; i > 0 ; i--) { //i=length-1,即去掉头部后的长度
snakeX[i] = snakeX[i-1]; //右移时,snakeX[1] = snakeX[0]
snakeY[i] = snakeY[i-1];
}
//吃食物
if(snakeX[0]==foodX && snakeY[0]==foodY){ //蛇的头部与食物重合即为吃到食物
//蛇的长度+1
length++;
//积分+10
score += 10;
//需要再次将食物随机地放置在界面上
foodX = 25+25*random.nextInt(34); //850÷25=34
foodY = 75+25*random.nextInt(24); //600÷25=24
}
//头部走向
if(fx.equals("R")){
snakeX[0] = snakeX[0]+25; //头部右移25个单位(即一格)
if(snakeX[0]>850){ //边界判断:如果蛇右移到了边界,则回到左边
snakeX[0] = 25;
}
}else if(fx.equals("L")){
snakeX[0] = snakeX[0]-25; //头部左移25个单位(即一格)
if(snakeX[0]<25){snakeX[0] = 850;} //边界判断
}else if(fx.equals("U")){
snakeY[0] = snakeY[0]-25; //向上移动应该是-25
if(snakeY[0]<75){snakeY[0] = 650;} //边界判断
}else if(fx.equals("D")){
snakeY[0] = snakeY[0]+25; //向下移动
if(snakeY[0]>650){snakeY[0] = 75;} //边界判断
}
//失败判定,撞到自己即失败
for (int i = 1; i < length; i++) { //头部与身体的某一节坐标重合,即撞到自己
if(snakeX[0] == snakeX[i] && snakeY[0]==snakeY[i]){
isFail=true;
init();
}
}
repaint(); //重画页面
}
timer.start(); //定时器开启
}
@Override
public void keyReleased(KeyEvent e) { }
@Override
public void keyTyped(KeyEvent e) { }
}
Data.class
注意:此时的素材资源放在Snake包下的static包中,不同的路径需自行改动代码
package com.huang.Snake;
import javax.swing.*;
import java.net.URL;
//素材中心
public class Data {
public static URL headerURL = Data.class.getResource("static/header.png");
public static ImageIcon header = new ImageIcon(headerURL);
public static URL upURL = Data.class.getResource("static/up.png");
public static URL downURL = Data.class.getResource("static/down.png");
public static URL leftURL = Data.class.getResource("static/left.png");
public static URL rightURL = Data.class.getResource("static/right.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 URL bodyURL = Data.class.getResource("static/body.png");
public static ImageIcon body = new ImageIcon(bodyURL);
public static URL foodURL = Data.class.getResource("static/food.png");
public static ImageIcon food = new ImageIcon(foodURL);
}
素材资源
将项目打成jar包
即可生成jar包!
最后说明:
代码无误、无报错的情况下,开始游戏时如果按下空格键没有反应,请先按下Tab键试试。