【问题标题】:Forward MouseEvent in transparent bitmap to underlying MovieClip将透明位图中的 MouseEvent 转发到底层的 MovieClip
【发布时间】:2011-03-24 08:40:52
【问题描述】:

所以我正在对一些重的东西进行位图以尝试降低根据 FlashBuilder 的配置文件相当高的 [pre-render] 和 [render]。我认为这一切进展顺利,直到我意识到一旦将 MovieClip 更改为位图,就会失去鼠标移动事件(Over、Out、Move...)的基于像素的精度,你剩下的就是位图的整个边界框,这是不太理想的。我有一个游戏,其中许多位图资产将在一个场景中,在舞台上,以各种方式排列,并且需要在每个之间移动像素精度,并且已经用尽了我的努力来实现与正常情况一样,位图人的鼠标移动结果相同。

http://seadersforums.appspot.com/static/bitmaps.fla 是一个显示此操作的 FLA,您可以在此处查看它的工作原理 http://seadersforums.appspot.com/static/bitmaps.html 。当它加载时,舞台上的两个项目都是绘制的形状,封装在 MovieClips 中,如果你将鼠标悬停在它们上面,它们都会发光。如果你完全点击舞台,紫色的家伙会变成一个位图,现在,他的“命中区域”,当涉及到 MouseEvents 时,就是他的整个边界框,你只能通过边缘裂片处的后面绿色项目.

我也在跟踪鼠标所在的像素,所以我可以清楚地知道鼠标何时在透明区域上,它是 0,但是我如何告诉事件沿着链向下转发到绿色影片剪辑?

以下是它现在对我来说非常有效的方式,

        var bitmap:Bitmap = this['bitmap'];
        var shouldMouseOver:Boolean = bitmap.bitmapData.getPixel(event.localX - bitmap.x, event.localY - bitmap.y);
        if(shouldMouseOver)
        {
            this.mouseOver();
        }
        else {
            this.mouseEnabled = false;
            var point:Point = new Point(event.stageX, event.stageY);
            for each(var other:DisplayObject in _topContainer.getObjectsUnderPoint(_topContainer.globalToLocal(point)))
            {
                if(other.mouseEnabled)
                {
                    point = other.globalToLocal(point);
                    other.dispatchEvent(new MouseEvent(MouseEvent.MOUSE_MOVE, false, false, point.x, point.y));
                    break;
                }
            }
            this.mouseEnabled = true;
        }

我为我的项目关闭了 mouseEnabled,当我知道它是错误的时,然后我搜索另一个符合要求的项目,如果有,则向该项目发送一个事件并中断。如果那个也错了,它会再次做同样的事情,但每次都会让自己脱离循环。

这确实完全按照我的意愿工作,但我总是更喜欢保留 globalToLocal 和循环之类的东西,并从数组中读取不经常更新的方法,例如 MOUSE_MOVE 的侦听器。有没有更有效的方法来做到这一点?

【问题讨论】:

  • 它被称为冒泡,它是事件流的重要组成部分。更多在这里:help.adobe.com/en_US/ActionScript/3.0_ProgrammingAS3/…
  • 实际上我不相信冒泡会到达调度程序下方的剪辑。据我了解,当冒泡事件时,它从调度程序的深度传播到显示列表的顶部(根/阶段),然后返回到调度程序,不确定它是否到达下面的任何内容。我可能是错的,但我对此表示怀疑,因为如果不是这种情况,那么每个冒泡事件都会遍历整个显示列表,这可能会破坏更大应用程序的性能。我想一个简单的测试是为了。

标签: flash actionscript-3 bitmap


【解决方案1】:

您可以使用 getObjectsUnderPoint 方法。任何显示对象类型的类都有这个方法。该方法采用 Point 类型的参数,您可以在其中传递鼠标坐标。我相信该方法会返回该点下方的对象数组,这些对象包含在您调用 getObjectsUnderPoint 方法的 DisplayObject 中。例如,如果您的这些影片剪辑嵌套在名为 Container 的父剪辑中,那么您可以执行以下操作:

var pt:Point = new Point(10, 20);
var objects:Array = container.getObjectsUnderPoint(pt);

然后对于您的特定场景,我会按深度对返回的对象进行排序,以获得列表中的下一个(下面的那个,或者我想要的任何一个)。

var i:int = 0;
var topContainer:DisplayObject;
var lastIndex:int = 0;
for(i; i < objects.length; ++i){
   if(topContainer:DisplayObject){
       if(container.getChildIndex(DisplayObject(objects[i])) < lastIndex){
            lastIndex = container.getChildIndex(DisplayObject(objects[i]));
       }
   }else{
       topContainer = DisplayObject(objects[i]);
       lastIndex = container.getChildIndex(DisplayObject(objects[i]));
   }   
}

所以基本上我在这里所做的是对所有结果进行排序并选择具有最低子索引的结果,以理想地获得下一个最接近表面的子。自从我进入 flash 以来已经有一段时间了,所以我不能 100% 确定显示列表是否在子索引方面以这种方式工作,也就是说,如果更近的对象具有更低或更高的索引值。换句话说,索引为 if 0 的孩子很可能是列表底部的孩子。你必须试一试,如果是这样的话,只需调整上面的代码。无论如何,一旦该函数运行(顺便说一句,它未经测试,因此可能是类型-o 和这种性质的东西,但这个概念应该有效),那么您将拥有 topContainer 引用的您真正想要访问的剪辑。从这里您可以将 mouseEvent 发送到该容器。

