团队项目结题

团队项目结题

小组项目名称:

俄罗斯方块代码实现

项目简介:

我们小组学习的是俄罗斯方块代码的简单实现,在原有代码的基础上,对代码进行理解和测试。我们的代码主要分两部分模块:一部分是图形界面的,一部分是操作界面的代码,对于操作界面的代码,由荆玉茗和史婧瑶同学负责,另一部分图形化的代码由韩昊辰和祁玮同学负责。

设计思路

1.界面设计 首先进行游戏区域的设计,主要的游戏功能都在游戏区域显示,比如方块的随机选择,画布的背景显示等等。 其次是系统功能显示区域,这里显示了当前所玩游戏的等级,右方可以选择不同的游戏级别,一共有1、2、3、4、5五种游戏级别的选择;下方是游戏画布背景的选择,一共有浅绿、浅黄、黑色三种背景颜色的选择;最下方有开始游戏、暂停游戏、结束游戏和退出游戏四中功能选项。
2.游戏功能设计 在无人操作时,游戏开始后会随机产生一个下坠物,先显示在界面右上角的显示区域,然后转到游戏区域,右上角又随机产生一个新的下坠物,当该下坠物下落到底后,新的下坠物再次进入游戏区域,如此循环,直到游戏结束/暂停,这就是游戏的正常工作。 上述过程是在无人操作时游戏自动运行的,当用户进行一定的操作交互的时候,运行程序可以根据用户的操作指示进行当前下坠物的控制。而这些操作都是响应相关的功能键而执行的,所以这里可以把它看成一种“中断”的操作。 在中断过程中,可以通过键盘包括按某些键进行操作。为了便于用户操作,用户可以自主选择操作键,但他们的作用不变。但还是应该设置默认键来方便操作。例如:光标向上键,光标向下键,光标向左键,光标向右键。
(1)按光标向上键 此事件产生下坠物旋转操作,下坠物并非任何情况都能旋转,如果旋转后与小方格矩阵显示的下坠物有冲突或超出边界时,均不能发生旋转。因此首先要判断是否有足够的空间进行旋转,然后选择是否旋转。
(2)按光标向下键 此事件产生下坠物加速下落操作,如果下坠物已经到达游戏区域底部或者已经有其他方块遮挡,则下坠物停止下降。
(3)按光标向左键此事件产生下落下坠物左移操作。首先要判断此下坠物是否能够发生左移,当越界或被其他显示下坠物阻挡时,则不能左移。
(4)按光标向右键 此事件产生下落下坠物右移操作。首先要判断此下坠物是否能够发生右移,当越界或被其他显示下坠物阻挡时,则不能右移。

代码注释(图形界面部分)

package fangkuai;

/**
 * Created by Administrator on 2016/6/5 0005.
 */


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[][][]={                              //游戏方块每种ID的方块的形状数据存储好(都是一些固定的数据)
            {{-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;                 //产生一个方块时只需要提供其ID即可
        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];
        }
    }

    public void movedown()         //方块下移
    {
        block_x++;                 //ID的x自加,y不变
        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--;                //ID的y自减,x不变
        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++;                //ID的y自加,x不变
        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)                          //这些方块旋转周期为2
        {
            if(id==2)                                           //id记录旋转次数,满二次就返回最开始形态,同时记录清零
            {
                block_type--;
                ic--;
            }
            else
            {
                block_type++;
                ic++;
            }
        }
        else                                                  //这些方块旋转次数周期为4
        {
            if(id==4)                                          //旋转周期已满,返回最开始形态,同时记录清零
            {
                block_type=block_type-3;
                ic=ic-3;
            }

            {
                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];
            }
        }
    }

    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 boolean Ishitbottom()
    {
        for(int i=0;i<4;i++)//适用游戏高度如果没有变化
        {
            if(block_box[i][0]+1>19)     //方块中任意一个部分满足碰底条件
            {
                for(int m=0;m<4;m++)
                {
                    game_space[block_box[m][0]][block_box[m][1]]=1;//改变了游戏原有的空间大小了
                    block_box_tt[m][0]=block_box[m][0];//利用新数组存储碰底的模块
                    block_box_tt[m][1]=block_box[m][1];
                    block_box[m][0]=0;//原模块清零
                    block_box[m][1]=0;
                }
                return true;
            }
        }
        for(int i=0;i<4;i++)
        {
            if(game_space[block_box[i][0]+1][block_box[i][1]]==1)//适用游戏高度如果变化了
            {
                for(int m=0;m<4;m++)
                {
                    game_space[block_box[m][0]][block_box[m][1]]=1;
                    block_box_tt[m][0]=block_box[m][0];
                    block_box_tt[m][1]=block_box[m][1];
                    block_box[m][0]=0;
                    block_box[m][1]=0;
                }
                return true;
            }
        }
        return false;//都不满足,则游戏中的模块还未碰底
    }

    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++)//判断底部是否都为1
            {
                if(game_space[a[i]][j]==1)
                    temp++;//底部每有一个,记录1
            }
            if(temp==10)//倒数四行中某一行满足都为1的话
            {
                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;
            }
        }
    }

    public boolean IsGameOver()
    {
        boolean flag=false;
        for(int i=0;i<sp_width;i++)
        {
            if(game_space[0][i]==1)//bug存在的地方,条件不充分
            {
                flag=true;
                break;
            }
        }
        return flag;
    }

    public void sure()
    {
        for(int i=0;i<4;i++)
            game_space[block_box[i][0]][block_box[i][1]]=1;
    }

    public void notsure()
    {
        for(int i=0;i<4;i++)
            game_space[block_box[i][0]][block_box[i][1]]=0;
    }

    public boolean judge(int i,int j)
    {
        if(game_space[i][j]==1)
            return true;
        else
            return false;
    }
}

