游戏效率优化之 如何降低容器子集好庞大时鼠标的滑动消耗

作者:唔系衰人/wxsr/saiman/sai
qq:457691057 
博客:http://wxsr.blogbus.com/ 

 

如果你有一定的as游戏开发经验,那相信你也应该知道但场景里放入好多的子可视对象时,鼠标的滑动会导致cpu的狂飙;

原因主要在于fp内部在不停的遍历可视对象.你可能会用过或者知道可视对象都有这个方法getObjectsUnderPoint ()

它能够返回鼠标点下的可视对象列表 .这也是鼠标滑过时消耗高的原因所在.当然它更大的用途是用于辅助重绘的实现以及其他.

有什么方法可以降低这块的消耗呢?答案是有的;

每个可视对象都有两个属性

 mouseChildren 跟mouseEnabled

mouseChildren的作用是确定对象的子项是否支持鼠标 而

mouseEnabled 的作用就在于  指定此对象是否接收鼠标消息。 
恩很显然  mouseEnabled跟 mouseChildren就是决定对象是否可以侦听鼠标事件的关键所在;
事实也证明只要设置了这两个属性鼠标滑过的高消耗就减少了,那是因为但这两个属性都设置为false后 就不在归到遍历树的对象中去了,

当然你也发现折辱这两个属性的功能呢个一样虽然我们可以把这两个属性都设置为false,但这不就意味着我们要放弃鼠标侦听?要知道游戏里对鼠标点击,鼠标松开,鼠标滑等鼠标事件都是十分依赖的.

