【问题标题】:Stage and classes舞台和班级
【发布时间】:2011-07-18 14:59:08
【问题描述】:

我是 AS3 的新手,正在尝试使用它的 OOP 方式。我遇到的问题是了解如何使用单独的类访问舞台。

这是我正在尝试做的一个示例:

package game{
import flash.display.*;

public class Main extends MovieClip{

    function Main(){
        var player = new Player();
        var playerBullets = new playerBullet();
        addChild(player.players);
    }

}

package game{
import flash.display.*;

public class Bullet extends Main // also tried with MovieClip and Sprite{

    function Bullet(){
        // empty
    }


    function blah(){
        var someSprite = new someSprite();

        Main.addChild(someSprite);
        stage.addChild(someSprite);
        root.addChild(someSprite);


    }
}
}

我省略了另一个调用 blah 方法的类,因为我觉得它不相关。

基本上我想知道的是如何在课堂上添加东西到舞台上,因为看起来我错过了一些重要的东西。

*编辑以包含错误*

TypeError:错误 #1009:无法访问空对象引用的属性或方法。 在游戏中::Bullet/blah() at game::Player/fire()

【问题讨论】:

  • Main 是我的类链接到 .fla

标签: actionscript-3


【解决方案1】:

您不一定要扩展 main 来创建类似子弹类的东西,这可以是它自己的扩展 Sprite 或 MovieClip 的类。舞台对象被认为是一个全局对象,因为它是一个单例(Adobe AIR 的情况除外,您可以在每个生成的 NativeWindow 中拥有一个舞台)。因此,任何扩展 DisplayObject 或在其继承链中包含 DisplayObject 的对象默认情况下都会通过 getter 引用舞台,当 displayObject 添加到显示列表时会自动填充该 getter。这可以通过将剪辑直接添加到根舞台对象或将剪辑添加为最终连接到舞台的另一个剪辑的子级来实现。例如:

var clip1:MovieClip = new MovieClip();

stage.addChild(clip1); //Clip 1 can now access the stage reference internally.

ver clip2:MovieClip = new MovieClip(); //Right now, clip2 cannot access the stage reference interally.

clip1.addChild(clip2); //Now clip2 can access the internal stage reference because it has been connected to the display list through clip1.

人们犯的另一个错误是访问 DisplayObject 类型类(例如 Main 类)中的舞台,而没有首先确保对象本身已添加到舞台中。您可以通过在类的构造函数中侦听 Event.ADDED_TO_STAGE 事件来做到这一点,如下所示:

public class Main extends MovieClip{

    function Main(){
        if(stage){
            //The stage reference is present, so we're already added to the stage
            init();
        }else{
            addEventListener(Event.ADDED_TO_STAGE, init);
        }
        var player = new Player();
        var playerBullets = new playerBullet();
        addChild(player.players);
    }

    private function init(e:Event = null)
    {
         trace("Added to stage, the stage reference is now populated and stage can be accessed");
    }

}

这可能是您遇到的问题,但很难说,因为您没有指定任何错误。但是,这可能是一个问题,或者将是您的问题,因为它很常见。在 init() 方法中,您可以设置一个标志,以便当外部类调用您的 Main.blah() 方法时,您可以确保在尝试将某些内容添加到阶段之前存在阶段引用。但是请注意,在您的 Main 类中,您只需说:

addChild(someChild);

this.addChild(someChild);

您不是将该子对象添加到舞台,而是添加到 Main 对象,这是一个基于 MovieClip 或 Sprite 的对象,当您将其设置为 Document 类时,它本身会自动附加到舞台。希望这些信息对您有所帮助。

更新
再解释一下显示列表:

