广工JAVA大作业——游戏程序(俄罗斯方块)

xiaoxiao2021-02-28  44

效果:

1. 概述

随着时代的发展,电子游戏逐渐出现,早起的一些桌面小游戏风靡全球,其中就有《俄罗斯方块》,《俄罗斯方块》(Tetris)是一款由俄罗斯人阿列克谢·帕基特诺夫1984年6月发明的休闲游戏。该游戏曾经被多家公司代理过。经过多轮诉讼后,该游戏的代理权最终被任天堂获得。任天堂对于俄罗斯方块来说意义重大,因为将它与GB搭配在一起后,获得了巨大的成功。《俄罗斯方块》的基本规则是移动、旋转和摆放游戏自动输出的各种方块,使之排列成完整的一行或多行并且消除得分,上手简单、老少皆宜、家喻户晓。本文将详述我个人开发的一款基于这款游戏的简易俄罗斯方块,实现该游戏基本功能,如自动出方块,可翻转、左移、右移、下降,暂停,增加难度,降低难度并且达到消行加分的功能。

 

2. 系统分析

 

系统分为两大板块,一个是方块工厂,另一个是游戏显示画板,方块工厂用来生成方、翻转、移动和固定,游戏画板用于绘画,并加上定时器和监听器,用于反馈用户按键事件,传递到方块工厂来控制方块,并且检查方块是否碰撞或消行等状态

 

3. 系统设计,

3.1系统目标

①在顶部生成方块

②方块反转、左移、右移、下降

③方块在特定的游戏区域内运动

④定时器定一特定长时间作为时间间隔来触发ActionEvent使方块定时下落一格

⑤检测是否碰撞(包括与围墙,底部,已固定方块),碰撞则停止并生成新的方块下落

⑥检查是否能消行,且消行要加分记录并显示在界面上,并且放出beep音效

⑦生成方块前检查最顶行有没有方块,有则弹框提示结束游戏并显示所得分数,

  按确认后重新开始游戏。

⑧游戏区域中,围墙隐藏起来,方块可运动区域有网格,右边有分数显示且有操作方法提示,方块下落时蓝色,固定时为绿色,便于区分。

⑨开始运行时弹对话框是否开始游戏

⑩可以暂停游戏

增加难度(下落速度增加)

⑫降低难度(下落速度减少)

⑬难度范围为0-10

3.2系统功能结构

3.3 系统预览

代码:

TetrisGame.java

