图形赋予游戏一种视觉的吸引力,但是能够让游戏的世界鲜活起来的还应该是内部的物理引擎。物理引擎是游戏引擎中的子模块,是一种软件组件,可仿真物理系统。它根据牛顿力学定律,计算游戏中物体的合理的物理位置,并将计算结果提供给渲染引擎,从而展示出真实的渲染效果。物理引擎的仿真包括柔性体和刚体力学、流体力学以及碰撞检测。以游戏为中心的物理引擎侧重于实时近似,而科学仿真中的物理引擎则更多地侧重于精确计算以获得高准确性。科学物理引擎依赖于超级计算机的处理能力,而游戏物理引擎则可运行于资源受限的平台(比如手持型游戏设备和移动手机)。
图 1. 游戏应用中的物理引擎
Bullet Physics SDK: real-time collision detection and multi-physics simulation for VR, games, visual effects, robotics, machine learning etc.
- Build bullet
下载Bullet的源代码 https://github.com/bulletphysics/bullet3/releases/latest,然后将其解压到合适的路径下。可以选择运行批处理文件生成VisualStudio工程,这里运行build_visual_studio.bat生成VS2010工程。
在Debug模式下生成解决方案,解压后的bullet3-2.86.1目录中会出现bin文件夹,其中包含了生成的静态库文件和可执行文件。
- HelloWorld example
新建一个空的控制台程序,然后在源文件目录中加入HelloWorld.cpp文件:
#include <btBulletDynamicsCommon.h> #include <stdio.h> #include <iostream> #include <fstream> using namespace std; /// This is a Hello World program for running a basic Bullet physics simulation int main(int argc, char** argv) { btBroadphaseInterface* broadphase = new btDbvtBroadphase(); ///collision configuration contains default setup for memory, collision setup. Advanced users can create their own configuration. btDefaultCollisionConfiguration* collisionConfiguration = new btDefaultCollisionConfiguration(); ///use the default collision dispatcher. For parallel processing you can use a differnt dispatcher btCollisionDispatcher* dispatcher = new btCollisionDispatcher(collisionConfiguration); ///the default constraint solver. For parallel processing you can use a different solver btSequentialImpulseConstraintSolver* solver = new btSequentialImpulseConstraintSolver; ///instantiate the dynamics world btDiscreteDynamicsWorld* dynamicsWorld = new btDiscreteDynamicsWorld(dispatcher, broadphase, solver, collisionConfiguration); ///sets the gravity. We have chosen the Y axis to be "up". dynamicsWorld->setGravity(btVector3(0,-10,0)); btCollisionShape* groundShape = new btStaticPlaneShape(btVector3(0, 1, 0), 1); btCollisionShape* fallShape = new btSphereShape(1); //The btDefaultMotionState provides a common implementation to synchronize world transforms with offsets. btDefaultMotionState* groundMotionState = new btDefaultMotionState(btTransform(btQuaternion(0, 0, 0, 1), btVector3(0, -1, 0))); ///instantiate the ground. Its orientation is the identity, Bullet quaternions are specified in x,y,z,w form. btRigidBody::btRigidBodyConstructionInfo groundRigidBodyCI(0, groundMotionState, groundShape, btVector3(0, 0, 0)); ///Bullet considers passing a mass of zero equivalent to making a body with infinite mass - it is immovable btRigidBody* groundRigidBody = new btRigidBody(groundRigidBodyCI); ///add the ground to the world dynamicsWorld->addRigidBody(groundRigidBody); //The btTransform class supports rigid transforms with only translation and rotation btDefaultMotionState* fallMotionState = new btDefaultMotionState(btTransform(btQuaternion(0, 0, 0, 1), btVector3(0, 50, 0))); btScalar mass = 1; btVector3 fallInertia(0, 0, 0); fallShape->calculateLocalInertia(mass, fallInertia); ///when bodies are constructed, they are passed certain parameters. This is done through a special structure Bullet provides for this. ///rigid body is dynamic if and only if mass is non zero, otherwise static btRigidBody::btRigidBodyConstructionInfo fallRigidBodyCI(mass, fallMotionState, fallShape, fallInertia); btRigidBody* fallRigidBody = new btRigidBody(fallRigidBodyCI); dynamicsWorld->addRigidBody(fallRigidBody); ofstream outfile("C:\\Users\\KC\\Desktop\\height.csv", ios::out); for (int i = 0 ; i < 300 ; i++) { /* prototype: btDynamicsWorld::stepSimulation(btScalar timeStep, int maxSubSteps=1, btScalar fixedTimeStep=btScalar(1.)/btScalar(60.)); */ //It's important that timeStep is always less than maxSubSteps*fixedTimeStep //The first and third parameters to stepSimulation are measured in seconds //By decreasing the size of fixedTimeStep, you are increasing the "resolution" of the simulation. //When you pass Bullet maxSubSteps > 1, it will interpolate movement for you dynamicsWorld->stepSimulation(1 / 60.f, 10); btTransform trans; fallRigidBody->getMotionState()->getWorldTransform(trans); std::cout << "sphere height: " << trans.getOrigin().getY() << std::endl; outfile<<trans.getOrigin().getY()<<endl; } outfile.close(); delete fallShape; delete groundShape; delete dynamicsWorld; delete solver; delete dispatcher; delete collisionConfiguration; delete broadphase; printf("Press a key to exit\n"); getchar(); }