1、Physac介绍

Physac是一个开源的物理引擎,所有代码实现在头文件中,仅仅有2100行代码,移植接口只需要一个画线函数,因此很容易移植到嵌入式设备等,GitHub地址为https://github.com/victorfisac/Physac

2、引擎接口

引擎具有以下特性:

  • 可以动态创建\销毁三种控件:圆形、多边形、四边形
  • 可以改变全局重力方向
  • 可以给控件添加力、角度力
  • 用爆炸力将多边形物理体破碎成小物理体

API接口如下:

// Initializes physics values, pointers and creates physics loop thread
void InitPhysics(void);

// Returns true if physics thread is currently enabled
bool IsPhysicsEnabled(void);

// Sets physics global gravity force
void SetPhysicsGravity(float x, float y);

// Creates a new circle physics body with generic parameters
PhysicsBody CreatePhysicsBodyCircle(Vector2 pos, float radius, float density);

// Creates a new rectangle physics body with generic parameters
PhysicsBody CreatePhysicsBodyRectangle(Vector2 pos, float width, float height, float density);

// Creates a new polygon physics body with generic parameters
PhysicsBody CreatePhysicsBodyPolygon(Vector2 pos, float radius, int sides, float density);

// Adds a force to a physics body
void PhysicsAddForce(PhysicsBody body, Vector2 force);

// Adds a angular force to a physics body
void PhysicsAddTorque(PhysicsBody body, float amount);

// Shatters a polygon shape physics body to little physics bodies with explosion force
void PhysicsShatter(PhysicsBody body, Vector2 position, float force);

// Returns the current amount of created physics bodies
int GetPhysicsBodiesCount(void);

// Returns a physics body of the bodies pool at a specific index
PhysicsBody GetPhysicsBody(int index);

// Returns the physics body shape type (PHYSICS_CIRCLE or PHYSICS_POLYGON)
int GetPhysicsShapeType(int index);

// Returns the amount of vertices of a physics body shape
int GetPhysicsShapeVerticesCount(int index);

// Returns transformed position of a body shape (body position + vertex transformed position)
Vector2 GetPhysicsShapeVertex(PhysicsBody body, int vertex);

// Sets physics body shape transform based on radians parameter
void SetPhysicsBodyRotation(PhysicsBody body, float radians);

// Unitializes and destroy a physics body
void DestroyPhysicsBody(PhysicsBody body);

// Unitializes physics pointers and closes physics loop thread
void ClosePhysics(void);

3、移植相关

正如上述所说,移植仅仅需要一个画线函数,就可以匹配所有的平台:X86、嵌入式等等,我制作了一个demo,具体表现为在屏幕上有一个旋转的长方块,随机落下圆形或者是随机多边形打在长方块上。

#include "physac.h"
InitPhysics();
PhysicsBody floor = CreatePhysicsBodyRectangle((Vector2){screenWidth / 2 , screenHeight / 2 + 50 }, screenWidth*0.75,screenHeight*0.1, 10);
floor->enabled = false;
    while(true)
    {
        QThread::usleep(30);
        if(flag1++>10)
        {
            flag1 = 0;
            Vector2 temp;
            temp.x = QRandomGenerator::global()->bounded(screenWidth/2-20,screenWidth/2+20);
            temp.y = QRandomGenerator::global()->bounded(10,20);
            CreatePhysicsBodyCircle(temp, QRandomGenerator::global()->bounded(10, 30), QRandomGenerator::global()->bounded(10, 30));

            temp.x = QRandomGenerator::global()->bounded(screenWidth/2-20,screenWidth/2+20);
            temp.y = QRandomGenerator::global()->bounded(10,20);
            CreatePhysicsBodyPolygon(temp, QRandomGenerator::global()->bounded(10, 30), QRandomGenerator::global()->bounded(3,8),QRandomGenerator::global()->bounded(10, 30));
        }
        if(flag2++>3)
        {
            flag2 = 0;
            angle++;
            angle = angle % 360 ;
            SetPhysicsBodyRotation((PhysicsBody)addr, angle*PHYSAC_DEG2RAD);
        }

        int bodiesCount = GetPhysicsBodiesCount();
        for (int i = bodiesCount - 1; i >= 0; i--)
        {
            PhysicsBody body = GetPhysicsBody(i);

            if ((body != NULL) && (body->position.y > screenHeight * 2))
                DestroyPhysicsBody(body);
        }
        RunPhysicsStep();
        bodiesCount = GetPhysicsBodiesCount();
        image.fill(Qt::white);
        for (int i = 0; i < bodiesCount; i++)
        {
            PhysicsBody body = GetPhysicsBody(i);

            if (body != NULL)
            {
                int vertexCount = GetPhysicsShapeVerticesCount(i);
                for (int j = 0; j < vertexCount; j++)
                {
                    // Get physics bodies shape vertices to draw lines
                    // Note: GetPhysicsShapeVertex() already calculates rotation transformations
                    Vector2 vertexA = GetPhysicsShapeVertex(body, j);

                    int jj = (((j + 1) < vertexCount) ? (j + 1) : 0); // Get next vertex or first to close the shape
                    Vector2 vertexB = GetPhysicsShapeVertex(body, jj);

                    QLineF line(vertexA.x, vertexA.y, vertexB.x, vertexB.y);// Draw a line between two vertex positions
                    Painter.drawLine(line);

                }
            }
        }

4、移植展示

QT5:

physac_qt.gif

ESP32:

physac_esp.gif

5、已知问题

代码简单也是有代价的,简单看了一下代码,计算复杂两是O(N^N),这就导致控件数量较多的时候,计算复杂度会大幅度上升(控件较多时,ESP32计算一帧甚至需要几十秒),并且涉及到大量的浮点运算,对单片机非常不友好!

文章目录