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

Изучение SphereCage

Функции игры и инсталляция объектов

В этом последнем параграфе мы напишем несколько функций, которые будут обрабатывать основные действия, напрямую не относящиеся к отдельным 3D-объектам, такие как учет количества очков, рисование интерфейса и непосредственное выполнение игры. В идеале для них нужно было бы использовать классы и ООП (интерфейс для запуска игры и поддержки рисования, или объект-доска для учета очков или жизней), но они требуют большего количества кода, и, к сожалению, в этой книге это рассматриваться не будет.

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

  1. Эта функция предназначена для сброса рабочего места в начале игры.
    initGame = function () { 
      paint();
      scoreTF.text = "00000000";
      lives = 4; 
      for (var i = 1; i < lives; i++) {
        var b = this.attachMovie("ball", "ball" + i, 160 + i);
        b._xscale = b._yscale = stageHeight/8;
        b._y = stageHeight/7;
        b._x = (stageWidth/40*(i*2-1) ) + b._width/2;
      }
      ball.reset();
      paddleMC.onEnterFrame = frameCode;
    };

    paint - это функция, рисующая в левой части интерфейса градиент случайным образом (ее мы создадим в следующем шаге). Сбрасываем количество очков на 0 и общее число жизней на 4. Пользуясь этим значением, добавляем на рабочее место 3 шарика (плюс текущий шарик, итого - 4) для графического отображения жизней. Изменяем размер шариков и располагаем их с левой стороны (позиция _x была определена методом "тыка"). Наконец, ball сбрасывает сам себя, и устанавливается событие onEnterFrame для объекта paddle для функции с именем frameCode. frameCode содержит код, который будет выполняться в игре повсеместно.

  2. Ниже приведена вызываемая функция paint.
    paint = function () {
      for (i=0; i<25; i++) {
        var p = painting.createEmptyMovieClip("p"+i, i);
        p. _x = Math.random()*(stageWidth/3-0);
        p._y = Math.random()*(stageHeight-stageHeight/4) + stageHeight/4;
        var w = Math.random0*50+100;
        var col = Math.ceil(Math.random()*100);
        col = col << 8;
        var colors = [col, col]; 
        var alphas = [50, 0]; 
        var ratios = [0, 200];
        var matrix = {matrixType:"box", x:-w/2, y:-w/2, w:w, h:w, r: 0};
        p.beginGradientFill("radial", colors, alphas, ratios, matrix);
        p.moveTo(-w/2, -w/2); 
        p.lineTo(w/2, -w/2); 
        p.lineTo(w/2, w/2); 
        p.lineTo(-w/2, w/2); 
        p.endFill(); 
      }
    };

    Этот код просто повторяется 25 раз и располагает фильмы случайно в левой части интерфейса (маскируется фильмом paintMask ). Каждый фильм содержит квадрат, закрашенный градиентом, начинающимся с непрозрачного и заканчивающимся прозрачным зеленым цветом. Видно, что для каждого фильма мы установили случайную позицию, размер квадрата и оттенок зеленого цвета.

  3. Далее создаем функцию для поддержки рисования стеклянной сферы в главной области просмотра игры в правой части экрана.
    drawSphereAssets = function (me, params, center, smoothness) { 
      var ang = 360/smoothness; 
      var rad = ang*(Math.PI/180); 
      mc.moveTo(0, radius);
      var matrix = {a:params.hotspot, b:0, c:0, d:0, e:params.hotspot, 
      Кf:0, g:center.x, h:center.y, i:0};
      me.beginGradientFill("radial", params.colors, params.alphas, 
        params.ratios, matrix);
      for (var i = 1; i<=smoothness; i++) {
        var dx = Math.sin(i*rad)*radius;
        var dy = Math.cos(i*rad)*radius;
        me.lineTo (dx, dy);
      }
      mc.endFill();
    };

    У вас будет возможность лучше понять, что означает каждый из аргументов, при дальнейших вызовах функций. Вкратце - эта функция рисует круг, закрашенный градиентом на основе аргументов. Наслаивая различные градиенты, создаем стеклянную сферу. Это отличный способ достижения сложных эффектов с использованием только рисования API и небольшой доли экспериментирования.

    Основные тригонометрические функции позволяют рисовать линии около границы нашего круга, заполняя его градиентом, определяемым аргументами. Изначально я сделал это с использованием матрицы типа 3х3, однако вы можете реализовать то же самое с помощью другого типа матриц градиента, доступного для beginGradientFill (содержащей параметры matrixType, x, y, w, h и r ).

  4. Для управления нашими целевыми объектами нужны две функции.
    setTarget = function () {
      target.vertexList[0].x = (ball.direction.x*radAdj) + ball.vertexList [0].x;
      target.vertexList[0].y = (ball.direction.y*radAdj) + ball.vertexList [0].y;
      target.vertexList[0].z = (ball.direction.z*radAdj) + ball.vertexList [0].z;
      target.vertexList[1].x = (ball.direction.x*radius) + ball.vertexList [0].x;
      target.vertexList[1].y = (ball.direction.y*radius) + ball.vertexList [0].y;
      target.vertexList[1].z = (ball.direction.z*radius) + ball.vertexList [0].z;
      drawTarget ();
    };
    drawTarget = function () {
      for (var i = 0; i<target.clip.length; i++) {
        target.clip[i].clear(); 
        if (target.vertexList[i].z>0) { 
          targetMC.swapDepths(99); 
          var col = 0xFFFFFF; 
        } else {
          targetMC.swapDepths(2); 
          var col = 0x000000;
        }
        target.clip[i].lineStyle(1, col, 100);
        target.clip[i].moveTo (-planeSize/10, -planeSize/10);
        target.clip[i].lineTo(planeSize/10, planeSize/10);
        target.clip[i].moveTo(planeSize/10, -planeSize/10);
        target.clip[i].lineTo(-planeSize/10, planeSize/10);
      }
      target.render();
    };
    Пример 11.9.

    Изначально я задавался вопросом создания этих методов для нового класса целей, но в итоге пришел к выводу, что проще поддерживать их таким способом. setTarget устанавливает 3D-координаты двух вершин, содержащихся в целевой модели (внешняя и внутренняя цели). Обе вершины определяются умножением единичного вектора направления шарика на скалярное значение. Вершина внутренней цели установлена приблизительно на точке, в которую шарик попадет на периметре сферы (смещение шарика при начале движения от центра может повлечь за собой его выход за периметр), а вершина внешней цели расположена немного дальше этой токи, чтобы игрок мог лучше отслеживать траекторию. После этого вызывается метод drawTarget для непосредственной прорисовки и расположения этих целей.

    Вам также нужно знать, как будет установлен целевой объект (который мы сейчас реализуем при создании инстансов всех наших объектов). У нас есть фильм с именем target, содержащий два вложенных фильма с именами topTarget и bottomTarget. Мы установили их в начале кода. Каждый фильм будет содержать крест - фигуру "X", и представлять одну из двух вершин нашей модели цели. Таким образом, мы можем просто нарисовать крест в центре вложенных фильмов и переместить фильмы по отдельности в 3D-пространстве, что мы и делаем в этой функции. Цикл обрабатывает оба клипа, устанавливает глубину либо перед, либо за двумя плоскостями, в зависимости от параметра z, устанавливает черный или белый цвет, в зависимости от стороны сферы, в которой он находится, и затем рисует простой крестик, представляющий собой цель. Наконец, он вызывает функцию render цели для расположения ее в нужной позиции на экране.

  5. Введите две следующие функции.
    sortDepths = function () {
      if (ball.vertexList[0].z>-1) {
        ballMC.swapDepths(25); 
      } else {
        ballMC.swapDepths(15);
      }
      if (paddle.zDepth> 0) {
        paddle.clip.swapDepths(30); 
      } else {
        paddle.clip.swapDepths(10); 
      }
    };
    spinWorld = function (axis, rate) { 
      paddle.render(); 
      plane.render(); 
      ball.render(); 
      if (axis == "y") {
        paddle.rotateY(rate); 
      } else {
        paddle.rotateX(rate);
      }
      plane.transformMatrix = paddle.transformMatrix.duplicate();
      target.transfonnMatrix = paddle.transformMatrix.duplicate();
      ball.transformMatrix = paddle.transformMatrix.duplicate();
      ball.rotateDirection(axis, rate); 
      drawTarget(); 
    };
    Пример 11.10.

    sortDepths располагает наш шарик и ракетку на соответствующей глубине, в зависимости от стороны сферы, в которой они находятся.

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

    Нашим первым шагом является прорисовка каждой модели для применения всех остальных отдельных преобразований (их быть не должно, однако лишняя предосторожность не повлияет на игру). После этого мы поворачиваем нашу ракетку по оси вращения, отправленной функции. Так как все матрицы преобразования были удалены при предыдущей прорисовке, преобразование, необходимое для каждой модели при данном вращении, будет в точности таким же. По этой причине, нам не нужно создавать одну и ту же матрицу четыре раза (один раз для каждой модели). Мы можем использовать одно и то же преобразование для всех моделей. С помощью метода duplicate объектов, которые мы создали ранее, мы можем скопировать преобразование для каждой модели и применить их по отдельности. Мне кажется, что этот способ безопаснее, нежели просто копирование ссылки в объект матрицы преобразования, что имело бы место, если бы мы не использовали метод duplicate.

    Перед выходом из функции, мы также изменяем вектор направления нашего шарика (помните созданиe метода rotateDirection? Здесь он и применяется!) и рисуем нашу цель на ее новом месте.

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

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

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

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

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

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