分析目录结构:
引入statics图片资源文件
//头部---(1)获取图片的路径 (2)new一个ImageIcon对象,将图片放到游戏里
public static URL headerURL = Data.class.getResource("statics/header.jpg");
public static ImageIcon header = new ImageIcon(headerURL);
新建一个窗口 JFrame 窗体类,给它一个宽和高,分别是长900,宽720的大小,给窗口设置为不可调整,setResizable()方法设置为false,并且将窗口展示出来。具体启动类的代码如下
public static void main(String[] args) {
JFrame frame = new JFrame("Steven-贪吃蛇小游戏");// new一个JFrame 窗体对象
frame.setResizable(false);//窗口大小不可变
frame.setBounds(10,10,900,720);
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);// 设置关闭事件,游戏可以关闭
frame.setVisible(true);//将窗口展示出来
}
先将需要的元素定义,再将定义的元素画到游戏面板上,最终让其动起来(利用键盘和事件监听)。
(1)定义属性:
游戏需要的基本元素:小蛇的长度、方向及位置,食物的位置。
利用布尔类型的属性来判断游戏是否开始及失败。
最后要让小蛇动起来,就需要有一个时间定时器Timer类。
代码如下:
int length;//蛇的长度
int[] snakeX = new int[600];//蛇的x坐标
int[] snakeY = new int[500];//蛇的y坐标
String dirction;//蛇的方向
boolean isStart = false;//游戏是否开始:默认是不开始
Timer timer = new Timer(100,this);//定时器:100毫秒执行一次
int foodX;//食物的x坐标
int foodY;//食物的y坐标
Random random = new Random();
boolean isFail = false;//游戏是否结束(失败)
int score;//游戏的分数
(2)初始化方法:
将定义的属性进行初始化,且游戏重新开始时,可直接调用初始化方法
代码如下:
public void init() {
length = 3;//初始小蛇有三节,包括小脑袋
//初始化开始的蛇,给蛇定位
snakeX[0] = 100;snakeY[0] = 100;//脑袋的坐标
snakeX[1] = 75;snakeY[1] = 100;//第一个身体的坐标
snakeX[2] = 50;snakeY[2] = 100;//第二个身体的坐标
dirction = "R";//初始方向向右
//把食物随机分布在界面上
foodX = 25 + 25*random.nextInt(34);
foodY = 75 + 25*random.nextInt(24);
score = 0;//初始化分数为零
}
创建GamePanel类继承JPanel类我们所有的游戏画面全部都是由画布组件来提供的,paintComponent方法这是整个游戏的核心方法,没有整个方法的话一切都是空白,这个画布上的所有图像都是依赖于paintComponent()方法的。
写入paintComponent方法,然后设置面板的背景颜色,绘制头部信息区域,食物,小蛇,游戏提示,失败判断,还有游戏区域等等全部添加上去。
@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.yellow);
g.setFont(new Font("微软雅黑", Font.BOLD, 18));//设置字体
g.drawString("长度:" + length, 750, 30);
g.drawString("分数:" + score, 750, 50);
//绘制食物
Data.food.paintIcon(this, g, foodX, foodY);
//绘制小蛇
if(dirction.equals("R")) {
Data.right.paintIcon(this, g, snakeX[0], snakeY[0]);
}else if(dirction.equals("L")) {
Data.left.paintIcon(this, g, snakeX[0], snakeY[0]);
}else if(dirction.equals("U")) {
Data.up.paintIcon(this, g, snakeX[0], snakeY[0]);
}else if(dirction.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("失败!按下空格重新开始...", 300, 300);
}
}
小蛇的移动以及吃食物问题:我们需要引入键盘监听和事件监听的接口。
public class GamePanel extends JPanel implements KeyListener,ActionListener{}
首先判断游戏状态是否为为开始,如果是开始那么小蛇就要动,小蛇脑袋先通过键盘监听事件获得要运动的方向,再通过事件监听,实现在此方向上往前移动一个格。
snakeX[0]+25、snakeX[0]-25、snakeY[0]+25、snakeY[0]-25
以上为小蛇头部分别向右、左、下、上移动的四种情况。
接下来就是小蛇身体,小蛇每移动一位,他的后一位就要往前移动,相当于代替前一位。
for(int i=length-1 ; i>0 ; i--) {
snakeX[i] = snakeX[i-1];
snakeY[i] = snakeY[i-1];
}
然后考虑吃食物,当蛇和头的食物坐标一样时,就算吃到了食物,吃到食物后就让长度加一。对应的分数也要增加,默认增加10分。吃到食物后食物不可能还在原地不动吧?所以这个时候要重新引入食物的随机分布,随机让食物出现在游戏界面的任意位置。
foodX = 25 + 25*random.nextInt(34);
foodY = 75 + 25*random.nextInt(24);
游戏结束:那结束怎么判断,当然是头和身体撞到一起就失败了嘛,同样的道理只要移动的过程中满足
snakeX[0] == snakeX[i] && snakeY[0] == snakeY[i];
就说明游戏结束。
注:小蛇不能反向移动,这是我第一次尝试出现的Bug,解决办法就是在键盘监听事件中,按键时多加一个条件,使得不能反向。例--键盘监听上转时,条件1为按上键,条件2为此时移动方向不为下:
if(keyCode == KeyEvent.VK_UP && !dirction.equals("D")) {
dirction = "U";
}
最后加上repaint()方法,需要不断的更新页面让动画动起来。
代码如下:
@Override
public void actionPerformed(ActionEvent e) {
if(isStart && isFail == false) {//如果游戏是开始状态,就让小蛇动起来
//吃食物
if(snakeX[0] == foodX && snakeY[0] == foodY) {
length++;//长度加一
score = score + 10;//分数加十
//重新随机生成食物
foodX = 25 + 25*random.nextInt(34);
foodY = 75 + 25*random.nextInt(24);
}
//移动
for(int i=length-1 ; i>0 ; i--) {//后一节移到前一节的位置
snakeX[i] = snakeX[i-1];
snakeY[i] = snakeY[i-1];
}
//走向
if(dirction == "R") {
snakeX[0] = snakeX[0]+25;
if(snakeX[0] > 850) {snakeX[0] = 25;}//边界判断
}else if(dirction == "L") {
snakeX[0] = snakeX[0]-25;
if(snakeX[0] < 25) {snakeX[0] = 850;}//边界判断
}else if(dirction == "U") {
snakeY[0] = snakeY[0]-25;
if(snakeY[0] < 75) {snakeY[0] = 650;}//边界判断
}else if(dirction == "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;
}
}
repaint();//重画页面
}
timer.start();//定时器开启
}
键盘按下方法如下:
@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 && !dirction.equals("D")) {
dirction = "U";
}else if(keyCode == KeyEvent.VK_DOWN && !dirction.equals("U")) {
dirction = "D";
}else if(keyCode == KeyEvent.VK_LEFT && !dirction.equals("R")) {
dirction = "L";
}else if(keyCode == KeyEvent.VK_RIGHT && !dirction.equals("L")) {
dirction = "R";
}
}
最后的最后,我们需要把我们自己编写好的画布背景添加到主程序类中。
frame.add(new GamePanel());
package com.snake;
import java.net.URL;
import javax.swing.*;
public class Data {
//头部---(1)获取图片的路径 (2)new一个ImageIcon对象,将图片放到游戏里
public static URL headerURL = Data.class.getResource("statics/header.jpg");
public static ImageIcon header = new ImageIcon(headerURL);
//上下左右
public static URL upURL = Data.class.getResource("statics/up.jpg");
public static ImageIcon up = new ImageIcon(upURL);
public static URL downURL = Data.class.getResource("statics/down.jpg");
public static ImageIcon down = new ImageIcon(downURL);
public static URL leftURL = Data.class.getResource("statics/left.jpg");
public static ImageIcon left = new ImageIcon(leftURL);
public static URL rightURL = Data.class.getResource("statics/right.jpg");
public static ImageIcon right = new ImageIcon(rightURL);
//身体
public static URL bodyURL = Data.class.getResource("statics/body.jpg");
public static ImageIcon body = new ImageIcon(bodyURL);
//食物
public static URL foodURL = Data.class.getResource("statics/food.jpg");
public static ImageIcon food = new ImageIcon(foodURL);
}
package com.snake;
import java.awt.*;
import javax.swing.*;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
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坐标
int[] snakeY = new int[500];//蛇的y坐标
String dirction;//蛇的方向
boolean isStart = false;//游戏是否开始:默认是不开始
Timer timer = new Timer(100,this);//定时器:100毫秒执行一次
int foodX;//食物的x坐标
int foodY;//食物的y坐标
Random random = new Random();
boolean isFail = false;//游戏是否结束(失败)
int score;//游戏的分数
//构造器
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;//第二个身体的坐标
dirction = "R";//初始方向向右
//把食物随机分布在界面上
foodX = 25 + 25*random.nextInt(34);
foodY = 75 + 25*random.nextInt(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.yellow);
g.setFont(new Font("微软雅黑", Font.BOLD, 18));//设置字体
g.drawString("长度:" + length, 750, 30);
g.drawString("分数:" + score, 750, 50);
//绘制食物
Data.food.paintIcon(this, g, foodX, foodY);
//绘制小蛇
if(dirction.equals("R")) {
Data.right.paintIcon(this, g, snakeX[0], snakeY[0]);
}else if(dirction.equals("L")) {
Data.left.paintIcon(this, g, snakeX[0], snakeY[0]);
}else if(dirction.equals("U")) {
Data.up.paintIcon(this, g, snakeX[0], snakeY[0]);
}else if(dirction.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("失败!按下空格重新开始...", 300, 300);
}
}
//键盘监听事件
@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 && !dirction.equals("D")) {
dirction = "U";
}else if(keyCode == KeyEvent.VK_DOWN && !dirction.equals("U")) {
dirction = "D";
}else if(keyCode == KeyEvent.VK_LEFT && !dirction.equals("R")) {
dirction = "L";
}else if(keyCode == KeyEvent.VK_RIGHT && !dirction.equals("L")) {
dirction = "R";
}
}
//事件监听---需要通过固定事件来刷新,1秒10次
@Override
public void actionPerformed(ActionEvent e) {
if(isStart && isFail == false) {//如果游戏是开始状态,就让小蛇动起来
//吃食物
if(snakeX[0] == foodX && snakeY[0] == foodY) {
length++;//长度加一
score = score + 10;//分数加十
//重新随机生成食物
foodX = 25 + 25*random.nextInt(34);
foodY = 75 + 25*random.nextInt(24);
}
//移动
for(int i=length-1 ; i>0 ; i--) {//后一节移到前一节的位置
snakeX[i] = snakeX[i-1];
snakeY[i] = snakeY[i-1];
}
//走向
if(dirction == "R") {
snakeX[0] = snakeX[0]+25;
if(snakeX[0] > 850) {snakeX[0] = 25;}//边界判断
}else if(dirction == "L") {
snakeX[0] = snakeX[0]-25;
if(snakeX[0] < 25) {snakeX[0] = 850;}//边界判断
}else if(dirction == "U") {
snakeY[0] = snakeY[0]-25;
if(snakeY[0] < 75) {snakeY[0] = 650;}//边界判断
}else if(dirction == "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;
}
}
repaint();//重画页面
}
timer.start();//定时器开启
}
@Override
public void keyTyped(KeyEvent e) {}
@Override
public void keyReleased(KeyEvent e) {}
}
package com.snake;
import javax.swing.*;
public class StartGame {
public static void main(String[] args) {
JFrame frame = new JFrame("Steven-贪吃蛇小游戏");// new一个JFrame 窗体对象
frame.setResizable(false);//窗口大小不可变
frame.setBounds(10,10,900,720);
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);// 设置关闭事件,游戏可以关闭
frame.add(new GamePanel()); //通常游戏界面都应该在面板上
frame.setVisible(true);//将窗口展示出来
}
}
作为一个外行人,对我来说还是蛮有挑战性的,不过庆幸的是终于给它做出来了!!
真的很考验自己的逻辑思维能力,希望自己能在自己喜欢的事业上坚持下去,并且越走越远!
感谢大家的支持!
原文:https://www.cnblogs.com/StevenPark/p/14725216.html