第一篇:基本应用
在塔防类、空战类游戏中,经常会出现同屏幕有很多对象的情况(如子弹)。而如果使用MovieClip来实现的话,当数量太多的时候,就会占用太多资源,令程序运行很慢。这个时候,可以通过使用图像缓冲来替代。同时,本文所描述的实现思路,也是一种将数据和显示分开进行的方法。在很多的游戏设计中经常会用到。
我们以同屏幕500个子弹为例,来看一下图像缓冲的实现方法。首先,来看一下总体的程序思路
1.主场景使用名为bitmapfill的BitmapData,通过graphics.beginBitmapFill来进行全场景填充。这样,我们就可以通过enterFrame事件,定期刷新bitmapfill的内容来实现动画。
2.声明子弹的时候,把bitmapfill传递给子弹类
3.enterFrame侦听,触发渲染事件。在渲染事件中循环全部子弹对象调用其自身的渲染函数
4.在子弹自身的渲染函数中,计算子弹当前的位置,然后使用copyPiexl方法将自身的素材复制到bitmapfill的正确位置
5.循环实现子弹的动画效果。
为了实现以上方法,我们首先构建一个子弹类:
package { import flash.display.BitmapData; import flash.geom.Point; /** * 图形缓冲范例子弹类 * Author:D5Power * www.d5power.com www.webgamei.com */ public class zidan { /** * 子弹的图形素材 */ private var doc:BitmapData; /** * 主场景的缓冲区 */ private var buffer:BitmapData; /** * x坐标 */ private var x:uint; /** * y坐标 */ private var y:uint; /** * x轴速度 */ private var speed_x:uint = 3; /** * y轴速度 */ private var speed_y:uint=3; /** * 子弹构造函数 * @param _doc 子弹素材 * @param _buffer 主场景缓冲区 */ public function zidan(_doc:BitmapData,_buffer:BitmapData) { doc = _doc; buffer=_buffer; } /** * 设置子弹的位置 */ public function set Pos(p:Point):void { x = p.x; y = p.y; } /** * 渲染函数,当子弹超出屏幕边界则反弹 */ public function render():void { x+=speed_x; y+=speed_y; if(x>main.W || x<0) { speed_x=speed_x*-1; } if(y>main.H || y<0) { speed_y=speed_y*-1; } // 将子弹的素材按照数据的标准位置,复制到缓冲区 buffer.copyPixels(doc,doc.rect,new Point(x,y),null,null,true); } } }
接下来看一下主场景的实现方法:
package { import flash.display.Sprite; import flash.display.BitmapData; import flash.events.Event; import flash.geom.Point; /** * 图形缓冲范例主场景 * Author:D5Power * www.d5power.com www.webgamei.com */ public class main extends Sprite { /** * 缓冲区 */ private var buffer:BitmapData; /** * 子弹序列 */ private var objects:Array; /** * 场景宽度 */ public static var W:uint = 550; /** * 场景高度 */ public static var H:uint = 400; public function main() { // 声明缓冲区 buffer = new BitmapData(W,H,true,0x00000000); objects = new Array(); // 获取子弹素材 // 本例用FLASH开发,因此zidan_doc实际为库中导出的BitmapData类 var doc:BitmapData = new zidan_doc(0,0); // 渲染侦听 addEventListener(Event.ENTER_FRAME,render); // 声明500个子弹,添加到子弹序列中 for(var i:uint=0;i<500;i++) { var zd:zidan = new zidan(doc,buffer); var rand_x:uint = Math.floor(Math.random()*W); var rand_y:uint = Math.floor(Math.random()*H); zd.Pos = new Point(rand_x,rand_y); addObjects(zd); } // 以缓冲区填充场景 graphics.beginBitmapFill(buffer); graphics.drawRect(0,0,buffer.width,buffer.height); graphics.endFill(); } /** * 向子弹序列中添加对象 * @param o */ private function addObjects(o:zidan):void { objects.push(o); } /** * 渲染 * @param e */ private function render(e:Event):void { buffer.fillRect(buffer.rect,0); // 循环全部子弹序列,渲染每个子弹 for each(var zd:zidan in objects) { zd.render(); } } } }
本文代码只是提供了一个基本的思路,在实际的使用过程中,还是要做一些必要的扩展。例如,本文的主场景只提供了addObject来向子弹序列中增加对象,而没有反操作的过程。同时,在RPG游戏中,可能需要可以触发鼠标事件,而我们使用了位图缓冲后,就不能直接进行事件侦听了。等等这些问题,都是需要大家在实际的开发中思考的。
第二篇 响应鼠标事件
采用图像缓冲后,整个场景其实只是在不断的重新绘制(渲染)。场景中再没有任何的childObject。因此是无法通过addEventListener来进行鼠标事件的判断的。因此我们需要通过其他的方法来实现。
在上一篇中,我们使用了addObjects方法把某个渲染对象增加到渲染序列中,我们可以对渲染序列加以利用,从而找出当前所点击的渲染对象。
我们已经知道每个渲染对象都有自己的坐标,而鼠标点下后,又可以获得舞台坐标stageX和stageY。于是我们可以计算当前的鼠标对应每个渲染对象的坐标(stageX-x,stageY-y),同时,Bitampdata为我们提供了像素级的碰撞检测。于是,我们就可以通过鼠标的相对坐标来判断与场景中的哪个渲染对象发生了碰撞,也就是点击的对象了。
重新写过的代码如下:
package { import flash.display.BitmapData; import flash.geom.Rectangle; public class gObject { /* 位图数据 */ public var bitmap:BitmapData; /* x坐标 */ public var x:Number; /* y坐标 */ public var y:Number; /* 当前渲染对象的ID */ public var ID:uint; public function gObject() { // constructor code // 绘制一个40*40的画布,并在画布中央画一个20*20的正方形,其他位置透明 bitmap = new BitmapData(40,40,true,0x00000000); bitmap.fillRect(new Rectangle(10,10,20,20),0xFF0000ff); } } }
以上类相当于子弹类。我们只保留了最基本的属性,以免程序变的不好读懂:)
再看一下实现功能的代码:
(以下代码没有通过类实现,而是直接写到了FLASH的时间轴上)
import flash.display.BitmapData; import flash.geom.Rectangle; import flash.geom.Point; // 渲染列表 var objList:Array = new Array(); // 缓冲区 var bd:BitmapData = new BitmapData(550,400,true,0x00000000); var rx:int; var ry:int for(var i:uint = 0;i<10;i++) { var obj:gObject = new gObject(); rx = Math.floor(550*Math.random()); ry = Math.floor(400*Math.random()); obj.x = rx; obj.y = ry; obj.ID=i; objList.push(obj); bd.copyPixels(obj.bitmap,obj.bitmap.rect,new Point(rx,ry)); } // 渲染 graphics.beginBitmapFill(bd); graphics.drawRect(0,0,550,400); // 增加侦听事件 stage.addEventListener(MouseEvent.CLICK,onclick); // 循环渲染列表,确认当前点击的是哪个对象 function onclick(e:MouseEvent):void { var tx:Number; var ty:Number; for each(var obj:gObject in objList) { tx = e.stageX-obj.x; ty = e.stageY-obj.y; if(obj.bitmap.hitTest(new Point(0,0),0xFF,new Point(tx,ty))) { trace("您点击了"+obj.ID); break; } } } // 向渲染列表中增加渲染对象 function addObject(obj:gObject):void { objList.push(obj); }
以上代码只是从功能上实现了对鼠标时间的相应,确认点击的是哪个对象。但是在实际的游戏开发中,同场景(如果你的场景非常大)的角色可能很多,全部循环过去是非常浪费资源的。所以,可以通过其他对象相对主角的距离等方式来加以限制。这样在渲染的时候也可以节约资源:)