Как стать автором
Обновить

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

Время на прочтение 7 мин
Количество просмотров 11K
Привет всем!

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

Часть 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

На сегодня достаточно. Спасибо за внимание. Буду рад вопросам, советам и предложениям.
Исходный код.
Теги:
Хабы:
+24
Комментарии 13
Комментарии Комментарии 13

Публикации

Истории

Работа

Ближайшие события

Московский туристический хакатон
Дата 23 марта – 7 апреля
Место
Москва Онлайн
Геймтон «DatsEdenSpace» от DatsTeam
Дата 5 – 6 апреля
Время 17:00 – 20:00
Место
Онлайн