如何将对象从节点移动到节点?Libgdx Box2d A*寻路

Des*_*121 6 java path-finding box2d libgdx

我无法成功地让敌人从一个节点移动到另一个节点。

我设法设置了整个寻路,我得到了玩家的完整路径和下一个节点的位置,一直到最后。

如何将 box2d 主体从一个节点移动到另一个节点?

或者更简单 - 如何将 box2d 主体移动到某个位置?我尝试施加冲动,力量无法做到。

这是我的简化代码。Monster 和 Player 类都扩展了 Sprite:

public class B2dSteeringEntity implements Steerable<Vector2>, Updateable {

public static final String TAG = B2dSteeringEntity.class.getName();

    Body body;
    boolean tagged;
    float maxLinearSpeed, maxLinearAcceleration;
    float maxAngularSpeed, maxAngularAcceleration;
    float boundingRadius;

SteeringBehavior<Vector2> behavior;
SteeringAcceleration<Vector2> steeringOutput;

public B2dSteeringEntity(Body body, float boundingRadius) {
    this.body = body;
    this.boundingRadius = boundingRadius;

    this.maxLinearSpeed = 250;
    this.maxLinearAcceleration = 200;
    this.maxAngularSpeed = 0;
    this.maxAngularAcceleration = 0;

    this.tagged = false;

    this.steeringOutput = new SteeringAcceleration<Vector2>(new Vector2());
}

@Override
public Vector2 getLinearVelocity() {
    return body.getLinearVelocity();
}

@Override
public float getAngularVelocity() {
    return body.getAngularVelocity();
}

@Override
public float getBoundingRadius() {
    return 0;
}

@Override
public boolean isTagged() {
    return tagged;
}

@Override
public void setTagged(boolean tagged) {
    this.tagged = tagged;
}

@Override
public float getZeroLinearSpeedThreshold() {
    return 0;
}

@Override
public void setZeroLinearSpeedThreshold(float value) {

}

@Override
public float getMaxLinearSpeed() {
    return maxLinearSpeed;
}

@Override
public void setMaxLinearSpeed(float maxLinearSpeed) {
    this.maxLinearSpeed = maxLinearSpeed;
}

@Override
public float getMaxLinearAcceleration() {
    return maxLinearAcceleration;
}

@Override
public void setMaxLinearAcceleration(float maxLinearAcceleration) {
    this.maxLinearAcceleration = maxLinearAcceleration;
}

@Override
public float getMaxAngularSpeed() {
    return maxAngularSpeed;
}

@Override
public void setMaxAngularSpeed(float maxAngularSpeed) {
    this.maxAngularSpeed = maxAngularSpeed;
}

@Override
public float getMaxAngularAcceleration() {
    return maxAngularAcceleration;
}

@Override
public void setMaxAngularAcceleration(float maxAngularAcceleration) {
    this.maxAngularAcceleration = maxAngularAcceleration;
}

@Override
public Vector2 getPosition() {
    return body.getPosition();
}

@Override
public float getOrientation() {
    return body.getAngle();
}

@Override
public void setOrientation(float orientation) {

}

@Override
public float vectorToAngle(Vector2 vector) {
    return SteeringUtils.vectorToAngle(vector);
}

@Override
public Vector2 angleToVector(Vector2 outVector, float angle) {
    return SteeringUtils.angleToVector(outVector, angle);
}

@Override
public Location<Vector2> newLocation() {
    return null;
}

public Body getBody() {
    return body;
}

public void setBehavior(SteeringBehavior<Vector2> behavior) {
    this.behavior = behavior;
}

public SteeringBehavior<Vector2> getBehavior() {
    return behavior;
}

private void applySteering(float deltaTime) {
    boolean anyAccelerations = false;

    if(!steeringOutput.linear.isZero()) {
        Vector2 force = steeringOutput.linear.scl(deltaTime);
        body.applyForceToCenter(force, true);
        anyAccelerations = true;
    }
    
    if(anyAccelerations) {
        Vector2 velocity = body.getLinearVelocity();
        float currentSpeedSquare = velocity.len2();
        if(currentSpeedSquare > maxLinearSpeed * maxLinearSpeed) {
            body.setLinearVelocity(velocity.scl(maxLinearSpeed / (float) Math.sqrt(currentSpeedSquare)));
        }
        
        if (body.getAngularVelocity() > maxAngularSpeed) {
            body.setAngularVelocity(maxAngularSpeed);
        }
    }
}

@Override
public void update(float deltaTime) {
    if (behavior != null) {
        behavior.calculateSteering(steeringOutput);
        applySteering(deltaTime);
    }
}
Run Code Online (Sandbox Code Playgroud)

}

