Nape 2 — Добавим графику

Сегодня поговорим как добавить к телу графику и обрабатывать ее.
Для работы с графикой у Nape есть два метода — свойство graphic и метод graphicUpdate класса Body.
Свойство graphic является без типовым, это значит что вы можете ему присвоить все что угодно.
Для начала поговорим о graphic.
Возьмем какую-нибудь картинку и прикрутим к телу, так чтобы она правильно отображалась.

Я взял картинку Шивы :)

[Embed(source="../../assets/sheeva.gif")]
private var SheevaImg:Class;
private var _sheevaSprite:Sprite;
...
_sheevaSprite = new Sprite();
_sheevaSprite.addChild(new SheevaImg());
addChild(_sheevaSprite);
...
var sheevaBody:Body = new Body(BodyType.DYNAMIC, new Vec2(APP_WIDTH/2, 100));
sheevaBody.shapes.add(new Polygon(Polygon.box(_sheevaSprite.width, _sheevaSprite.height)));
sheevaBody.space = _core.space();
 
// add graphics
sheevaBody.graphic = _sheevaSprite;

Если запустить вы увидите вот такое вот.
Графика отображает некорректно. Тела в Nape выровнены по центру масс (в принципе это настраиваемое, но по умолчанию так), а наш спрайт рисуется с левого верхнего угла. Надо отцентрировать спрайт чтобы pivot point (точка привязки) была по середке. Для этого отцентрируем битмапу в спрайте.

_sheevaSprite = new Sprite();
var sheevaImg:Bitmap = new SheevaImg();
_sheevaSprite.addChild(sheevaImg);
sheevaImg.x -= sheevaImg.width/2;
sheevaImg.y -= sheevaImg.height/2;
addChild(_sheevaSprite);

Теперь работает правильно.

Если вы используете только свойство graphic это предполагает то, что ваш объект является наследником DisplayObject и тогда Nape будет сам его вращать. Если же объект не является DisplayObject или вы хотите как то по особенному обрабатывать перемещение и вращение тогда вам нужно использовать graphicUpdate.

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

Обработчик graphicUpdate должен принимать один параметр — Body. Это то тело к которому относится этот обработчик.
Допустим используется графический движок Starling или Genome2D. Объекты этих двигов в отличие от DisplayObject принимают в качестве вращения радианы, а не градусы (Nape тоже оперирует радианами).

body.graphic = starlingDisplayObject;
body.graphicUpdate = function(b:Body) 
{
    b.graphic.x = b.position.x;
    b.graphic.y = b.position.y;
 
    // Так как Nape использует радианы и графический двиг тоже, 
    // то можно смело без всяких трансформаций присваивать угол
    b.graphic.rotation = b.rotation;
}

Если же в обработчике обрабатывается обычный DisplayObject, тогда надо перевести радианы в градусы и обрезать значение так, чтобы оно находилось на промежутке [-360; 360].

...
b.graphic.rotation = (b.rotation * 180/Math.PI) % 360;
...

Исходник

This movie requires Flash Player 9

Поделиться в соц. сетях

