继续俄罗斯方块,下半部分

头序:上半部分最后实现了移动,那接下来就该实现旋转了;

 1,实现旋转最重要也是最难的部分,就是该用什么算法实现旋转;

 2,根据四方法的初始形态不同,每个四方格有1~4中不同的表现个数,例如L型方格有四种,O型就只有一种;

      由此可以在四方格的父类中定义一个状态的数组,但不定义个数,由每个子类来提供数组的大小和每种表现形态,

      每个四方法都选一个小方格作为轴,在此方格中,不论任何形态轴的坐标是不变的,然后其他的方格的状态坐标都定义为对        于轴的相对坐标;提供一个rotateRight()方法通过相对坐标来获得在坐标轴中的绝对坐标然后实现顺时针向右旋转,也可以实现逆时针旋转,可顺时针类似,就不多做详解了;

  //旋转状态属性,状态个数以数组的形式进行存储
protected State[] states;

     //四方格顺时针向右旋转

public void rotateRight(){
//旋转有一次,计算器增长1
   count++;
   State s=states[count%states.length];
   //需要获取轴的行号和列号
   Cell c= cells[0];
   int row=c.getRow();
   int col=c.getCol();
   cells[1].setRow(row+s.row1);
   cells[1].setCol(col+s.col1);
   cells[2].setRow(row+s.row2);
   cells[2].setCol(col+s.col2);
   cells[3].setRow(row+s.row3);
   cells[3].setCol(col+s.col3);

}

       T形态中的各种的形态的相对坐标给值实例

         public T(){
cells[0]=new Cell(0,4,Tetris.T);
cells[1]=new Cell(0,3,Tetris.T);
cells[2]=new Cell(0,5,Tetris.T);
cells[3]=new Cell(1,4,Tetris.T);
states =new State[4];
states[0]=new State(0,0,0,-1,0,1,1,0);
states[1]=new State(0,0,-1,0,1,0,0,-1);
states[2]=new State(0,0,0,1,0,-1,-1,0);
states[3]=new State(0,0,1,0,-1,0,0,1);

 }

3,在Tetris类中写rotateRightAction()方法调用四方格父类(Tetromino)中的旋转方法(rotateRight()),并在监听器中加上up(↑)键完成旋转   下面方法体中的noMove()和coincide()在上半部分中详细讲解过,此处就不多做解释

//按up键顺时针旋转的方法
public void rotateRightAction(){
currentOne.rotateRight();
if(noMove()||coincide()){
currentOne.rotateLeft();
}

}

4,旋转完后,接下来就该是满行消去了

 消行可以从上往下消也可以从下往上消,我用的是从下往上消,因为它相对更安全和方便一些,不会出现从下往上的下消上消不了的情况

我用的算法是,先找出次方格中四格小格的最小行,然后从此行开始消,直到底部,若碰到满行就消去,然后上面的每行都向下平移,其间会调用isFullLine(int row)判断此行是否满 ,且在此方法中用局部变量用来记录此四方格消去的行数,并把次四方法的分数和消去的行数分别加入了全局变量总分和总行数中,    代码实例如下

/*
* 满一行,就进行消除,上面的方块都要向下平移
*/
public void destroyLine(){
//统计销毁行的行数
int lines=0;
Cell [] cells=currentOne.cells;
int minRow=100;
//找出四方格中行数最小的行数
for(Cell c1:cells){
if(c1.getRow()<minRow){
minRow=c1.getRow();
}
}
while(minRow<20){
//查看标记,标记没变时说明满行
if(isFullLine(minRow)){
//记录次四方格能满行的个数
lines++;
//使用null填满数组元素
//Arrays.fill(wall[minRow],null);

wall[minRow]=new Cell[10];
for(int i=minRow;i>0;i--){
//wall[i]=wall[i-1];
System.arraycopy(wall[i-1], 0, wall[i], 0, 10);
}
Arrays.fill(wall[0], null);
}
minRow++;
}
//从分数池中取出分数加入总分
totalScore+=scores_pool[lines];
totalline+=lines;

}
//判断此行是否满
public boolean isFullLine(int row){
Cell line[]=wall[row];
for(Cell l:line){
if(l==null){
return false;
}
}
return true;

}

5,消去实现后,就可以在游戏界面的右边空闲的地方加上分数,和总共消去的行数,还有此时的状态(暂停,游戏中,gameover) 提供一个画分加画行的方法paintScore(Graphics g),规定了画的格式 ,字体大小等,总分,行数已经在消去方法中算出,在paint()中调用此方法即可

 //画分的方法
public void paintScore(Graphics g){
g.setFont(new Font(Font.SANS_SERIF, Font.ITALIC, 30));
g.drawString("SCORES:"+ totalScore, 285,165);
g.drawString("LINES:"+ totalline, 285,215);

}

    //定义三个常量充当游戏的状态
public static final int PLAYING=0;//表示正在游戏
public static final int PAUSE=1;//表示暂停

public static final int GAMEOVER=2;//表示gameover,游戏死亡(结束)

      然后在paint()方法中加入三个判断,分别判断相应的状态,并画出对应状态应有的状态

     if(gameState==GAMEOVER){//当此判断成立时,画出结束的那张图片
g.drawString(showState[GAMEOVER],285,265);
g.drawImage(game_over,0,0,null);
}
if(gameState==PLAYING){
g.drawString(showState[PLAYING],285,265);
}
if(gameState==PAUSE){
g.drawString(showState[PAUSE],285,265);
}

 6,接下来就是在监听器中再加入四个键P,SHIFT,C,S来分别实现,暂停,暂停后的继续,重来一局,直接gameover    

       通过用键来作为if语句中的判断条件,来改变全局变量int型gameState(代表着状态)的值,         

                            //获取一下键子的代号

int code=e.getKeyCode();
if(code==KeyEvent.VK_P){
gameState=PAUSE;
}
if(code==KeyEvent.VK_ENTER){
gameState=PLAYING;
}
if(code==KeyEvent.VK_S){
gameState=GAMEOVER;
}
if(code==KeyEvent.VK_C){
restart();
}

                  if(gameState==PLAYING){                                       
try {
Thread.sleep(300);


} catch (InterruptedException e) {
e.printStackTrace();
}
if(canDrop()){
currentOne.softDrop();
/*
* 下落之后要重新绘制,才能看到下落后的位置调用repaint方法, 也是JPanel类中提供的
* repaint方法中调用了paint方法
*/

}else{
landWall();
destroyLine();
if(!isGameOver()){
currentOne=nextOne;
nextOne=Tetromino.randomOne();
}else{
gameState=GAMEOVER;
}

}

                     while (true) {  //这里面包含了调用下落的方法
                        /*
* 程序执行到此,进入睡眠状态 睡眠时间为300毫秒 300毫秒后,会自动执行后面的代码
*/
if(gameState==PLAYING){//只有当此判断条件成立时才会执行if中的下落方法,才会继续下落
try {
Thread.sleep(300);
                                        } catch (InterruptedException e) {
e.printStackTrace();
}
if(canDrop()){
currentOne.softDrop();
/*
* 下落之后要重新绘制,才能看到下落后的位置调用repaint方法, 也是JPanel类中提供的
* repaint方法中调用了paint方法
*/

}else{
landWall();
destroyLine();
if(!isGameOver()){
currentOne=nextOne;
nextOne=Tetromino.randomOne();
}else{
gameState=GAMEOVER;
}

}
repaint();
}

       

7,到此基本完成了,所有的功能都能实现了;

猜你喜欢

转载自blog.csdn.net/qq_40881157/article/details/80542842