天天看点

游戏中实现物体的精确选择

有两种方法,不过都比较相似: (转自AS3天地会)

游戏中的人物很多都是用png位图来做的,有的时候想让人物能够接收一些鼠标事件,必须用到精确选取。

这种精确的选取至少需要两个功能。

第一:不能受到png透明区域的干扰。比如玩家A的位图的透明区域和玩家B的不透明区域重叠,A在上面。鼠标点击的时候应该选中B,而不是A

第二:必须返回这个位图所在的正确的类容器。理解起来比较复杂。简单的来说比如有个人物类叫human,他在初始化的时候通过类中的一个Loader

对象加载一个位图。那应该返回的是这个类human的引用。

原理:通过getObjectUnderPoint取得鼠标点下面所有的物体,然后从上往下判断,如果该点的位置不是透明的就向上找父级有没有你所要找的。

然后设定几个判断条件直到找到正确的引用或者返回null.

复制内容到剪贴板

代码:

public static function SelectObject(contain : DisplayObject,mousex : Number,mousey : Number,classname : String) : * {

   var arr = contain.stage.getObjectsUnderPoint(new Point(mousex, mousey));//取得容器中鼠标处的对象数组

   for(var i = 0;i = 0;i--)//从上至下比较 {

    var bitmapData:BitmapData = new BitmapData(arr[i].width, arr[i].height,true,0);//定义一个位图数据对象准备拷贝数组中的显示对象

    bitmapData.draw (arr[i]);//复制一个显示对象

    if (bitmapData.getPixel32 (arr[i].mouseX,arr[i].mouseY)!=0)//如果不是透明的则表示这个对象是你选择的。

                {

                     bitmapData.dispose ();//清空位图

                      if(arr[i] is (getDefinitionByName(classname) as Class))//选择的物体就是自己本身

                            return arr[i];

                      var j=1;//做一个循环数

                      while(arr[i].parent!=null)//向上找到正确的容器,循环10次找不到就返回null

                      {

                           arr[i]=arr[i].parent;

                           if(arr[i] is (getDefinitionByName(classname) as Class))//如果与你写的容器类型一致表示当前引用正确

                           {

                                return arr[i];

                           }

                           j++;

                           if(j>10||i==0)

                                return null;

                       }

                 }

  }

使用方法:比如要找鼠标下面是否有一个BaseSprite的类的实例,这个类在game包里面

SelectObject (mc,mouseX,mouseY,"game.BaseSprite")//注意要写类的全路径

另一种方法

另类像素级精确选择对象法—蓝图法

  本方法主要是运用bitmapData以及ColorTransform类。

  实现原理是把“层深值”图像化然后根据鼠标当前像素颜色值去取对象。

  至于为什么叫蓝图法,原因是这张用来判断的图片显示的时候基本全是蓝和黑色,要出现其他颜色的话对象数量就必须达到一个巨大的量。所以在同一层级不上万个对象的情况下图片基本就是蓝黑色的。实际用的办法比较另类,也是属于一时突发奇想的结果。    蓝图.jpg (68.59 KB)

判断对象主要分三种情况

1、全静态显示对象处理

2、动态显示对象处理

3、混合对象处理

一、全静态显示对象取得当前鼠标所在位置对象的过程如下:

1、初始化对象。

2、建立对象索引表或者把需要判断的对象addChild到相应容器中。

3、创建一个蓝图的bitmapData尺寸是场景大小。

4、遍历某一层级容器下的显示列表比如stage的,前提是这个容器必须是DisplayObjectContainer子类。同时利用ColorTransform类和DisplayObject对象的transform.colorTransform属性把对象设置成单色,颜色值等于层深值。由于深度值在这个层级下肯定是唯一的,那么每个对象就变成了有唯一颜色的单色对象(完全透明区域不会变色,仍然保持完全透明)。

5、把单色对象按深度从小到大的顺序向蓝图bitmapData里draw(),draw()完再次设置DisplayObject对象的transform.colorTransform属性为空把对象颜色恢复正常。最终循环完成的时一张蓝黑色的图片也就是蓝图就生成了。

6、生成的蓝图并不需要显示或者addChild到任何地方存在即可。

7、用enterFrame或者鼠标事件或者setInterval来作为触发来获得当前鼠标的X,Y坐标。

8、用bitmapData的getPixel(x,y)方法从蓝图中获取相应坐标的颜色值,也就是获得当前鼠标下那点的蓝图颜色。而这个颜色就是一个深度,那么直接stage.getChildAt()就可以方便的返回这个深度的对象了。

  全静态的蓝图只需要制作一次,因为里面的视觉元素根本不会变化。如果场景平移那么蓝图也相应平移即可。所以比较消耗资源的就是初始化的时候,之后由于只调用了getPixel()和getChildAt()可以说速度相当优异(100000次循环在100毫秒以内,那么单次大概0.001毫秒以下)。

二、动态对象获取

  基本原理和静态的相同,但是由于待选对象是运动的可能是动画可能是复杂移动,那么蓝图只做一次明显就无法满足。那么就需要随时刷新蓝图,但是如果对象过多明显draw()这个占资源的东西会造成太多的系统消耗。那么我们就不要去把所有动态对象做进蓝图,只需要把鼠标点所在的那个点下的动态对象做成蓝图并取色即可。

  这里获取当前点下的目标列表可以使用stage.getObjectsUnderPoint(point)方法。而获得对象深度值则使用getChildIndex(dislplayObject)来取得唯一颜色值。

  在触发判断的瞬间把列表中的对象变色并且取得鼠标坐标点颜色然后再把对象变回去。由于过程很快实际显示上不会有任何影响。

三、混合对象处理

  比如游戏场景中的房屋街道家具树木这些都是静态对象,而玩家敌人宠物或者一些场景动画就属于动态对象。混合处理就是把静态的做静态蓝图,再在对动态对象获取的时候把静态对象从列表中排除。检测结果如果没有任何动态对象被选中再检测静态对象,毕竟静态对象只需要获取一次颜色就可以直接返回对象。