小组第五周学习博客

小组第五周学习博客

本周我们对俄罗斯方块的游戏实现有了一个大概总体的认识

1.方块类:

每个方块都要记录自己的ID、形状。形状采用4对整数坐标来表示,分别记录其4个小方块的相对位置(以方块内或旁边任一点为中心,称为参考点)。建议可以在方块类中定义一个或多个private static数组,将每种ID的方块的形状数据存储好(都是一些固定的数据),这样产生一个方块时只需要提供其ID即可。
为了处理方块的旋转,我们不能只记录上述7种方块,而应该把它们旋转后产生的每种形状都认为是一种不同的方块(因为它们的4对坐标都不同),然后在方块ID之间建立映射关系,即哪种ID的方块旋转后变成哪种ID的方块,只要求处理一个方向的旋转。这个映射关系也可以用private static数组来实现。
此外,方块类还要记录自己的参考点在指定的游戏区域中的位置。
方块类主要操作:(自行决定哪些为public哪些为private)
构造函数:参数至少有:ID和参考点初始坐标。
填充:把自己填充到指定的游戏区域中。返回填充成功或失败的信息。
清除:把自己从指定的游戏区域中清除。
移动:在指定的游戏区域中移动,包括向左、右、下移动,以1个单元格为单位。如果可以移动,则修改参考点的位置,并在指定的游戏区域中重新填充自己(先清除、然后修改参考点位置,再填充)。返回是否移动成功的信息。
旋转:在指定的游戏区域中旋转一次,此时参考点位置不变,但要改变自己的ID,然后在指定的游戏区域中重新填充自己(先清除、然后修改参考点位置,再填充)。
获得占用行:返回本方块4个小方块所占用的各行,可能有1~4行,要返回每一行的行号(1~20)。这些数据可用参考点位置与自己的4对坐标计算出来。

public class RussionBlockGame
{
private int aa=0;
private int ic=0;
private final int sp_width=10; //游戏界面宽格
private final int sp_height=20; //游戏界面高格
private final int types[][][]={ //游戏方块
{{-1,0},{0,0},{1,0},{2,0}}, //长条
{{0,-1},{0,0},{0,1},{0,2}},
{{-1,0},{0,0},{1,0},{1,1}}, //直角(右)
{{0,1},{0,0},{0,-1},{1,-1}},
{{1,0},{0,0},{-1,0},{-1,-1}},
{{0,-1},{0,0},{0,1},{-1,1}},
{{-1,0},{0,0},{0,1},{1,0}}, //直角(中)
{{0,1},{0,0},{1,0},{0,-1}},
{{1,0},{0,0},{0,-1},{-1,0}},
{{0,-1},{0,0},{-1,0},{0,1}},
{{-1,1},{-1,0},{0,0},{1,0}}, //直接(左)
{{1,1},{0,1},{0,0},{0,-1}},
{{1,-1},{1,0},{0,0},{-1,0}},
{{-1,-1},{0,-1},{0,0},{0,1}},
{{0,-1},{0,0},{1,0},{1,1}},
{{-1,0},{0,0},{0,-1},{1,-1}},
{{0,1},{0,0},{1,0},{1,-1}},
{{1,0},{0,0},{0,-1},{-1,-1}},
{{0,0},{0,1},{1,0},{1,1}} //正方形
};

private int[][] block_box=new int[4][2];                     //四个方块坐标
private int[][] block_box_tt=new int[4][2];
private int block_x=0,block_y=0;                             //游戏方块在游戏界面中的坐标
private int block_type=0;                                    //方块类别
private int[][] game_space=new int[20][10];                  //空间数据
private int movetype=0;
private int scroe=0;
private int speed=5;

public RussionBlockGame()
{
    clearspace();
    makenewblock();
}

public void clearspace()                                     //初始化空间数据
{
    for(int i=0;i<sp_height;i++)
        for(int j=0;j<sp_width;j++)
            game_space[i][j]=0;
}

public void makenewblock()                                   //随机出现方块
{
    aa=(int)(Math.random()*100%7+1);
    ic=aa*10+1;
    switch(aa)
    {
        case 1:
            block_type=0;
            break;
        case 2:
            block_type=2;
            break;
        case 3:
            block_type=6;
            break;
        case 4:
            block_type=10;
            break;
        case 5:
            block_type=14;
            break;
        case 6:
            block_type=16;
            break;
        case 7:
            block_type=18;
            break;
    }
    block_x=1;
    block_y=sp_width/2;
    for(int i=0;i<4;i++)
    {
        block_box[i][0]=block_x-types[block_type][i][1];
        block_box[i][1]=block_y+types[block_type][i][0];
    }
}

2.游戏区域类

其主要属性:游戏区域数据,可以用一个二维数组方便地实现。状态:游戏未开始(初态)和游戏已开始。
主要操作为:(自行决定哪些为public哪些为private)
游戏区域读写:指定游戏区域的一个单元格坐标后,返回或设置单元格的值。
绘制:清除屏幕后将游戏区域绘制出来。
命令处理:命令为一个字符串,格式自定。要求实现如下命令:
游戏开始:注意:只有在游戏未开始状态才能执行该命令!当接收到此命令时,将状态切换为游戏已开始。先初始化游戏区域(全空格),然后利用随机数产生一个新的方块ID,并根据此ID和一个初始单元格坐标(在游戏区域的上部中间的位置)产生一个新的方块(称为活动方块),并将此方块填充到本游戏区域中(所以本游戏区域应该作为方块类大部分操作的参数),然后绘制本游戏区域。
注意:以下各命令只有在游戏已开始状态才能执行!
方块左移、右移、旋转:对活动方块调用相应的操作,不论是否能移动成功都重新绘制本游戏区域,如果移动失败绘制完毕后给出文字提示信息。为增加游戏难度,可以在执行几次左右移或旋转后自动执行“方块下移”命令。
方块下移:对活动方块调用相应的操作,如果移动成功,则重新绘制本游戏区域后操作结束,如果移动失败,则说明方块已经掉到最下面,需要执行方块落地操作。
方块落地:

