Flash as3, ссылающийся на родительские переменные с дочерними

#flash #actionscript-3

#flash #actionscript-3

Вопрос:

У меня возникли некоторые проблемы с пониманием того, как дочерний элемент может ссылаться на переменные родителей.

Вот мой Main.as :

 package
  {
        import flash.display.MovieClip;

    public class Main extends MovieClip
    {
        public var i;
        public var child: Child;

        public function Main()
        {
            i = 4;
            child = new Child;
        }
    }
}
 

И вот мой Child.as:

 package
{
    import flash.display.MovieClip;

    public class Child extends MovieClip
    {
        public function Child()
        {
            trace([parent as MovieClip].i);
        }
    }
}
 

дочерний элемент пытается получить доступ к общедоступной переменной i в родительском элементе, но я получаю сообщение об ошибке:

не определено

Используя trace(MovieClip(parent).i) вместо этого я получаю:

Ошибка TypeError: ошибка # 1009: не удается получить доступ к свойству или методу нулевой ссылки на объект. в дочернем () в Main()

Я чувствую, что мне не хватает чего-то довольно фундаментального (например, мне не было задано значение к моменту создания дочернего элемента), но я не могу понять, что.

Разъяснения были бы весьма признательны.

Ответ №1:

parent Свойство a DisplayObject устанавливается при его добавлении к другому DisplayObjectContainer через .addChild() or .addChildAt() . Эти вызовы выполняются неявно пользовательским интерфейсом Flash для добавления дочерних элементов к видимому объекту.

Итак, в том, что вы делаете, есть несколько ошибок, некоторые из которых связаны с неправильным пониманием того, как DisplayObjects работать во flash, а другие — с синтаксическими проблемами.

При вызове вы new Child() вызываете конструктор Child классов, в вашем случае:

 public function Child()
{
    trace([parent as MovieClip].i);
}
 

На данный момент он не был добавлен как дочерний в a DisplayObjectContainer . Простое присвоение ему имени a child в вашем родительском классе не создает эту связь. Таким образом, в этом случае внутри вашего конструктора this.parent всегда будет null, потому что он просто создает новый экземпляр Child класса, он не знает ни о каком «родителе»

Здесь также есть довольно большая проблема с синтаксисом. Вы выполняете как as приведение (которое вернет null , если типы несовместимы). В этом случае, однако, parent уже null есть , но тогда вы используете [] , что является способом записи литерала Array . Таким образом, вы, по сути, создаете новый Array с одним элементом, который есть null . Затем вы пытаетесь получить свойство, вызываемое i для Array не родительского.

Очевидно, это не то, что вы хотите сделать… Вообще. Вероятно, вы имели в виду что-то вроде этого:

 public function Child()
{
    trace((parent as MovieClip).i);
}
 

Но даже это неверно, потому i что это не свойство a MovieClip , а свойство вашего подкласса Main . Так что вы, вероятно, действительно имели в виду:

 public function Child()
{
    trace((parent as Main).i);
}
 

Но это все равно не удастся, потому что, как я уже говорил ранее, parent == null потому что он еще не был добавлен как дочерний.

Рассмотрим следующий код:

 public class Main extends MovieClip
{
    public var i;
    public var child:Child;

    public function Main()
    {
        i = 4;
        child = new Child();
        child.doWork();
        this.addChild(child)
        child.doWork();
    }
}
 

И:

 public class Child extends MovieClip
{
    public function Child()
    {
        doWork();
    }

    public function doWork()
    {
        if(parent == null) {
            trace("No parent yet.")
            return;
        }

        var mainParent:Main = (parent as Main);
        if(mainParent == null) {
            trace("Parent isn't a instance of Main!")
            return;
        }

        trace(mainParent.i);
    }
}
 

Это должно вызвать метод doWork() три раза: один раз в дочернем конструкторе, затем перед добавлением в дерево отображения и, наконец, после добавления в дерево отображения. И, надеюсь, это будет иметь больше смысла, если посмотреть, как это работает.

Еще многое предстоит узнать, особенно о событиях, потоке событий и о том, как и связаны / вложены. DisplayObjects DisplayObjectContainers Но, возможно, это подтолкнет вас в правильном направлении. Просто знайте, что parent это просто свойство a DisplayObject (один из предков MovieClip и многих других классов во flash). Понимание того, как эти классы работают друг с другом, и расширение их собственной функциональностью — ключ к настоящему пониманию flash.

Комментарии:

1. Вау, спасибо за ваш невероятно исчерпывающий ответ, 32bitkid. Недавно я начал изучать as3, имея небольшой опыт работы с C — код, который я написал в 1-м ключевом кадре на временной шкале, обычно делал то, что я хотел. Однако сегодня я подумал, что попробую выяснить, как использовать файлы extenal .as и разделить их, но это был полный сбой (пока вы не ответили). Итак, еще раз спасибо, что нашли время написать потрясающий ответ, вы сделали мой день!

Ответ №2:

В основном parent не устанавливается, пока вы не вызовете метод addChild, поэтому:

 public class Main extends MovieClip
{
    public var i;
    public var child: Child;

    public function Main()
    {
        i = 4;
        child = new Child;
        addChild(child);
    }
}
 

конечно, это означает, что parent недоступен и будет не определен в вашем конструкторе. Иногда я передаю родительский элемент, к которому я хочу добавить дочерний элемент, в аргументы конструктора, чтобы упростить добавление вместе с позицией x / y, если это уместно.

 public class Child extends MovieClip
{
    public function Child(targetParent:DisplayObjectContainer = null, xpos:Number = 0, ypos:Number =  0)
    {
      if (targetParent != null)
     {
        targetParent.addChild(this);
     }

     this.x = xpos;
     this.y = ypos;
    }
}
 

Комментарии:

1. Спасибо за ваш ответ, ястребы! Я полностью забыл о addChild (и, следовательно, сбой конструктора)