2
\$\begingroup\$

Carrying on from this question, I'm still stuck trying to implement the behaviour mentioned in this tutorial. My movement has improved slightly thanks to the help I received:

movement gif

But the character still does this weird loop after reaching one of the waypoints.

AiSystem::steerToNextNode() is the relevant function that I'm having the problem with. It's the one doing the rotation with impulses:

//a few possibly non-obvious things:
//angle = npcBody->body()->GetAngle()
//angularVelocity = npcBody->body()->GetAngularVelocity()
//inertia = npcBody->body()->GetInertia()
//pos = npcBody->getWorldCenter()
//targetPos = nextNode->pos()

#define DEGTORAD 0.0174532925199432957f
#define RADTODEG 57.295779513082320876f

void AiSystem::steerToNextNode(ComponentEntity *npc, QSharedPointer<Action> action) const
{
    Path *path = action->path();
    Q_ASSERT(!path->isEmpty());

    QSharedPointer<PhysicsComponent> npcPhysics = npc->component<PhysicsComponent>();
    Box2dBody *npcBody = npcPhysics->body();
    QSharedPointer<PathNode> nextNode = path->front();

    if (!isNextToTargetPos(npc, nextNode->pos())) {
        // We're not close enough to the next node (waypoint); keep going.
        // The following code is based off this tutorial: http://www.iforce2d.net/b2dtut/rotate-to-angle
        float desiredAngle = MathUtils::cartesianDirectionTo(npcBody->getWorldCenter(), nextNode->pos());
        desiredAngle = qDegreesToRadians(desiredAngle);

        // Take the current angular velocity into aaccount, as it affects future time steps.
        float frequency = 60.0; //Hz
        float nextAngle = npcBody->body()->GetAngle() + npcBody->body()->GetAngularVelocity() / frequency ;
        float totalRotation = desiredAngle - nextAngle;

        while ( totalRotation < -180 * DEGTORAD ) totalRotation += 360 * DEGTORAD;
        while ( totalRotation >  180 * DEGTORAD ) totalRotation -= 360 * DEGTORAD;
        float desiredAngularVelocity = totalRotation * frequency;
        // Allow 10 degrees of rotation per time step, to achieve a gradual turn.
        float change = 10 * DEGTORAD;
        desiredAngularVelocity = qMin(change, qMax(-change, desiredAngularVelocity));
        float impulse = npcBody->getInertia() * desiredAngularVelocity;
        npcBody->applyAngularImpulse(impulse);

        // This applies a force in the direction of the current angle (npcBody->body()->GetAngle()).
        PhysicsSystem::moveForward(npcPhysics);
    }
    // unrelated stuff...
}

I've also included functions that it uses in case I'm doing something wrong there:

float MathUtils::cartesianDirectionTo(const QPointF &source, const QPointF &target) {
    QPointF toTarget(target.x() - source.x(), target.y() - source.y());
    float facingTarget = MathUtils::clampAngle(qRadiansToDegrees(atan2(toTarget.y() * -1, toTarget.x())));
    return facingTarget;
}

static float32 stupidForce(float32 mass)
{
    return mass * 15.0f;
}

void PhysicsSystem::moveForward(QSharedPointer<PhysicsComponent> physicsComponent)
{
    float32 force = stupidForce(physicsComponent->body()->getMass());
    float rotation = physicsComponent->body()->rotation();
    QPointF directionVector = MathUtils::directionVector(qDegreesToRadians(rotation));
    physicsComponent->body()->applyForce(directionVector * force, physicsComponent->body()->position());
}

bool AiSystem::isNextToTargetPos(ComponentEntity *npc, const QPointF &targetPos) const
{
    const Box2dBody *npcBody = npc->component<PhysicsComponent>()->body();
    return MathUtils::fuzzyCompare(npcBody->getWorldCenter(), targetPos, mPathFinder.targetLeniency());
}

Can anyone spot any mistakes?

\$\endgroup\$
  • \$\begingroup\$ It would be easier to check for logical errors if the code were commented. Could you add comments or perhaps explain in general terms the method you've intended to implement here? \$\endgroup\$ – Anko Oct 17 '15 at 19:19
  • \$\begingroup\$ @Anko, I can try, but I copied most of it from the article. \$\endgroup\$ – Mitch Oct 17 '15 at 19:34
  • \$\begingroup\$ @Anko, I added a few comments. Please let me know if there's something specific you think is unclear, and I'll try to explain, or, more likely, point to a specific part of the article. :p \$\endgroup\$ – Mitch Oct 17 '15 at 19:44
  • \$\begingroup\$ I didn't put this in an answer because I wasn't sure, but was it intended to use cartesianDirectionTo on the world center or does that access the registration point? \$\endgroup\$ – newton1212 Oct 18 '15 at 0:53
  • 2
    \$\begingroup\$ The wall of text was explaining what was going on in terms of the variables that determine the rotation, something I think is quite important to the question. If I post too little, it'll get closed for not enough info and "extensive back-and-forth", too much and it's a "wall of text". Thanks for the vote, and pointing out (editing) specific parts that could be improved. It's much more useful to have that, rather than some generic sentence that doesn't tell me anything. I think closing is a bit heavy-handed, considering he could have just left a comment. \$\endgroup\$ – Mitch Oct 18 '15 at 14:39

Browse other questions tagged or ask your own question.