AS3 - 检查父项的子项是否存在抛出错误1009

时间:2016-01-10 13:10:57

标签: actionscript-3

我正在尝试检查父母是否有孩子。例如,我在我的子弹类enterframe中有这个代码。子弹需要检查父级(哪个级别)内部是否有僵尸。

if (MovieClip(parent).zombie != null)
        {
            MovieClip(parent).zombie.checkCollisionWithEnemies(this);
        }

但是,并非所有级别都有僵尸,所以如果将此子弹添加到父级,我会收到错误。

这是添加项目符号时启动的功能。这段代码在僵尸类中。

public function checkCollisionWithEnemies(bullet: MovieClip)
    {
        if (this.hitTestObject(bullet))
        {
            if (this.meter != null)
            {
                if (this.meter.scaleX > 0)
                {
                    this.meter.scaleX -= 0.5;
                }

                if (this.meter.scaleX <= 0.01)
                {
                    this.gotoAndStop(2);
                }
            }
            this.parent.removeChild(bullet);
        }
    }

因此,由于僵尸仅处于某个级别,如果我处于没有僵尸的级别,它将引发此错误:

TypeError:错误#1009:无法访问空对象引用的属性或方法。     在Bullet / update()[Bullet.as:47]

该错误来自这一行:

if (MovieClip(parent).zombie != null)

我正在尝试检查父母内部是否有僵尸,如果确实如此,则运行僵尸的功能。

我在下面尝试过这些代码,但仍然遇到了同样的错误。

if (MovieClip(parent).contains(zombie))
if (MovieClip(parent).getChildByName("zombie"))

1 个答案:

答案 0 :(得分:2)

你真正的问题是:

  

我的子弹类enterframe中有这个代码

此代码不应位于Bullet类中!为了测试碰撞,您需要两个要相互测试的对象。因此,如果您想在Bullet类中执行此操作,则bullet对象必须获取对另一个对象的引用(这是您遇到问题的位置)。

要求对另一个对象的引用不必要地使问题复杂化,因为bullet所做的一切都将自己传递给zombie的某个函数。它不应该在Bullet类中进行该方法调用,而应该在已知zombieBullet的情况下进行:level

该级别引用了bulletzombie对象,这使得在那里执行碰撞检测变得容易:

var zombie:Zombie = new Zombie();
addChild(zombie);

var bullet:Bullet = new Bullet();
addChild(bullet);

addEventListener(Event.ENTER_FRAME, gameLoop);

function gameLoop(e:Event):void
{
    zombie.checkCollisionWithEnemies(bullet);
}

这很常见,建议使用一个ENTER_FRAME函数来调用游戏中所有对象的其他功能。它比每个对象都具有ENTER_FRAME功能更清晰(并且它对性能也有害)

您可以通过覆盖hitTestObject来改进代码,而不是引入一个基本上做同样事情的新方法名checkCollisionWithEnemies

override public function hitTestObject(obj:DisplayObject):Boolean
{
    var result:Boolean = super.hitTestObject(obj);

    if(result)
    {
        if (this.meter != null)
        {
            if (this.meter.scaleX > 0)
            {
                this.meter.scaleX -= 0.5;
            }

            if (this.meter.scaleX <= 0.01)
            {
                this.gotoAndStop(2);
            }
        }
        this.parent.removeChild(bullet);
    }

    return result;
}

这样您就不必记住任何新的方法名称,而您的课程只是为现有的hitTestObject功能添加了一些功能。

现在,如果您想拥有仅适用于项目符号的专用功能,那么您应该更加具体了解您的类型,仅接受Bullet

public function checkCollisionWithEnemies(bullet: Bullet)
{
    if (this.hitTestObject(bullet))
    {
        if (this.meter != null)
        {
            if (this.meter.scaleX > 0)
            {
                this.meter.scaleX -= 0.5;
            }

            if (this.meter.scaleX <= 0.01)
            {
                this.gotoAndStop(2);
            }
        }
        this.parent.removeChild(bullet);
    }
}