public Skeleton(B2WorldCreator creator, GameScreen screen, float x, float y, State state, HeroKnight player) {
    super(creator, screen, x, y, state, player);
    controller = screen.getController();
    setBounds(x, y, 150 / Constants.PPM, 150 / Constants.PPM);
    enemyAgentComponent = new EnemyAgentComponent(b2body, b2dSteeringEntity, controller, player);

}
Run Code Online (Sandbox Code Playgroud)

这是 EnemyAgentComponent 类:

public class EnemyAgentComponent implements Component, Telegraph, Updateable, Pather<Node> {

public static final String TAG = EnemyAgentComponent.class.getName();
public StateMachine<EnemyAgentComponent, EnemyState> stateMachine;

public boolean isInProximity() {
    return inProximity;
}

public boolean inProximity = false;
public boolean lowHP = false;
public boolean isShot = false;
private boolean isRequested = false;
private boolean requestingMovement;

private Steering wayPoint;
private Body body;
private Controller controller;

private HeroKnight player;
private Node startNode, endNode, previousStartNode, previousEndNode;
private Vector2 goal;
private PathfindingTarget newGoal;
private float impulseMag;

private boolean nodeReached;
private boolean pathGenerated;
private Arrive<Vector2> arriveSB;
private Steering waypoint;
private B2dSteeringEntity steering;
private IndexedAStarPathFinder<Node> pathFinder;
private GraphPathImp resultPath = new GraphPathImp();
private float pathfindingTimer;
private boolean firstPathGenerated;

public boolean isTouchingPlayer() {
    return touchingPlayer;
}

public void setTouchingPlayer(boolean touchingPlayer) {
    this.touchingPlayer = touchingPlayer;
}

private boolean touchingPlayer;

public EnemyAgentComponent(Body body, B2dSteeringEntity steering, Controller controller, HeroKnight player) {
    construct(body, steering, controller, player);
}

public void construct(Body body, B2dSteeringEntity steering, Controller controller, HeroKnight player) {
    this.steering = steering;
    this.controller = controller;
    this.body = body;
    this.player = player;
    stateMachine = new DefaultStateMachine<>(this, EnemyState.SEEKING);
    MessageManager.getInstance().addListener(this, Messages.PLAYER_IN_SIGHT);
    MessageManager.getInstance().addListener(this, Messages.PLAYER_ATTACKED_ENEMY);
    MessageManager.getInstance().addListener(this, Messages.LOW_HP);
    MessageManager.getInstance().addListener(this, Messages.PLAYER_OUT_OF_SIGHT);
    MessageManager.getInstance().addListener(this, Messages.TOUCHING_PLAYER);

    pathFinder = new IndexedAStarPathFinder<Node>(LevelManager.groundGraph, false);
    requestingMovement = false;
    goal = null;
    nodeReached = false;
    pathGenerated = false;
    firstPathGenerated = false;
    pathfindingTimer = 0;
    touchingPlayer = false;
Run Code Online (Sandbox Code Playgroud)

}

public Vector2 getGoal() {
    return goal;
}

public boolean isRequestingMovement() {
    return requestingMovement;
}

public void setRequestingMovement(boolean requestingMovement) {
    this.requestingMovement = requestingMovement;
}

public boolean isPathGenerated() {
    return pathGenerated;
}

public GraphPathImp getResultPath() {
    return resultPath;
}

public void generatePath() {
    previousEndNode = endNode;
    previousStartNode = startNode;
    resultPath.clear();
    pathFinder.searchNodePath(startNode, endNode, new HeuristicImp(), resultPath);
    newGoal = new PathfindingTarget(resultPath.get(0).getNodePosition());
    pathGenerated = true;
}

public void update(float deltaTime) {
    startNode = LevelManager.groundGraph.getNodeByXY(body.getPosition().x, body.getPosition().y);
    endNode = LevelManager.groundGraph.getNodeByXY(player.b2body.getPosition().x, player.b2body.getPosition().y);
    //If player gets in certain range of the enemy and is not touching the enemy, enemy's path is being generated
    if (inProximity) {
        if (!firstPathGenerated && !touchingPlayer)
            if ((pathfindingTimer == 0)) {
                generatePath();
                previousStartNode = startNode;
                previousEndNode = endNode;
                firstPathGenerated = true;
            }

        //If a path was already created, a new path is being requested only if player's position changes
        if (firstPathGenerated && (previousEndNode != endNode) && pathfindingTimer == 0 && !touchingPlayer) {
            generatePath();
        }
        if (firstPathGenerated)
            pathfindingTimer += deltaTime;
        //Paths are generated every 2 seconds
        if (pathfindingTimer >= 2) {
            pathfindingTimer = 0;
        }
        //If enemy touches the player pathfinding ends
        if (touchingPlayer) {
            pathfindingTimer = 0;
            pathGenerated = false;
            resultPath.clear();
            body.setLinearVelocity(0, 0);
        }
    }
    //The arrive behaviour is set, newGoal being the position of next node
    if (steering.getLinearVelocity().x == 0 && steering.getLinearVelocity().y == 0 && newGoal != null) {
            steering.setBehavior(new Arrive<Vector2>(steering, newGoal));
    }

    steering.update(deltaTime);
    //Updating the next node position based on the enemy reaching a node
    if (pathGenerated)
        if (Math.abs(body.getPosition().x - newGoal.getPosition().x) <= 0.1f && Math.abs(body.getPosition().y - newGoal.getPosition().y) <= 51 / 2f / Constants.PPM)
        {
            updatePath();
    }
}


public void updatePath() {
    //Setting the next target position
    if (resultPath.getCount() > 0) {
        resultPath.removeIndex(0);
        if (!touchingPlayer && resultPath.getCount() > 0) {
            newGoal.setPosition(resultPath.get(0).getNodePosition());
            requestingMovement = true;
            nodeReached = false;
        }
    }
}


@Override
public boolean handleMessage(Telegram telegram) {
    if (telegram.message == Messages.PLAYER_IN_SIGHT) {
        inProximity = true;

        return true;
    }
    if (telegram.message == Messages.PLAYER_OUT_OF_SIGHT) {
        inProximity = false;
        firstPathGenerated = false;
        pathGenerated = false;
        pathfindingTimer = 0;
        resultPath.clear();
    }
    if (telegram.message == Messages.LOW_HP) {
        lowHP = true;

        return true;
    }
    if (telegram.message == Messages.PLAYER_ATTACKED_ENEMY) {
        isShot = true;

        return true;
    }
    if (telegram.message == Messages.TOUCHING_PLAYER) {
        touchingPlayer = true;
        return true;
    }

    return false;
}


@Override
public void acceptPath(PathFinderRequest<Node> request) {
    if (request.pathFound) {
        resultPath = (GraphPathImp) request.resultPath;
    }
}
Run Code Online (Sandbox Code Playgroud)

}

