⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 greedsnake.java

📁 java编写的一个小游戏(贪吃蛇游戏
💻 JAVA
字号:
import java.awt.*; 
import java.awt.event.*; 
import javax.swing.*; 
import java.util.*; 

//Main Class 

public class GreedSnake extends KeyAdapter{
JFrame mainFrame; 
Canvas paintCanvas; //Canvas 组件表示屏幕上一个空白矩形区域,应用程序可以在该区域内绘图,或者可以从该区域捕获用户的输入事件。
JLabel labelScore;//计分牌
SnakeModel snakeModel=null;// 蛇 
public static final int DEFAULT_WIDTH=500; 
public static final int DEFAULT_HEIGHT=300; 
public static final int nodeWidth=10; 
public static final int nodeHeight=10; 

//GreedSnake():初始化游戏界面 

public GreedSnake(){ 

//设置界面元素 
mainFrame=new JFrame("贪吃蛇游戏"); 
Container cp=mainFrame.getContentPane(); //返回此窗体的 contentPane 对象
labelScore=new JLabel("所得分数为:",JLabel.CENTER); 
/*这是一个布置容器的边界布局,它可以对容器组件进行安排,并调整其大小,
使其符合下列五个区域:南、北、东、西和中间区域。每个区域最多只能包含一个组件
,并通过相应的常量进行标识:NORTH、SOUTH、EAST、WEST 和 CENTER。
当使用边界布局将一个组件添加到容器中时,要使用这五个常量之一*/
cp.add(labelScore,BorderLayout.NORTH); 

paintCanvas=new Canvas(); 

//调整组件的大小,使其宽度为 width,高度为 height
paintCanvas.setSize(DEFAULT_WIDTH+1,DEFAULT_HEIGHT+1); 
//添加指定的按键侦听器,接收此组件发出的按键事件。
paintCanvas.addKeyListener(this); 
//以上两个都是从从类 java.awt.Component 继承的方法 
//下面在容器中装入paintCanvas
cp.add(paintCanvas,BorderLayout.CENTER); 


JPanel panelButtom=new JPanel(); 
//设置此容器的布局管理器(从类 java.awt.Container 继承)
panelButtom.setLayout(new BorderLayout()/*构造一个组件之间没有间距的新边界布局*/); 
JLabel labelHelp;// 帮助信息 
//把帮助信息加入到加入到 panelButtom中
labelHelp=new JLabel("按 PageUP 或 PageDown 键改变速度",JLabel.CENTER); 
panelButtom.add(labelHelp,BorderLayout.NORTH); 
labelHelp=new JLabel("按 Enter 或 S 键重新开始游戏",JLabel.CENTER); 
panelButtom.add(labelHelp,BorderLayout.CENTER); 
labelHelp=new JLabel("按 SPACE 键或 P 键暂停游戏",JLabel.CENTER); 
panelButtom.add(labelHelp,BorderLayout.SOUTH); 
//把panelButtom加入到加入到cp中
cp.add(panelButtom,BorderLayout.SOUTH); 
//监听键盘消息
mainFrame.addKeyListener(this); 
//调整此窗口的大小,以适合其子组件的首选大小和布局。(从类 java.awt.Window 继承)
mainFrame.pack(); 
//设置此 frame 是否可由用户调整大小。(从类 java.awt.Frame 继承)
mainFrame.setResizable(false); 
//用户关闭窗口时,默认的行为只是简单地隐藏 JFrame。
//要更改默认的行为,可调用方法 setDefaultCloseOperation(int)。
//EXIT_ON_CLOSE(在 JFrame 中定义):使用 System exit 方法退出应用程序
mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
//根据setVisible的参数的值显示或隐藏此组件。
mainFrame.setVisible(true); 
begin(); 
} 

//keyPressed():按键检测 

public void keyPressed(KeyEvent e){ 

int keyCode=e.getKeyCode(); 
if(snakeModel.running) 
switch(keyCode){ 
case KeyEvent.VK_UP: 
snakeModel.changeDirection(SnakeModel.UP); 
break; 
case KeyEvent.VK_DOWN: 
snakeModel.changeDirection(SnakeModel.DOWN); 
break; 
case KeyEvent.VK_LEFT: 
snakeModel.changeDirection(SnakeModel.LEFT); 
break; 
case KeyEvent.VK_RIGHT: 
snakeModel.changeDirection(SnakeModel.RIGHT); 
break; 
case KeyEvent.VK_ADD: 
case KeyEvent.VK_PAGE_UP: 
snakeModel.speedUp();// 加速 
break; 
case KeyEvent.VK_SUBTRACT: 
case KeyEvent.VK_PAGE_DOWN: 
snakeModel.speedDown();// 减速 
break; 
case KeyEvent.VK_SPACE: 
case KeyEvent.VK_P: 
snakeModel.changePauseState();// 暂停或继续 
break; 
default:
} 
//重新开始 
if(keyCode==KeyEvent.VK_S || keyCode==KeyEvent.VK_ENTER){ 
snakeModel.running=false; 
begin(); 
} 
} 

//repaint():绘制游戏界面(包括蛇和食物) 

void repaint(){ 
Graphics g=paintCanvas.getGraphics(); 
//画背景
g.setColor(Color.LIGHT_GRAY); 
g.fillRect(0,0,DEFAULT_WIDTH,DEFAULT_HEIGHT); 
//画蛇
g.setColor(Color.BLACK); 

LinkedList na=snakeModel.nodeArray; 
//返回在此列表中的元素上进行迭代的迭代器(按适当顺序)。
//Iterator是对集合进行迭代的迭代器。
Iterator it=na.iterator(); 
//如果仍有元素可以迭代,则返回 true。
while(it.hasNext()){ 
//返回迭代的下一个元素。
Node n=(Node)it.next(); 
drawNode(g,n); 
} 
//添足
g.setColor(Color.RED); 
Node n=snakeModel.food; 
drawNode(g,n); 
updateScore(); 
} 

//drawNode():绘画某一结点(蛇身或食物) 

private void drawNode(Graphics g,Node n){ 
/*
 填充指定的矩形。
 该矩形左边和右边位于 x 和 x + width - 1。
 顶边和底边位于 y 和 y + height - 1。
 得到的矩形覆盖的区域宽度为 width 像素,高度为 height 像素。
 使用图形上下文的当前颜色填充该矩形。*/
g.fillRect(n.x*nodeWidth,n.y*nodeHeight,nodeWidth-1,nodeHeight-1); 
} 

//updateScore():改变计分牌 

public void updateScore(){ 
String s="所得分数为: "+snakeModel.score; 
labelScore.setText(s); 
} 

//begin():游戏开始,放置贪吃蛇 

void begin(){ 

if(snakeModel==null||!snakeModel.running){ 
snakeModel=new SnakeModel(this,DEFAULT_WIDTH/nodeWidth, 
DEFAULT_HEIGHT/nodeHeight); 
(new Thread(snakeModel)).start(); 
} 
} 

//main():主函数 

public static void main(String[] args){ 

GreedSnake gs=new GreedSnake(); 
} 
} 

//Node:结点类 

class Node{ 
int x; 
int y; 
Node(int x,int y){ 
this.x=x; 
this.y=y; 
} 
} 

//SnakeModel:贪吃蛇模型 

class SnakeModel implements Runnable{ 

GreedSnake gs; 
boolean[][] matrix;// 界面数据保存在数组里 ,即每一个点的状态
LinkedList nodeArray=new LinkedList(); //贪吃蛇的队列
//除了实现 List 接口外,LinkedList 类还为在列表的开头及结尾 get、remove 和 insert 元素提供了统一的命名方法。
Node food; 
int maxX;//最大宽度 
int maxY;//最大长度 
int direction=2;//初始方向 

boolean running=false; 


int timeInterval=800;// 间隔时间(速度) 
double speedChangeRate=0.75;// 速度改变程度 
boolean paused=false;// 游戏状态 
int score=0; //分数
int countMove=0; //移动步数,用于计算分数用
// UP和DOWN是偶数,RIGHT和LEFT是奇数 
public static final int UP=2; 
public static final int DOWN=4; 
public static final int LEFT=1; 
public static final int RIGHT=3; 

//GreedModel():初始化界面 ,全部为空

public SnakeModel(GreedSnake gs,int maxX,int maxY){ 

this.gs=gs; 
this.maxX=maxX; 
this.maxY=maxY; 
matrix=new boolean[maxX][]; 
for(int i=0;i<maxX;++i){ 
matrix[i]=new boolean[maxY]; 
Arrays.fill(matrix[i],false);// 没有蛇和食物的地区置false 
} 
//初始化贪吃蛇 
int initArrayLength=maxX>20 ? 10 : maxX/2; //初始化蛇身长度
for(int i=0;i<initArrayLength;++i){ 
int x=maxX/2+i; 
int y=maxY/2; 
nodeArray.addLast(new Node(x,y)); 
matrix[x][y]=true;// 蛇身处置true 
} 
food=createFood(); 
matrix[food.x][food.y]=true;// 食物处置true 
} 

//changeDirection():改变运动方向 

public void changeDirection(int newDirection){ 
// 避免冲突 方向
if(direction%2!=newDirection%2){
	

	direction=newDirection;
	synchronized(this) {
	this.notify();
	}
} 
} 

/*moveOn():贪吃蛇运动函数 
如果向莫个方向并没有碰撞,那么需要增加头坐标,删除尾坐标
如果越界或者撞到身体,那么退出下面running函数的循环语句,不再运动
如果没有越界并没有撞到身体,吃到东西,那么把食物的坐标加到头坐标,并计算分数
*/
public boolean moveOn(){ 

Node n=(Node)nodeArray.getFirst(); //蛇头坐标
int x=n.x; 
int y=n.y; 
switch(direction){ 
case UP: 
y--; 
break; 
case DOWN: 
y++; 
break; 
case LEFT: 
x--; 
break; 
case RIGHT: 
x++; 
break; 
} 
if((0<=x&&x<maxX)&&(0<=y&&y<maxY)){ //没有越界

if(matrix[x][y]){// 吃到食物或者撞到身体 

if(x==food.x&&y==food.y){// 吃到食物 

nodeArray.addFirst(food);// 在头部加上一结点 
//计分规则与移动长度和速度有关 
//计分方式:先计算scoreGet,如果scoreGet<0,那么就+10分
int scoreGet=(10000-200*countMove)/timeInterval; //应该加的分数
score+=scoreGet>0 ? scoreGet : 10; 
countMove=0;
food=createFood(); 
matrix[food.x][food.y]=true; 
return true; 
} 
else return false;// 撞到身体 
} 
else{//什么都没有碰到 
nodeArray.addFirst(new Node(x,y));// 加上头部 
matrix[x][y]=true; 
n=(Node)nodeArray.removeLast();// 去掉尾部 
matrix[n.x][n.y]=false; 
countMove++; 
return true; 
} 
} 
return false;//越界(撞到墙壁) 
} 

/*run():贪吃蛇运动线程 
每次走一步都会有一定的时间停顿,用Thread.sleep()实现
如果上面的moveON()函数返回的是false,那么程序结束
*/
public void run(){ 
running=true; 
while(running){ 
try{ 
 synchronized(this) {
	this.wait(timeInterval);
 		} //线程挂起
//Thread.sleep(timeInterval);
} 
catch(Exception e){ 
break; 
} 
if(!paused){ 

if(moveOn()){// 未结束 
gs.repaint(); 
} 
else{//游戏结束 
JOptionPane.showMessageDialog(null,"GAME OVER", 
"Game Over",JOptionPane.INFORMATION_MESSAGE); 

 
//gs.repaint();
break; 
} 
} 
} 
running=false; 
} 

//createFood():生成食物及放置地点 

private Node createFood(){ 

int x=0; 
int y=0; 
do{ 
Random r=new Random(); 
x=r.nextInt(maxX); 
y=r.nextInt(maxY); 
} 
while(matrix[x][y]); //判断是否有点重复
return new Node(x,y); 
} 

//speedUp():加快蛇运动速度 

public void speedUp(){ 
timeInterval*=speedChangeRate; 
} 

//speedDown():放慢蛇运动速度 

public void speedDown(){ 
if(timeInterval>400)return;
timeInterval/=speedChangeRate; 
} 

//changePauseState(): 改变游戏状态(暂停或继续) 

public void changePauseState(){ 

paused=!paused; 
} 

}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -