STDIO
Tìm kiếm gần đây
    • Nội dung
    • QR Code
    • 0
    • 0
    • Sao chép

    Box2D - Phần 2: Thuật Ngữ Và Khái Niệm

    Giới thiệu về collision và các thao tác xử lý liên quan trên Cocos2d-x.
    23/01/2015
    30/09/2020
    8 phút đọc
    Box2D - Phần 2: Thuật Ngữ Và Khái Niệm

    Collision

    Collision là gì?

    Là va chạm của các body trong physics world, trong dự án games sử dụng rất nhiều thông tin từ nó để dùng cho logic games của bạn, ví dụ như:

    • Khi bắt đầu một va chạm và kết thúc một va chạm.
    • Những điểm trên body va chạm với body khác.
    • Các lực va chạm.
    • ...

    Thông tin một collision

    Thông tin về một vụ va chạm được chứa trong một đối tượng b2Contact. Từ đây, có thể kiểm tra rằng khi nào va chạm, và tìm hiểu về vị trí và hướng của phản ứng va chạm. Có hai cách chính, có thể nhận được các đối tượng b2Contact từ Box2D. 

    Kiểm tra danh sách b2Contact:

    for (b2Contact* contact = world->GetContactList(); contact; contact = contact->GetNext())
          contact->... // làm điều gì đó với va chạm này.

    Hoặc với một body:

    for (b2ContactEdge* edge = body->GetContactList(); edge; edge = edge->next)
          edge->contact->... //làm gì đó với va chạm này.
    

    Contact listeners

    Nếu trong dự án games liên tục xảy ra va chạm. Như bên trên chủ để có một va chạm, cần phải theo dõi từ lúc bắt đầu cho đến kết thúc va chạm đó, còn bây giờ không còn phải làm điều đó nữa. Một contact listeners là một class (b2ContactListener) với bốn chức năng mà cần ghi đè lên khi cần thiết.

     void BeginContact(b2Contact* contact);
     void EndContact(b2Contact* contact);
     void PreSolve(b2Contact* contact, const b2Manifold* oldManifold);
     void PostSolve(b2Contact* contact, const b2ContactImpulse* impulse);

    Nhưng đôi khi trong dự án games, một vài trường hợp phải dùng b2Contact thay cho b2ContactListener. Nhưng dù bằng cách nào nhận được những contact listener, chúng đều chứa thông tin tương tự. Phần cơ bản nhất của thông tin là những fixtures của va chạm lấy được bằng cách:

    b2Fixture* a = contact->GetFixtureA();
    b2Fixture* b = contact->GetFixtureB();

    Chú ý:

    • Từ các fixtuare mà thu được có thể tìm thấy các body trong vụ va chạm bằng phương thức GetBody() và từ đó sử dụng thông tin này cho logic games.
    • Khi một sự kiện va chạm xảy ra, tức là có 2 fixture va chạm, nhưng không thể biết chính xác thứ tự của các fixture này. Ví dụ, có một va chạm giữa player và enemy, không thể biết chắc rằng b2Fixture A là của body player hay của là body enemi và ngược lại. 

    Collision filtering

    Ví dụ như sau, trong dự án games có 3 loại đối tượng: người chơi, quái vậtcảnh quan và muốn chúng va chạm theo quy tắc sau:

    • Người chơi không va chạm với người chơi khác, tương tự với con quái vật.
    • Người chơi phải va chạm với con quái vật và ngược lại. Tất nhiên là người chơi và quái vật va chạm với cảnh quan.
    • Các đối tượng canh quan sẽ va chạm với nhau.

    Filter catefories và mask

    Categoriesmask là điều tốt nhất cho collision filtering, mục đích là để xác định thể loại cho mỗi đối tượng và sử dụng mask để lọc các va chạm giữa các đối tượng. Với ví dụ ở trên xác định có 3 loại đối tượng trong va chạm

    short CATEGORY_PLAYER = 0x0001; // 0000000000000001 
    short CATEGORY_MONSTER = 0x0002; // 0000000000000010
    short CATEGORY_SCENERY = 0x0004; // 0000000000000100

    Và:

    player1FixtureDef.filter.categoryBits = CATEGORY_PLAYER;
    player2FixtureDef.filter.categoryBits = CATEGORY_PLAYER;
     
    monster1FixtureDef.filter.categoryBits = CATEGORY_MONSTER;
    monster2FixtureDef.filter.categoryBits = CATEGORY_MONSTER;
    monster3FixtureDef.filter.categoryBits = CATEGORY_MONSTER;
    monster4FixtureDef.filter.categoryBits = CATEGORY_MONSTER;
     
    groundFixtureDef.filter.categoryBits = CATEGORY_SCENERY;
    crate1FixtureDef.filter.categoryBits = CATEGORY_SCENERY;
    crate2FixtureDef.filter.categoryBits = CATEGORY_SCENERY;

    Chú ý

    Ở đây sử dụng các giá trị 0×001, 0×002 và các 0×004. Tại sao không phải 1, 2 và 3? Đó là số lượng phân loại các loại đối tượng. Điều đó có nghĩa rằng các loại có thể là chi hết cho 2 (trong thập phân: 1, 2, 4, 8, 16, 32, 64, 128..., hoặc trong hệ thập lục phân: 0×1, 0×2, 0×4, 0×8, 0×10, 0×20, 0×40, 0×80...). 16 bit có nghĩa là có 16 thư mục có thể, từ 0×0001 để 0×8000.

    Bây giờ, hãy xác định các maskBits. Chúng làm việc như sau:

    for each object o1 in the world
        for each object o2 in the world
            isCollisionEnabled = (o1.filter.maskBits & o2.filter.collisionBits) ≠ 0

    Kết quả của sự va chạm trong ví dụ trên.

    short MASK_PLAYER = CATEGORY_MONSTER | CATEGORY_SCENERY; // ~CATEGORY_PLAYER
    short MASK_MONSTER = CATEGORY_PLAYER | CATEGORY_SCENERY; // ~CATEGORY_MONSTER
    short MASK_SCENERY = -1;
    • Đối tượng loại phong cảnh va chạm với tất cả mọi thứ. Vì vậy sẽ đặt maskBits là -1 cũng tương đương với 0xFFFF trong lĩnh vực bit.
    • maskBits có giá trị 0xFFFF (hoặc -1) sẽ cho phép các va chạm với tất cả các đối tượng khác. Nếu bạn thiết lập giá trị của maskBits là 0×0000 (hoặc 0) thì nó sẽ không va chạm với đối tượng nào cả

    Đoạn mã ở dưới khởi tạo các giá trị maskBits cho các đối tượng có trên ví dụ trên:

    player1FixtureDef.filter.maskBits = MASK_PLAYER;
    player2FixtureDef.filter.maskBits = MASK_PLAYER;
     
    monster1FixtureDef.filter.maskBits = MASK_MONSTER;
    monster2FixtureDef.filter.maskBits = MASK_MONSTER;
    monster3FixtureDef.filter.maskBits = MASK_MONSTER;
    monster4FixtureDef.filter.maskBits = MASK_MONSTER;
     
    groundFixtureDef.filter.maskBits = MASK_SCENERY;
    crate1FixtureDef.filter.maskBits = MASK_SCENERY;
    crate2FixtureDef.filter.maskBits = MASK_SCENERY;
    

    Filter group

    Có thể làm tất cả mọi thứ về va chạm sao cho phù hợp với logic game của với categoryBitsmaskBits. Nhưng đôi khi không cần đến nó vì sự phức tạp về các giá trị bit. Filter group được xây dựng để nhanh chóng tắt hoặc bật kiểm tra va chạm giữa các đối tượng có liên quan bằng cách nào đó. Nó cho phép chỉ định một chỉ số tích phân nhóm và có thể có tất cả mọi thứ về va chạm với:

    • Chỉ số nhóm giống luôn luôn va chạm (chỉ số tích cực)  
    • Chỉ số nhóm khác nhau không bao giờ va chạm (chỉ số tiêu cực).

    Với ví dụ trên sử dụng filter group:

    short GROUP_PLAYER = -1;
    short GROUP_MONSTER = -2;
    short GROUP_SCENERY = 1;
    

    Chỉ định như sau:

    player1FixtureDef.filter.groupIndex = GROUP_PLAYER;
    player2FixtureDef.filter.groupIndex = GROUP_PLAYER;
     
    monster1FixtureDef.filter.groupIndex = GROUP_MONSTER;
    monster2FixtureDef.filter.groupIndex = GROUP_MONSTER;
    monster3FixtureDef.filter.groupIndex = GROUP_MONSTER;
    monster4FixtureDef.filter.groupIndex = GROUP_MONSTER;
     
    groundFixtureDef.filter.groupIndex = GROUP_SCENERY;
    crate1FixtureDef.filter.groupIndex = GROUP_SCENERY;
    crate2FixtureDef.filter.groupIndex = GROUP_SCENERY;
    

    Có thể thấy đoạn mã ngắn hơn rất nhiều. Tuy nhiên, không có cách nào để vô hiệu hóa các va chạm giữa các nhóm, tức là một khi muốn người chơi không va chạm với người chơi khác, nhưng đôi khi có một số trường hợp muốn người chơi khác va chạm với nhau, không thể làm được. Đó là lý do tại sao việc sử dụng categoryBitsmaskBits lại mạnh hơn so với filter group.

    Chú ý

    Quay trở lại với chú ý của mục Collision, tôi đã nói rằng đôi khi việc bạn sử dụng b2Contact thay vì cho b2ContactListeners để bắt một sự kiện va chạm lại tốt hơn. Vì b2ContactListeners không thể bắt được sự kiện va chạm giữa các đối tượng không va chạm với nhau. Có nghĩa là khi một người chơi va chạm với một người khác, chỉ có b2Contact bắt được sự kiện này, còn b2ContactListeners thì không. 

    Di chuyển một body

    Forces và impulses

    Forces hành động theo thời gian để thay đổi vận tốc của một body trong khi impulses có thể thay đổi vận tốc của body ngay lập tức. Tùy vào mục đích bạn mô phỏng một sự việc nào đó để chọn cách tác động lực lên body cho nó di chuyển.

    myBodyDef.type = b2_dynamicBody;
        
    b2PolygonShape polygonShape;
    polygonShape.SetAsBox(1, 1); 
      
    b2FixtureDef myFixtureDef;
    myFixtureDef.shape = &polygonShape;
    myFixtureDef.density = 1;
    
    myBodyDef.position.Set(20, 20);
    bodies = m_world->CreateBody(&myBodyDef);
    bodies->CreateFixture(&myFixtureDef);

    Với forces:

    bodies->ApplyForce(b2Vec2(0,50), bodies->GetWorldCenter(), true);

    Với impulses:

    bodies->ApplyLinearImpulse(b2Vec2(0,50), bodies->GetWorldCenter(), true);

    Với transform:

    bodies->SetTransform(b2Vec2(10,20), 0);
    • Với forces và impulses có 2 tham số: lực tác động và điểm tác động trên body. Tùy vào điểm tác động trên body mà sẽ quyết định body đó quay theo chiều cùng với kim đồng hồ hay ngược chiều kim đồng hồ. 
    • Với transform có 2 tham số là: lực tác động và góc quay.

    Linear

    Tác động một lực lên body, nhưng body sẽ không đồng thời quay tròn.​

    bodies->SetLinearVelocity(b2Vec2(-10.0f, 0.0f));

    Với việc sử dụng fores và impulses với điểm tác động là trung tâm body và transform với góc quay là 0 cũng sẽ làm cho body di chuyển mà không đồng thời quay tròn.

    Chú ý: Khi tác động một lực lên một body nó luôn là một vector, có phương và độ lớn tương ứng.

    User data

    Chức năng này có tác dụng thêm một data vào trong các đối tượng. Các đối tượng gồm

    • b2Body
    • b2Fixture
    • b2Joint

    Box2D cần biết thông tin này là gì và nó không làm bất cứ điều gì với data đó. Nó chỉ giữ data và sẽ trả lại khi được yêu cầu. Đối tượng trên có các phương thức sau:

    void SetUserData(void* data);
    void* GetUserData();

    Thường thì trong games bạn hay sử dụng data là một sprite. Việc bạn thiết lập data cho các body, fixtuare là sẽ vô cùng hữu ích. 

    int myInt = 123;
    body->SetUserData((void*)myInt);
    
    int udInt = (int)body->GetUserData();
      
    //*********************************************************
    struct bodyUserData {
          int entityType;
          float health;
          float stunnedTimeout;
    };
    
    bodyUserData* myStruct = new bodyUserData;
    myStruct->health = 4;//set structure contents as necessary
    body->SetUserData(myStruct);
    
    bodyUserData* udStruct = (bodyUserData*)body->GetUserData();
    
    0 Bình luận
    Lập Trình Game

    Lập Trình Game

    Kiến thức, kỹ thuật, kinh nghiệm lập trình game.

    Đề xuất

    Box2D - Phần 1: Giới Thiệu - Một Số Thuật Ngữ và Khái Niệm
    Giới thiệu engine xử lý vật lý Box2D, các khái niệm, cách thành phần ...
    Thuật Ngữ Server và Thuật Ngữ Client
    Khái niệm Server và Client mở rộng không chỉ nhằm hiểu về Server và ...
    02/05/2014

    Khám phá

    Chipmunk - Phần 1: Giới Thiệu
    Giới thiệu thư viện xử lý vật lý Chipmunk và giới thiệu các khái niệm, ...
    Chipmunk - Phần 2: Một Số Thuật Ngữ Và Khái Niệm
    Giới thiệu về một số thuật ngữ và khái niệm khác trong Chipmunk như cách ...
    Kỹ Thuật Grayscale và Nhị Phân Hoá Ảnh (Adaptive Threshold)
    Giới thiệu và chi tiết các thuật toán Grayscale, ảnh nhị phân và một số ...
    Rvalue References và Move Semantics
    Khái niệm rvalue reference, và ứng dụng để định nghĩa "move semantic" ...
    02/12/2014
    Phân Biệt Compile và Interpret
    Phân biệt 2 khái niệm Compile và Interpret, sự khác nhau và giống nhau ...
    Giới Thiệu về Kỹ Thuật Phần Mềm – Software Engineering
    Software Engineering là một phần của System Engineering - liên quan đến ...
    22/09/2014
    Phân Biệt Tham Chiếu và Con Trỏ trong C++
    Phân biệt tham chiếu và con trỏ trong C++ theo phương pháp đơn giản.
    GPU - Double Buffer và 1 Số Khái Niệm
    Giải thích các khái niệm double buffer, front buffer, back buffer, ...
    Khi bạn nhấn vào liên kết sản phẩm do STDIO đề xuất và mua hàng, STDIO có thể nhận được hoa hồng. Điều này hỗ trợ STDIO tạo thêm nhiều nội dung hữu ích. Tìm hiểu thêm.
    STDIO
    Trang chính
    Công ty TNHH STDIO

    30, Trịnh Đình Thảo, Hòa Thạnh, Tân Phú, Hồ Chí Minh
    +84 28.36205514 - +84 942.111912
    developer@stdio.vn

    383/1 Quang Trung, Phường 10, Quận Gò Vấp, Hồ Chí Minh
    Số giấy phép ĐKKD: 0311563559 do sở Kế hoạch và Đầu Tư TPHCM cấp ngày 23/02/2012

    ©STDIO, 2013 - 2020