首页 > 解决方案 > 如何在 PlayRho 中为身体设置“用户数据”?

问题描述

如何将“用户数据”(即我的应用程序的任意数据)与PlayRho 0.10.0 物理引擎中的主体相关联?

Box2D 2.4.1 物理引擎中,我可以使用传递给函数userData的实例字段将“用户数据”与身体相关联,并通过调用. 你如何在 PlayRho 中做到这一点?b2BodyDefb2World::CreateBodyb2Body::GetUserData()

标签: c++box2dplayrho

解决方案


Possible Solution:

In your application, you can use an array whose elements are your user-data values and whose indices match the underlying values returned from creating your bodies in PlayRho.

For example, a simple/naive implementation for any void* compatible user data might be like:

int main() {
    struct MyEntity {
        int totalHealth;
        int currentHealth;
        int strength;
        int intellect;
    };

    std::vector<void*> myUserData;

    auto world = World{};

    // If you want to pre-allocate 100 spaces...
    myUserData.resize(100);

    const auto body = world.CreateBody();

    // If your # bodies is unlimited or you don't want to preallocate space...
    if (body.get() >= myUserData.size()) myUserData.resize(body.get());

    // Set your user data...
    myUserData[body.get()] = new MyEntity();

    // Gets your user data...
    const auto myEntity = static_cast<MyEntity*>(myUserData[body.get()]);
    
    // Frees your dynamically allocated MyEntity instances.
    // Even with Box2D `userData` you may want to free these.
    for (const auto& element: myUserDAta) {
        delete element;
    }
    return 0;
}

But if you'd like to avoid dealing with headaches like memory leaks, myUserData could instead be std::vector<MyEntity> myUserData;, and new MyEntity() and delete element; calls could be avoided.

Some advantages to this:

  • Provides greater flexibility. User data is often application specific. Since you implement this storage yourself when using PlayRho, you're freer to make elements be any type you want and you don't have to make all of your uses of the physics engine have the same user data type. Your user data type could be world specific for instance whereas in Box2D all of your uses of its userData field would have the same type.
  • Avoids wasting memory. Not all applications need user data so those that don't won't be wasting this memory or having you modify the library code to avoid it.

Disadvantages:

  • This is different than in Box2D.
  • This may require more coding effort on your part if you don't care about having the extra flexibility (since the extra flexibility might save you some coding effort too).

Background/explanation:

While the PlayRho physics engine started off as a fork of the Box2D physics engine, PlayRho has moved away from reference semantics (pointers) and towards value semantics (values). So pointers were replaced and "user data" was outright removed in favor of alternatives like this possible solution. Additionally, with this shift to value semantics, the concept of creating a body changed from getting a pointer to a new body back from the world, to basically getting an integer index to the new body back from the world instead. That index acts as an identifier to the new body within the world and is basically an incremented counter from the world starting from 0 and incremented every time you create a new body. This means that you can have O(1) lookups from an array using the underlying body ID value as the index to the element that stores your user data. Using std::unordered_map<b2Body*, b2BodyUserData> would also provide O(1) lookups but hashed maps tend to be less cache friendly on modern hardware than arrays so it makes more sense to avoid such overhead in Box2D by setting aside storage for a user data value per body than it does in PlayRho.


推荐阅读