如果你正在调用只有Bullet类的方法,那么这就是你要走的路。但是你没有这样做,这就是为什么你应该只是override hitTestObject

您之前称为hitTestObject的{​​{1}}方法仍有问题。这就是这条线:

checkCollisionWithEnemies

与你的this.parent.removeChild(bullet); 类似的问题,恰恰相反。现在,您的Bullet正在使用zombie。它适用于这种情况,因为bullet作为参数传递,但这个想法仍然存在缺陷,因为它依赖于显示列表的结构来工作。

如果您以后决定将一些项目符号移动到一个容器中,或者要让一群僵尸想要与容器一起分组,则此代码将失败。 它假定bulletzombie都有相同的父级。

解决方案与我的答案的第一部分相同:将与其他对象有关的代码部分移到bullet

level

现在级别代码看起来像这样:

override public function hitTestObject(obj:DisplayObject):Boolean
{
    var result:Boolean = super.hitTestObject(obj);

    if(result)
    {
        if (this.meter != null)
        {
            if (this.meter.scaleX > 0)
            {
                this.meter.scaleX -= 0.5;
            }

            if (this.meter.scaleX <= 0.01)
            {
                this.gotoAndStop(2);
            }
        }
        // line removed
    }

    return result;
}

<强> TL;博士

尝试创建彼此之间没有太多联系的对象。 在你的情况下,在var zombie:Zombie = new Zombie(); addChild(zombie); var bullet:Bullet = new Bullet(); addChild(bullet); addEventListener(Event.ENTER_FRAME, gameLoop); function gameLoop(e:Event):void { if(zombie.hitTestObject(bullet)) { removeChild(bullet); //or bulletContainer.removeChild(bullet); //or whatever } } 类中使用特定于僵尸的代码和在Bullet类中使用特定于子弹的代码会在两者之间产生不必要的耦合。这种耦合也称为依赖,因为每个类不能再独立使用了。

我在这个答案中指出的解决方案是以Zombie的形式保留现有的API,并将应用程序特定的逻辑移到另一个地方。 Thsi方式每个类都可以独立工作,在hitTestObject的情况下处理一些与被击中时相关的逻辑。

这会使Zombie遍布整个地方更加清洁,正如您所发现的那样,这不是一个好的解决方案。

不可否认,这就是为什么你不应该像你在问题中那样编写代码的原因,但实际上并没有解决问题。我不能强调(现在再次这样做)你遇到的问题主要是因为代码设计有缺陷,这将导致更多的问题。

好的,足够了,现在问题是:

你已经尝试了这3行,你得到了#1009

!=null

这是一个快速的经验法则,告诉你某些代码是否可怕:它包含if (MovieClip(parent).zombie != null) if (MovieClip(parent).contains(zombie)) if (MovieClip(parent).getChildByName("zombie")) 的强制转换,如下所示:MovieClip。在某些情况下,进行这样的演员是有道理的,但在大多数情况下,人们滥用它来获得一些非常可怕的代码。

这是因为MovieClip(...)是一个MovieClip类,如果你转换为这样的类,编译器会抛出更少的错误。这听起来不错,但问题是它并没有真正解决导致错误的任何问题。它只是将它们隐藏在一个&#34;让我们希望这是有效的&#34;时尚。就像你说的那样:

  

我已尝试过以下代码

请尝试理解代码,而不是仅仅提出更多解决方案&#34;在它试图解决&#34;问题。

如果某些内容为dynamic,那么您应首先找出null。在您的情况下,null的结果为MovieClip(parent),这意味着投射失败。换句话说: null不是parent

原因很可能是你将子弹添加到舞台上,如下所示:

MovieClip

这是另一种常见的不良做法。问题是stage.addChild(bullet); 不是stage,你甚至不应该MovieClip任何东西。为什么这种情况不在这个已经很久的答案的范围内。简短的回答是,您永远不应该向addChild添加任何标记,只需添加我在上面的代码中添加的内容,而不是stage。这会将其添加到时间轴。 (不,stage.不是主时间线)