×

俄罗斯方块游戏开发(二)–方块的显示与定时下落

发表于3年前(Dec 23, 2014 7:08:32 PM)  阅读 781  评论 0

分类: Flex 俄罗斯方块

标签: tetris 俄罗斯方块 定时下落

这节我们主要讲如何让方块在游戏区域显示以及定时下落。首先我们知道,标准tetris一共有7种方块,每种方块最多有四种形态,有的只有一种如正方形块,有的只有两种如竖条横条,当然我们可以把他们也看做有四种形态,只是有些形态样式相同而已,这个影响不大。参考传智播客的视频方法,方块横向竖向最大宽度为4个格子,我们可以用一个4*4的方阵来表示每个方块以及他们的状态,如下面的方阵,转化为程序,我们可以用一个二维数组来表示,红色的部分值为1,白色部分值为0,画图时,我们只对1的部分画单元格,模型大概就是这样。

我们新建一个包,取名为shape,用来存放图形有关的类。新建一个Shape类,继承于UIComponent,UIComponent是一个可视化的组件,因为GamePanel是一个Canvas,我们可以将Shape加入到Canvas里面来进行显示。在Shape里我们先定义一些必要的变量:


/**
 * 存储方块cell方阵值,一个方块有四种形态,所以这里cells是一个三维数组
 */
private var _cells:Array;

/**
 * 当前方块的状态,值为0,1,2,3,4
 * 4表示该方块已经落地,应该销毁了
 */
private var _status:Number;

/**
 * 方块的颜色
 */
private var _color:uint;

/**
 * 方块方阵离游戏区域左边界的cell数,初始为5,从中间下落
 */
private var _left:Number = 5;

/**
 * 方块方阵离游戏区域上边界的cell数,初始为0,从顶部下落
 */
private var _top:Number = 0;

然后定义一个draw方法,这个方法用来画方块,当方块发生变化(包括坐标变化,形态变化),我们必须调用draw方法进行重画。然后我们给前面定义的私有变量定义set、get方法,在这里的set方法,我让值改变时就自动重画,避免了手动调用draw方法,关于效率方面的问题有待考证,姑且先这么做。


public function get left():Number {
	return _left;
}

/**
 * 设置left值,值改变时,自动重画
 */
public function set left(value:Number):void {
	if(_left!=value) {
		_left = value;
		draw();
	}
}

public function get top():Number {
	return _top;
}

/**
 * 设置top值,值改变时,自动重画
 */
public function set top(value:Number):void {
	if(_top!=value) {
		_top = value;
		draw();
	}
}

public function get status():Number {
	return _status;
}

/**
 * 设置status值,值改变时,自动重画
 */
public function set status(value:Number):void {
	if(_status!=value) {
		_status = value;
		draw();
	}
}

public function get cells():Array {
	return _cells;
}

public function get color():uint {
	return _color;
}

然后方块有移动变形下落方法,我们给加上:


/**
 * 旋转变形
 */
public function rotate():void {
	status = (status+1)&cells.length;
}

public function moveLeft():void {
	left--;
}

public function moveRight():void {
	left++;
}

public function moveDown():void {
	top++;
	trace("moveDown");
}

现在方块大部分方法都已经加上了,但是方块还没有画出来,draw方法还是空的。我们知道方块都是由一个个小方格组成的,我们先再新建一个类,叫Cell类,也继承于UIComponent,他的任务就是画一个单元格。然后我们在draw方法里面创建单元格,让一个个单元格组成我们所需要的方块图形。

/**
 * 画方块
 */
public function draw():void {
	var cell:Cell = null;

	//清除之前的图像
	graphics.clear();
	while((cell = drawCells.pop())!=null) {
		removeChild(cell);
	}

	//获得当前状态的点阵,是一个二维数组
	var cells:Array = this.cells[status];
	for(var i:Number=0;i<cells.length;i++) {
            for(var j:Number=0;j<(cells[i] as Array).length;j++) {
			var flag:Number = cells[i][j];
			//如果这个方块还没进入游戏面板区域或已经超出游戏面板区域就不画了
			if(flag==1&&(top+j>=0&&top+j<=19)) {
				cell = new Cell(color);
				cell.x = (left+i)*Cell.WIDTH;
				cell.y = (top+j)*Cell.WIDTH;
				drawCells.push(cell);
				addChild(cell);
			}
		}
	}
}

在这里我们还得建立一个数组drawCells用来保存当前所画的cell,下次重画时,必须先清除这些cell,这样Shape类就大概完成了。如何产生Shape呢,很自然的想到了工厂模式,建立一个工厂类ShapeFactory,建立一个静态方法getRandomShape用来获得一个随机方块以及他的状态。ShapeFactory.as:


package com.cangzhitao.flex.tetris.shape
{
	/**
	 * shape工厂类
	 *
	 */
	public class ShapeFactory
	{

		/**
		 * 所有方块点阵,四维数组
		 */
		private static const CELLS:Array = new Array(
			//第一个方块
			new Array(
				//第一个形态
				new Array (
					new Array (1, 1, 1, 0),
					new Array (0, 0, 1, 0),
					new Array (0, 0, 0, 0),
					new Array (0, 0, 0, 0)
				),
				//第二个形态
				new Array (
					new Array (0, 1, 0, 0),
					new Array (0, 1, 0, 0),
					new Array (1, 1, 0, 0),
					new Array (0, 0, 0, 0)
				),
				//第三个形态
				new Array (
					new Array (1, 0, 0, 0),
					new Array (1, 1, 1, 0),
					new Array (0, 0, 0, 0),
					new Array (0, 0, 0, 0)
				),
				//第四个形态
				new Array (
					new Array (1, 1, 0, 0),
					new Array (1, 0, 0, 0),
					new Array (1, 0, 0, 0),
					new Array (0, 0, 0, 0)
				)
			)
		);

		/**
		 * 方块的颜色,每种方块对应一种颜色
		 */
		private static const COLORS:Array = new Array(
			0x95B448,
			0x95B448,
			0x95B448,
			0x95B448,
			0x95B448,
			0x95B448,
			0x95B448
		);

		public function ShapeFactory()
		{

		}

		public static function getRandomShape():Shape {
			var shapeType:Number = getRandom(0, CELLS.length-1);
			var status:Number = getRandom(0, 3);
			return new Shape(CELLS[shapeType], status, COLORS[shapeType]);
		}

		/**
		 * 获得[x,y]之间的随机整数
		 */
		public static function getRandom(x:Number, y:Number):Number {
			return Math.round(Math.random()*(y-x))+x;
		}

	}
}

注意,AS不支持多维数组,我们只能通过数组里面存数组来实现,但是我们可以使用a[i][j]来访问数组里面的元素。这里我给出了一个图形的四种状态点阵,后来显示时发现显示的方块是和这个正好相反的那个,不是我预期的那个,是四维数组最后两维的存储弄反了,这也没关系,反正这些都要写的。

最后我们给方块加上定时下落方法,实现方块在游戏区域的显示。定时下落很简单,flash.utils包里提供了一个setInterval方法,类似js里面的定时方法,在这里我们可以很方便地让方块定时去调用它的下落方法,并且通过设置定时时间达到控制方块下落速度的目的,这是后话,这里我们先固定下落时间为1s。 这样方块就能自动下落了。

这节源码下载地址:/upload/2014/11/23/TETRIS1.02.zip

发表评论