Опубликован: 07.11.2006 | Уровень: специалист | Доступ: платный
Лекция 12:

Изучение SphereCage

  1. Ниже приведены два последних метода для BallModel.
    BallModel.prototype.rotateDirection = function(axis, degree) { 
      var rad = degree*Math.PI/180; 
      var sin = Math.sin(rad); 
      var cos = Math.cos(rad); 
      if (axis == "y") {
        var x = cos*this.direction.x-sin*this.direction.z;
        this.direction.z = cos*this.direction.z+sin*this.direction.x;
        this.direction.x = x; 
      } else {
        var z = cos*this.direction.z-sin*this.direction.y;
        this.direction.y = cos*this.direction.y+sin*this.direction.z;
        this.direction.z = z; 
      }
    };
    BallModel.prototype.reset = function() { 
      this.clip[0]._visible = 1; 
      this.velocity = 1.5;
      this.vertexList [0] = {x:0, y:0, z:0, w:l}; 
      this.setDirection(Math.random()-1, Math.random()-.5, 
        Math. random()); 
      setTarget();
    };

    rotateDirection вызывается каждый раз, когда пользователь поворачивает весь трехмерный мир. При этом нам не только необходимо настраивать расположение каждого объекта в трехмерном пространстве, но также и вектор направления самого шарика. Это не сложно выполнить. ball.direction - единичный вектор, содержащий компоненты x, y и z (его направление определяется по отношению к центру пространства), и нам нужно повернуть координаты этого вектора около соответствующей оси. Код вам знаком, если вы проделывали тривиальные действия с любым типом трехмерного содержимого во Flash. Аналогичная функция использовалась в начале предыдущей лекции. Мы осуществляем поворот около осей x и y (мы не реализуем поворот вокруг оси z в этой игре) с помощью уже готовых формул.

    При начале проигрывания нового звука (каждый раз после того, как игрок теряет шарик) вызывается метод reset. Он отображает фильм innerball (хранимый в массиве clip ), устанавливает velocity обратно на 1.5, устанавливает случайное направление и устанавливает координаты шарика на центр пространства (обратите внимание на параметр w, который всегда равен 1). Наконец, он устанавливает цель в соответствующем месте. В этом легко разобраться, за исключением функции setTarget, которую мы сейчас обсудим.

    Вот мы и создали класс BallModel! Помните, что при создании шарика с помощью выражения

    ball = new BallModel();

    он будет содержать не только все методы класса BallModel, но и все параметры и методы класса Model.

  2. Создание BallModel завершено. Теперь нужно определить плоскости.
    PlaneModel = function() {
    };
    PlaneModel.prototype = new Model(); 
    PlaneModel.prototype.render = function() {
      super.applyTransform();
      this.clip.clear();
      this.clip.lineStyle(1, 0, 100);
      var verts2D = [];
      this.zDepth = 0;
      for (var i = 0; i<this.vertexList.length; i++) {
        var whichVert = this.vertexList[i]; 
        verts2D[i] = {};
        var scale = focalLength/(focalLength-whichVert.z); 
        verts2D[i].x = whichVert.x*scale; 
        verts2D[i].y = whichVert.y*scale; 
        this.zDepth += whichVert.z;
      }
      this.clip.moveTo(verts2D[0].x, verts2D[0].y);
      this.clip.beginFill(this.getSideColor(this.side[0]), 100);
      for (var j = 1; j<verts2D.length; j++) {
        this.clip.lineTo(verts2D[j].x, verts2D[j].y);
      }
      this.clip.lineTo(verts2D[0].x, verts2D[0].y); 
      this.clip.endFill(); 
    };

    Вы обрадуетесь, узнав, что этот блок кода полностью представляет собой определение класса PlaneModel. Для добавления ракеток (которые являются плоскостями) нам нужен только метод прорисовки.

    Минуточку! У нас уже метод render, определенный для класса Model! Да, это так, однако давайте вернемся к обсуждению цепочек наследования. При использовании paddle.render, Flash будет сначала искать метод с именем render в классе PlaneModel. Если он будет найден, поиск будет прекращен. Flash никогда не дойдет до класса Model для выяснения метода с таким же именем. Таким образом, мы предоставили отдельный метод render для наших плоскостей, однако разрешили использование оставшихся методов Model.

    Этот метод сначала вызывает метод applyTransform своего большого класса, в данном случае - Model. Так же, как this ссылается на его собственные методы, super будет обращаться к методам своего суперкласса, используя при этом ссылки на них в параметре prototype (посмотрите, в каком месте предыдущего кода мы установили его).

    После применения преобразования удаляем фильм с рисованием объектов, обрабатываем циклом вершины и рисуем плоскость. Этот процесс очень похож на то, что мы делали в предыдущей лекции, поэтому я не хочу вдаваться в более мелкие подробности, однако вы должны иметь в виду добавление параметра zDepth. Он содержит сумму координат z всех вершин и будет использоваться лишь для определения того, расположена ли плоскость спереди или сзади сферы. Поэтому дальнейшие вычисления z нам не нужны.

  3. Рассмотрим последний имеющийся у нас тип модели. Ранее, когда вы пробовали играть в игру, вы, наверняка, даже не подозревали о его присутствии в этой программе. Введите следующий код.
    PopUp = function (w, h) { 
      this.vertexList = [];
      this.vertexList.push({x:-w, y:-h, z:20, w:l});
      this.vertexList.push({x:-w, y:h, z:20, w:l});
      this.vertexList.push({x:w, y:h, z:20, w:l});
      this.vertexList.push({x.-w, y:-h, z:20, w:l});
      this.vertexList.push({x:-w-15, y:-h-15, z:0, w:l});
      this.vertexList.push({x:-w-15, y:h+15, z:0, w:l});
      this.vertexList.push({x:w+15, y:h+15, z:0, w:l});
      this.vertexList.push({x:w+15, y:-h-15, z:0, w:l});
      this.vertexList.push({x:-w+5, y:-h+5, z:15, w:l});
      this.vertexList.push({x:-w+5, y:h-5, z:15, w:l});
      this.vertexList.push({x:w-5, y:h-5, z:15, w:l});
      this.vertexList.push({x:w-5, y:-h+5, z:15, w:l});
      this.side = [];
      this.side.push({vertices:[0, 1, 2, 3], sideColor:"666666"});
      this.side.push({vertices:[0, 4, 5, 1], sideColor:"888888"});
      this.side.push({vertices:[0, 3, 7, 4], sideColor:"888888"});
      this.side.push({vertices:[7, 3, 2, 6], sideColor:"333333"});
      this.side.push({vertices:[1, 5, 6, 2], sideColor:"333333"});
      this.side.push({vertices:[8, 9, 10, 11], sideColor:"666666"});
      this.side.push({vertices:[0, 8, 9, 1], sideColor:"444444"});
      this.side.push({vertices:[3, 2, 10, 11], sideColor:"777777"});
      this.side.push({vertices:[9, 10, 2, 1], sideColor:"777777"}) ;
      this.side.push({vertices:[0, 3, 11, 8], sideColor:"444444"});
    };
    PopUp.prototype = new Model();
    Пример 11.8.

    Да, всплывающие окна также являются моделями. Здесь видно, что конструктору будут отправлены переменные ширины (w) и высоты (h) для изменения размера всплывающего окна, однако я оставил размер таким же. Почти весь этот блок кода вам знаком из работы с предыдущей лекцией. Обратите внимание на присутствие дополнительного параметра w для каждой вершины так, чтобы можно было использовать матрицы размером 4x4. Также видно, что я оптимизировал 3D-подсветку модели, добавив свои собственные настройки применительно к sideColor освещенных и затемненных сторон.

  4. Для модели PopUp есть только одна функция.
    PopUp. prototype.render = function() { 
      this.clip.clear(); 
      var verts2D = []; 
      for (var i = 0; i<this.vertexList.length; i++) {
        var whichVert = this.vertexList[i];
        verts2D[i] = {};
        var scale = focalLength/(focalLength-whichVert.z);
        verts2D[i].x = whichVert.x*scale;
        verts2D[i].y = whichVert.y*scale; 
    }
      for (var i = 0; i<this.side.length; i++) {
        this.clip.moveTo(verts2D[this.side[i].vertices[0]].x, 
        Кverts2D[this.side[i].vertices[0]].y);
        this.clip.beginFillthis.getSideColor(this.side[i]), 100); 
        for (var j = 1; j<this.side[i].vertices.length; j++) {
          this.clip.lineTo(verts2D[this.side[i].vertices[j]].x, 
          Кverts2D[this.side[i].vertices[j]].y);
        }
        this.clip.lineTo(verts2D[this.side[i].vertices[0]].x, 
        verts2D[this.side[i].vertices[0]].y); 
        this.clip.endFill(); 
      } 
    };

    Это еще один метод render. По идее нужно объединить этот метод с методом render PlaneModel и сделать PopUp подклассом Plane, однако это лишь несколько строк уже знакомого вам дополнительного кода.

    Последним фрагментом кода объекта будет расширение самого объекта Object.

    Object.prototype.duplicate = function() { 
      var temp = {}; 
      for (var i in this) { 
        temp[i] = this[i];
      }
      return temp;
    };

    Когда в игре пользователь поворачивает трехмерный мир, этот метод используется для создания копии модели матрицы преобразования. Так как Flash хранит ссылки на объекты, а не копии, копии нужно создавать вручную. Я объясню, как это делается, когда мы дойдем до этой части кода.

    Мы достигли конца нашего ООП-кода Flash, и теперь осталось лишь определить несколько _root-функций, перед запуском нашей игры. Если вы сейчас запустите фильм, вы не увидите абсолютно никакой разницы по сравнению с предыдущим запуском. Так что же мы делали все это время? Это была подготовка. Теперь все наши собственные классы определены и настроены на обработку их собственных действий, и остается создать лишь несколько служебных функций.

Игорь Хан
Игорь Хан

у меня аналогичная ситуация. Однако, если взять пример из приложения (ball_motion_04_click for trial.fla) то след остается. при этом заметил, что в моем проекте в поле "One item in library" виден кружок, в то время как в приложенном примере такого кружка нет.

Вопрос знатокам, что не так?

Александр Коргапольцев
Александр Коргапольцев

объект созданый мной упорно не желает оставлять след(единственное что добился, так это то что шарик резво гоняется за курсором) функция duplicateMovieClip остаётся не активной, т.е. следа от объекта не остаётся, но если я тоже самый код вбиваю в учебный файл всё работает, не могу понять где я ошибаюсь и почему в документе созданном заново, не работает код начиная от функции duplicateMovieClip? 

Тамара Ионова
Тамара Ионова
Россия, Нижний Новгород, НГПУ, 2009
Магомед Алисултанов
Магомед Алисултанов
Россия, Волгоград, лицей 2