Опубликован: 07.11.2006 | Доступ: свободный | Студентов: 3400 / 338 | Оценка: 3.94 / 3.71 | Длительность: 37:11:00
Лекция 12:

Изучение SphereCage

  1. Ниже приведены три несложные функции, которые вызываются во время выполнения игры.
    killBall = function () { 
      lives--;
      this ["ball"+lives].removeMovieClip();
      if (lives == 0) {
        delete paddleMC.onEnterFrame;
        ball.clip[0]._visible = 0;
        gameOverPopUp(); 
      } else {
        ball.reset(); 
      }
    };
    updateScore = function () {
      var score = parselnt(scoreTF.text, 10); 
      score += Math.floor(ball.velocity*20); 
      score += "";
      while (score.length<8) {
        score = "0"+score;
      }
        scoreTF.text = score;
      };
      playBlip = function (velocity) {
        this ["blip"+(Math.ceil(velocity/2)-1)].start();
    };

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

    updateScore использует текст из текстового поля scoreTF и превращает его в число. parseInt используется для указания Flash на использование десятичных чисел, в противном случае он воcпринимает их как двоичные. После этого данное значение помещается обратно в параметр text текстового поля scoreTF после проверки наличия в числе восьми цифр.

    Наконец, playBlip определяет соответствующий звук для проигрывания в зависимости от параметра velocity шарика: blip0, blip1 или blip2.

  2. Следующая функция, которую мы присвоили событию onEnterFrame объекта paddle, выполняется в каждом кадре для обновления игры.
    frameCode = function () { 
      if (Key.isDown(65)) {
        spinWorld("y", -paddle.rate);
      } else if (Key.isDown(68)) {
        spinWorld("y", paddle.rate);
      }
      if (Key.isDown(83)) {
        spinWorld("x", -paddle.rate);
      } else if (Key.isDown(87)) {
        spinWorld("x", paddle.rate);
      }
      ball.move();
      plane.rotateX(2);
      plane.rotateY(1);
      if (Key.isDown(Key.RIGHT)) {
        paddle.rotateY(-paddle.rate); 
      } else if (Key.isDown(Key.LEFT)) {
        paddle.rotateY(paddle.rate);
      }
      if (Key.isDown(Key.UP)) {
        paddle.rotateX(-paddle.rate); 
      } else if (Key.isDown(Key.DOWN)) {
        paddle.rotateX(paddle.rate);
      }
      plane.render(); 
      paddle.render(); 
      ball.render(); 
      sortDepths();
    };
    Пример 11.11.

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

    Первые два выражения if/else работают с клавишами A, S, D и W, поворачивая пространство по определенной оси при нажатии клавиши. Выполнение кода затем продолжается вызовом метода move шарика и поворотом плоскости по двум осям. Далее в коде есть еще два выражения if/else, предназначенные для проверки того, нажаты ли в данный момент клавиши стрелок, и, если это так, ракетка будет двигаться.

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

  3. Следующая функция (а также следующая за ней) длинная, но относительно простая. Большая часть кода обрабатывает расположение и форматирование текстовых полей во всплывающих окнах.
    startGamePopUp = function () {
      this.createEmptyMovieClip("dim", 200000);
      dim.beginFill(0x656565, 80);
      dim.lineTo(stageWidth, 0);
      dim.lineTo(stageWidth, stageHeight);
      dim.1ineTo(0, stageHeight);
      dim.endFill();
      var popUpWidth = stageWidth/4;
      var popUpHeight = stageHeight/4;
      this.createEmptyMovieClip("sg", 200001);
      sg._x = stageWidth/2;
      sg._y = stageHeight/2;
      startGame = new PopUp(popUpWidth, popUpHeight);
      startGame.clip = sg;
      startGame.render();
      sg.createTextField("title", 0, 0, -popUpHeight, 0, 0);
      statsTitle.size = stageHeight/10;
      sg.title.setNewTextFormat(statsTitle);
      sg.title.embedFonts = 1;
      sg.title.autoSize = "center";
      sg.title.text = "SphereCage";
      sg.createTextField("message" , 1, -popUpWidth*.9, -popUpHeight*.4,
      КpopUpWidth*1.8, popUpHeight);
      stats.align = "center";
      stats.size = stageHeight*.035;
      sg.message.setNewTextFormat(stats);
      sg.message.embedFonts = 1;
      sg.message.wordwrap = 1;
      sg.message.text = "use your arrows keys to maneuver the
      Кpaddle\n\nuse the a, s, d, w keys\nto spin the sphere";
      sg.createTextField("replay" , 3, 0, popUpHeight*.6, 0, 0);
      stats.align = "left";
      stats.size = stageHeight*.05;
      sg.replay.setNewTextFormat(stats);
      sg.replay.embedFonts = 1;
      sg.replay.textColor = 0x808080;
      sg.replay.autoSize = "center";
      sg.replay.text = "click to start";
      sg.col = 80;
      sg.direction = 1;
      sg.onEnterFrame = function() {
        this.col += 5*this.direction;
        if (this.col>254 |  this.col<80) {
          this.direction *= -1;
        }
        var r = this.col << 16;
        var g = this.col << 8;
        var b = this.col;
        this.replay.textColor = r | g | b;
      };
      sg.onPress = function() {
        stats.align = "left";
        dim.removeMovieClip();
        initGame();
        this.removeMovieClip(); 
      }; 
    };
    Пример 11.12.

    Я не буду разбирать каждую строку этого кода, так как он довольно прост и говорит сам за себя. Функция создает исходное всплывающее окно при загрузке игры. Она создает серый, частично прозрачный квадрат для "затуманивая" интерфейса, и затем создается новый инстанс класса PopUp, над которым мы работали ранее.

    Функция onEnterFrame, расположенная ближе к концу блока кода, выполняет цветовой переход для закрашивания сообщения о начале игры. При этом, значение параметра textColor текстового поля replay изменяется от серого до белого цвета. onPress ожидает щелчок мыши пользователя. Как только он происходит, "размытый" фильм удаляется вместе со всплывающим окном, и вызывается управляющий элемент initGame.

  4. Это аналогичный код, но для другого всплывающего окна.
    gameOverPopUp = function () {
      this.createEmptyMovieClip("dim", 200000);
      dim.beginFill(0x656565, 80);
      dim.lineTo(stageWidth, 0);
      dim.lineTo(stageWidth, stageHeight);
      dim.lineTo(0, stageHeight);
      dim.endFill() ;
      var popUpWidth = stageWidth/4;
      var popUpHeight = stageHeight/4;
      this.createEmptyMovieClip("gm", 200001);
      gm._x = stageWidth/2;
      gm._y = stageHeight/2;
      gameOver = new PopUp(popUpWidth, popUpHeight);
      gameOver.clip = gm;
      gameOver.render();
      gm.createTextField("title", 0, 0, -popUpHeight*.9, 0, 0);
      gm.title.setNewTextFormat (statsTitle);
      gm.title.embedFonts = 1;
      gm.title.autoSize = "center";
      gm.title.text = "Game Over";
      gm.createTextField("message", 1, 0, -popUpHeight*.2, 0, 0);
      gm.message.setNewTextFormat(stats);
      gm.message.embedFonts = 1;
      gm.message.autoSize = "center";
      gm.createTextField("score", 2, 0, popUpHeight*.1, 0, 0);
      gm.score.setNewTextFormat(stats);
      gm.score.embedFonts = 1;
      gm.score.autoSize = "center";
      var score = parselnt(scoreTF.text, 10);
      gm.score.text = score;
      if (score>parselnt(highTF.text, 10)) {
        gm.message.text = "You made high score!";
        highTF.text = scoreTF.text; 
      } else {
        gm.message.text = "You scored:";
      }
      gm.createTextField("replay", 3, 0, popUpHeight*.5, 0, 0);
      gm.replay.setNewTextFormat(stats);
      gm.replay.embedFonts = 1;
      gm.replay.textColor = 0x808080;
      gm.replay.autoSize = "center";
      gm.replay.text = "click to play again";
      gm.col = 80;
      gm.direction = 1;
      gm.onEnterFrame = function() {
        this.col += 5*this.direction;
        if (this.col>254 | | this.col<80) {
          this.direction *= -1;
        }
        var r = this.col << 16;
        var g = this.col << 8;
        var b = this.col;
        this.replay.textColor = r | g | b;
      };
      gm.onPress = function() { 
        dim.removeMovieClip(); 
        initGame(); 
        this.removeMovieClip();
      }; 
    };
    Пример 11.13.

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

  5. Мы уже на финишной прямой! Все функции и классы определены. Теперь нужно просто создать инстансы наших объектов.
    paddle = new PlaneModel();
    paddle.clip = paddleMC;
    paddle.rate = 8;
    paddle.vertexList = [];
    paddle.vertexList.push({x:-planeSize, y:-planeSize, z:radAdj, w:1});
    paddle.vertexList.push({x:-planeSize, y:planeSize, z:radAdj, w:1});
    paddle.vertexList.push({x:planeSize, y:planeSize, z:radAdj, w:1});
    paddle.vertexList.push({x-.planeSize, y:-planeSize, z:radAdj, w:1});
    paddle.side = [];
    paddle.side.push({vertices:[0, 1, 2, 3], sideColor:"550055"});
    plane = new PlaneModel(); 
    plane.clip = planeMC; 
    plane.vertexList = [];
    plane.vertexList.push({x:-planeSize, y:-planeSize, z:0, w:1}); 
    plane.vertexList.push({x:-planeSize, y:planeSize, z:0, w:1}); 
    plane.vertexList.push({x: planeSize, y:planeSize, z:0, w:1});
    plane.vertexList.push({x:planeSize, y:-planeSize, z:0, w:1}); 
    plane.side = [];
    plane.side.push({vertices:[0, 1, 2, 3], sideColor:"234523"});
    
    ball = new BallModel();
    ball.clip = [ballMC.innerBall];
    ball.rad = ballMC._width/2;
    ball.vertexList = [];
    ball.vertexlist.push({x:0, y:0, z:0, w:l});
    
    target = new Model();
    target.clip = [targetMC.topTarget, targetMC.bottomTarget];
    target.vertexList = [];
    target.vertexList.push({x:0, y:0, z:0, w:l});
    target.vertexList.push({x:0, y:0, z:0, w:l});
    
    light = new LightSource(-20000, -20000, 20000, 100);
    Пример 11.14.

    Теперь это все уже вам знакомо, не так ли? paddle, plane, ball и target являются инстансами наших классов. Мы предоставляем им все vertexList с вершинами, а также стороны для плоскостей. Имейте в виду, что нам нужно хранить наш шарик и целевой фильм в параметре массива из-за подхода, использованного при написании кода. Мы также создаем новый инстанс источника света в конце кода.

    Осталось всего лишь десять строк, и работа будет завершена!

  6. Следующие восемь строк рисуют сферические объекты в правой части.
    sphereHL = {colors:[OxFFFFFF, OxFFFFFF], alphas:[20, 0], ratios:[0,
    К225], hotspot:radAdj};
    sphereShadow = {colors:[0x000000, 0x000000], alphas:[0, 30],
    Кratios:[100,155], hotspot:radius*3.6};
    sphereBackShadow = {colors:[0x090909, 0x000000], alphas:[20, 0],
    Кratios:[80,140], hotspot:radius*4};
    sphereRim = {colors:[0xFFFFFF, 0xFFFFFF], alphas:[0, 2],
    Кratios:[230,230], hotspot:radius*2};
    drawSphereAssets(backShadow, sphereBackShadow, {x:50, y:40},
    Кsmoothness);
    drawSphereAssets(sphere.shadow, sphereShadow, {x:-radius/4,
    Кy: -radius/4}, smoothness);
    drawSphereAssets(sphere.highlight, sphereHL, {x:-radius/3,
    Кy: -radius/3}, smoothness);
    drawSphereAssets(sphere.rim, sphereRim, {x:0, y:0}, smoothness);

    Сначала создаются отдельные объекты для наших градиентов, затем вызывается созданная нами функция drawSphereAssets. Обратитесь к коду этой функции и посмотрите на соотношение данных значений. sphereHL окрашивает верх сферы белым цветом, а sphereShadow и sphereBackShadow создают тень в правой нижней части сферы. sphereRim представляет собой границу сферы, придавая ей некоторую толщину.

  7. Вы, наверное, не поверите, если я скажу, что ниже приведены две последние строки программы. Да, последние! Введите их в ваш код.
    padle.rotateY(45);
    startGamePopUp();

    В первой строке мы поворачиваем ракетку в начале таким образом, что она не закрывает собой шарик и плоскость в начале игры. Наконец, вызывается функция startGamePopUp, рисующая стартовое всплывающее окно и претворяющая в жизнь весь созданный нами код. Соглашусь, вам пришлось работать с большим количеством кода, однако созданная программа представляет собой красивую и интересную трехмерную игру. Тестирование игры на моем компьютере показало, что установленная частота кадров 24 fps редко опускается ниже 22 fps.

  8. Запустите фильм, чтобы увидеть результаты ваших стараний.

Дальнейшая разработка SphereCage

На компакт-диск я записал другую версию SphereCage с некоторыми дополнительными улучшениями. Наиболее заметно добавление звуковой дорожки к игре (предоставлено Imagescore Music ), а также то, что шарик "подается" в каждом раунде со специальным эффектом градиента. Начало каждой игры изменяет цвет интерфейса, а также происходит небольшой "танец" ракеток, управляемый прекрасным новым параметром Sound.duration. У самой ракетки пониженное затемнение, поэтому вы сможете видеть шарик через нее. Я также добавил функцию паузы, активизирующуюся при нажатии пользователем клавиши (пробел). Последним и наиболее сложным дополнением является такое расположение целей траектории, что внутренняя цель располагается именно на том месте, где шарик ударится о стенку периметра сферы. Откройте этот вариант игры, чтобы посмотреть на код, необходимый для данных улучшений.

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

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

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

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

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

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