俄罗斯方块开发小结

俄罗斯方块.png

学习Java半年有余,总想做点什么出来,而不是只纠结与语法和各种类包不可自拔。 于是我想,何不搞一个小游戏出来。 俄罗斯方块?脑海里浮现出这个名字,就它了,逻辑简单,对于我而言更可控, 在网上查找相关的规则之后,经过五天的编码和设计,终于做了出来。

我也不多说废话了,直接贴代码,注释很详细了,希望可以帮到大家~

  1. 主逻辑的实现:游戏流程控制

    //package 俄罗斯方块;

    /**俄罗斯方块
    *date 14.11.17
    *@version 1.0

    @author TaXueWWL
    /
    import java.awt.
    ;
    import java.awt.event.
    ;
    import javax.swing.
    ;
    import javax.swing.Timer;

    class Tetrisblok extends JPanel implements KeyListener{
    /**
    *俄罗斯方块类Tetrisblok继承JPanel,同时实现键盘事件接口KeyListener
    */
    private static final long serialVersionUID = 1L;
    private int blockType;//方块类型
    private int turnState;//方块旋转状态
    private int score = 0;//分数
    private int nextblockType = -1, nextturnState = -1;//下一方块的类型和状态
    private int x, y;//当前方块的位置
    private Timer timer;//定时器
    //游戏地图,存储已经放下的方块(1)及围墙(2),空白处为(0)
    int[][] map = new int[12][21];
    //方块的形状,有倒Z,Z,L,J,I,田,和T 7种

    p/**
    *三维数组shapes存储7种方块形状及其旋转变形,代码如下
    */
    private final int shapes[][][] = new int[][][]{
    //长条T形 ***
    // ×
    { {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}},
    //倒Z字形 **
    // **
    { {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}},
    //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}},
    //田字形 **
    // **
    { {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}},
    //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}},
    //⊥字形 *
    // ***
    { {0,1,0,0,1,1,1,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},
    {1,1,1,0,0,1,0,0,0,0,0,0,0,0,0,0},
    {0,1,0,0,0,1,1,0,0,1,0,0,0,0,0,0}}};

    /**
    产生下一方块及其旋转状态
    /
    public void newblock(){
    //没有下一方块
    if(nextblockType == -1 && nextturnState == -1){
    blockType = (int)(Math.random()
    1000)%7;
    turnState = (int)(Math.random()
    1000)%4;
    nextblockType = (int)(Math.random() * 1000)%7;
    nextturnState = (int)(Math.random() * 1000)%4;
    }
    else{//已有下一方块
    blockType = nextblockType;
    turnState = nextturnState;
    nextblockType = (int)(Math.random() * 1000)%7;
    nextturnState = (int)(Math.random() * 1000)%4;
    }
    x = 4; y = 0;//屏幕上方中央
    if(gameover(x, y) == 1){//游戏结束
    newmap();
    drawwall();
    score = 0;
    JOptionPane.showMessageDialog(null, "GAME OVER...");
    }
    }
    /**
    *画围墙
    */
    public void drawwall(){
    int i, j;
    for(i = 0; i < 12; i++){
    map[i][20] = 2;
    }
    for(j = 0; j < 21; j++){//在0列和11列
    map[11][j] = 2;
    map[0][j] = 2;
    }

    }
    //初始化地图
    public void newmap(){
    int i, j;
    for(i = 0; i < 12; i++){
    for(j = 0; j < 21; j++){
    map[i][j] = 0;
    }
    }
    }
    /**
    *Tetrisblok()构造方法产生一个新的下落方块,并启动定时器。
    *定时器触发事件完成屏幕屏幕重画,判断当前方块是否可以下落
    产生新的方块
    /
    Tetrisblok(){
    newblock();
    newmap();
    drawwall();
    timer = new Timer(500, new TimerListener());//0.5s
    timer.start();
    }
    //定时器监听事件
    class TimerListener implements ActionListener{
    public void actionPerformed(ActionEvent e){
    if(blow(x, y + 1, blockType, turnState) == 1){//可以下落
    y = y + 1;//当前方块下落
    }
    if(blow(x, y + 1, blockType, turnState) == 0){//不可以下落
    add(x, y, blockType, turnState);//固定当前方块
    delline();//消去满行
    newblock();//产生新的方块
    }
    repaint();
    }
    }
    //菜单事件,达到游戏暂停和继续
    public void newGame()//新游戏
    {
    newblock();
    newmap();
    drawwall();
    }
    public void pauseGame()//暂停游戏
    {
    timer.stop();
    }
    public void continueGame()//继续游戏
    {
    timer.start();
    }
    /

    *turn()旋转当前方块,旋转次数加一后,blow判断是否可以
    *旋转,不可以则旋转次数恢复为原来的值
    */

     //旋转当前方块
     public void turn(){
         int tempturnState = turnState;
         turnState = (turnState + 1) % 4;
         if(blow(x, y, blockType, turnState) == 1){//可以旋转
         }
         if(blow(x, y, blockType, turnState) == 0){//不可旋转
             turnState = tempturnState;//将旋转次数恢复为原来的值
         }
         repaint();
     }
     //方向移动方块,判断可移动后重画
    public void left(){
     if(blow(x - 1, y, blockType, turnState) == 1){
         x = x - 1;
     }
     repaint();
    }
    
    public void right(){
     if(blow(x + 1, y, blockType, turnState) == 1){
         x = x + 1;
     }
     repaint();
    }
    
    public void down(){
     if(blow(x, y + 1, blockType, turnState) == 1){//可以下落
         y = y + 1;
     }
     if(blow(x, y + 1, blockType, turnState) == 0){//不能下落
         add(x, y, blockType, turnState);
         newblock();
         delline();
     }
     repaint();
    }
    //判断移动或旋转后位置是否合法,是否与墙壁碰撞
    public int blow(int x, int y, int blockType, int turnState){
         for(int a = 0; a < 4; a++){
             for(int b = 0; b < 4; b++){
                 if(((shapes[blockType][turnState][a * 4 + b] == 1) &&
                 (map[x + b + 1][y + a] == 1)) || 
                 ((shapes[blockType][turnState][a * 4 + b] == 1) &&
                 (map[x + b + 1][y + a] == 2))){
                     return 0;
                 }
             }
         }
         return 1;
    }
    //delline()消去满行,第d行满则上方方块下移
    public void delline(){
         int c = 0;
         for(int b = 0; b < 21; b++){
             for(int a = 0; a < 12; a++){
                 if(map[a][b] == 1){
                     c += 1;
                     if(c == 10){//该行满
                         score += 10;
                         for(int d = b; d > 0; d--){
                             for(int e = 0; e < 12; e++){
                                 //上方方块下移
                                 map[e][d] = map[e][d - 1];
                             }
                         }
                     }
                 }
             }
             c = 0;
         }
    }
    //gameover
    public int gameover(int x, int y){
         if(blow(x, y, blockType, turnState) == 0){
             return 1;
         }
         return 0;
    }
    //添加当前方块到地图
    public void add(int x, int y, int blockType, int turnState){
         int j = 0;
         for(int a = 0; a < 4; a++){
             for(int b = 0; b < 4; b++){
                 if(shapes[blockType][turnState][j] == 1){
                     map[x + b + 1][y + a] = shapes[blockType][turnState][j];
                 }
                 j++;
             }
         }
    }
    //paint()重画屏幕
    public void paint(Graphics g){
         super.paint(g);//调用父类的paint()方法,实现初始化清屏
         int i, j;
         //画当前方块
         for(j = 0; j < 16; j++){
             if(shapes[blockType][turnState][j] == 1){
                 g.fillRect((j % 4 + x + 1) * 15, (j / 4 + y) * 15, 15, 15);  
             }
         }
         //画已经固定的方块和围墙
         for(j = 0; j < 21; j++){
             for(i = 0; i < 12; i++){
                 if(map[i][j] == 1){
                     //画方块
                     g.fillRect(i * 15, j * 15, 15, 15);
                 }
                 if(map[i][j] == 2){
                     //画围墙
                     g.fillRect(i * 15, j * 15, 15, 15);
                 }
             }
         }
         g.drawString("SCORE= " + score, 225, 15);
         g.drawString("nextBlockShape ", 225, 50);
         //在窗口右侧区域绘制下一方块
         for(j = 0; j < 16; j++){
             if(shapes[nextblockType][nextturnState][j] == 1){
                 g.fillRect(225 + (j % 4) * 15, (j / 4) * 15 + 100, 15, 15);
             }
         }
    }
    //键盘监听
    public void keyPressed(KeyEvent e){
         switch(e.getKeyCode()){
             case KeyEvent.VK_DOWN:
                 down();
                 break;
             case KeyEvent.VK_UP:
                 turn();
                 break;
             case KeyEvent.VK_RIGHT:
                 right();
                 break;
             case KeyEvent.VK_LEFT:
                 left();
                 break;  
         }
    }
    //No use to type other keys
    public void keyReleased(KeyEvent e){
    }
    public void keyTyped(KeyEvent e){
    } 
    

    }
    2.游戏面板呈现

    //package 俄罗斯方块;

    /**
    *显示游戏面板Tetrisblok界面,加入菜单以及时间监听
    */

    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    //import 俄罗斯方块.Tetrisblok;
    import javax.swing.*;
    @SuppressWarnings("serial")
    public class TetrisFrame extends JFrame implements ActionListener{
    static JMenu game = new JMenu("游戏");
    JMenuItem newgame = game.add("新游戏");
    JMenuItem pause = game.add("暂停");
    JMenuItem goon = game.add("继续");
    JMenuItem exit = game.add("退出");
    static JMenu help = new JMenu("帮助");
    JMenuItem about = help.add("关于");
    Tetrisblok a = new Tetrisblok();
    public TetrisFrame(){
    addKeyListener(a);
    this.add(a);
    newgame.addActionListener(this);//"新游戏"菜单项
    pause.addActionListener(this);//"暂停"菜单项
    goon.addActionListener(this);//"继续"菜单项
    about.addActionListener(this);//"关于"菜单项
    exit.addActionListener(this);//"退出"菜单项
    }

     public void actionPerformed(ActionEvent e){
         if(e.getSource() == newgame)//"新游戏"菜单项
         {
             a.newGame();
         }
         else if(e.getSource() == pause)//"暂停"菜单项
         {
             a.pauseGame();
         }
         else if(e.getSource() == goon)//"继续"菜单项
         {
             a.continueGame();
         }
         else if(e.getSource() == about)//"关于"菜单项
         {
             DisplayToast("按左右键移动\n上键进行方块旋转~\n\r\rMade_by_WWL\nFor SSR_LSC_LWW_CYD_XM");
         }
         else if(e.getSource() == exit)//"退出"菜单项
         {
             System.exit(0);
         }
     }
    
     public void DisplayToast(String str){
         JOptionPane.showMessageDialog(null, str, "游戏提示", 
                                     JOptionPane.ERROR_MESSAGE);
     }
    
     //Main Method
     public static void main(String[] args){
         TetrisFrame frame = new TetrisFrame();
         JMenuBar menu = new JMenuBar();
         frame.setJMenuBar(menu);
         menu.add(game);
         menu.add(help);
         frame.setLocationRelativeTo(null);
         frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//"结束"按钮可使用
         frame.setSize(320, 380);
         frame.setTitle("俄罗斯方块V1.0--for XM");
         //frame.setUndecorated(true);
         frame.setVisible(true);
         frame.setResizable(false);
     }
    

    }
    鸣谢:

