Пишем игру для Android c помощью AndEngine. Часть 4

    Привет всем!

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

    Часть 1.
    Часть 2.
    Часть 3.

    Интересно?

    Шаг 1. Ограничения.
    Начнем немного из далека. Как все знают идеальный лазер движется со скоростью света и если не его пути нет препятствий врядли он когда либо остановится. Наш LaserLine не будет идеальным и первое что нужно сделать, это не дать ему возможности «улететь» за пределы игрового поля. Для этого добавим в класс GameObjectsMap метод:
    public static boolean outOfArea(Point pos) {
      return pos.x < 0 || pos.y < 0 || pos.x > WIDTH - 1
          || pos.y > HEIGHT - 1;
    }


    * This source code was highlighted with Source Code Highlighter.

    Будем проверять положения лазера этим методом при его построении.
    Шаг 2. LaserGun.
    Надеюсь все помнят вторую часть статьи, где помимо класса лазера у нас был класс лазерной пушки которая могла поворачиваться и препятствовать движению лазера. Добавим к ней поле:

    private LaserLine mLaserLine;

    В конструктор добавим:

    mLaserLine = new LaserLine(new Point(posX, posY), angle);

    В attachTo метод добавим:

    scene.getChild(GameObjectsMap.LASER_LAYER).attachChild(mLaserLine);

    Ну вот теперь у нашей пушки есть лазер, вот только его не видно. Чтоб его видеть нужно задать ему последовательность точек через которые он будет проходить. Для этого в классе LaserGun напишем метод:
    public void buildLaser(GameObject[][] map) {
      mLaserLine.setAngle(getAngle());
      mLaserLine.build(map);
    }


    * This source code was highlighted with Source Code Highlighter.


    Здесь на первый взгляд все понятно, путь лазера зависит от первоначального направления и от объектов на его пути, как раз для этого нам и понадобилась карта. Но метода LaserLine.build() еще не существует. Займемся им.
    public void build(GameObject[][] map) {
      sBuilder.buildPath(this, map);
      buildLines();
    }


    * This source code was highlighted with Source Code Highlighter.

    Метод короткий но про sBuilder я еще не рассказывал. Там то и скрыта магия. Не забываем добавить в класс LaserLine:

    private static LaserLineBuilder sBuilder = new LaserLineBuilder();

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

    Шаг 3. LaserBuilder
    Пришло время показать как выглядит построение лазера:
    public class LaserLineBuilder {

      private static final int MAX_STEPS = 200;

      public void buildPath(LaserLine laserLine, GameObject[][] map) {
        laserLine.clearPoints();
        int step = 0;
        int angle = laserLine.getAngle();
        Point position = new Point(laserLine.getStartPosition());
        GameObject gameObject;
        while (step < MAX_STEPS) {
          nextPosition(position, angle);
          if (GameObjectsMap.outOfArea(position)) {
            laserLine.addPoint(position);
            return;
          }
          gameObject = map[position.x][position.y];
          if (gameObject == null) {
            continue;
          } else {
            laserLine.addPoint(position);
            int reflection = gameObject.onLaser(angle);
            if (reflection < 0) {
              return;
            } else {
              angle = reflection;
            }
          }
        }
        laserLine.addPoint(position);
      }

      private void nextPosition(Point position, final int angle) {
        switch (angle) {
        case DynamicGameObject.DEG_0:
          position.x++;
          break;
        case DynamicGameObject.DEG_90:
          position.y++;
          break;
        case DynamicGameObject.DEG_180:
          position.x--;
          break;
        case DynamicGameObject.DEG_270:
          position.y--;
          break;
        case DynamicGameObject.DEG_45:
          position.x++;
          position.y++;
          break;
        case DynamicGameObject.DEG_135:
          position.x--;
          position.y++;
          break;
        case DynamicGameObject.DEG_225:
          position.x--;
          position.y--;
          break;
        case DynamicGameObject.DEG_315:
          position.x++;
          position.y--;
          break;
        default:
          break;
        }
      }
    }


    * This source code was highlighted with Source Code Highlighter.

    Идем по порядку. Ограничение на 200 шагов лазера думаю понятно зачем. Далее, первая точка лазера добавляется при создании и совпадает с координатами LaserGun, остальные же точки добавляются при выходе из игровой зоны, при остановке и при отражении. Замечу что изначально точки добавлялись по прохождению каждой клетки, в Mirrors Maze до сих пор так. Но там и не был использван AndEngine.
    nextPosition, надеюсь, не нуждается в объяснении.

    Шаг 4. Зеркало.
    До сих пор у нас не было объектов позволяющих отражать лазеры. Теперь нам нужно сделать один такой, и проверить все ли работает как задуманно. Не забываем добавить нужных текстур.
    public class Mirror extends DynamicGameObject {

      public Mirror(final int posX, final int posY, final int angle,
          final TextureRegion region) {
        super(posX, posY, angle, region);
      }

      @Override
      void attachTo(Scene scene) {
        scene.getChild(GameObjectsMap.GAME_OBJECTS_LAYER).attachChild(
            getSprite());
      }

      @Override
      int onLaser(int angle) {
        int a = getAngle() % 4;
        if (angle == (a + 1) % 8) return (a + 7) % 8;
        if (angle == (a + 7) % 8) return (a + 1) % 8;
        if (angle == (a + 3) % 8) return (a + 5) % 8;
        if (angle == (a + 5) % 8) return (a + 3) % 8;
        return -1;
      }

    }


    * This source code was highlighted with Source Code Highlighter.

    Создать новый игровой объект очень просто. Обойдемся без спойлеров и я предоставлю заинтересованному читателю самому разобраться в том, как работает метод onLaser. Двустороннее зеркало готово. Осталось добавить его инициализацию в GameObjectsMap.
    Шаг 4.Инициализация лазера.
    После всех манипуляций метод add теперь выглядит так:
    public void add(Type type, final int posH, final int posW, final int angle) {
        GameObject object = null;
        switch (type) {
        case lasergun:
          LaserGun l = new LaserGun(posH, posW, angle, mTextures
              .getLaserGun());
          mLaserGuns.add(l);
          object = l;
          break;
        case mirror:
          object = new Mirror(posH, posW, angle, mTextures.getMirror());
          break;
        case target:

          break;
        default:
          break;
        }
        mMap[posH][posW] = object;
      }


    * This source code was highlighted with Source Code Highlighter.

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

    private LinkedList mLaserGuns;
    и
    public void buildLasers() {
      for (LaserGun gun : mLaserGuns) {
        gun.buildLaser(mMap);
      }
    }


    * This source code was highlighted with Source Code Highlighter.

    Ну вот теперь посмотрим что у нас получилось. Добавим с StageActivity код:
    private void initMap() {
      mGameObjectsMap = new GameObjectsMap(mTextures);
      mGameObjectsMap.add(Type.lasergun, 0, 0, 3);
      mGameObjectsMap.add(Type.lasergun, 3, 2, 7);
      mGameObjectsMap.add(Type.lasergun, 3, 4, 4);
      mGameObjectsMap.add(Type.mirror, 3, 3, 3);
      mGameObjectsMap.addToScene(mEngine.getScene());
      mGameObjectsController = new GameObjectsController(mGameObjectsMap, this);
      final Scene scene = getEngine().getScene();
      mGameObjectsMap.buildLasers();
    }


    * This source code was highlighted with Source Code Highlighter.

    В итоге мы увидим что то на пободие:
    image

    На сегодня достаточно. Спасибо за внимание. Буду рад вопросам, советам и предложениям.
    Исходный код.
    Поделиться публикацией
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама

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

      0
      Есть вопрос не по посту, но по AndEngine.
      Возможно ли с его помощью построить карту(горный рельеф) в стиле марио игр?
        0
        Посмотрите AndEngineExamples. Там есть демка TMXTiledMap. Думаю это то что вам нужно. Если конечно я правильно вас понял.
          0
          не совсем мне не нужны TiledMap, мне нужно что-то более красивое
            0
            AutoParallaxBackground смотрели из примеров?
            Пример(скриншот) можете какой нибудь привести?
              0
              мне не бекграунд нужен, а физические объекты.
              пример игра Dead Rider. вот видите какая там карта.
                0
                Вот теперь понял о чем вы, я сам не пробовал но есть похожая игра сделанная на AndEngine. Wheelz
                  +3
                  Я разработчик игры Dead-Rider. Для создания таких уровней как в моей игре пришлось дописать много всего, стандартных средств AndEngine нет.
                    0
                    Было бы очень интересно поподробнее узнать.
                      0
                      расскажете что именно?
                      0
                      @irafa, не могли бы Вы рассказать чуть подробней о том, как Вам удалось сделать такой ландшафт? Я так понимаю, Вы модифицировали/расширили Andengine? Я совсем только разбираюсь с написанием игр, и пока непонятно, как можно такое сделать в Andengine. Был бы очень признателен, если бы Вы хотя бы указали, в каком направлении двигаться!
            +1
            Спасибо за эту и предыдущие статьи.
            Буду ждать продолжения.

            Исправьте ссылку на вторую часть, а то у вас две ссылки на первую ведут.
              +1
              Готово.
                0
                Оперативно.
                Правда, жаль что не продолжение ;)

            Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

            Самое читаемое