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

    Всем привет!
    Как и обещал, вторая часть статьи.
    Во избежание недопонимания, перед прочтением ознакомьтесь с первой частью статьи.
    Уже ознакомились? Тогда добро пожаловать под кат где я познакомлю читателя с игровыми объектами.

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

    Теперь обо всем по порядку.

    Шаг 1: Entity

    Entity — класс AndEngine, не буду долго объяснять зачем он нужен, просто скажу что это некий аналог класса ViewGroup. Он то нам и понадобится для наших игровых объектов.

    Объектов у нас будет 2 вида: статические и динамические. Динамические могут поворачиваться, а статические, соответственно, нет.

    Шаг 2: Статический объект

    Для пущего удобства создадим класс GameObject.

    public abstract class GameObject extends Entity {

      protected Sprite mSprite;
      
      public GameObject(final int posX, final int posY, final TextureRegion region) {
        mSprite = new Sprite(posX * GameObjectsMap.CELL_SIZE, posY
            * GameObjectsMap.CELL_SIZE, region);
        attachChild(mSprite);
      }

      protected Sprite getSprite() {
        return mSprite;
      }

      public static enum Type {
        lasergun, mirror, target
      }

      abstract int onLaser(int angle);

      abstract void attachTo(Scene scene);

    }


    * This source code was highlighted with Source Code Highlighter.


    Хочу обратить ваше внимание на метод onLaser который принимает значение угла под которым лазер попадает на наш игровой объект. И будет возвращать он отрицательное значение если от объекта ничего не отражается и угол отражения в обратном случае.

    Шаг 3: Динамический объект

    Вышеописанный класс подходит для статических объектов. Для динамических нам придется его немного расширить.

    public abstract class DynamicGameObject extends GameObject {

      public static final int DEG_0 = 0;
      public static final int DEG_45 = 1;
      public static final int DEG_90 = 2;
      public static final int DEG_135 = 3;
      public static final int DEG_180 = 4;
      public static final int DEG_225 = 5;
      public static final int DEG_270 = 6;
      public static final int DEG_315 = 7;

      private static final float ANGLE = 45f;

      private int mAngle;

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

      public int getAngle() {
        return mAngle;
      }

      public void setAngle(int angle) {
        this.mAngle = angle;
        rotateSprite();
      }

      public void rotateLeft() {
        mAngle--;
        if (mAngle < DEG_0) {
          mAngle = DEG_315;
        }
        rotateSprite();
      }

      public void rotateRigth() {
        mAngle++;
        if (mAngle > DEG_315) {
          mAngle = DEG_0;
        }
        rotateSprite();
      }

      public float getRotationAngle() {
        return ANGLE * mAngle;
      }

      protected void rotateSprite() {
        mSprite.setRotation(getRotationAngle());
      }

    }


    * This source code was highlighted with Source Code Highlighter.


    Вот, теперь мы готовы к великим свершениям, можем вертеть наш объект и его спрайт как нам вздумается. Но до проверки работоспособности кода еще далеко, да и лазера у нас нет. Что нам понадобится для лазера? Для начала выберем примитив который будем использовать. Очевидно, это будет линия, но учитывая что наш лазер будет отражаться от объектов нам понадобится нечто большее.

    Шаг 4: Сложный игровой объект

    public class LaserLine extends Entity {

      private static final float LINE_WIDTH = 3f;

      LinkedList<Point> mPoints;
      private final Point mStartPosition;
      private int mAngle;

      public LaserLine(Point startPosition, int angle) {
        init();
        mStartPosition = startPosition;
        mPoints.add(mStartPosition);
        mAngle = angle;
      }

      public void init() {
        mPoints = new LinkedList<Point>();
      }

      public void clearPoints() {
        mPoints.clear();
        mPoints.add(mStartPosition);
      }

      public void addPoint(final int cellNumX, final int cellNumY) {
        mPoints.add(new Point(cellNumX, cellNumY));
      }

      public void addPoint(Point point) {
        mPoints.add(new Point(point));
      }

      public void buildLines() {
        detachChildren();
        Point previousPoint = null;
        for (Point point : mPoints) {
          Ln.i(point.x + " " + point.y);
          if (previousPoint != null) {
            attachChild(makeLine(previousPoint, point));
          }
          previousPoint = point;
        }
        Ln.i("-------------");
      }

      public final Line makeLine(final Point start, final Point end) {
        final Line line = new Line(
            (start.x + 0.5f) * GameObjectsMap.CELL_SIZE_X,
            (start.y + 0.5f) * GameObjectsMap.CELL_SIZE_Y,
            (end.x + 0.5f) * GameObjectsMap.CELL_SIZE_X,
            (end.y + 0.5f) * GameObjectsMap.CELL_SIZE_Y);
        line.setLineWidth(LINE_WIDTH);
        line.setColor(1f, 0.1f, 0.1f, 0.75f);
        return line;
      }

      public Point getStartPosition() {
        return mStartPosition;
      }

      public void setAngle(int angle) {
        mAngle = angle;
      }

      public int getAngle() {
        return mAngle;
      }
    }


    * This source code was highlighted with Source Code Highlighter.


    Здесь у нас много всего интересного. Забегая вперед скажу что все наши объекты будут храниться в одном двумерном массиве. Этот факт объясняет наличие полей CELL_SIZE, а так же необходимость прибавлять 0.5f в методе makeLine, в надежде оказаться в самой середине клетки. Для построения лазера нам понадобится список точек. Каждый отрезок лазера будет «прикрепляться» к LaserLine. В итоге при добавлении нашего объекта на сцену мы увидим все отрезки лазера.

    Последнее что нам осталось сделать чтоб увидеть результат наших трудов это написать еще один небольшой класс.

    public class LaserGun extends DynamicGameObject {

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

      @Override
      int onLaser(int angle) {
        return -1;
      }

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

    }


    * This source code was highlighted with Source Code Highlighter.


    От нашей пушки не отражаются лазеры, и спрайт отображается на слое игровых объектов.

    Шаг 5: Рисуем объекты

    Теперь добавим в наше StageActivity код который позволит нам все это увидеть.
      public Scene onLoadScene() {
        this.mEngine.registerUpdateHandler(new FPSLogger());

        final Scene scene = new Scene(NUMBER_OF_LAYERS);
        scene.setBackground(new SpriteBackground(new Sprite(0, 0, mTextures
            .getBackground())));
        LaserLine line = new LaserLine(new Point(3, 3), 0);
        line.addPoint(new Point(4, 4));
        line.addPoint(new Point(5, 2));
        line.buildLines();
        scene.getChild(GameObjectsMap.LASER_LAYER).attachChild(line);
        LaserGun gun = new LaserGun(3, 3, 4, mTextures.getLaserGun());
        gun.attachTo(scene);
        return scene;
      }


    * This source code was highlighted with Source Code Highlighter.

    На своем Hero я увидел такую картину:


    Ищем код здесь.

    На сегодня это все. Ожидайте третью часть.

    Как всегда принимаются пожелания, конструктивные предложения и вопросы.

    P.S. По какой то неведомой мне причине на Hero толщина лазера всегда 1px, так что не пугаемся.
    P.P.S. В коде неоднократно упоминается класс GameObjectsMap, тоже не пугаемся, о нем в следующей статье. А вот и она
    • +31
    • 10,6k
    • 4
    Поделиться публикацией
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

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

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

      0
      Толщина линии на AndEngine всегда — 1px. Если вам требуется толще, то юзайте Rectangle
        +5
        В этом вы не правы. Толщина линии задается методом line.setLineWidth(LINE_WIDTH). Так выглядит скриншот с эмулятора толщина линии 3px:
        +1
        спасибо за статью
        3-ья часть намечается? =)
          0
          Да, вот только с сессией разберусь:) Если есть какие то конкретные вопросы, буду рад ответить.

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

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