感谢一直支持我的父亲,默默地爱着我的母亲,还有9102的小伙伴们,以及316的兄弟们。
也感谢努力着的自己,多年以后回头看到现在的自己,会笑着说声:“谢谢你,曾经这样的努力~”

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,219评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,363评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,933评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,020评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,400评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,640评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,896评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,597评论 0 199
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,327评论 1 244
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,581评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,072评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,399评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,054评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,083评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,849评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,672评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,585评论 2 270

推荐阅读更多精彩内容

  • 背景 一年多以前我在知乎上答了有关LeetCode的问题, 分享了一些自己做题目的经验。 张土汪:刷leetcod...
    土汪阅读 12,662评论 0 33
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,295评论 18 399
  • 【程序1】 题目:古典问题:有一对兔子,从出生后第3个月起每个月都生一对兔子,小兔子长到第三个月后每个月又生一对兔...
    叶总韩阅读 5,066评论 0 41
  • Java经典问题算法大全 /*【程序1】 题目:古典问题:有一对兔子,从出生后第3个月起每个月都生一对兔子,小兔子...
    赵宇_阿特奇阅读 1,789评论 0 2
  • 回溯算法 回溯法:也称为试探法,它并不考虑问题规模的大小,而是从问题的最明显的最小规模开始逐步求解出可能的答案,并...
    fredal阅读 13,508评论 0 89