package MyTetris2; import javax.swing.JFrame; import javax.swing.JOptionPane; /** * 游戏主程序 * @author LEUNG * */ public class TetrisGame extends JFrame{ /** * */ private static final long serialVersionUID = 1L; private GamePanel t ; private static int flag=0; /** * 构造方法 */ TetrisGame(){ setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setLocation(800,100); setTitle("俄罗斯方块"); setSize(600,750); setResizable(true); //不可缩放 } /** * 添加画板 * 开始跑Timer */ public void startGame(){ t = new GamePanel(); add(t); addKeyListener(t); t.timer.start(); } public static void main(String[] args){ TetrisGame tetris = new TetrisGame(); tetris.setVisible(true); flag = JOptionPane.showConfirmDialog(tetris, //开始选择对话框 "按【是】开始新游戏\n按【否】退出游戏", "new Game", JOptionPane.YES_NO_OPTION); if(flag==JOptionPane.YES_OPTION){ tetris.startGame(); tetris.setVisible(true); } else{ tetris.dispose(); System.exit(0); } } }

Block.java

package MyTetris2; /** * 方块类 * 内含方块的基本方法(方块固定、新建、翻转、左移、右移、下移) * @author LEUNG * */ public class Block { /** * 方块用一个三维数组来存,分别是形状,形态,坐标(在4×4方格中,1表示要填充,0表示不填充) *7种图形分别是J,L,S,Z,T,O,I *4种形态,旋转得到的 */ public final int shapes[][][] = new int[][][]{ //J {{0,1,0,0,0,1,0,0,1,1,0,0,0,0,0,0}, {1,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0}, {1,1,0,0,1,0,0,0,1,0,0,0,0,0,0,0}, {1,1,1,0,0,0,1,0,0,0,0,0,0,0,0,0}}, //L {{1,0,0,0,1,0,0,0,1,1,0,0,0,0,0,0}, {1,1,1,0,1,0,0,0,0,0,0,0,0,0,0,0}, {1,1,0,0,0,1,0,0,0,1,0,0,0,0,0,0}, {0,0,1,0,1,1,1,0,0,0,0,0,0,0,0,0}}, //S {{0,1,1,0,1,1,0,0,0,0,0,0,0,0,0,0,}, {1,0,0,0,1,1,0,0,0,1,0,0,0,0,0,0,}, {0,1,1,0,1,1,0,0,0,0,0,0,0,0,0,0,}, {1,0,0,0,1,1,0,0,0,1,0,0,0,0,0,0,}}, //Z {{1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0}, {0,1,0,0,1,1,0,0,1,0,0,0,0,0,0,0}, {1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0}, {0,1,0,0,1,1,0,0,1,0,0,0,0,0,0,0}}, //T {{0,1,0,0,1,1,1,0,0,0,0,0,0,0,0,0}, {1,0,0,0,1,1,0,0,1,0,0,0,0,0,0,0}, {1,1,1,0,0,1,0,0,0,0,0,0,0,0,0,0}, {0,1,0,0,1,1,0,0,0,1,0,0,0,0,0,0}}, //O {{1,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0}, {1,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0}, {1,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0}, {1,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0}}, //I {{0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0}, {0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0}, {0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0}, {0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0}}, }; /** * x,y用来记录方块4×4区域中的(0,0)点的位置,x,y中的(0,0)相对于map中是(1,0) */ public int x; public int y; public int blockType=0; //方块类型 public int blockState=0; //方块状态 public int initX = 4; //初始位置的X,Y值 public int initY = 0; GamePanel game; /** * 构造方法 */ Block(GamePanel game){ //引用游戏画板 this.game = game; } /** * 新建方块(初始化) */ public void newblock(){ blockType = (int)(Math.random()*1000)%7; //范围0-6 blockState = (int)(Math.random()*1000)%4; //范围0-3 x=initX; y=initY; } /** * 把需要固定的方块固定 * 存放在map数组中 */ public void add(){ int i=0; for(int a=0;a<4;a++){ for(int b=0;b<4;b++){ if(game.map[x+1+b][y+a]==0){ // map[列][行] game.map[x+1+b][y+a]=shapes[blockType][blockState][i]; } i++; } } } /** * 右移 */ public void right() { if(!game.isCollied(x+1,y)){ x++; } game.repaint(); } /** * 下移 */ public void down() { if(!game.isCollied(x,y+1)){ y++; }else{ add(); //碰撞到底部后,把方块添加到画布上去 game.deleteLine(); newblock(); } game.repaint(); } /** * 左移 */ public void left() { if(!game.isCollied(x-1,y)){ x--; } game.repaint(); } /** * 转换状态 */ public void turnState() { int temp = blockState; //首先记录本状态 blockState = (blockState+1)%4; if(game.isCollied(x,y)){ blockState = temp; } game.repaint(); } }

GamePanel.java

package MyTetris2; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Font; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Toolkit; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.Timer; /** * 游戏界面画板 * @author LEUNG * */ public class GamePanel extends JPanel implements KeyListener{ /** * */ private static final long serialVersionUID = 1L; public int MAPCOL=13; //游戏画布的列数 public int MAPROW=23; //游戏画布的行数 public int PIXEL=30; //像素,单位格子的长、宽 public int score; public int map[][]=new int[MAPCOL][MAPROW]; public Timer timer ; private int Level=5; //初始难度5 private Block block; //引用Block类 private TimerListener timerlistener; /** * 内部类,被timer触发 * @author LEUNG * */ private class TimerListener implements ActionListener{ //实现接口 @Override public void actionPerformed(ActionEvent e) { if(!isCollied(block.x,block.y+1)){ block.y++; }else{ block.add(); //碰撞到底部后,把方块添加到画布上去 deleteLine(); if(true==isGameover()){ JOptionPane.showMessageDialog(null, "Game Over\n你的分数是:"+score); cleanMap(); drawWall(); score=0; } block.newblock(); } repaint(); //重画 } } /** * 构造方法 */ GamePanel(){ block = new Block(this); //引用Block类,参数是GamePanel类 cleanMap(); drawWall(); block.newblock(); timerlistener=new TimerListener(); timer = new Timer(800, timerlistener); //计时器,在指定时间间隔触发TimerListener() } /** * 增加难度 */ private void upLevel(){ if(Level<10){ Level++; timer.setDelay(1300-100*Level); repaint(); } } /** * 降低难度 */ private void downLevel(){ if(Level>0){ Level--; timer.setDelay(1300-100*Level); repaint(); } } /** * 围墙内的画面置零,相当于清除画面 */ private void cleanMap(){ for(int i=1;i<MAPCOL-2;i++){ for(int j=0;j<MAPROW-2;j++){ map[i][j]=0; } } } /** * 画围墙,用2标示为围墙 * */ private void drawWall(){ for(int i=0;i<MAPCOL-1;i++){ map[i][MAPROW-2]=2; } for(int j=0;j<MAPROW-2;j++){ map[0][j]=2; map[MAPCOL-2][j]=2; } } /** * 得到画笔来画TeTirsPanel * 画面来源 */ protected void paintComponent(Graphics g) { super.paintComponent(g); //每次调用repaint()都清除原先的组件,不调用会保留原组件 for(int j=0;j<MAPROW-1;j++){ for(int i=0;i<MAPCOL-1;i++){ /* if(2==map[i][j]){ g.setColor(Color.ORANGE); g.fillRect(i*PIXEL, j*PIXEL, PIXEL, PIXEL); //画围墙格子 } */ //现把其隐藏 //画出固定好的方块 if(1==map[i][j]){ g.setColor(Color.GREEN); g.fill3DRect(i*PIXEL, j*PIXEL, PIXEL, PIXEL,true); } } } //画竖线 for(int i=1;i<MAPCOL-1;i++){ g.setColor(Color.BLACK); g.drawLine(i*PIXEL, 0, i*PIXEL, (MAPROW-2)*PIXEL); } //画横线 for(int j=0;j<MAPROW-1;j++){ g.setColor(Color.BLACK); g.drawLine(PIXEL*1, j*PIXEL, (MAPCOL-2)*PIXEL, j*PIXEL); } //画未固定方块,16是shapes数组的第一维长度 //x,y是方块正处于的坐标(提示x要加1) for(int i=0;i<16;i++){ if(1==block.shapes[block.blockType][block.blockState][i]){ g.setColor(Color.BLUE); g.fill3DRect((block.x+1+i%4)*PIXEL,(block.y+i/4)*PIXEL,PIXEL,PIXEL,true); //用shapes数组来画出方块 } } g.setColor(Color.darkGray); g.setFont(new Font("黑体", Font.BOLD, 30)); g.drawString("分数:"+score,MAPCOL*PIXEL,3*PIXEL-35); Graphics2D g2 = (Graphics2D)g; g2.setStroke(new BasicStroke(3.0f)); g2.drawLine(MAPCOL*PIXEL-30, 3*PIXEL-85, MAPCOL*PIXEL+170, 3*PIXEL-85); //横线1 g2.drawLine(MAPCOL*PIXEL-30, 3*PIXEL, MAPCOL*PIXEL+170, 3*PIXEL); //横线2 g2.drawLine(MAPCOL*PIXEL-30, 3*PIXEL-85, MAPCOL*PIXEL-30, 3*PIXEL); //竖线1 g2.drawLine(MAPCOL*PIXEL+170, 3*PIXEL-85, MAPCOL*PIXEL+170, 3*PIXEL); //竖线2 g2.drawLine(MAPCOL*PIXEL-30, 5*PIXEL-40, MAPCOL*PIXEL+170, 5*PIXEL-40); //横线1 g2.drawLine(MAPCOL*PIXEL-30, 5*PIXEL+20, MAPCOL*PIXEL+170, 5*PIXEL+20); //横线2 g2.drawLine(MAPCOL*PIXEL-30, 7*PIXEL+20, MAPCOL*PIXEL+170, 7*PIXEL+20); //横线3 g2.drawLine(MAPCOL*PIXEL-30, 9*PIXEL+20, MAPCOL*PIXEL+170, 9*PIXEL+20); //横线4 g2.drawLine(MAPCOL*PIXEL-30, 11*PIXEL+20, MAPCOL*PIXEL+170, 11*PIXEL+20); //横线5 g2.drawLine(MAPCOL*PIXEL-30, 13*PIXEL+20, MAPCOL*PIXEL+170, 13*PIXEL+20); //横线6 g2.drawLine(MAPCOL*PIXEL-30, 15*PIXEL+20, MAPCOL*PIXEL+170, 15*PIXEL+20); //横线7 g2.drawLine(MAPCOL*PIXEL-30, 17*PIXEL+20, MAPCOL*PIXEL+170, 17*PIXEL+20); //横线8 g2.drawLine(MAPCOL*PIXEL-30, 19*PIXEL+20, MAPCOL*PIXEL+170, 19*PIXEL+20); //横线9 g2.drawLine(MAPCOL*PIXEL-30, 5*PIXEL-40, MAPCOL*PIXEL-30, 19*PIXEL+20); //竖线1 g2.drawLine(MAPCOL*PIXEL+170, 5*PIXEL-40, MAPCOL*PIXEL+170, 19*PIXEL+20); //竖线2 g.setFont(new Font("黑体", Font.BOLD, 22)); g.drawString("操作方法", MAPCOL*PIXEL, 5*PIXEL); g.drawString("↑ 翻转",MAPCOL*PIXEL,7*PIXEL); g.drawString("↓ 下降一格",MAPCOL*PIXEL,9*PIXEL); g.drawString("← 左移",MAPCOL*PIXEL,11*PIXEL); g.drawString("→ 右移",MAPCOL*PIXEL,13*PIXEL); g.drawString("F1 暂停", MAPCOL*PIXEL, 15*PIXEL); g.drawString("F2 增加难度", MAPCOL*PIXEL, 17*PIXEL); g.drawString("F3 降低难度", MAPCOL*PIXEL, 19*PIXEL); g.drawString("当前难度:"+Level+" 相当于"+(double)(1300-Level*100)/1000+ "秒下降一格", 2*PIXEL, 21*PIXEL+40); } @Override public void keyPressed(KeyEvent e) { switch(e.getKeyCode()){ case KeyEvent.VK_UP: block.turnState(); break; case KeyEvent.VK_LEFT: block.left(); break; case KeyEvent.VK_DOWN: block.down(); break; case KeyEvent.VK_RIGHT: block.right(); break; case KeyEvent.VK_F1: timer.stop(); JOptionPane.showMessageDialog(null, "按确认取消暂停"); timer.restart(); break; case KeyEvent.VK_F2: upLevel(); break; case KeyEvent.VK_F3: downLevel(); break; } } @Override public void keyTyped(KeyEvent e) { } @Override public void keyReleased(KeyEvent e) { } /** * 判断是否碰撞 * @param x * @param y * @return boolean */ public boolean isCollied(int x,int y){ for(int a=0;a<4;a++){ //遍历4×4方块区域 for(int b=0;b<4;b++){ if((block.shapes[block.blockType][block.blockState][a*4+b]==1)&&(map[x+1+b][y+a]==1)){ //判断与已有方块是否重合 return true; }else if((block.shapes[block.blockType][block.blockState][a*4+b]==1)&&(map[x+1+b][y+a]==2)){ //与围墙 return true; } } } return false; } /** * 判断是否结束游戏,判断条件:第一行有方块 * @return boolean */ public boolean isGameover(){ for(int i=1;i<MAPCOL-3;i++){ if(map[i][0]==1){ return true; } } return false; } /** * 消行 */ public void deleteLine(){ int count = 0; for(int i=0;i<MAPROW-2;i++){ for(int j=1;j<MAPCOL-2;j++){ if(map[j][i]==1){ count++; if(count==MAPCOL-3){ //一行都满的话,总数为MAPCOL-3个,满足则消行 score+=10; Toolkit.getDefaultToolkit().beep(); //消行提示音 for(int a=i;a>0;a--){ //从第i行开始 for(int b=1;b<MAPCOL-2;b++){ map[b][a]=map[b][a-1]; //当前行等于上一行 } } } } } count=0; } } }

转载请注明原文地址: https://www.6miu.com/read-2612730.html

最新回复(0)