使用子弹物理汽车和高度场,汽车卡住我该如何解决?

Using bullet physics car and heightfield, car get stuck how can I fix?

我一直致力于使用子弹物理学在我的游戏中实现一辆汽车。汽车的物理学使用 btRaycastVehicle,而代码主要基于 ForkLift Demo。 在这一点上,车辆似乎可以在平坦的地面上正常工作,但后来我开始在非平坦的地形上工作,我看到 class btHeightfieldTerrainShape 它采用一系列高度来构造一个形状.
所以我设法使用了这个 class 并且车辆可以在上面使用,但它有时会卡在地形的空洞中,即使爬升的高度真的很小。

我正在使用 MLCP 约束求解器和经过测试的 PGS 求解器,但没有帮助。
这是相关代码:

Vehicle.hpp

#define USE_MLCP_SOLVER
// I removed other #define because all were just floats
 
class Vehicle {
public:
    // theses bools are set to true when the corresponding key is pressed
    bool m_foward = false, m_backward = false, m_leftward = false, m_rightward = false;
    bool m_reset = false;
 
    Vehicle(Vao *chassisVao, Vao *wheelVao, const float *heights, uint32_t gridsize, float amplitude);
 
    ~Vehicle();
 
    // this function runs the logic of the physics simulation, it gets executed each frame
    void stepSimulation(uint32_t frameTime);
 
    // this function instantiate objects to rendered, it gets executed each frame
    // not including definition, not revalent
    void prepareRendering(std::vector<const Entity *> &entities);
 
private:
    // members are declared here --->  <---
 
    // create physics world and vehicle
    void initPhysics(const float *heights, uint32_t gridsize, float amplitude);
 
    // cleanup things
    // not including definition, not revalent
    void exitPhysics(void);
 
    // reset vehicle position, rotation, momentum, etc..
    // not including definition, not revalent
    void resetVehicle(void);
 
    // helper function to create rigid body
    // not including definition, not revalent
    btRigidBody* localCreateRigidBody(btScalar mass, const btTransform& startTransform, btCollisionShape* shape);
};

Vehicle.cpp

#include "Vehicle.hpp"
 
Vehicle::Vehicle(Vao *chassisVao, Vao *wheelVao, const float *heights, uint32_t gridsize, float amplitude) {
    initPhysics(heights, gridsize, amplitude);
 
    if (chassisVao) {
        m_chassisEntity = new Entity(chassisVao);
    }
    for (int i = 0; i < 4; ++i) {
        m_wheelEntities.push_back(Entity(wheelVao));
    }
}
 
Vehicle::~Vehicle() {
    exitPhysics();
 
    if (m_chassisEntity) {
        delete m_chassisEntity;
    }
    m_wheelEntities.clear();
}
 
