这是一个非常令人沮丧的Actionscript 2问题,我有关于在时间轴上创建的类关联的影片剪辑,需要立即在代码中引用:
- 我的库中有一个与“C”类关联的影片剪辑“C”。
- “C”类扩展了MovieClip。
- 我在舞台上有一个影片剪辑,框架标签为“关”和“开”。此影片剪辑的实例名称为“mc”。
- 在“on”框架上有一个实例名为“inst”的类“C”的实例。
- 类“C”的构造函数包含一个trace语句,用于输出“C构造函数!”告诉我何时创建舞台上的实例。
现在让我说我运行这段代码:
mc.gotoAndPlay("on");
var inst_mc:MovieClip = mc.inst;
if (inst_mc){
trace("inst_mc found!");
}else{
trace("inst_mc NOT FOUND!");
}
var inst_c:C = C(mc.inst);
if (inst_c){
trace("inst_c found!");
}else{
trace("inst_c NOT FOUND!");
}
似乎在C类之类的任何对象的创建都不会发生,直到当前帧的所有代码都完成执行,因为输出将是:
inst_mc found!
inst_c NOT FOUND!
C constructor!
这到底是怎么回事?我已明确告诉Flash创作环境,影片剪辑C与C类相关联,而C类是MovieClip派生。所以在我的代码中,gotoAndPlay(“on”)将创建在“on”帧上的影片剪辑“inst”。它能够找到实例,但是当我将它视为C类时,它就会失败。然后构造函数在所有这些之后发生。我该如何解决?我希望一旦你在时间轴上改变某些东西,就会立即创建相应的对象 - 除了它们的显式类类型之外,它们都是。我可以引用我的实例,但只能作为MovieClip。我怎么解决这个问题?它应输出:
C constructor!
inst_mc found!
inst_c found!
感谢您的帮助!
*更新* 谢谢你的回复!遗憾的是,我的项目没有简单的解决方案,现在规模很大,不能轻易重组(它也太大而无法转换为AS3)。我想过将MC保留在第1帧并隐藏它们,但我认为不必要地增加了开销。即使将_visible设置为false,它是否仍然会耗尽资源? (这是一个不同但又相关的问题 - 如果你有一个复杂的,固定的MC,而不是那么可观,而不是完全没有它,那么表现会有所不同吗?)
我目前的策略是这样的:
mc.gotoAndPlay("on");
var inst_mc:MovieClip = mc.inst;
var inst_c:C = C(mc.inst);
if (inst_c){
// Even though I moved to the "on" frame,
// the object was already initialized/existed already
// so i can use its class code now
inst_c.do_something_now();
}else{
// The class is not accessible, so set a boolean flag
// which will get dynamically assigned to the *movie clip*.
// The constructor in class C will look to see if the flag
// has already been set. If so, it calls do_something_now()
// within C's constructor.
// In class C, trigger_do_something_now is a defined as a
// Boolean with no default value.
// It is not set in the constructor.
inst_mc.trigger_do_something_now = true;
}
这种方法困扰我。这是混乱和混乱。但是,我认为这是一个合理的解决方法。你们有什么感想?谢谢!
答案 0 :(得分:2)
不幸的是,这就是AS2中“面向对象”代码的工作原理。这就是你应该想象它的方式:当时间轴播放头在框架A上,并且你调用gotoAndStop(B)
时,播放头会立即前进,并且会创建任何必要的对象,但是你在框架B上的任何时间轴脚本都不是然后执行,因为Flash仍在完成帧A的执行。直到Flash的内部循环的下一次迭代才处理帧B.
这就是自从众所周知的牛奶人眼中只是一个闪光点的日子以来的情况。正如你可以猜到的,对于类附加的剪辑,一切都是一样的 - 当你将播放头移动到帧B时,Flash会在帧B上创建所有内容,但它不会运行驻留在帧B上的任何脚本 - 这包括构造函数刚刚创建的剪辑 - 直到下一帧。
最简单的解决方法是重新设计时间轴,以便在第1帧中创建相关的MC,但保持隐藏(或不活动,在舞台外等)直到gotoAndStop命令。您可以通过在给定帧上更改IDE中的alpha(等),或者在执行gotoAndStop的同时调用init方法来完成此操作。 最佳解决方法是使用AS3,如果这是一个选项。还有其他方法,但我建议使用其中一种。
更新以进行编辑: 是的,“其他方法之一”就是将参数放在剪辑或其父级中,并在剪辑初始化时让剪辑查找它们,但是任何最终维护代码的人都不会感谢你。但我当然已经做到了。 ;)
在回答你的副作用问题时,如果某个片段位于舞台的范围内,那么带有_visible = false的片段会产生非常小的开销。它没有渲染,但有一个非常小的命中,我认为与Flash计算其界限或某些事情有关。 (我进行了一次测试,你真的需要成千上万才能测量它。)即使这是性能损失也是一个问题,修复方法是将不可见的剪辑移到舞台边界之外。然后他们没有发现任何处理器命中,我发现,他们只是使用脚本内存(如果你把它们的初始化推迟到他们准备好使用它们之后,就会有很少的内存)。
请记住,无论剪辑是否可见,事件处理程序都会触发,因此在初始化之前不要让剪辑注册事件。我要做的是使用属于构造函数的所有内容创建一个init()方法,并将其调用到您构建剪辑的位置。
答案 1 :(得分:1)
首先,让我为仅仅关注这个问题的一个方面而道歉(在我对fenomas回答的评论中);虽然我已经提出了我的观点,我甚至同意面向对象,或者至少是一个正式的类语法被添加到AS1作为事后的想法,称为AS2 - 并且在某些方面,它显示 - ,我忽略了回答你的实际问题
重新阅读这个帖子,我认为这一切都归结为“等待一帧”。这是一个解决方法,我不得不在AS2中多次使用,所以看起来很奇怪我忽略了这是真正的问题。
无论如何,你的解决方法可能会有效,但正如你所说,它可能会很快变成维护噩梦。 Fenomas的选择是另一种有效的解决方法。在这种情况下,如果我能避免它,我就不会这样做,但不是因为性能;相反,因为当你已经布置好东西时重新安排很多东西可能会有很多工作。
所以,也许你可以尝试一些非常简单的东西:
mc.gotoAndPlay("on");
this.onEnterFrame = function():Void {
trace(mc.inst instanceof C);
delete this.onEnterFrame;
};
我认为它应该有效。注册后,您的处理程序将在一帧后捕获enterFrame事件。此时,您现在已经准备好移动到的帧,因此您只需要清理处理程序并执行gotoAndPlay发布后通常执行的操作。
我记得我曾经不得不做一些非常相似的事情(它不是完全相同的情况,但它归结为等待一帧),所以当时我写了一个非常简单的类来集中这个代码。这是以下几点:
class FrameDelay {
function FrameDelay(scope:Object,callback:Function,args:Array) {
// get a reference to the current enterFrame handler
// (if any), so we can restore it back when we're done
var oldEnterFrameHandler:Function = _root.onEnterFrame;
_root.onEnterFrame = function():Void {
oldEnterFrameHandler();
callback.apply(scope,args);
_root.onEnterFrame = oldEnterFrameHandler;
}
}
}
你会像这样使用它:
mc.gotoAndPlay("on");
new FrameDelay(this,onFrameReady,["a","1"]);
function onFrameReady():Void {
// arguments (if any), will be available in the arguments array
trace(arguments.length);
trace(mc.inst instanceof C);
trace(this);
}
传递范围,函数和可选的参数数组。最有可能的是,您不需要它们,也可以让类构造函数进行回调。但在某些情况下,您的回调可能会出现范围问题(这是AS2问题),因此明确传递范围更安全。
另外,请注意我正在使用_root.onEnterFrame。虽然我正在对原始处理程序进行“备份”并在完成后将其恢复,但这并不一定意味着代码的其他部分是礼貌的(!)。因此,处理程序可能会被覆盖。如果您认为这可能是一个问题,也许您可以在根目录中以某种深度创建一个movieClip,您知道它没有被使用,并将_root.onEnterFrame替换为_root.dummy_mc.onEnterFrame。
无论如何,请记住,一个简单的内联onEnterFrame将完成这项工作,所以也许你甚至不需要为此使用类。