IOS Box2D - body follows a specific path based on an array of points with fluctuating speed

社会主义新天地 提交于 2019-12-10 09:57:19

问题


i have a question about a body, which follows a specific path. First of all here the method to move the body to a target point:

const float destinationControl = 0.3f;
b2Vec2 targetPosition = path[counter];
b2Vec2 missilePosition = _physicalBody->GetPosition();
b2Vec2 diff = targetPosition - missilePosition;
float dist = diff.Length();

if (dist > 0)
{

    if(dist < destinationControl){
        ++counter;
        return;
    }

    // compute the aiming direction
    b2Vec2 direction = b2Vec2(diff.x / dist, diff.y / dist);

    // get the current missile velocity because we will apply a force to compensate this.
    b2Vec2 currentVelocity = _physicalBody->GetLinearVelocity();

    // the missile ideal velocity is the direction to the target multiplied by the max speed
    b2Vec2 desireVelocity = b2Vec2(direction.x * maxSpeed, direction.y * maxSpeed);

    // compensate the current missile velocity by the desired velocity, based on the control factor

    b2Vec2 finalVelocity = control * (desireVelocity - currentVelocity);

    // transform our velocity into an impulse (get rid of the time and mass factor)
    float temp = (_physicalBody->GetMass() / normalDelta);

    b2Vec2 finalForce = b2Vec2(finalVelocity.x * temp, finalVelocity.y * temp);

    _physicalBody->ApplyForce(finalForce, _physicalBody->GetWorldCenter());

}

The array path represent as the name implies a specific path which the body follow when the user trigger a touch event.

My problem is that the variable maxSpeed is not constant. So when the path is very short and the maxSpeed is very high the body moves over the current target point. With my method above the body return to the current target point and does not focus the next point.

At the moment i have a constant destinationControl which controls if the distance between the body and the current target point is short enough to focus the next target point.

The focus is to increase a counter which represent the index of the target points.

So my final question is: Can you imagine another possible solution, because mine is not very safe. Is there a way to figure out if the body moves over the current target point?

thank you in advance and sorry for my bad english.


回答1:


I had a similar problem in a path following demonstration I was putting together(here is the project and here is the just the video).

I found the most workable solution was to use a combination of 2 functions. The first detects if the entity is "close" to the next point they want to get to.

bool MovingEntity::IsNearTarget()
{
   Vec2 toTarget = GetTargetPos() - GetBody()->GetPosition();

   if(toTarget.LengthSquared() < GetMinSeekDistanceSquared())
   {
      return true;
   }
   return false;
}

This entity is using a "seek" operation to move from point to point. If it is close to the point, the function returns true. It uses the square of the distance to avoid square roots for length.

The second function is the path following function executed every update:

void MovingEntity::ExecuteNavigateToPoint()
{
   vector<Vec2>& path = GetPath();
   bool isNearTarget = IsNearTarget();
   bool isNearNavTarget = IsNearTarget(_navigatePos,5.0);

   /* If the state tick timer expires, it means we
    * have spent too long trying to reach the next waypoint
    * and we need to replan on how to get to it.
    */
   if(IsStateTickTimerExpired())
   {
      CCLOG("Tick Timer Expired!!!");
      ChangeState(ST_NAVIGATE_TO_POINT);
   }
   /* If we are close to the navigation target point,
    * just seek to it.
    */
   if(isNearNavTarget || path.size() == 0)
   {  // Must be really close...just seek to it.
      CommandSeek(_navigatePos);
   }
   else if(!IsNodePassable(GetTargetPos(),false))
   {
      ChangeState(ST_NAVIGATE_TO_POINT);
   }
   else
   {  // If we are near the target and there are more points
      // on the list, pop the next point and navigate to it.
      if(isNearTarget && path.size() > 0)
      {  // Still more points on the list.
         GetTargetPos() = path.back();
         path.pop_back();
         ResetStateTickTimer(2*TICKS_PER_SECOND);
         /* If we can't get past the current nodes, replan.
          */
         if(path.size() > 0 && !IsNodePassable(path.back(),false))
         {
            ChangeState(ST_NAVIGATE_TO_POINT);
         }
      }
      ApplyThrust();
      ApplyTurnTorque();
   }
}

This one is a bit harrier, but what it comes down to is checking for whether the entity has reached the final target (_navigatePos), reached the next path point, or unable to approach the target point for some reason, so it times out and replans the path to it.

This is part of a simulation of a ship flying through a moving asteroid field, so the scenery was changing and the ship could get "stuck" in certain situations, so it had to replan every now and then.

The code base for this is located on github.

Was this helpful?




回答2:


What I think you might want to do is to compare your maxSpeed to the distance possible to move. When moving farther than the distance available, set the misslePosition to the targetPosition and retrieve the next target position in the path, and subtract the distance already moved from maxSpeed.

if (maxSpeed > dist)
{
    maxSpeed -= dist;
    misslePosition = targetPosition;
    targetPosition = path[counter + 1];
}

Another possible solution would be to make smaller steps and perform a loop while totalDistanceCovered < maxSpeed, this might be a little easier to capture more edge cases, although more CPU time is used.

const float stepDistance(0.1f); //Some relatively small value depending on your units.
float totalDistance(0.0f);
while (totalDistance < maxSpeed)
{
    totalDistance += stepDistance;

    //Perform the same movement logic you already have.
}

--- My original answer below ---

You may need to set the definition of the _physicalBody to be a bullet if you are attempting to collide with a dynamic target.

By default, Box2D uses continuous collision detection (CCD) to prevent dynamic bodies from tunneling through static bodies. This is done by sweeping shapes from their old position to their new positions. The engine looks for new collisions during the sweep and computes the time of impact (TOI) for these collisions. Bodies are moved to their first TOI and then halted for the remainder of the time step.

Normally CCD is not used between dynamic bodies. This is done to keep performance reasonable. In some game scenarios you need dynamic bodies to use CCD. For example, you may want to shoot a high speed bullet at a stack of dynamic bricks. Without CCD, the bullet might tunnel through the bricks.

From box2d.org/manual



来源:https://stackoverflow.com/questions/23157562/ios-box2d-body-follows-a-specific-path-based-on-an-array-of-points-with-fluctu

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!