正在使用 skeleton.update(deltaTime) 和 skeleton.draw(deltaTime) 更新和绘制骨架类。

EDIT2:现在尝试使用线性脉冲,运动非常笨重:

if (newGoal != null) {
        Vector2 direction = new Vector2(newGoal.getPosition().x - body.getPosition().x, newGoal.getPosition().y - body.getPosition().y).nor();
        float speed = Constants.SKELETON_SPEED;
        Vector2 velocity = new Vector2(speed * direction.x, speed * direction.y);
        body.applyLinearImpulse(velocity.x * body.getMass(), velocity.y * body.getMass(), body.getWorldCenter().x, body.getWorldCenter().y, true);
    }
if (pathGenerated)
        if (Math.abs(body.getPosition().x - newGoal.getPosition().x) <= 0.1f && Math.abs(body.getPosition().y - newGoal.getPosition().y) <= 51 / 2f / Constants.PPM)
        {
            Gdx.app.debug(TAG, "Node reached!");
            updatePath();
    }
Run Code Online (Sandbox Code Playgroud)

Seb*_*ian 1

移动实体与人工智能没有直接关系。人工智能只是找到一条路,实体遵循它。

使用box2d,您必须向正确的方向施加力或脉冲,确保调用box2d 世界的step() 方法并更新实体渲染坐标(例如精灵)以匹配box2d 的主体坐标。然后在下一个循环中再次从新坐标调用 AI,确定您现在必须移动的方向并再次施加力。

所以循环看起来像

  1. 通过人工智能找到方法
  2. 查找从您所在位置到下一个节点的方向
  3. 施加力/脉冲
  4. box2d 世界步骤
  5. 调整实体渲染坐标以匹配 box2d 主体位置
  6. 重复

为您提供更详细的答案需要更多有关如何渲染实体以及循环的知识。请分享一些示例代码

编辑:在您添加一些代码后,我发现仍然不清楚您的实际问题是什么。你可以移动你的实体但无法阻止它吗?它有动吗?你调用world.step()吗?快速查看后我发现的唯一明显的问题是,您使用 deltaTime 缩放 applyForceToCenter ,这是不需要的,因为 world.step 已经将 timeStep 作为参数。