topContainer.dispatchEvent(new MouseEvent(MouseEvent.MOUSE_DOWN, false, false, MOUSE_POSITION_X, MOUSE_POSITION_Y));

现在,如果您在 topContainer 中有一个侦听器等待接收它自己的鼠标事件,您会这样做。也就是说,有一个事件监听器直接附加到 topContainer 对象,如下所示:

topContainer.addEventListener(MouseEvent.MOUSE_DOWN, onMouse);

或者,在 topContainer 类中:

this.addEventListener(MouseEvent.MOUSE_DOWN, onMouse);

原因是我们将事件直接分派到没有事件目标的 topContainer 引用,并且关闭了事件冒泡。

另一方面,如果您的模型在舞台级别或其他级别涉及单个 MouseEvent 处理程序,并且您的 onMouse 处理程序有一个 switch 语句来根据事件目标执行某些操作,如下所示:

function onMouse(e:MouseEvent):void
{
   switch(e.currentTarget){
      case MyMovieClip1:
          //Do Something
      break;

      case MyMovieClip2:
          //Do Something
      break;
   }
}

那么您将需要以稍微不同的方式调度该事件。您需要包含对 topContainer 对象的引用并启用事件冒泡:

someRelativeClip.dispatchEvent(new MouseEvent(MouseEvent.MOUSE_DOWN, true, false, MOUSE_POSITION_X, MOUSE_POSITION_Y, topContainer));

希望这有助于让我知道您是否需要任何澄清。

【讨论】:

  • 另请注意,在您按深度对返回的对象进行排序的方法中,您必须添加检查以确保您正在排序的对象不是对象( s) 已展平为位图。
  • 感谢 Jesse 的回答,它的帮助令人难以置信,我只是想知道一个部分,getObjectsUnderPoint 所需的点,然后是调度的 MouseEvent 的本地 X 和 Y。我目前已经让它工作了,但这是通过将 topContainer 的全局转换为本地,使用我们从鼠标事件获得的阶段 X 和 Y,然后将全局 X 和 Y 转换为 MouseEvent 的本地。我主要处理的事件是 MOUSE_MOVE,所以每次鼠标移动都有点重,对此部分有什么想法吗?
  • Ohhhhh 你的意思是它变得越来越密集,因为你每次移动鼠标时都会调用这个过程。是的,这将是一个问题大声笑。你知道吗,我想我只是想到了一个更好的答案。将该剪辑(或其他剪辑)转换为位图后,是否需要它再接收鼠标事件,也就是说,只要它是位图?
  • 我现在已经在游戏中加入了它,即使屏幕上有很多资产,它实际上并没有对性能造成太大影响。是的,发生的情况是许多这些资产都在舞台上,当它们被鼠标悬停时,它们会发光,然后它们会被带到前面并可以点击。鼠标移出时,它们会回到原来的索引并恢复正常。令人恼火的是不能只启用不透明的 mouseEnabled 或类似的东西。 Flash 使用矢量来做到这一点,并且可以区分位图上的 alpha 和 opaque,因此应该更容易!
  • 好吧,我只是在想,当对象被带到前面并且您不希望/不需要它接收鼠标事件时,它会妨碍您,您可以尝试设置 MovieClip.mouseEnabled =错误的。这应该使位图所在的容器影片剪辑和任何子元素都不会接收鼠标输入,因此允许其下方的对象接收鼠标输入。试试那个。如果这确实有效,我会将其发布在新答案中,以便其他发现此问题的人知道真正的解决方案是什么。
【解决方案2】:

我使用了我在网上某处找到但再也找不到的技巧。基本上是这样的:

  • 将位图放入 Sprite 中。
  • 在那个 sprite 上创建一个 EnterFrame 处理程序 检查下方的像素 每次都使用光标。
  • 使用getPixel32() 查找颜色 以及该像素的 alpha。
  • 当颜色的 alpha 分量 低于某个阈值,您设置精灵的 mouseEnabled 为 false (否则 真的)。

这样,您只有在滚动图像的不透明部分时才能单击位图。如果不是 mouseEnabled,则位图下方的对象会发生 MouseEvents。

【讨论】:

  • 虽然这可能行得通,但从我对 onEnterFrame 的每一点经验来看,我认为在处理舞台上的许多资产时,让他们都同意这一点增加对游戏性能的不良影响。不过,感谢您的回答。
  • 我猜它使用 ENTER_FRAME 因为当对象不是 mouseEnabled 时你无法获得 MOUSE_MOVED 事件。您可以创建另一个包含所有位图的类,并执行一个 MOUSE_MOVED 事件侦听器,该侦听器向下钻取到光标下的所有位图并检查它们的 alpha 值以达到相同的效果。但是,当您的位图自行移动时,这将无济于事。
  • 好吧,我明白了。从长远来看,这很可能是一个解决方案,但目前,游戏的设置方式需要重新设计,并且在当前设置中不起作用,但总体而言可能是更好的方式去。再次感谢您的回复和额外的信息。
猜你喜欢
  • 2013-09-10
  • 1970-01-01
  • 2011-10-06
  • 1970-01-01
  • 1970-01-01
  • 2012-09-24
  • 2012-08-07
  • 2014-07-09
  • 1970-01-01
相关资源
最近更新 更多