将您的所有电影剪辑视为盘子,将舞台视为桌子。如果盘子直接放在桌子上,或者盘子堆叠在另一个接触桌子的盘子的顶部,您只能从盘子访问桌子。如果你有 10 个盘子叠放在一起,它们最终都会通过彼此的连接接触到桌子。这本质上是一个 Flash 显示列表的可视化。将盘子放在桌子上的方法是使用 addChild(dish)。如果您没有在桌子上的某个地方放置一个对象,并尝试从该对象访问该表,那么您将失败。您收到“访问未定义”错误,因为您正在调用“blah()”方法,该方法在将项目符号(盘)添加到阶段(表)之前访问阶段(表)。因此,您必须首先将项目符号直接添加到舞台,或者将其添加到已添加到舞台的另一个对象中。像这样更改您的代码:

var myBullet:Bullet = new Bullet();
stage.addChild(myBullet);
//Or, if this class, assuming it's the player class, has already been added to the stage, just do this:
this.addChild(myBullet);
myBullet.blah();

即便如此,您仍应在“blah”方法中进行一些错误检查,以确保该阶段可用:

  function blah(){
        var someSprite = new someSprite();
        if(stage){
            Main.addChild(someSprite);
            stage.addChild(someSprite);
            root.addChild(someSprite);
        }else{
            trace("error, stage not present");
        }
    }

但是您还应该注意,通过将此子对象依次添加到 Main、stage、root all,这不会复制 someSprite 对象。当您将显示对象添加到新的父对象时,该对象会自动从其当前父对象中拉出并移动到新的父对象。所以这段代码最终要做的就是将 someSprite 添加到 root,我相信这会失败,因为 root 不是显示对象,而是一个全局引用,主要用于访问全局对象,例如舞台和用于加载 SWF 的 Loader 对象.

【讨论】:

  • 我收到的错误是 TypeError:错误 #1009:无法访问空对象引用的属性或方法。 at game::Bullet/blah() at game::Player/fire() 我试图阅读您的回复,但我似乎无法理解您对第一个代码块的意思。如果可能的话,你能用更通俗的说法解释一下吗?
  • 用显示列表的类比更新了我的答案+更多信息。
  • 非常感谢您为解释所做的努力,非常有帮助!
  • 对舞台和展示对象的出色解释。
【解决方案2】:

您不应该调用 stage.addChild。 Stage 应该只有一个子级,那就是文档类。

您可以通过将 MovieClip 添加到舞台的显示列表中来在屏幕上显示它。

Stage
   + Main Timeline
       +Anything
       +Else
       +You
       +Want

所以假设 Main 是您的主时间线的文档类...

 // inside of Main's constructor...

 public function Main(){
   var anything:MovieClip = new MovieClip();
   var Else:TextField = new TextField();
   var you:SimpleButton = new SimpleButton();
   var want:Sprite = new Sprite();
   this.addChild(anything);
   this.addChild(Else);
   this.addChild(you);
   this.addChild(want);
 }       

然后为了添加更低的子级,例如,如果您希望某样东西成为“任何东西”的子级,这样您就拥有......

Stage
   + Main Timeline
       +Anything
            +And
            +Everything
       +Else
       +You
       +Want




 public function Main(){
   var anything:MovieClip = new MovieClip();
   var Else:TextField = new TextField();
   var you:SimpleButton = new SimpleButton();
   var want:Sprite = new Sprite();

   this.addChild(anything);
   this.addChild(Else);
   this.addChild(you);
   this.addChild(want);

   var And:Sprite = new Sprite();
   var everything:Sprite = new Sprite();
   anything.addChild(And);
   anything.addChild(everything);
 }  

编辑:Ascension Systems 询问为什么您不应该直接添加任何显示对象作为舞台的子级。最简单的答案是,您永远无法保证您认为自己正在创建的文档类,或者实际上作为主时间线的内容实际上会被使用。您对舞台的使用可能会阻止您的 swf 被加载为更大应用程序的子应用程序,具体取决于您所做的事情。直接依赖舞台可能意味着您正在对显示列表的性质做出一些假设,这些假设将来可能不会成立。这就是它破坏模块化的方式(这与破坏 oop 不同)。