        public void movedown()  //方块下落的方法函数
        {
            block_x++;
            for(int i=0;i<4;i++)
            {
                block_box[i][0]=block_x-types[block_type][i][1];
            }
            movetype=1;
        }

    public void moveleft()  //方块左移的方法函数
    {
        block_y--;
        for(int i=0;i<4;i++)
        {
            block_box[i][1]=block_y+types[block_type][i][0];
        }
        movetype=2;
    }

    public void moveright()  //方块右移的方法函数
    {
        block_y++;
        for(int i=0;i<4;i++)
        {
            block_box[i][1]=block_y+types[block_type][i][0];
        }
        movetype=3;
    }

    public void turnright()  //方块向右旋转九十度的方法函数
    {
        int[][] block_box_temp=new int[4][2];
        int ic_temp=ic;
        int block_type_temp=block_type;
        int id=ic%10;
        for(int i=0;i<4;i++)
        {
            block_box_temp[i][0]=block_box[i][0];
            block_box_temp[i][1]=block_box[i][1];
        }
        if(aa==7)
            return;
        else if(aa==1||aa==5||aa==6)
        {
            if(id==2)
            {
                block_type--;
                ic--;
            }
            else
            {
                block_type++;
                ic++;
            }
        }
        else
        {
            if(id==4)
            {
                block_type=block_type-3;
                ic=ic-3;
            }
            else
            {
                block_type++;
                ic++;
            }
        }
        for(int i=0;i<4;i++)
        {
            block_box[i][0]=block_x-types[block_type][i][1];
            block_box[i][1]=block_y+types[block_type][i][0];
        }
        if(Iscanmoveto()==false)
        {
            ic=ic_temp;
            block_type=block_type_temp;
            for(int i=0;i<4;i++)
            {
                block_box[i][0]=block_box_temp[i][0];
                block_box[i][1]=block_box_temp[i][1];
            }
        }
    }

