文章目录
(结尾有成果演示)
(结尾有成果演示)
(结尾有成果演示)
贪吃蛇(结尾有成果演示)
1.项目介绍
能写出简单的界面,实现键盘蛇的显现,移动与吃食物的可视化。并加入时间,暂停,碰撞判定等设置。
2.项目分步
1.写出简单的可视化界面(包括棋盘信息,静止的蛇);
2.让蛇化静为动,包括蛇的移动设置和方向,定时器的刷新界面设置与监听设置;
3.写出在棋盘上随机生成的食物,并实现蛇吃食物的过程;
4.实现蛇与自身本体的碰撞与和墙壁的碰撞结果。
3.代码实现
(1).可视化界面的实现
<1>.可视化边框的初始化函数(GamePanel类)
添加属性画板(包括set和get方法,此处省略)
//面板
private JPanel jp;
添加边框初始化方法
private void initFrame(){
//设置窗体的大小
setSize(800,800);
//设置窗体的位置
setLocation(400,20);
//设置关闭按钮的作用(退出程序)
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//设置窗体大小的不可变性
setResizable(false);
//设置窗体的标题
setTitle("SnakeGame from A Active Boy");
}
<2>.面板内容的初始化
//初始化棋盘方法(包括棋盘)
public void initPanel(){
jp = new JPanel(){
@Override
public void paint(Graphics g) {
//画横竖交叉的棋盘
for (int i = 0;i < 40;i++){
g.drawLine(i*20,0,i*20,800);
g.drawLine(0,i*20,800,i*20);
}
}
};
add(jp);
}
<3>.将初始化加入GamePanel的构造方法中
//构造方法
public GamePanel() {
//初始化面板
initFrame();
//初始化棋盘
initPanel();
}
<4>.主函数调用函数(StartGame类)
public static void main(String[] args) {
//初始化对象的创建(目的是执行GamePanel类的构造方法)
new GamePanel().setVisible();
}
<5>.基础可视化实现效果
<6>.绘制一条静止的蛇
在面板类中加入蛇属性
//蛇
private Snake snake;
在构造方法中添加初始化蛇的方法
//构造方法
public GamePanel() throws HeadlessException{
//初始化面板
initFrame();
//初始化棋盘
initPanel();
//初始化蛇
initSnake();
}
初始化蛇的方法
//初始化一条蛇的方法
public void initSnake(){
snake = new Snake();
}
在画板上画蛇
//初始化棋盘方法(包括棋盘和静态蛇)
public void initPanel(){
jp = new JPanel(){
@Override
public void paint(Graphics g) {
//清空界面
g.clearRect(0,0,800,800);
//画横竖交叉的棋盘
for (int i = 0;i < 40;i++){
g.drawLine(i*20,0,i*20,800);
g.drawLine(0,i*20,800,i*20);
}
//绘制一条静态的蛇
//获得蛇身体的集合
LinkedList<Node> body = snake.getBody();
//遍历蛇身体集合,给每个元素填充即可
for (Node node:body){
g.fillRect(node.getX()*20,node.getY()*20,20,20);
}
}
};
add(jp);
}
<7>.主函数测试
public static void main(String[] args) {
//初始化对象的创建(目的是执行GamePanel类的构造方法)
new GamePanel().setVisible();
}
<8>.测试效果图
(2).让蛇化静为动的实现
<1>.蛇的移动
创建一个枚举类型存储蛇的移动方向
//蛇的方向
public enum Direction {
UP,DOWN,LEFT,RIGHT;
}
在蛇类中添加方向属性
//蛇移动的方向(枚举)
private Direction direction;
在蛇类的构造方法中初始化蛇的方向
//构造方法
public Snake(){
//初始化身体的结点集合
body = new LinkedList <>();
//初始化蛇的头和身体结点
for (int i = 19;i > 15;i--){
body.addFirst(new Node(i,20));
}
//初始化蛇的方向
direction = Direction.LEFT;
}
蛇的移动方法实现
public void move(){
//获取蛇头结点
Node head = body.getFirst();
switch (direction){
case UP :
body.addFirst(new Node(body.getFirst().getX(),body.getFirst().getY()-1));
break;
case DOWN :
body.addFirst(new Node(body.getFirst().getX(),body.getFirst().getY()+1));
break;
case LEFT :
body.addFirst(new Node(body.getFirst().getX()-1,body.getFirst().getY()));
break;
case RIGHT :
body.addFirst(new Node(body.getFirst().getX()+1,body.getFirst().getY()));
}
//删除尾结点
body.removeLast();
//重新获取头结点
head = body.getFirst();
}
<2>.定时器和定时器任务的引入实现
添加定时器的属性(GamePanel类)
//定时器
private Timer timer;
定时器的初始化方法实现
private void initTimer() {
//创建定时器对象
timer = new Timer();
//创建定时器任务对象
TimerTask timerTask = new TimerTask() {
@Override
public void run() {
//蛇移动
snake.move();
//重绘棋盘
jp.repaint();
}
};
//每100ms执行一次定时任务
timer.scheduleAtFixedRate(timerTask,5,100);
}
将初始化定时器方法加入构造方法
//构造方法
public GamePanel() throws HeadlessException{
//初始化面板
initFrame();
//初始化棋盘
initPanel();
//初始化蛇
initSnake();
//初始化定时器
initTimer();
}
<3>设置键盘的监听
实现键盘的监听功能方法
//设置键盘监听的方法
private void setKeyListener() {
addKeyListener(new KeyAdapter() {
//键盘按下时自动执行以下方法
@Override
public void keyPressed(KeyEvent e) {
switch (e.getKeyCode()){
case KeyEvent.VK_UP:
if (snake.getDirection()!= Direction.DOWN){
snake.setDirection(Direction.UP);
}
break;
case KeyEvent.VK_DOWN:
if (snake.getDirection()!= Direction.UP){
snake.setDirection(Direction.DOWN);
}
break;
case KeyEvent.VK_LEFT:
if (snake.getDirection()!= Direction.RIGHT){
snake.setDirection(Direction.LEFT);
}
break;
case KeyEvent.VK_RIGHT:
if (snake.getDirection()!= Direction.LEFT){
snake.setDirection(Direction.RIGHT);
}
break;
}
}
});
}
将监听加入构造方法
//构造方法
public GamePanel() throws HeadlessException{
//初始化面板
initFrame();
//初始化棋盘
initPanel();
//初始化蛇
initSnake();
//初始化定时器
initTimer();
//设置键盘的监听
setKeyListener();
}
(3).食物功能的实现
<1>.食物结点的显现
添加食物属性(GamePanel类)
//食物
private Node food;
在结点类中加入生成随机数方法(Node类)
//随机生成位置的方法
public void random(){
//创建随机类对象
Random r = new Random();
//随机生成横坐标
this.x = r.nextInt(40);
//随机生成纵坐标
this.y = r.nextInt(40);
}
食物的位置初始化方法
//食物的初始化
private void initFood(){
//新建一个食物结点对象
food = new Node();
//得到随机食物的位置
food.random();
}
在构造方法中添加食物初始化
//构造方法
public GamePanel() throws HeadlessException{
//初始化面板
initFrame();
//初始化棋盘
initPanel();
//初始化蛇
initSnake();
//初始化定时器
initTimer();
//设置键盘的监听
setKeyListener();
//食物的初始化方法
initFood();
}
食物的绘制(GamePanel类)
//初始化棋盘方法(包括棋盘和静态蛇和食物)
public void initPanel(){
jp = new JPanel(){
@Override
public void paint(Graphics g) {
//清空棋盘
g.clearRect(0,0,800,800);
//画横竖交叉的棋盘
for (int i = 0;i < 40;i++){
g.drawLine(i*20,0,i*20,800);
g.drawLine(0,i*20,800,i*20);
}
//绘制一条静态的蛇
//获得蛇身体的集合
LinkedList<Node> body = snake.getBody();
//遍历蛇身体集合,给每个元素填充即可
for (Node node:body){
g.fillRect(node.getX()*20,node.getY()*20,20,20);
}
//绘制食物
g.fillRect(food.getX()*20,food.getY()*20,20,20);
}
};
add(jp);
}
结果测试
<2>.蛇吃食物的动作实现
蛇吃食物的方法实现
//蛇吃食物的方法
public void eat(Node food){
switch (direction){
case UP:
body.addFirst(new Node(body.getFirst().getX(),body.getFirst().getY()-1));
break;
case DOWN:
body.addFirst(new Node(body.getFirst().getX(),body.getFirst().getY()+1));
break;
case LEFT:
body.addFirst(new Node(body.getFirst().getX()-1,body.getFirst().getY()));
break;
case RIGHT:
body.addFirst(new Node(body.getFirst().getX()+1,body.getFirst().getY()));
break;
}
}
定时器任务中加入蛇吃食物的判定
//初始化定时器的方法
private void initTimer() {
//创建定时器对象
timer = new Timer();
//创建定时器任务对象
TimerTask timerTask = new TimerTask() {
@Override
public void run() {
//蛇移动
snake.move();
//判断蛇移动后是否吃到食物
if (snake.getBody().getFirst().getX()== food.getX()&&snake.getBody().getFirst().getY() == food.getY()){
//调用蛇的吃食物方法
snake.eat(food);
//随机生成一枚食物
food.random();
}
//重绘棋盘
jp.repaint();
}
};
//每100ms执行一次定时任务
//一定要有延迟!不然会报食物初始化失败的空指针异常。
timer.scheduleAtFixedRate(timerTask,5,100);
}
(4).蛇与本体和墙壁的碰撞判定
<1>.蛇的死亡状态添加
在蛇的属性栏添加生死状态(Snake类)
//蛇的生死状态
private boolean isLive;
在蛇的移动方法中加入生死判定
//移动的方法
//蛇的移动仅需要改变蛇的头结点和尾结点即可
public void move(){
if (isLive) {
//获取蛇头结点
Node head = body.getFirst();
switch (direction) {
case UP:
body.addFirst(new Node(head.getX(), head.getY() - 1));
break;
case DOWN:
body.addFirst(new Node(head.getX(), head.getY() + 1));
break;
case LEFT:
body.addFirst(new Node(head.getX() - 1, head.getY()));
break;
case RIGHT:
body.addFirst(new Node(head.getX() + 1, head.getY()));
break;
}
//删除尾结点
body.removeLast();
}
在蛇的移动方法中加入撞墙事件
//重新获取头结点
Node head = body.getFirst();
//判断撞墙事件
if (head.getX()<=0||head.getX()>=40||head.getY()<=0||head.getY()>=40){
isLive = false;
}
在蛇的移动中加入追尾事件
//判断追尾事件
for (int i = 1;i < body.size();i++) {
if (head.getX()==body.get(i).getX()&&head.getY()==body.get(i).getY()){
//System.out.println(body.get(i).getX());
isLive = false;
}
}
(5).完整代码展示(共五个类)
<1>.StartGame类:
//主类,包含主方法,启动游戏类类(.exe)
public class StartGame {
public static void main(String[] args) {
new GamePanel().setVisible(true);
}
}
<2>.GamePanel类:
import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.util.LinkedList;
import java.util.Timer;
import java.util.TimerTask;
public class GamePanel extends JFrame {
//蛇
private Snake snake;
//面板
private JPanel jp;
//定时器
private Timer timer;
//食物
private Node food;
//构造方法
public GamePanel() throws HeadlessException{
//初始化面板
initFrame();
//初始化棋盘
initPanel();
//初始化蛇
initSnake();
//初始化定时器
initTimer();
//设置键盘的监听
setKeyListener();
//食物的初始化方法
initFood();
}
//边框的初始化
//初始化窗体的参数方法
private void initFrame(){
//设置窗体的大小
setSize(800,800);
//设置窗体的位置
setLocation(400,20);
//设置关闭按钮的作用(退出程序)
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//设置窗体大小的不可变性
setResizable(false);
//设置窗体的标题
setTitle("SnakeGame from A Active Boy");
}
//初始化棋盘方法(包括棋盘和静态蛇和食物)
public void initPanel(){
jp = new JPanel(){
@Override
public void paint(Graphics g) {
//清空棋盘
g.clearRect(0,0,800,800);
//画横竖交叉的棋盘
for (int i = 0;i < 40;i++){
g.drawLine(i*20,0,i*20,800);
g.drawLine(0,i*20,800,i*20);
}
//绘制一条静态的蛇
//获得蛇身体的集合
LinkedList<Node> body = snake.getBody();
//遍历蛇身体集合,给每个元素填充即可
for (Node node:body){
g.fillRect(node.getX()*20,node.getY()*20,20,20);
}
//绘制食物
g.fillRect(food.getX()*20,food.getY()*20,20,20);
}
};
add(jp);
}
//初始化一条蛇的方法
public void initSnake(){
snake = new Snake();
}
//初始化定时器的方法
//初始化定时器的方法
private void initTimer() {
//创建定时器对象
timer = new Timer();
//创建定时器任务对象
TimerTask timerTask = new TimerTask() {
@Override
public void run() {
//蛇移动
snake.move();
//蛇吃食物
Node head = snake.getBody().getFirst();
if (head.getX()==food.getX()&&head.getY()==food.getY()){
snake.eat(food);
food.random();
}
//重绘棋盘
jp.repaint();
}
};
//每100ms执行一次定时任务
timer.scheduleAtFixedRate(timerTask,5,100);
}
//设置键盘监听的方法
private void setKeyListener() {
addKeyListener(new KeyAdapter() {
//键盘按下时自动执行以下方法
@Override
public void keyPressed(KeyEvent e) {
switch (e.getKeyCode()){
case KeyEvent.VK_UP:
if (snake.getDirection()!= Direction.DOWN){
snake.setDirection(Direction.UP);
}
break;
case KeyEvent.VK_DOWN:
if (snake.getDirection()!= Direction.UP){
snake.setDirection(Direction.DOWN);
}
break;
case KeyEvent.VK_LEFT:
if (snake.getDirection()!= Direction.RIGHT){
snake.setDirection(Direction.LEFT);
}
break;
case KeyEvent.VK_RIGHT:
if (snake.getDirection()!= Direction.LEFT){
snake.setDirection(Direction.RIGHT);
}
break;
}
}
});
}
//食物的初始化
private void initFood(){
//新建一个食物结点对象
food = new Node();
//得到随机食物的位置
food.random();
}
}
<3>.Snake类:
import java.util.LinkedList;
public class Snake {
//蛇的身体与头用一个链表集合储存
private LinkedList <Node> body;
//蛇移动的方向(枚举)
private Direction direction;
//蛇的生死状态
private boolean isLive;
//构造方法
public Snake(){
//初始化身体的结点集合
body = new LinkedList <>();
//初始化蛇的头和身体结点
for (int i = 19;i > 15;i--){
body.addFirst(new Node(i,20));
}
//初始化蛇的方向
direction = Direction.LEFT;
//初始化蛇的生死
isLive = true;
}
//移动的方法
//蛇的移动仅需要改变蛇的头结点和尾结点即可
public void move(){
if (isLive) {
//获取蛇头结点
Node head = body.getFirst();
switch (direction) {
case UP:
body.addFirst(new Node(head.getX(), head.getY() - 1));
break;
case DOWN:
body.addFirst(new Node(head.getX(), head.getY() + 1));
break;
case LEFT:
body.addFirst(new Node(head.getX() - 1, head.getY()));
break;
case RIGHT:
body.addFirst(new Node(head.getX() + 1, head.getY()));
break;
}
//删除尾结点
body.removeLast();
}
//重新获取头结点
Node head = body.getFirst();
//判断撞墙事件
if (head.getX()<0||head.getX()>=40||head.getY()<0||head.getY()>=40){
isLive = false;
}
//判断追尾事件
for (int i = 2;i < body.size();i++) {
if (head.getX()==body.get(i).getX()&&head.getY()==body.get(i).getY()){
//System.out.println(body.get(i).getX());
isLive = false;
}
}
}
//蛇吃食物的方法
public void eat(Node food){
switch (direction){
case UP:
body.addFirst(new Node(body.getFirst().getX(),body.getFirst().getY()-1));
break;
case DOWN:
body.addFirst(new Node(body.getFirst().getX(),body.getFirst().getY()+1));
break;
case LEFT:
body.addFirst(new Node(body.getFirst().getX()-1,body.getFirst().getY()));
break;
case RIGHT:
body.addFirst(new Node(body.getFirst().getX()+1,body.getFirst().getY()));
break;
}
}
//set方法和get方法
public void setBody(LinkedList <Node> body) {
this.body = body;
}
public LinkedList <Node> getBody() {
return body;
}
public Direction getDirection() {
return direction;
}
public void setDirection(Direction direction) {
this.direction = direction;
}
}
<4>.Diraction类(枚举):
//蛇的方向
public enum Direction {
UP,DOWN,LEFT,RIGHT;
}
<5>.Node类:
import java.util.Random;
public class Node {
//结点的横坐标
private int x;
//结点的纵坐标
private int y;
//构造方法
public Node(){
}
public Node(int x,int y){
this.x = x;
this.y = y;
}
//随机生成位置的方法
public void random(){
//创建随机类对象
Random r = new Random();
//随机生成横坐标
this.x = r.nextInt(40);
//随机生成纵坐标
this.y = r.nextInt(40);
}
//Set和Get方法
public void setX(int x) {
this.x = x;
}
public int getX() {
return x;
}
public void setY(int y) {
this.y = y;
}
public int getY() {
return y;
}
}
(6).效果展示:
贪吃蛇项目成果演示
4.拓展内容
<1>.空格键控制游戏的开始与否,并可用于复活死亡的蛇
向蛇的属性栏里添加游戏是否开始变量
//游戏的暂停状态
public boolean isStart;
向构造方法里添加游戏是否开始的初始化
//构造方法
public Snake(){
//初始化身体的结点集合
body = new LinkedList <>();
//初始化蛇的头和身体结点
for (int i = 19;i > 15;i--){
body.addFirst(new Node(i,20));
//System.out.println(body.get(0).getX());
}
//初始化蛇的方向
direction = Direction.LEFT;
//初始化蛇的生死
isLive = true;
//游戏是否开始
isStart = false;
}
在蛇的移动代码里添加判定条件
public void move(){
if (isLive&&isStart) {
(代码省略)
}
}
加入键盘空格键的监听
//设置键盘监听的方法
private void setKeyListener() {
addKeyListener(new KeyAdapter() {
//键盘按下时自动执行以下方法
@Override
public void keyPressed(KeyEvent e) {
switch (e.getKeyCode()) {
(此处代码省略)
case KeyEvent.VK_SPACE:
//如果蛇死了,复活
//如果蛇活着,暂停
if (!snake.isLive){
initSnake();
initFood();
snake.isLive = true;
snake.isStart = true;
}else {
snake.isStart = !snake.isStart;
if (snake.isStart){
System.out.println("游戏已开始!");
}else{
System.out.println("游戏已暂停!");
}
}
break;
}
}
});
<2>.加入计分功能
向蛇的属性中加入得分
//分数
public int score;
向蛇的构造方法中添加分数的初始化
//构造方法
public Snake(){
//初始化身体的结点集合
body = new LinkedList <>();
//初始化蛇的头和身体结点
for (int i = 19;i > 15;i--){
body.addFirst(new Node(i,20));
//System.out.println(body.get(0).getX());
}
//初始化蛇的方向
direction = Direction.LEFT;
//初始化蛇的生死
isLive = true;
//游戏是否开始
isStart = false;
//初始化分数
score = 0;
}
在蛇的移动方法中添加死亡打印分数语句
//移动的方法
//蛇的移动仅需要改变蛇的头结点和尾结点即可
public void move(){
if (isLive&&isStart) {
(代码省略)
}
//死亡时打印分数
if (!isLive){
//打印分数
System.out.println("游戏结束,你的分数为"+score+"。");
}
}
在蛇的吃食物任务中添加加分功能
//初始化定时器的方法
private void initTimer() {
//创建定时器对象
timer = new Timer();
//创建定时器任务对象
TimerTask timerTask = new TimerTask() {
@Override
public void run() {
//蛇移动
snake.move();
//蛇吃食物
Node head = snake.getBody().getFirst();
if (head.getX() == food.getX() && head.getY() == food.getY()) {
snake.eat(food);
food.random();
//分数+1
snake.score++;
}
//重绘棋盘
jp.repaint();
}
};
//每100ms执行一次定时任务
timer.scheduleAtFixedRate(timerTask, 5, 170);
}
完!