既然您可以将整个应用程序创建为一个完全独立的电影剪辑,而不依赖于学习世界坐标所需的“舞台”概念,为什么还要添加到舞台?这样一来,您的设计就可以更加模块化,并且不会牺牲任何东西。

在某些人的工作中,这可能被视为边缘情况。在我的工作中,这发生在我创建应用程序时,我当时认为纯粹是独立的,后来被重新调整为一个模块,也发生在其他的 swfs人们创建的目的是严格独立的,但后来我将其作为模块集成到更大的应用程序中。在所有情况下,都有一些令人讨厌的副作用需要应对。在那里我学会了不要过于依赖舞台来超越世界坐标。

【讨论】:

  • 很想知道从不向舞台添加任何东西背后的逻辑。这是一个完全没有根据和荒谬的规则。这不是反空接,事实上它在任何地方都没有违反任何规则。
  • 谢谢 我并不是要粗鲁,它只是看起来更像是您个人的偏好,而不是绝对的。我会注意编辑。 :)
  • 可以接受,但可能不明智,将内容直接添加到舞台,与根一起。它属于更可能出现错误的“边缘情况”类别。例如root.width 将不再等同于您内容的宽度。更容易搞砸。而且我不确定我是否相信 adobe 也能做到这一点。
  • @scriptocalypse:这比我的理由要好得多 :) 我想使用 stage 相当于 not 使用 as2 的 _lockroot
  • 我理解你们的推理,这确实有道理,我的反对意见是,该声明是作为明确的不,没有解释,但这一点非常有效,尽管我仍然不同意它必须成为绝对规则。您仍然可以编写代码以直接与舞台一起工作,并利用各种内置事件来确保所有内容都正确且有序地加载和组合。但是,是的,如果您正在构建使用外部 swfs 的应用程序或您可能希望稍后在其他地方加载的应用程序,这绝对是有道理的。 +1
【解决方案3】:

每个显示对象都有一个名为stage 的属性,在将该对象添加到显示树之前,该属性为空。

当您不确定某个对象是否已添加到舞台时,您可以为此目的使用一个侦听器:

public class Main extends MovieClip
{
    import flash.events.Event;

    public function Main():void
    {
        if(stage) {
            init();
        } else {
            this.addEventListener(Event.ADDED_TO_STAGE,init);
        }
    }

    private function init(evt:Event = null):void
    {
        this.removeEventListener(Event.ADDED_TO_STAGE,init);
        //object is now definitely on the display tree
    }
}

【讨论】:

    【解决方案4】:

    我要在这里在黑暗中猛烈抨击。

    stage 是一个类似这样实现的属性:

    public function get stage():Stage {
        var s:DisplayObject = this;
        while(s.parent) s = s.parent;
        return s as Stage;
    }
    

    root 非常相似,但在阶段以下停止(root 是阶段的子级)。

    这些属性仅在您调用它们的对象在舞台某处时才起作用。在哪里都无所谓,因为 while 循环将沿着层次结构向上到达顶部的阶段节点。但如果它不在舞台上,那么parent 将为空。

    因此,如果您的影片剪辑不在舞台上,那么它对stage 的引用将为空。 root 也是如此。

    我猜你在子弹被添加到舞台之前打电话给blah?在这种情况下,您的调用 stage.addChild(someSprite) 将是空引用错误(阶段为空)。

    所以你要么需要先将项目符号添加到舞台,要么需要将stage作为参数传入:

    function blah(s:Stage){
        var someSprite = new someSprite();
        s.addChild(someSprite);
    }
    

    【讨论】:

    • 我还要补充一点,您应该可能将内容添加到root 而不是stage。以防万一。
    猜你喜欢
    • 1970-01-01
    • 2012-11-22
    • 2023-03-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-04-26
    • 2016-02-22
    相关资源
    最近更新 更多