代码注释(操作部分)

package afterclass;

import java.awt.*;  //引入AWT包,因为要使用到颜色类
import java.awt.event.*;
import java.awt.geom.*;
import javax.swing.*;

public class Win 
{
	public static void main(String[] args) 
	{
        GameWin frame=new GameWin();  //frame是带有标题和边框的顶层窗口
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);  //利用JFrame设置单机关闭按钮时执行关闭窗口
        frame.setVisible(true);  //JFrame创建的窗口默认是不可见的,设置为可见的方法,并将入口参数设为true
	}
	
	public static boolean isplaying=true;
}

class GameWin extends JFrame
{
	public GameWin()
	{
		this.setFocusable(true);  //将控件设置成可获取焦点状态,设置成true,才能获得控件的点击事件
		getContentPane().requestFocus();
		this.setAlwaysOnTop(true);  //将窗口一直处于屏幕的最前端,永远挡住前的窗口
		setTitle("super russionBlock");
		setBounds(350,90,Default_X,Default_Y);  //获得边界,前两个参数是窗体左上角在容器中的坐标,后两个参数是窗体的宽度和高度
		setResizable(false);  //当resizable为false时,表示生成的窗体大小是由程序员决定的,用户不可以自由改变该窗体的大小
		add(jpy1);  
		jpy1.setDividerLocation(304);  //设置分隔条的大小
		jpy1.setDividerSize(4);   //给定一个整数,以像素为单位
		addKeyListener(jpy2);  //为每个按钮添加消息监听
		Thread t=new Thread(jpy2);
		t.start();
	}
	
	public static final int Default_X=500;
	public static final int Default_Y=630;
    private left jpy2=new left();
    private right jpy3=new right();
	private JSplitPane jpy1=new JSplitPane(JSplitPane.HORIZONTAL_SPLIT,jpy2,jpy3);
}  //

class right extends JPanel implements ActionListener  //JPanel是java图形化界面中最常用的容器
{
	public right()
	{
		initialframe();
		initialListener();
	}
	
	public void initialframe()  //初始化框架
	{
		setLayout(null);  // 设置窗体布局为空布局
		add(jlArray[0]);
		jlArray[0].setBounds(30,60,70,30);  //前两个参数是组件左上角在容器中的坐标,后两个参数是组件的宽度和高度
		jlArray[0].setFocusable(false);  //将控件设置成可获取焦点状态,设置成true,才能获得控件的点击事件
		add(jlArray[1]);
		jlArray[1].setBounds(30,140,70,30);
		jlArray[1].setFocusable(false);
		add(jcArray[0]);
		jcArray[0].setBounds(100,60,70,30);
		jcArray[0].setFocusable(false);
		add(jcArray[1]);
		jcArray[1].setBounds(100,140,70,30);
		jcArray[1].setFocusable(false);
		add(jbArray[0]);
		jbArray[0].setBounds(50,240,100,35);
		jbArray[0].setFocusable(false);
		add(jbArray[1]);
		jbArray[1].setBounds(50,310,100,35);
		jbArray[1].setFocusable(false);
		add(jbArray[2]);
		jbArray[2].setBounds(50,380,100,35);
		jbArray[2].setFocusable(false);
		add(jbArray[3]);
		jbArray[3].setBounds(50,450,100,35);     
		jbArray[3].setFocusable(false);
	}

	public void initialListener()  //初始化监听哨
	{
		for(int i=0;i<4;i++)
		    jbArray[i].addActionListener(this);  //ActionListener是用于接收操作事件的监听器接口
	}
	
	public void actionPerformed(ActionEvent e)  //ActionListener接口里的方法
	{
		if(e.getSource()==jbArray[0])  //获取事件中所作用的对象
		{
			Win.isplaying=true;
		}
		else if(e.getSource()==jbArray[1])
		{
			Win.isplaying=false;
		}
		else if(e.getSource()==jbArray[2])
		{
			Win.isplaying=false;
		}
		else if(e.getSource()==jbArray[3])
		{
			System.exit(0);
		}
	}
	