void Vehicle::initPhysics(const float *heights, uint32_t gridsize, float amplitude) {
    // setup dynamics world
    m_collisionConfiguration = new btDefaultCollisionConfiguration();
    m_dispatcher = new btCollisionDispatcher(m_collisionConfiguration);
    btVector3 worldMin(-1000, -1000, -1000);
    btVector3 worldMax(1000, 1000, 1000);
    m_overlappingPairCache = new btAxisSweep3(worldMin, worldMax);
 
#ifdef USE_MLCP_SOLVER
    btDantzigSolver* mlcp = new btDantzigSolver();
    // btSolveProjectedGaussSeidel* mlcp = new btSolveProjectedGaussSeidel();
    btMLCPSolver* sol = new btMLCPSolver(mlcp);
    m_solver = sol;
#else
    m_solver = new btSequentialImpulseConstraintSolver();
#endif
 
    m_world = new btDiscreteDynamicsWorld(m_dispatcher, m_overlappingPairCache, m_solver, m_collisionConfiguration);
#ifdef USE_MLCP_SOLVER
    m_world->getSolverInfo().m_minimumSolverBatchSize = 1;
#else
    m_world->getSolverInfo().m_minimumSolverBatchSize = 128;
#endif
    m_world->getSolverInfo().m_globalCfm = 0.00001;
 
    // create ground object
    // btVector3 groundExtents(100, 3, 100);
    // btCollisionShape* groundShape = new btBoxShape(groundExtents);
    btCollisionShape* groundShape = new btHeightfieldTerrainShape(gridsize + 1, gridsize + 1, heights, 0.0f, amplitude, 1, false);
    m_collisionShapes.push_back(groundShape);
    btTransform tr;
    tr.setIdentity();
    tr.setOrigin(btVector3(gridsize * 0.5f, WHEEL_RADIUS, gridsize * 0.5f));
    localCreateRigidBody(0, tr, groundShape);
 
 
 
 
    // create vehicle
        // BEGIN - create chassis shape
    btVector3 vehicleExtents(1.76f, 1.1f, 4.0f);
    btCollisionShape* chassisShape = new btBoxShape(vehicleExtents);
    m_collisionShapes.push_back(chassisShape);
 
    btCompoundShape* compound = new btCompoundShape();
    m_collisionShapes.push_back(compound);
    btTransform localTrans;
    localTrans.setIdentity();
    //localTrans effectively shifts the center of mass with respect to the chassis
    localTrans.setOrigin(btVector3(0, 1, 0));
    compound->addChildShape(localTrans, chassisShape);
 
    tr.setOrigin(btVector3(0, 0, 0));
    m_carChassis = localCreateRigidBody(800, tr, compound);
        // END - create chassis shape
 
        // BEGIN - create vehicle
    m_vehicleRayCaster = new btDefaultVehicleRaycaster(m_world);
    m_vehicle = new btRaycastVehicle(m_tuning, m_carChassis, m_vehicleRayCaster);
    m_carChassis->setActivationState(DISABLE_DEACTIVATION); // never deactivate the vehicle
    m_world->addVehicle(m_vehicle);
 
    // choose coordinate system
    m_vehicle->setCoordinateSystem(0, 1, 2);
 
    btVector3 wheelDirection(0, -1, 0);
    btVector3 wheelAxis(-1, 0, 0);
    btVector3 connectionPoint(0.5f * vehicleExtents.x(), WHEEL_RADIUS, 0.5f * vehicleExtents.z() - WHEEL_RADIUS);
    m_vehicle->addWheel(connectionPoint, wheelDirection, wheelAxis, SUSPENSION_REST_LENGTH, WHEEL_RADIUS, m_tuning, true);
    connectionPoint = btVector3(-0.5f * vehicleExtents.x(), WHEEL_RADIUS, 0.5f * vehicleExtents.z() - WHEEL_RADIUS);
    m_vehicle->addWheel(connectionPoint, wheelDirection, wheelAxis, SUSPENSION_REST_LENGTH, WHEEL_RADIUS, m_tuning, true);
    connectionPoint = btVector3(0.5f * vehicleExtents.x(), WHEEL_RADIUS, -0.5f * vehicleExtents.z() + WHEEL_RADIUS);
    m_vehicle->addWheel(connectionPoint, wheelDirection, wheelAxis, SUSPENSION_REST_LENGTH, WHEEL_RADIUS, m_tuning, false);
    connectionPoint = btVector3(-0.5f * vehicleExtents.x(), WHEEL_RADIUS, -0.5f * vehicleExtents.z() + WHEEL_RADIUS);
    m_vehicle->addWheel(connectionPoint, wheelDirection, wheelAxis, SUSPENSION_REST_LENGTH, WHEEL_RADIUS, m_tuning, false);
 
    for (int i = 0; i < m_vehicle->getNumWheels(); i++) {
        btWheelInfo& wheel = m_vehicle->getWheelInfo(i);
        wheel.m_suspensionStiffness = SUSPENSION_STIFFNESS;
        wheel.m_wheelsDampingRelaxation = SUSPENSION_DAMPING;
        wheel.m_wheelsDampingCompression = SUSPENSION_COMPRESSION;
        wheel.m_frictionSlip = WHEEL_FRICTION;
        wheel.m_rollInfluence = ROLL_IN_INFLUENCE;
    }
 
    resetVehicle();
}