恩,这是事实,但我们却可以用别的方法来取代他们;取代现有提供的鼠标事件方法;
而我这里说的方法就是动态4叉树.相信了解的人都知道4叉树主要用于搜索,跟剔除.而且效率相当高
当然一个4叉树还不够,我们还需要一个碰撞检测的方法来辅助下,下边会说说原理. 
(没有了解的可以去看看我这帖子http://wxsr.blogbus.com/logs/60788934.html) 

我之所以采用动态4叉树除了它效率高之外还在于它可以忽略遮挡物,对,fp的鼠标事件必须要点击到可视对象中去,而且如果某可视对象注册了鼠标事件但如果他上边有另外一个可视对象遮挡住的话,那么它依然无法触发到事件,但动态4叉树却可以给我们解决掉这个游戏里紧要需求的问题.

 动态4叉树的工作原理我就不多说了,大家可以查找资料或者看看我之前发的帖子.
下边说说这么用动态4叉树来取代鼠标事件;
这里要声明下,我这里说的取代鼠标事件并非完全不用鼠标,至少我这里还是需要两个鼠标事件一个是mouseDown跟mouseUp没办法要捕获鼠标动态只能通过InteractiveObject的这两个事件来实现,但这个注册的容器却是不需要固定的,它可以是任意的一个,只要你觉得方便即可,而关键的一点在于,,他解决掉了一大部分鼠标滑过的cpu消耗;

好下边继续, 动态4叉树主要的左右就是搜索出鼠标点下一个范围内的以注册可视对象;他的最终目的是高效的返回某个点下的以注册的可视对象列表.
然后我们需要做的就是对着先对象进行排序,这里要说明一点虽然这里我排序了,但不影响消耗,为什么呢?主要是因为我们可以通过限制我们的搜索范围来达到 缩小排序对象的数目,一般情况下,搜索出来的对象几8个以内具体似乎你的重叠数目了,这部分的排序的目的就是为了将他们按深度高到低来排序,然后一个循环,从最高为开始.检测该对象是否于鼠标点碰撞.也就是说鼠标点是否在他的对象范围矩形内.由高往低排序也是为了符合深度最上的对象优先的原则;
只要俺这个顺序某个碰撞了,那么我们就可以确定到我们鼠标点下的可视对象是什么了然后跳出该循环即可,
整个取代鼠标事件的主要实现过程也就是这些了;

当然还有一些优化的方法,对于游戏来说很多时候我们游戏的物品不一定就在同一夫级下的,那种情况我们有怎么界定他们的深度关系呢?

这个问题其实还是比较好解决的,比方说A跟B 两个容器下,他们都有n个子集对象,我们只要先知道A跟B的深度关系来确定一个夫级系数然后让他们的子对象乘以该系数就好了,比方说A跟B位于最外图层,那么我们可以分派给最外层的系数为10000,A跟B的子集深度系数就是10000乘以A跟B各自深度+1(深度是从0开始的)然后各自的子集对象只要拿他们的深度加上这个夫级系数即可相似的看作是一个有序的深度大小关系了,

比方说AB位于场景下,A的深度是0,它有两个子对象a,b他们的深度分别为a-0,b-1 . B的深度是1它也有两个子对象c,d 他们的深度为别为c-0,d-1
假设我们给A,B定的系数是100
那么abcd 的深度大小关系就是
a-1001   
b-1002  
c-2001  
d-2002
 这样我们就可以确定出每层的不同父级的对象的深度关系了.


同样的但我们面对的对象是一张png位图 时除了确定深度关系我们还要排序非同透明区域,,也就是精准像素碰撞的方法,这个实现在这里就相当简单了,只要我们每检测一个对象时以该对象下的鼠标点为值画一个像素的bitmapdata然后提取一下它的alpha通道值是否为0即可了.
好了就说到这了,下边是例子:
http://filer.blogbus.com/1587898/resource_158789812690578840.rar
 
 <?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
creationComplete="init()"
backgroundColor="0xcacaca"
minWidth="955" minHeight="600">
<fx:Declarations>
<!-- 将非可视元素(例如服务、值对象)放在此处 -->
</fx:Declarations>
<fx:Script>
<![CDATA[
import com.quadTrees.Hash;
import com.quadTrees.INoder;
import com.quadTrees.Noder;
import com.quadTrees.QuadTrees;

import flash.filters.GlowFilter;
import flash.utils.getTimer;

import mx.core.UIComponent;
private var qtree:QuadTrees=new QuadTrees
private var keys:Vector.<String>=new Vector.<String>
private var array:Vector.<INoder>=new Vector.<INoder>
private var s:Sprite=new Sprite
private var arr:Vector.<INoder>=new Vector.<INoder>
private var searhSize:int=200;
private var ui:UIComponent=new UIComponent
private var definition:int=10
private var _idIndex:int=0
private var item:Noder
private var selete:INoder
private var mouseDown:Boolean
private var clickItem:Noder
private function init():void
{

this.addElement(ui)
this.addElement(txt)
this.addElement(box)
this.addElement(this.add)
this.addElement(this.remove)
this.addElement(this.numStepper)
this.addElement(this.numStepper0)
this.addEventListener(MouseEvent.MOUSE_DOWN,mouseDownFunc);

this.addEventListener(MouseEvent.MOUSE_UP,mouseUpFunc)
for(var i:int=0;i<300;i++){
var s:Noder=new Noder(this.qtree)
s.graphics.lineStyle(1)
s.graphics.beginFill(0xffff00)
s.graphics.drawCircle(0,0,40)
s.x=Math.random()*900>>0
s.y=Math.random()*450>>0
s.vo.id=idIndex.toString()
s.mouseChildren=false
s.mouseEnabled=false
s.cacheAsBitmap=true
ui.addChild(s)
array.push(s)
keys.push(s.vo.id)
if(i==0)item=s;

}


var rect:Rectangle=new Rectangle(0,0,1000,1000)
qtree.setUP(1000/2,1000/2,rect,array,keys);
qtree.graphics=ui.graphics
this.qtree.minSize=10
var b:Timer=new Timer(80)
b.addEventListener(TimerEvent.TIMER,moveFunc);
var hash:Hash=this.qtree.nodes

b.start()
}

private function mouseUpFunc(event:MouseEvent):void
{
mouseDown=false
if(clickItem)clickItem.filters=[]
clickItem=null
}
private function mouseDownFunc(event:MouseEvent):void
{
mouseDown=true
var array:Vector.<INoder>=this.getMouseUnderPoint()

var item:INoder=this.getHitTestPoint(array)

if(item)
{
Noder(item).filters=[new GlowFilter(0x0000ff,1,20,20)]
clickItem=item as Noder
}
}
private function comparFunc(a:INoder,b:INoder):Number
{
var index1:int=DisplayObject(a).parent.getChildIndex(DisplayObject(a))
var index2:int=DisplayObject(b).parent.getChildIndex(DisplayObject(b))
return index2-index1
}
private function get idIndex():int
{
this._idIndex++
return this._idIndex
}
protected function add_clickHandler(event:MouseEvent):void
{
for(var i:int=0;i<10;i++){
var s:Noder=new Noder(this.qtree)
s.graphics.lineStyle(2)
s.graphics.beginFill(0)
s.graphics.drawCircle(0,0,(Math.random()*3>>0)+20)
s.x=Math.random()*900>>0
s.y=Math.random()*450>>0
s.vo.id=idIndex.toString()
s.mouseEnabled=false;
s.mouseChildren=false
s.cacheAsBitmap=true
ui.addChild(s)
array.push(s)
}
}

public function getHitTestPoint(array:Vector.<INoder>):INoder
{
if(array.length<1)return null;
array=array.sort(comparFunc);
for(var i:int=0;i<array.length;i++)
{
var item:INoder=array[i]
if(DisplayObject(item).hitTestPoint(this.mouseX,this.mouseY)){
return item
break
}
}
return null
}
protected function remove_clickHandler(event:MouseEvent):void
{
try{
for(var i:int=0;i<10;i++)
{
var item:Noder=array.shift() as Noder
item.unload()
item.parent.removeChild(item)

}
}catch(e:Error)
{

}
}
private function moveFunc(event:TimerEvent):void
{

if(!this.mouseDown){
for(var i:int=0;i<this.array.length;i++){
var item:Noder=array[i] as Noder
item.x+=item.vx*item.a
if(item.x>900||item.x<0)
{
item.a=-item.a;
}
item.y+=item.vy*item.b
if(item.y>500||item.y<0)
{
item.b=-item.b;
}
}

if(selete)
{
Noder(selete).filters=[]
}

var t:Number=getTimer();
// arr=getMouseUnderPoint()
// selete=getHitTestPoint(arr)
// this.txt.text='区域内共有:'+arr.length+'个对象 搜索时间:'+String(getTimer()-t)+' 毫秒'
// this.box.text='对象数:'+array.length.toString()
if(selete){
Noder(selete).filters=[new GlowFilter];
}
}

}

private function getMouseUnderPoint():Vector.<INoder>
{
var vx:Number=this.mouseX-searhSize/2;
var vy:Number=this.mouseY-searhSize/2;

this.ui.graphics.clear();
this.ui.graphics.drawRect(vx,vy,searhSize,searhSize)
var rect2:Rectangle=new Rectangle(vx,vy,searhSize,searhSize)
return qtree.find(this.qtree.node,rect2,definition)

}
private function sizeChangeFunc():void
{
this.searhSize=this.numStepper.value
}
private function sizeChangeFunc2():void
{
this.definition=this.numStepper0.value

}



]]>
</fx:Script>
<s:NumericStepper visible="false" id="numStepper" width="55"
change="sizeChangeFunc()" snapInterval="2" x="313" y="39" minimum="10" maximum="500" value="100" stepSize="10"/>
<s:NumericStepper visible="false" id="numStepper0" width="55"
change="sizeChangeFunc2()" snapInterval="5" x="423" y="39" minimum="10" maximum="200" value="10" stepSize="5"/>
<s:TextInput id="txt" x="10" y="10" width="468"/>
<s:TextInput id="box" x="10" y="36"/>
<s:Button click="add_clickHandler(event)" id="add" x="210" y="40" label="+" width="31"/>
<s:Button click="remove_clickHandler(event)" id="remove" x="249" y="40" label="-" width="26"/>
<s:Label x="146" y="49" text="添加屏遮物"/>
</s:Application>

from:http://wxsr.blogbus.com/logs/62864249.html
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值