Опубликовать в LiveJournal
Опубликовать в Google Plus
  • Alik

    >> Тела в Nape выровнены по центру масс (в принципе это настраиваемое, но по умолчанию так)

    Подскажи пожалуйста какое свойство позволяет регулировать этот параметр.

  • VirtualMaestro

    На самом деле не совсем так как я написал. Тела не выровнены по-умолчанию, просто если создавать допустим коробку через фабрику Polygon.box (new Polygon(Polygon.box)), то вершины автоматически выстроены вокруг центра масс. А если создавать круг (new Circle), то вторым параметром можно передать координаты центра масс (но по-умолчанию если создается круг он выровнен по центру). Если создавать полигон задавая вершины руками, то для того, чтобы выровнять полигон по центру масс надо вызвать метод align у тела после задания фигур.

    Поменять центр масс можно путем сдвига фигур в теле с помощью метода translateShapes в классе Body. В следующем примере фигура выровняна по центру масс, а потом она снова падет но уже со смещением (центр будет находится в правом нижнем углу)
    _sheevaBody.translateShapes(new Vec2(_sheevaBody.bounds.width/2, _sheevaBody.bounds.height/2));

    http://megaswf.com/serve/2351197

  • Vlad

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

  • VirtualMaestro

    Рамка рисуется потому, что в включена дебажная отрисовка нейпа. В моем классе InitNape за это отвечает метод initDebugDraw. Просто закомментируйте строку addChild(_debug.display);

  • Vlad

    Спасибо большое!

  • Alik

    Спасибо за вклад!

  • pelikan

    А как обратится к мувиклипу, который привязан к телу в момент столкновения чтоб переключить не другой кадр? Пробовал так:
    cb.int1.castBody.graphic.gotoAndStop(2);
    Но не вышло.

  • VirtualMaestro

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

    (cb.int1.castBody.graphic as MovieClip).gotoAndStop(2);

  • pelikan

    точно, спасибо!!! Работает!

  • xrapa

    а как сейчас прикрепить графику и чтоб она двигалась

    • VirtualMaestro

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

      Рассмотрим пример где графика это наследник родного DisplayObject.

      Саму графику можно хранить в динамическом свойстве userData, которое существует у Body — например.так — body.userData.graphics = myGraphics.

      Тогда можно написать такой код:


      var displayObject:DisplayObject;
      var position:Vec2;
      _space.liveBodies.foreach(function (body:Body):void
      {
      position = body.position;
      displayObject = body.userData.graphic;
      displayObject.x = position.x;
      displayObject.y = position.y;
      displayObject.rotation = (body.rotation * 180/Math.PI) % 360;;
      });

      • xrapa

        чот у меня не чего не получается, я новичок в as3 и nape

        var displayObject:DisplayObject=Atlas.platformalLefSprite;
        addChild(displayObject);
        var position:Vec2;
        var body:Body=new Body(BodyType.KINEMATIC);
        body.userData.graphic=displayObject;
        body.shapes.add(new Polygon(Polygon.box(Atlas.platformalLefSprite.width, Atlas.platformalLefSprite.height), Material.steel()));
        body.space=space;

        body.velocity.setxy(0, 100);
        space.liveBodies.foreach(function (body:Body):void
        {
        position = body.position;
        displayObject = body.userData.graphic;
        displayObject.x = position.x;body
        displayObject.y = position.y;
        displayObject.rotation = (body.rotation * 180/Math.PI) % 360;;

        });

        • VirtualMaestro

          Достоверно не известно должен ли liveBodies включать кинематические тела. Луке был задан вопрос месяц назад по этому поводу, он сказал что проверит, но пока не ответил.

          Потому если надо точно все тела обновить, нужно использовать bodies вместо liveBodies — space.bodies.foreach

          Ну и на всякий случай уточню, что код:

          _space.bodies.foreach(function (body:Body):void
          {
          position = body.position;
          displayObject = body.userData.graphic;
          displayObject.x = position.x;
          displayObject.y = position.y;
          displayObject.rotation = (body.rotation * 180/Math.PI) % 360;;
          });

          должен выполнятся каждый кадр.

          • xrapa

            ехха!!!поехало!
            liveBodies заменил на bodies и этот код поместил в enterFrameHandler.
            спасибо!

          • Krauser

            Попробовал сделать, по вашему обсуждению… что-то не получилось… Мое изображение застряло в углу экрана, по координатам 0, 0
            Можно посоветоваться, как исправить недуг?

          • VirtualMaestro

            Без кода не смогу угадать, что не так. Принцип правильный, у меня работает. Вылейте простой пример вашего кода на http://pastebin.com/ , попробуем посмотреть что не так.

          • Krauser

            Прошу прощения, не дождался — хотел уже прикрепить графику хоть каким-либо способом — скачал Nape 2.0.0 и стал руководствоваться этой статьей.
            Однако теперь ошибка стала отписываться такая: «… строка 106 1119: Обращение возможного неопределенного свойства graphic через ссылку со статическим типом nape.phys:Body .»

            Свойство «graphic» с версии 2.0.0 было убрано? Тогда запутался, почему цикл Ваших статей про Nape содержит цифру 2? ))

          • VirtualMaestro

            Это старый цикл статьей для предыдущей версии Nape. Дело в том, что после самой первой версии Nape, Лука переписал его и АПИ поменялось. Для того чтобы не путаться с первой версией (когда я начинал этот цикл статьей) назвал его 2. У Nape было два перерождения и на самом деле это как бы уже третья версия, просто Лука назвал его 2, потому и происходит путаница версий с моими статьями. Я не стал обновлять статьи, так как с этой версией Лука запилил отличный сайт с хорошей документацией, потому я не видел смысла этого делать. Если не считать нескольких крупных изменений (CCD, убрана поддержка графики), то эти версии не сильно отличаются.

          • Krauser

            Понятно. В которой версии, из самых последних, поддержка свойства graphic еще присутствовала, подскажите, пожалуйста?

          • VirtualMaestro

            Вот тут кажется http://deltaluca.me.uk/nape/v1/m10.1/r15/ но я не советую использовать ту версию — за ней нету будущего, ни баг фиксов ни саппорта, да и нового функционала и улучшений тоже нету.

          • Krauser

            Воздержался, по вашему совету — решил еще немного покапать — и накопал! )) Прикрутил MovieClip к боди)

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

            1. Создаем в библиотеке fla символ, типа MovieClip, называем, к примеру, box1, не забываем пометить галочками, про экспорт в AS;
            2. Далее в функцию постоянного обновления мира вписываем следующее:

            private function update(e:Event):void {
            var bodies:BodyList=world.bodies;
            for (var i:int = 0; i < bodies.length; i++) {
            var body:Body=bodies.at(i);
            if(body.userData.sprite!=null){
            body.userData.sprite.x=body.position.x
            body.userData.sprite.y=body.position.y
            body.userData.sprite.rotation=body.rotation*57.2957795;
            }
            }
            }

            (если вы делаете на примерах Лука, с сайта Nape, и расширяете класс его Templare.as то вставлять нужно именно туда, в последнюю функцию, и замените world на space)

            3. При создании объекта, после задания координат позиции, пропишите эти команды:
            ИМЯ_ТЕЛА.userData.sprite=new ИМЯ_СИМВОЛА_ИЗ_БИБЛИОТЕКИ();
            addChild(ИМЯ_ТЕЛА.userData.sprite);

            Теперь, когда нужно натянуть текстуру на следующий объект, используйте эти две команды при каждом создании тела, заменяя "имя тела" и "имя символа из библиотеки" на нужные вам.

          • Krauser

            Всем успехов! )

          • VirtualMaestro

            Благодарю :)