void Vehicle::stepSimulation(uint32_t frameTime) {
    float speed = m_vehicle->getCurrentSpeedKmHour();
    
    m_vehicleEngineForce = 0.0f;
    m_vehicleBreakingForce = 0.0f;
    /* --->
    Processing input sets m_vehicleEngineForce, m_vehicleBreakingForce, m_vehicleSteering
    <--- */

 
    m_vehicle->applyEngineForce(m_vehicleEngineForce, 2);
    m_vehicle->setBrake(m_vehicleBreakingForce, 2);
    m_vehicle->applyEngineForce(m_vehicleEngineForce, 3);
    m_vehicle->setBrake(m_vehicleBreakingForce, 3);
 
    m_vehicle->setSteeringValue(m_vehicleSteering, 0);
    m_vehicle->setSteeringValue(m_vehicleSteering, 1);
 
    m_world->stepSimulation(frameTime * 0.001f, 2);
    btMLCPSolver *solver = (btMLCPSolver *) m_world->getConstraintSolver();
    int numFallbacks = solver->getNumFallbacks();
    if (numFallbacks) {
        std::cerr << "MLCP solver failed " << numFallbacks << " times, falling back to btSequentialImpulseSolver" << std::endl;
    }
    solver->setNumFallbacks(0);
}

这里有一个视频来说明:link

谢谢

我终于解决了这个问题,我使用了子弹物理调试抽屉来查看边界框。问题是底盘形状在地形上发生碰撞,因为 btBoxShape 占一半,所以我将所有内容乘以 0.5,现在效果很好。

这里是用 C++ 为现代 OpenGL 编写的调试器代码,基于此 forum thread :

BulletDebugDrawer.hpp

#ifndef BULLET_DEBUG_DRAWER_H
#define BULLET_DEBUG_DRAWER_H

#include <bullet/LinearMath/btIDebugDraw.h>
#include <vector>

class BulletDebugDrawer : public btIDebugDraw {
private:
    int m_debugMode;

    std::vector<float> m_lines;

public:
    BulletDebugDrawer();

    virtual void drawLine(const btVector3& from,const btVector3& to,const btVector3& color);

    virtual void reportErrorWarning(const char* warningString);

    virtual void setDebugMode(int debugMode);

    virtual int getDebugMode(void) const;

    virtual void drawContactPoint(const btVector3& PointOnB, const btVector3& normalOnB, btScalar distance, int lifeTime, const btVector3& color) {

    }

    virtual void draw3dText(const btVector3& location, const char* textString) {

    }

    void glfw3_device_create(void);

    void glfw3_device_render(const float *matrix);

    void glfw3_device_destroy(void);

};
#endif

BulletDebugDrawer.cpp

#include "BulletDebugDrawer.hpp"

#include <algorithm>
#include <cstdint>
#include <iostream>
#include <glad/gl.h>

#define MAX_LINES_DRAWCALL 1000

GLuint dev_program;
GLint dev_uniform_proj;
GLint dev_uniform_col;
GLint dev_attrib_pos;

GLuint dev_vao;
GLuint dev_vbo;

BulletDebugDrawer::BulletDebugDrawer() : m_debugMode(0) {

}

void BulletDebugDrawer::drawLine(const btVector3& from,const btVector3& to, const btVector3& color) {
    m_lines.push_back(from.getX());
    m_lines.push_back(from.getY());
    m_lines.push_back(from.getZ());

    m_lines.push_back(to.getX());
    m_lines.push_back(to.getY());
    m_lines.push_back(to.getZ());
}

void BulletDebugDrawer::setDebugMode(int debugMode) {
    m_debugMode = debugMode;
}