	private String[] level={"1"};
	private String[] color={"浅黄"};
	private JComboBox[] jcArray={new JComboBox(level),new JComboBox(color)};  //构造函数
	private JLabel[] jlArray={new JLabel("游戏等级"),new JLabel("空间背景")};  
	private JButton[] jbArray={new JButton("开始游戏"),new JButton("暂停游戏")  
	                          ,new JButton("结束游戏"),new JButton("退出游戏")};
}

class left extends JComponent implements KeyListener,Runnable  //JComponent类是除容器顶层外所有Swing组件的基类
{
	public left()
	{
		game=new RussionBlockGame();
	}
	
	public void paintComponent(Graphics g)  //绘制容器中的每个组件
	{
		Graphics2D g2=(Graphics2D)g; //抽象类,将paint()方法作为画笔
		super.paintComponent(g);  //把整个面板用背景色重画一遍,起到清屏的作用
		double width=300,height=600;
		Rectangle2D rect=new Rectangle2D.Double(0,0,width,height);  //指定坐标空间的一个区域左上方(0,0)点 ,宽度、高度
		g2.setColor(Color.black);  //设置颜色
		g2.draw(rect);
		g2.setColor(Color.yellow);  //背景为黄色填充
		g2.fill(rect);
		g2.setColor(Color.black);  //边框为黑色
		g2.draw(rect);  //绘制一个矩形
		for(int i=0;i<20;i++)
			for(int j=0;j<10;j++)
			{
				if(game.judge(i,j)==true)
				{
					Rectangle2D rect3=new Rectangle2D.Double(j*boxSize,i*boxSize,boxSize,boxSize);
					g2.setColor(Color.black);
					g2.draw(rect3);
					g2.setColor(Color.red);  //方块为红色
					g2.fill(rect3);
					g2.setColor(Color.black);
					g2.draw(rect3);
				}
			}
		game.notsure();
	}
	
	public void keyTyped(KeyEvent e)  //keyTyped表示键盘按下,然后释放
	{}
	
	public void keyPressed(KeyEvent e)   //keyPressed表示键盘按下,未释放
	{
		if(Win.isplaying==false)
			return;
		switch(e.getKeyCode())
		{
		case KeyEvent.VK_LEFT:                                       //LEFT
			game.moveleft();
			movejudge();
			break;	
		case KeyEvent.VK_UP:                                         //UP
			game.turnright();
			movejudge();
			break;                                   
		case KeyEvent.VK_RIGHT:                                      //RIGHT
			game.moveright();
			movejudge();
			break;
		case KeyEvent.VK_DOWN:                                       //DOWN
			game.movedown();
			movejudge();
			break;
		}
	}

	public void keyReleased(KeyEvent e)  //keyReleased表示键盘被释放
	{}	
	
	public void movejudge()  //判断消行的方法
    {
    	if(game.Iscanmoveto()==true)
    	{
    		game.sure();
    		repaint();
    	}
    	else if(game.Ishitbottom()==true)
    	{
    		game.CheckAndCutLine();
    		game.makenewblock();
    		repaint();
    		if(game.IsGameOver()==true)
    			Win.isplaying=false;
    	}
    }

	public void run()  //下落线程
	{
		try
		{
			while(Win.isplaying)
			{
		        Thread.sleep(500);  //通过设置睡眠时间控制下落速度
	    	    game.movedown();
    		    movejudge();
			}
		}
		catch(Exception e)
		{
			e.printStackTrace();
		}
	}
	
	private RussionBlockGame game;
	public static final int boxSize=30;
}

程序结果运行

程序暂停

俄罗斯方块削行前

俄罗斯方块削行后

程序流程图

小组具体分工

  • 韩昊辰:代码的调试以及前几周博客的编写,以及对图形化代码部分的注释
  • 祁 玮:代码的寻找以及前几周博客的编写,以及对图形化代码部分的注释
  • 荆玉茗:对于代码进行bug测试,以及对操作代码的注释
  • 史婧瑶:对于代码进行bug测试,以及对操作代码的注释

最后分值分配

  • 韩昊辰:30分
  • 祁 玮:20分
  • 荆玉茗:25分
  • 史婧瑶:25分

总结体会

因为之前学的java内容还不是很熟练,所以我们对代码的注释差不多都是一行一行百度到的。通过本次学习我了解到,很多我们看似复杂的程序其实都是有据可循,比如一个小游戏,他是在图形界面里面完成的,将我们以前看见的代码行以图形的界面展示出来,并且很多东西在java的API中都有相应的内容,比如frame类是带有标题和边框的顶层窗口;利用JFrame设置单机关闭按钮时执行关闭窗口......很多很多我们所获取的每一个界面都有相应的程序与之对应,其实掌握并不难,API的东西就像固定的语法,只要知道它是怎么用,用来干什么的就可以。通过本次的学习让我真正的感受到了一个游戏的实现,其实并没有我们想象的那么难,只要找到问题,将问题细化,我们就能得到找到解决的方法。

原文地址:https://www.cnblogs.com/dky201452/p/5597297.html