Поместите элемент сцены без родителей под другим элементом сцены

В моей игре я хотел бы стрелять ракетами из ракетницы. Игрок держит ракетную установку как дочерний предмет. Ракеты должны быть без родителей. Я пытаюсь расположить ракету так, чтобы ее задняя часть совпадала с задней частью ракетной установки (на скриншотах игрок смотрит на север) и располагалась горизонтально внутри нее:

правильно

Вместо этого я получаю:

неверно

Поворот также неверен (запустите пример и переместите курсор мыши, чтобы понять, что я имею в виду). Где я ошибаюсь в своем коде?

#include <QtWidgets>

QPointF moveBy(const QPointF &pos, qreal rotation, float distance)
{
    return pos - QTransform().rotate(rotation).map(QPointF(0, distance));
}

float directionTo(const QPointF &source, const QPointF &target) {
    QPointF toTarget(target.x() - source.x(), target.y() - source.y());
    float facingTarget = qRadiansToDegrees(atan2(toTarget.y(), toTarget.x())) + 90.0f;
    facingTarget = fmod(facingTarget, 360.0f);
    if(facingTarget < 0)
        facingTarget += 360.0f;
    return facingTarget;
}

class Controller : public QObject
{
public:
    Controller(QGraphicsScene *scene) :
        mScene(scene)
    {
        mPlayer = scene->addRect(0, 0, 25, 25, QPen(Qt::blue));
        mPlayer->setTransformOriginPoint(mPlayer->boundingRect().width() / 2, mPlayer->boundingRect().height() / 2);

        mRocketLauncher = scene->addRect(0, 0, 16, 40, QPen(Qt::green));
        mRocketLauncher->setParentItem(mPlayer);
        mRocketLauncher->setPos(mPlayer->boundingRect().width() * 0.9 - mRocketLauncher->boundingRect().width() / 2,
            -mRocketLauncher->boundingRect().height() * 0.3);

        mRocket = scene->addRect(0, 0, 16, 20, QPen(Qt::red));
        scene->installEventFilter(this);

        QGraphicsTextItem *playerText = scene->addText("Player");
        playerText->setPos(0, 100);
        playerText->setDefaultTextColor(Qt::blue);
        QGraphicsTextItem *rocketLauncherText = scene->addText("Rocket launcher");
        rocketLauncherText->setPos(0, 120);
        rocketLauncherText->setDefaultTextColor(Qt::green);
        QGraphicsTextItem *rocketText = scene->addText("Rocket");
        rocketText->setPos(0, 140);
        rocketText->setDefaultTextColor(Qt::red);
    }

    bool eventFilter(QObject *, QEvent *event) {
        if (event->type() == QEvent::GraphicsSceneMouseMove) {
            const QGraphicsSceneMouseEvent *mouseEvent = static_cast<QGraphicsSceneMouseEvent*>(event);
            mPlayer->setRotation(directionTo(mPlayer->sceneBoundingRect().center(), mouseEvent->scenePos()));

            qreal rocketX = mRocketLauncher->sceneBoundingRect().center().x() - mRocket->boundingRect().width() / 2;
            QPointF rocketPos(rocketX, 0);
            rocketPos = moveBy(rocketPos, mPlayer->rotation(), mRocketLauncher->boundingRect().height() - mRocket->boundingRect().height());
            mRocket->setPos(rocketPos);
            mRocket->setRotation(mPlayer->rotation());

            return true;
        }
        return false;
    }
private:
    QGraphicsScene *mScene;
    QGraphicsRectItem *mPlayer;
    QGraphicsRectItem *mRocketLauncher;
    QGraphicsRectItem *mRocket;
};

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    QGraphicsView view;
    view.setMouseTracking(true);
    QGraphicsScene *scene = new QGraphicsScene;
    view.setScene(scene);

    Controller controller(scene);

    view.resize(300, 300);
    view.show();

    return app.exec();
}

person Mitch    schedule 18.04.2014    source источник


Ответы (1)


Идея состоит в том, чтобы:

  1. установить поворот обоих элементов;
  2. получить положение нижнего левого угла пусковой установки и ракеты в глобальных (сценовых) координатах;
  3. сдвиньте ракету, чтобы сделать positinos равными.

Код:

mPlayer->setRotation(directionTo(mPlayer->sceneBoundingRect().center(), 
                                 mouseEvent->scenePos()));
mRocket->setRotation(mPlayer->rotation());
QPointF launcherPos = mRocketLauncher->mapToScene(
  mRocketLauncher->boundingRect().bottomLeft());
QPointF currentRocketPos = mRocket->mapToScene(
  mRocket->boundingRect().bottomLeft());
mRocket->setPos(mRocket->pos() - currentRocketPos + launcherPos);
person Pavel Strakhov    schedule 19.04.2014
comment
Отличный ответ, спасибо! Я уточнил одну часть своего вопроса, которую, как я заметил, я не упомянул, а именно: как я могу центрировать ракету горизонтально внутри пусковой установки? Например, если вы вдвое уменьшите ширину ракеты: mRocket = scene->addRect(0, 0, 8, 20, QPen(Qt::red)); - person Mitch; 25.04.2014
comment
Использование QPointF(mRocketLauncher->boundingRect().center().x(), mRocketLauncher->boundingRect().bottom()) (как для пусковой установки, так и для ракеты), кажется, работает. :) - person Mitch; 26.04.2014