前些日子看了youtube上bro code的贪吃蛇代码视频 于是自己跟着视频实现了一下 顺便写了一下注释 。视频链接(需特殊工具才可以浏览 进不去就别进了 直接看我写的注释和代码):snakegame
该项目主要包含三个class:SnakeGame,GamePanel,GameFrame
下面上代码和代码注释:
首先新建三个class:SnakeGame,GamePanel,GameFrame(别写同一个大类里,放在同一个包里 分别新建三个类)
public class SnakeGame {
public static void main(String[] args){
new GameFrame();//直接new一个新的GameFrame实例 剩下的全都交给GamePanel和GameFrame做
}
}
import javax.swing.JFrame;
//GameFrame继承自JFrame用于生成贪吃蛇程序的初始化窗体
public class GameFrame extends JFrame {
GameFrame(){
this.add(new GamePanel());//添加Panel面板
this.setTitle("Snake");//窗口标题为Snake
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//添加关闭按钮 确保窗口可关闭
this.setResizable(false);//窗口大小无法被使用者调整,由程序员写死
this.pack();//调整此窗口的大小,以适合其子组件的首选大小和布局
this.setVisible(true);//窗口可视化
this.setLocationRelativeTo(null);//调节窗口相对于指定组件的位置 为null则默认窗口在屏幕中央弹出
}
}
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.Random;
import java.util.Timer.*;
public class GamePanel extends JPanel implements ActionListener {
//窗口大小 默认600*600 单位大小为贪吃蛇地图大小 数字越大地图越大 判定点越大。
static final int SCREEN_WIDTH = 600;
static final int SCREEN_HEIGHT = 600;
static final int UNIT_SIZE = 20;
static final int GAME_UNITS = (SCREEN_WIDTH*SCREEN_HEIGHT)/UNIT_SIZE;
static final int DELAY = 75; //DELAY为贪吃蛇移动速度 数字越小 贪吃蛇移动速度越快
final int[] x=new int[GAME_UNITS];//
final int[] y=new int[GAME_UNITS];
int bodyParts = 1;//蛇身初始化长度为1
int applesEaten;//纪录蛇吃了多少苹果
int appleX;//苹果的坐标
int appleY;
char direction = 'R';//默认初始化窗口贪吃蛇的运动方向为向右
boolean running =false;
Timer timer;//引入计时器参数timer
Random random;//随机数random
GamePanel(){
//面板构造器
random = new Random();
this.setPreferredSize(new Dimension(SCREEN_WIDTH,SCREEN_HEIGHT));//组件高度长度确定为刚才设定的SCREEN_WIDTH和SCREEN_HEIGHT
this.setBackground(Color.BLACK);//背景面板颜色设置为黑色
this.setFocusable(true);//让窗口可以被操作 改为false 那么无法用键盘操纵蛇
this.addKeyListener(new MyKeyAdapter());//确保窗口内的东西可以被键盘指定键操控
startGame();//开始游戏
}
public void startGame(){
newApple();//生成新苹果
running =true;//开始运行
timer = new Timer(DELAY,this);//计时器启动
timer.start();
}
public void paintComponent(Graphics g){
super.paintComponent(g);//绘制组件
draw(g);//绘制容器
}
public void draw(Graphics g){
if(running){
for(int i = 0;i<SCREEN_HEIGHT/UNIT_SIZE;i++){
g.drawLine(i*UNIT_SIZE,0,i*UNIT_SIZE,SCREEN_HEIGHT);
g.drawLine(0,i*UNIT_SIZE,SCREEN_HEIGHT,i*UNIT_SIZE);//绘制地图 根据窗口大小来画出每次蛇移动的格子
}
g.setColor(Color.red);
g.fillOval(appleX,appleY,UNIT_SIZE,UNIT_SIZE);//苹果占一个格子
for(int i = 0 ; i< bodyParts;i++){
if(i == 0){
g.setColor(Color.green);
g.fillRect(x[i],y[i],UNIT_SIZE,UNIT_SIZE);
}//画出初始化绿色的蛇头位置,前面设定的x[],y[]用来确认蛇头
else{
g.setColor(new Color(45,180,0));//绿色
g.fillRect(x[i],y[i],UNIT_SIZE,UNIT_SIZE);//确认蛇身
}
}
g.setColor(Color.red);
g.setFont(new Font("Ink Free",Font.BOLD,40));
FontMetrics metrics = getFontMetrics(g.getFont());
g.drawString("Score:"+applesEaten,(SCREEN_WIDTH - metrics.stringWidth("Score:"+applesEaten))/2,g.getFont().getSize());//画出分数 分数的位置在窗口最上方中间
}
else{
gameOver(g);
}
}
public void newApple(){
appleX = random.nextInt((int) SCREEN_WIDTH/UNIT_SIZE)*UNIT_SIZE;
appleY = random.nextInt((int) SCREEN_HEIGHT/UNIT_SIZE)*UNIT_SIZE;//随机生成新苹果
}
public void move(){
for(int i = bodyParts;i>0;i--){
x[i] = x[i-1];
y[i] = y[i-1];
}
switch (direction){
case 'U':
y[0] = y [0] - UNIT_SIZE;
break;//当向上移动时,蛇头的纵坐标变为原纵坐标-单位长度
case 'D':
y[0] = y [0] + UNIT_SIZE;
break;//当向下移动时,蛇头的纵坐标变为原纵坐标+单位长度
case 'L':
x[0] = x [0] - UNIT_SIZE;
break;//当向左移动时,蛇头的横坐标变为原横坐标-单位长度
case 'R':
x[0] = x [0] + UNIT_SIZE;
break;//当向右移动时,蛇头的横坐标变为原横坐标+单位长度
}
}
public void checkApple(){
if((x[0] == appleX) &&(y[0] == appleY)){
bodyParts++;
applesEaten++;
newApple();//当蛇头坐标与苹果坐标相同时,蛇身变长,分数加一,然后再生成新苹果
}
}
public void checkCollisions(){
//如果蛇头和蛇身相撞 停止运行
for(int i = bodyParts;i>0;i--){
if((x[0] == x[i]) && (y[0] ==y[i])){
running = false;
}
}
//如果蛇头撞到窗口左边界 停止运行
if(x[0] < 0){
running = false;
}
//如果蛇头撞到窗口右边界 停止运行
if (x[0] > SCREEN_WIDTH){
running = false;
}
//如果蛇头撞到窗口上边界 停止运行
if(y[0]<0){
running = false;
}
//如果蛇头撞到窗口下边界 停止运行
if(y[0]>SCREEN_HEIGHT){
running = false;
}
//如果停止运行,则计时器也停止
if (!running){
timer.stop();
}
}
public void gameOver(Graphics g){
//当游戏失败时,也显示分数
g.setColor(Color.red);
g.setFont(new Font("Ink Free",Font.BOLD,40));
FontMetrics metrics1 = getFontMetrics(g.getFont());
g.drawString("Score:"+applesEaten,(SCREEN_WIDTH - metrics1.stringWidth("Score:"+applesEaten))/2,g.getFont().getSize());
//游戏失败时,显示gameover
g.setColor(Color.red);
g.setFont(new Font("Ink Free",Font.BOLD,75));
FontMetrics metrics2 = getFontMetrics(g.getFont());
g.drawString("Game Over",(SCREEN_WIDTH - metrics2.stringWidth("Game Over"))/2,SCREEN_HEIGHT/2);
}
@Override
public void actionPerformed(ActionEvent e) {
if(running){
move();//窗口运行时,让蛇开始运动
checkApple();//检测蛇是否吃到苹果
checkCollisions();//检测蛇头是否撞到了边界或者蛇身
}
repaint();//停止运行就改变画面
}
public class MyKeyAdapter extends KeyAdapter{
//首先我们要弄清楚一件事便是,当蛇前进时,是不能后退的,只能向左或向右,同理,蛇向左运动时,是没有办法向右的。
public void keyPressed(KeyEvent e){
switch (e.getKeyCode()){
case KeyEvent.VK_LEFT:
if(direction!= 'R'){
//键盘侦听,接受到的键为←时,如果这时蛇没有向右运动,则方向可变为←
direction='L';
}
break;
case KeyEvent.VK_RIGHT:
if(direction != 'L'){
//接受到的键为→时,如果这时蛇没有向左运动,则方向可变为→
direction='R';
}
break;
case KeyEvent.VK_UP:
if(direction != 'D'){
//接受到的键为↑时,如果这时蛇没有向下运动,则方向可变为↑
direction='U';
}
break;
case KeyEvent.VK_DOWN:
if(direction != 'U'){
//接受到的键为↓时,如果这时蛇没有向右运动,则方向可变为↓
direction='D';
}
break;
}
}
}
}