贪吃蛇游戏开发
实验要求
①实现贪吃蛇游戏基本功能,屏幕上随机出现一个“食物”,称为豆子。上下左右控制“蛇”的移动,吃到“豆子”以后“蛇”的身体加长一点。
②“蛇”碰到边界或蛇头与蛇身相撞,蛇死亡,游戏结束。
③为游戏设计友好的交互界面;例如欢迎界面,游戏界面,游戏结束界面。要有开始键、暂停键和停止退出的选项。
④对蛇吃到豆子进行分值计算,可以设置游戏速度,游戏音乐等拓展元素。
实验过程
本次实验依然使用Java来完成。一共包括4个大类。
Background类
Background类完成游戏界面的设计。
重要变量定义
private boolean gameover=false; //判断游戏是否结束
private Font fontGameover=new Font("宋体",Font.BOLD,50); //字体属性
private int score=0; //分数
public long beginTime=0; //游戏开始时间
public static final int row=30; //行数
public static final int col=30; //列数
public static final int yard=15; //游戏区域
通过两个for循环画出贪吃蛇游戏的表格背景
for(int i=1;i<row;i++) {
g.drawLine(0, yard*i, col*yard, yard*i);
}
for(int i=1;i<col;i++) {
g.drawLine(yard*i, 0, yard*i, yard*row);
}
创建用于双缓冲的图像,未添加该图像之前,游戏画面会不断闪烁,查阅相关资料之后发现需要添加图像用于双缓冲来避免闪烁。
public void update(Graphics g) {
if(offScreenImage==null) {
offScreenImage=this.createImage(col*yard,row*yard); //创建用于双缓冲的图像,避免图像闪烁
}
Graphics gOff=offScreenImage.getGraphics();
paint(gOff);
g.drawImage(offScreenImage,0,0,null);
}
Snake类
Snake类完成蛇头和蛇身的创建以及相关函数的创建。
重要变量定义
private Node head=null; //头结点
private Node tail=null; //尾结点
private int size=0; //大小
/*
* 设置起始位置和方向
*/
private Node n=new Node(20,30,D.L);
private Background b;
创建私有类Node,通过Node类完成蛇体和蛇身构建
private class Node{
int w=Background.yard;
int h=Background.yard;
int row, col;
D d=D.L;
Node next=null;
Node prev=null;
Node(int row,int col,D d){
this.row=row;
this.col=col;
this.d=d;
}
void draw(Graphics g) {
Color c=g.getColor();
g.setColor(Color.BLACK);
g.fillRect(Background.yard*col, Background.yard*row,w,h);
g.setColor(c);
}
}
addToHead函数实现从头部添加
public void addToHead(){
Node node=null;
switch(head.d) {
case L:
node=new Node(head.row,head.col-1,head.d);
break;
case U:
node=new Node(head.row-1,head.col,head.d);
break;
case R:
node=new Node(head.row,head.col+1,head.d);
break;
case D:
node=new Node(head.row+1,head.col,head.d);
break;
}
head.prev=node;
node.next=head;
head=node;
size++;
}
move函数实现移动
private void move() {
addToHead();
deleteFromTail();
checkDead();
}
checkDead函数判断是否死亡
private void checkDead() {
/*
* 碰触边界
*/
if(head.row<2||head.col<0||head.row>Background.row||head.col>Background.col) {
b.stop();
}
/*
* 头结点与身体相撞
*/
for(Node n=head.next;n!=null;n=n.next) {
if(head.row==n.row&&head.col==n.col) {
b.stop();
}
}
}
eat函数完成吃豆子
public void eat(Food f) {
if(this.getRect().intersects(f.getRect())) {
f.reAppear();
this.addToHead();
b.setScore(b.getScore()+1); //吃到豆子加分
}
}
keyPressed函数接收键盘按键
public void keyPressed(KeyEvent e) {
int key=e.getKeyCode();
switch(key) {
case KeyEvent.VK_LEFT:
//朝向非右即可向左转
if(head.d!=D.R)
head.d=D.L;
break;
case KeyEvent.VK_RIGHT:
//朝向非左即可向右转
if(head.d!=D.L)
head.d=D.R;
break;
case KeyEvent.VK_UP:
//朝向非下即可向上转
if(head.d!=D.D)
head.d=D.U;
break;
case KeyEvent.VK_DOWN:
//朝向非上即可向下转
if(head.d!=D.U)
head.d=D.D;
break;
}
}
Food类
Food类完成豆子的生成。
构造方法完成初始位置生成
public Food() {
this(r.nextInt(Background.row-2)+2,r.nextInt(Background.col));
}
reAppear函数完成豆子的随机生成
public void reAppear() {
this.row=r.nextInt(Background.row-2)+2;
this.col=r.nextInt(Background.col);
}
D类
D类通过enum完成常量定义
public enum D {
L,U,R,D
}
界面展示
以上就是关于第二个实验:贪吃蛇游戏开发的全部记录。
源代码如下
Background类
package software02;
import java.awt.Color;
import java.awt.Font;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.event.KeyListener;
import java.awt.event.*;
public class Background extends Frame{
PaintThread paintThread=new PaintThread();
Snake s=new Snake(this);
Food f=new Food();
//基本属性
private boolean gameover=false; //判断游戏是否结束
private Font fontGameover=new Font("宋体",Font.BOLD,50); //字体属性
private int score=0; //分数
public long beginTime=0; //游戏开始时间
public static final int row=30; //行数
public static final int col=30; //列数
public static final int yard=15; //游戏区域
/*抽象类Image是表示图形图像的所有类的超类
* 必须以特定于平台的方式获取图像
*/
Image offScreenImage= null;
//结束游戏
public void stop() {
gameover=true;
}
private class KeyFunction extends KeyAdapter{
public void keyPressed(KeyEvent e) {
int key=e.getKeyCode();
//暂停游戏
if(key==KeyEvent.VK_F1) {
paintThread.pause=true;
}
//恢复游戏
else if(key==KeyEvent.VK_F9) {
paintThread.pause=false;
}
s.keyPressed(e);
}
}
//设置窗口、部分键盘功能
public void function(){
this.setLocation(90,10); //确定窗口位置
this.setSize(col*yard,row*yard); //确定窗口大小
/*
* 为窗口添加监听器
*/
this.addWindowListener(new WindowAdapter(){
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
this.setVisible(true); //窗口可视化
this.addKeyListener(new KeyFunction());
new Thread(paintThread).start();
}
public void paint(Graphics g) {
Color c=g.getColor(); //获取该图形上下文颜色
g.setColor(Color.GRAY); //指定图形上下文颜色
g.fillRect(0,0,col*yard,row*yard); //填充区域
g.setColor(Color.DARK_GRAY); //换色,区分绘图颜色和文字颜色
/*
* 画出表格
*/
for(int i=1;i<row;i++) {
g.drawLine(0, yard*i, col*yard, yard*i);
}
for(int i=1;i<col;i++) {
g.drawLine(yard*i, 0, yard*i, yard*row);
}
g.setColor(Color.YELLOW); //文字颜色
g.drawString("操作方法:方向键控制移动,F1停止游戏", 10, 40);
g.drawString("当前游戏分数"+getScore(), 10, 60);
g.drawString("耗时:"+(System.currentTimeMillis()-beginTime)/1000,10, 80);
/*
* 判断游戏是否结束
*/
if(gameover) {
g.setFont(fontGameover);
g.drawString("Game Over", 90, 170);
paintThread.pause();
}
if(score>10) {
g.drawString("太强了吧!", 90, 150);
}
if(score>30) {
g.drawString("牛!", 90, 170);
}
g.setColor(c);
s.eat(f);
f.draw(g);
s.draw(g);
}
public void update(Graphics g) {
if(offScreenImage==null) {
offScreenImage=this.createImage(col*yard,row*yard); //创建用于双缓冲的图像,避免图像闪烁
}
Graphics gOff=offScreenImage.getGraphics();
paint(gOff);
g.drawImage(offScreenImage,0,0,null);
}
private class PaintThread implements Runnable{
private boolean running=true;
private boolean pause=false;
public void run() {
while(running) {
if(pause)continue;
else repaint();
try {
Thread.sleep(200);
}catch(InterruptedException e) {
e.printStackTrace();
}
}
}
public void pause() {
this.pause=true;
}
public void gameover() {
running=false;
}
}
public int getScore() {
return score;
}
public void setScore(int score) {
this.score=score;
}
public static void main(String[]args) {
Background b=new Background();
b.beginTime=System.currentTimeMillis();
b.function();
}
}
Snake类
package software02;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.event.KeyEvent;
public class Snake {
private Node head=null; //头结点
private Node tail=null; //尾结点
private int size=0; //大小
/*
* 设置起始位置和方向
*/
private Node n=new Node(20,30,D.L);
private Background b;
//初始化
public Snake(Background b) {
head=n;
tail=n;
size=1;
this.b=b;
}
//结点类
private class Node{
int w=Background.yard;
int h=Background.yard;
int row, col;
D d=D.L;
Node next=null;
Node prev=null;
Node(int row,int col,D d){
this.row=row;
this.col=col;
this.d=d;
}
void draw(Graphics g) {
Color c=g.getColor();
g.setColor(Color.BLACK);
g.fillRect(Background.yard*col, Background.yard*row,w,h);
g.setColor(c);
}
}
public void bianSe(Graphics g) {
Food f=null;
if(eat(f)) {
g.setColor(Color.BLUE);
}
}
//从头部开始添加
public void addToHead(){
Node node=null;
switch(head.d) {
case L:
node=new Node(head.row,head.col-1,head.d);
break;
case U:
node=new Node(head.row-1,head.col,head.d);
break;
case R:
node=new Node(head.row,head.col+1,head.d);
break;
case D:
node=new Node(head.row+1,head.col,head.d);
break;
}
head.prev=node;
node.next=head;
head=node;
size++;
}
public void draw(Graphics g) {
if(size<=0) {
return;
}
move ();
for(Node n=head;n!=null;n=n.next) {
n.draw(g);
}
}
//移动函数
private void move() {
addToHead();
deleteFromTail();
checkDead();
}
private void checkDead() {
/*
* 碰触边界
*/
if(head.row<2||head.col<0||head.row>Background.row||head.col>Background.col) {
b.stop();
}
/*
* 头结点与身体相撞
*/
for(Node n=head.next;n!=null;n=n.next) {
if(head.row==n.row&&head.col==n.col) {
b.stop();
}
}
}
/*
* 尾结点删除
*/
private void deleteFromTail() {
if(size==0) {
return;
}
tail=tail.prev;
tail.next=null;
}
public boolean eat(Food f) {
if(this.getRect().intersects(f.getRect())) {
f.reAppear();
this.addToHead();
b.setScore(b.getScore()+1); //吃到豆子加分
}
return true;
}
private Rectangle getRect() {
return new Rectangle(Background.yard*head.col,Background.yard*head.row,head.w,head.h);
}
/*
* 接收键盘按键
*/
public void keyPressed(KeyEvent e) {
int key=e.getKeyCode();
switch(key) {
case KeyEvent.VK_LEFT:
//朝向非右即可向左转
if(head.d!=D.R)
head.d=D.L;
break;
case KeyEvent.VK_RIGHT:
//朝向非左即可向右转
if(head.d!=D.L)
head.d=D.R;
break;
case KeyEvent.VK_UP:
//朝向非下即可向上转
if(head.d!=D.D)
head.d=D.U;
break;
case KeyEvent.VK_DOWN:
//朝向非上即可向下转
if(head.d!=D.U)
head.d=D.D;
break;
}
}
}
Food类
package software02;
import java.awt.Color;
import java.awt.Rectangle;
import java.util.Random;
import java.awt.Graphics;
public class Food {
int row,col;
int w=Background.yard;
int h=Background.yard;
private static Random r=new Random();
private Color color=Color.PINK;
//运动时的位置
public Food(int row,int col) {
this.row=row;
this.col=col;
}
//初始位置
public Food() {
this(r.nextInt(Background.row-2)+2,r.nextInt(Background.col));
}
public void reAppear() {
this.row=r.nextInt(Background.row-2)+2;
this.col=r.nextInt(Background.col);
}
public Rectangle getRect() {
return new Rectangle(Background.yard*col,Background.yard*row,w,h);
}
public void draw(Graphics g) {
Color c=g.getColor();
g.setColor(color);
//使用当前颜色填充指定矩形框的椭圆
g.fillOval(Background.yard*col,Background.yard*row, w, h);
g.setColor(c);
if(color==Color.GREEN) {
color=Color.RED;
}
else color=Color.GREEN;
}
public int getCol() {
return col;
}
public void setCol(int col) {
this.col=col;
}
public int getRow() {
return row;
}
public void setRow(int row) {
this.row=row;
}
}
D类
package software02;
public enum D {
L,U,R,D
}