我是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() 在游戏:: Player / fire()
答案 0 :(得分:5)
你不一定要扩展main来创建类似子弹类的东西,这可以是它自己的类,它扩展了Sprite或MovieClip。 stage对象被认为是一个全局对象,因为它是一个单例(除了Adobe AIR,你可以为每个NativeWindow生成一个阶段)。因此,任何扩展DisplayObject或在其继承链中具有DisplayObject的对象默认都会通过getter引用该stage,当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()方法时,可以确保在尝试向舞台添加内容之前存在舞台引用。但请注意,在您的主课程中,只需说:
的addChild(someChild);
或
this.addChild(someChild);
您不是将该子项添加到舞台上,而是添加到Main对象,该对象是一个MovieClip或基于Sprite的对象,当您将其设置为Document类时,该对象本身会自动附加到舞台上。希望这些信息有所帮助。
<强>更新强>
要更多地解释显示列表:
将所有影片剪辑视为菜肴,将舞台视为表格。如果菜肴直接放在桌子上,或者如果菜肴堆放在另一个接触餐桌的菜肴的顶部,您只能从菜肴中取出餐桌。如果你有10块板堆叠在一起,它们最终都会通过它们相互连接而触及桌面。这实际上是闪存显示列表的可视化。你把菜放在桌子上的方法是使用addChild(菜)。如果你没有在表格的某个位置放置一个对象,并尝试从该对象访问该表,那么你将会失败。您正在获取“访问未定义”错误,因为您正在调用“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,这不会复制someSprite对象。将显示对象添加到新的父对象时,该对象将从其当前父对象中自动拉出并移动到新对象。因此,所有这些代码都会最终将someSprite添加到root,我相信这会失败,因为root不是显示对象,而是一个主要用于访问全局对象的全局引用,例如用于加载SWF的stage和Loader对象
答案 1 :(得分:3)
你不应该一直在调用stage.addChild。舞台上应该只有一个孩子,那就是文档类。
通过将其添加到舞台的显示列表中,可以在屏幕上显示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);
}
然后为了让孩子们更低,例如,如果你想要某些东西成为“Anything”的孩子,那么你就拥有......
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不同)。
为什么要将整个应用程序创建为一个完全独立的MovieClip,而不依赖于学习世界坐标所需的“舞台”概念,为什么要添加到舞台?这样你就可以在你的设计中更加模块化而且你不会牺牲任何东西。
在一些人的工作中,这可能被认为是一个边缘案例。在我的工作中,当我创建了当时我认为的应用程序纯粹是独立的,最后被重新用作模块,以及swfs其他创建的人员本质上是完全独立的,但我当时要将其作为一个模块集成到一个更大的应用程序中。在所有情况下,都有一些令人讨厌的副作用。这就是我学会了在世界坐标之外不要过分依赖舞台的地方。
答案 2 :(得分:2)
每个显示对象都有一个名为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
}
}
答案 3 :(得分:1)
我会在黑暗中疯狂刺伤。
stage
是一个实现类似的属性:
public function get stage():Stage {
var s:DisplayObject = this;
while(s.parent) s = s.parent;
return s as Stage;
}
root
非常相似,但停止在舞台下方(root是舞台的孩子)。
当您在上调用它们的对象位于舞台上时,这些属性仅适用。无关紧要,因为while循环将沿层次结构向上移动到达顶部的stage节点。但如果它不在舞台上,则parent
将为空。
因此,如果您的movieclip不在舞台上,那么它对stage
的引用将为null。同样适用于root
。
我猜你在将子弹添加到舞台之前打电话给blah
?在这种情况下,您的调用stage.addChild(someSprite)
将为空引用错误(阶段为空)。
因此,您需要先将项目符号添加到阶段,或者需要将stage
作为参数传递:
function blah(s:Stage){
var someSprite = new someSprite();
s.addChild(someSprite);
}