    public void moveback()
    {
        if(movetype==1)
        {
            block_x--;
            for(int i=0;i<4;i++)
            {
                block_box[i][0]=block_x-types[block_type][i][1];
            }
        }
        else if(movetype==2)
        {
        block_y++;
        for(int m=0;m<4;m++)
        {
            block_box[m][1]=block_y+types[block_type][m][0];
        }
    }
    else if(movetype==3)
    {
        block_y--;
        for(int n=0;n<4;n++)
        {
            block_box[n][1]=block_y+types[block_type][n][0];
        }
    }
}



先执行清行操作:获得活动方块占用了哪些行的信息,然后逐行进行扫描:对每一行,检查其10列单元格是否都已经被填充,如果没有,则继续检查下一行,如果是,则将该行以上(不包括该行)的游戏区域整体下移一行,并在最上面一行填充一行空格。可以从上往下检查,也可以从下往上检查。可以每清除一行就重新绘制一次游戏区域,获得动画效果。 
执行完清行操作后,要产生下一个方块,方法类似于“游戏开始”命令中的步骤,此时要把活动方块指定为新产生的方块,而且还要检查新产生的方块是否能成功填充,如果填充失败,说明游戏结束,此时将游戏区域状态恢复为游戏未开始,抛出异常通知游戏结束(类型自定),但在本类内不处理该异常,让类的使用者处理。

3.程序总体流程

程序运行后,生成一个游戏区域类对象,然后不停地接收用户输入的命令,并进行处理。命令格式自定。
游戏开始命令。此命令被转发给游戏区域处理。
退出命令。用户可随时退出游戏,不需要转发给游戏区域处理,直接退出程序。
方块左移、右移、旋转、下移命令:转发给游戏区域处理。注意,由于此时游戏区域对象的函数可能会抛出异常表示游戏结束,所以需要捕获这些异常。捕获到异常时说明游戏结束,此时提问用户是退出还是继续游戏,用户要求退出则退出程序,如果要求继续游戏则执行一次“ 游戏开始命令”即可。

同时我们掌握了代码中定义游戏边界的部分:

public boolean Iscanmoveto()  //围墙的边界
{
    for(int i=0;i<4;i++)
    {
        if(block_box[i][0]<0||block_box[i][0]>19)
        {
            moveback();
            return false;
        }
        else if(block_box[i][1]<0||block_box[i][1]>9)
        {
            moveback();
            return false;
        }
        else if(game_space[block_box[i][0]][block_box[i][1]]==1)
        {
            moveback();
            return false;
        }
    }
    return true;
}

以及消行的方法:

public void CheckAndCutLine()    //判断并消除最低行的方法
{
    int a[]={block_box_tt[0][0],block_box_tt[1][0],block_box_tt[2][0],block_box_tt[3][0]};
    int b[]={30,30,30,30};
    int temp=0;
    int temp1=0;
    int count=0;
    int ss=0;
    for(int i=0;i<4;i++)
    {
        for(int j=0;j<10;j++)
        {
            if(game_space[a[i]][j]==1)
                temp++;
        }
        if(temp==10)
        {
            for(int m=0;m<4;m++)
                if(b[m]==a[i])
                {
                    break;
                }
                else
                    ss++;
            if(ss==4)
            {
                b[count]=a[i];
                count++;
            }
        }
        temp=0;
        ss=0;
    }
    for(int i=0;i<3;i++)
        for(int j=i+1;j<4;j++)
        {
            if(b[i]>b[j])
            {
                temp1=b[i];
                b[i]=b[j];
                b[j]=temp1;
            }
        }
    for(int n=0;n<4;n++)
    {
        if(b[n]==30)
            break;
        else
        {
            for(int aa=b[n]-1;aa>=0;aa--)
            {
                for(int bb=0;bb<10;bb++)
                {
                    game_space[aa+1][bb]=game_space[aa][bb];
                }
            }
            for(int cc=0;cc<10;cc++)
                game_space[0][cc]=0;
        }
    }
}


本周感想:

大致的了解了俄罗斯方块的游戏思路,大概明白每个函数对应的功能,但是关于监听哨的部分了解还不是很详细,希望下周再安排好考试的前提下,完成剩余的内容。

posted on 2016-06-12 14:12  美女与劳工  阅读(206)  评论(0编辑  收藏  举报