int BulletDebugDrawer::getDebugMode() const {
    return m_debugMode;
}

void BulletDebugDrawer::reportErrorWarning(const char* warningString) {
    std::cout << warningString << std::endl;
}

void BulletDebugDrawer::glfw3_device_create(void) {
    GLint status;
    static const GLchar *vertex_shader =
        "#version 150\n"
        "uniform mat4 ProjMtx;\n"
        "in vec3 Position;\n"
        "void main() {\n"
        "   gl_Position = ProjMtx * vec4(Position, 1);\n"
        "}\n";
    static const GLchar *fragment_shader =
        "#version 150\n"
        "uniform vec3 Color;\n"
        "out vec4 Out_Color;\n"
        "void main(){\n"
        "   Out_Color = vec4(Color, 1);\n"
        "}\n";

    dev_program = glCreateProgram();
    GLuint vert_shdr = glCreateShader(GL_VERTEX_SHADER);
    GLuint frag_shdr = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(vert_shdr, 1, &vertex_shader, 0);
    glShaderSource(frag_shdr, 1, &fragment_shader, 0);
    glCompileShader(vert_shdr);
    glCompileShader(frag_shdr);
    glGetShaderiv(vert_shdr, GL_COMPILE_STATUS, &status);
    assert(status == GL_TRUE);
    glGetShaderiv(frag_shdr, GL_COMPILE_STATUS, &status);
    assert(status == GL_TRUE);
    glAttachShader(dev_program, vert_shdr);
    glAttachShader(dev_program, frag_shdr);
    glLinkProgram(dev_program);
    glGetProgramiv(dev_program, GL_LINK_STATUS, &status);
    assert(status == GL_TRUE);
    glDetachShader(dev_program, vert_shdr);
        glDetachShader(dev_program, frag_shdr);
        glDeleteShader(vert_shdr);
        glDeleteShader(frag_shdr);

    dev_uniform_proj = glGetUniformLocation(dev_program, "ProjMtx");
    dev_uniform_col = glGetUniformLocation(dev_program, "Color");
    dev_attrib_pos = glGetAttribLocation(dev_program, "Position");

    {
        /* buffer setup */
        glGenBuffers(1, &dev_vbo);
        glGenVertexArrays(1, &dev_vao);

        glBindVertexArray(dev_vao);
        glBindBuffer(GL_ARRAY_BUFFER, dev_vbo);
        glBufferData(GL_ARRAY_BUFFER, MAX_LINES_DRAWCALL * 24, nullptr, GL_STREAM_DRAW);

        glEnableVertexAttribArray(dev_attrib_pos);

        glVertexAttribPointer(dev_attrib_pos, 3, GL_FLOAT, GL_FALSE, 12, 0);
    }

    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glBindVertexArray(0);
}

void BulletDebugDrawer::glfw3_device_render(const float *matrix) {
    glUseProgram(dev_program);
    glUniformMatrix4fv(dev_uniform_proj, 1, GL_FALSE, matrix);
    glUniform3f(dev_uniform_col, 1.0f, 0.0f, 0.0f);

    glBindVertexArray(dev_vao);
    glBindBuffer(GL_ARRAY_BUFFER, dev_vbo);

    for (int i = 0; i < m_lines.size(); i += 2 * MAX_LINES_DRAWCALL) {
        int batchVertexCount = std::min<int>(m_lines.size() - i, 2 * MAX_LINES_DRAWCALL);
        glBufferSubData(GL_ARRAY_BUFFER, 0, batchVertexCount * 12, reinterpret_cast<void *>(m_lines.data() + i));
        glDrawArrays(GL_LINES, 0, batchVertexCount);
    }

    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glBindVertexArray(0);
    glUseProgram(0);

    m_lines.clear();
}

void BulletDebugDrawer::glfw3_device_destroy(void) {
    glDeleteProgram(dev_program);
    glDeleteBuffers(1, &dev_vbo);
    glDeleteVertexArrays(1, &dev_vao);
}