Search…

Box2D - Phần 6: Ray Casting

30/09/20204 min read
Giới thiệu Ray Casting và nhiệm vụ của nó trong mô phỏng thế giới vật lý?

Ray Casting là gì?

Là một đoạn thẳng, được xác định trong Physics World. Có tác dụng tìm ra các đối tượng đang ở trong một phần nhất định của Physics World, có thể tưởng tượng rằng Ray Casting khá giống với rada.

Thành phần

Input

Là thành phần mà bạn xác định khởi tạo.

  • position1
  • position2
  • maxFraction

Phân tích

  • Với position1 và position2 bạn có thể xác định được hướng của Ray Casting. Có điểm đầu là position1 và điểm cuối là position2.
  • Bạn có khoảng cách của position1 và position2 là một giá trị lenght. Lúc này, độ dài của Ray Casting bằng giá trị của length*maxFraction.

Output

Là thành phần mà bạn nhận được khi Ray Casting bị một đối tượng cắt qua.

  • normal
  • fraction
ss_1

Ví dụ

Khởi tạo dự án của bạn. Ở trong dự án đó sẽ có các file như sau:

ss_2

Với các file GLES-Render.hGLES-Render.cpp có tác dụng vẽ những gì liên quan tới Physics World. Bạn có thể download ở phần Download của bài viết.

Header

Trong file HelloWorldScene.h có như sau:

#ifndef __HelloWorld_Scene_h__
#define __HelloWorld_Scene_h__

#define PTM_RATIO 32

#include "cocos2d.h"
#include "Box2D\Box2D.h"
#include "GLES-Render.h"

USING_NS_CC;

using namespace cocos2d::ui;

class RayCastMultipleCallback : public b2RayCastCallback {
public:
	enum {
		e_maxCount = 3
	};

	RayCastMultipleCallback() {
		m_count = 0;
	}

	float32 ReportFixture(b2Fixture* fixture, const b2Vec2& point, const b2Vec2& normal, float32 fraction) {
		b2Body* body = fixture->GetBody();
		void* userData = body->GetUserData();
		if (userData) {
			int32 index = *(int32*)userData;
			if (index == 0)
				return -1.0f;
		}

		b2Assert(m_count < e_maxCount);

		m_points[m_count] = point;
		m_normals[m_count] = normal;
		++m_count;

		if (m_count == e_maxCount)
			return 0.0f;
		return 1.0f;
	}

	b2Vec2 m_points[e_maxCount];
	b2Vec2 m_normals[e_maxCount];
	int32 m_count;
};

class RayCastClosestCallback : public b2RayCastCallback {
public:
	RayCastClosestCallback() {
		m_hit = false;
	}

	float32 ReportFixture(b2Fixture* fixture, const b2Vec2& point, const b2Vec2& normal, float32 fraction) {
		b2Body* body = fixture->GetBody();
		void* userData = body->GetUserData();
		if (userData) {
			int32 index = *(int32*)userData;
			if (index == 0)
				return -1.0f;
		}

		m_hit = true;
		m_point = point;
		m_normal = normal;
		return fraction;
	}

	bool m_hit;
	b2Vec2 m_point;
	b2Vec2 m_normal;
};

class HelloWorld : public cocos2d::Layer, public b2ContactListener {
public:
    static cocos2d::Scene* createScene();
    virtual bool init();
	void update(float dt);

	void draw(Renderer *renderer, const Mat4 &transform, uint32_t flags);
	void onDraw(const Mat4 &transform, uint32_t flags);

	void addbox(float, float);
	
    CREATE_FUNC(HelloWorld);

private:
	b2World* m_world;
	float32 m_angle;
	GLESDebugDraw* debugDraw;
	CustomCommand _customCmd;
};

#endif // __HelloWorld_Scene_h__

Phân tích

  • Dòng 14-72: Hiện thực 2 loại Ray Casting. Mỗi loại có một tác dụng, tác dụng của từng loại sẽ nói đến ở phần sau.

Source

Trong file HelloWorldScene.cpp, chỉ cần thật sự chú ý tới hàm update().

void HelloWorld::update(float dt) {

    int positionIterations = 10;  // Vị trí
    int velocityIterations = 10; // Vận tốc

    float deltaTime = dt; // Bước thời gian

    std::vector<b2Body *>toDestroy;
   	m_world->Step(dt, velocityIterations, positionIterations);

    for (b2Body *body = m_world->GetBodyList(); body != NULL; body = body->GetNext()) {
	if (body->GetUserData()) {
		Sprite *sprite = (Sprite *)body->GetUserData();
		sprite->setPosition(Point(body->GetPosition().x * PTM_RATIO, body->GetPosition().y * PTM_RATIO));
		sprite->setRotation(-1 * CC_RADIANS_TO_DEGREES(body->GetAngle()));
    }
		
	m_world->ClearForces();
	m_world->DrawDebugData();
    }


	float32 L = 11.0f;
    b2Vec2 point1(10.0f, 10.0f);
    b2Vec2 d(L * cosf(m_angle), L * sinf(m_angle));
    b2Vec2 point2 = point1 + d;

    // Loại 1
    /*RayCastAClosestCallback callback; 
    m_world->RayCast(&callback, point1, point2);

    if (callback.m_hit) {
        debugDraw->DrawPoint(callback.m_point, 5.0f, b2Color(0.4f, 0.9f, 0.4f));
		debugDraw->DrawSegment(point1, callback.m_point, b2Color(0.8f, 0.8f, 0.8f));
		b2Vec2 head = callback.m_point + 0.5f * callback.m_normal;
		debugDraw->DrawSegment(callback.m_point, head, b2Color(0.9f, 0.9f, 0.4f)); 
	} else {
		debugDraw->DrawSegment(point1, point2, b2Color(0.8f, 0.8f, 0.8f));
	}*/

    //Loại 2
    /*RayCastMultipleCallback callback;
	m_world->RayCast(&callback, point1, point2);
	debugDraw->DrawSegment(point1, point2, b2Color(0.8f, 0.8f, 0.8f));

	for (int32 i = 0; i < callback.m_count; ++i) {
		b2Vec2 p = callback.m_points[i];
		b2Vec2 n = callback.m_normals[i];
		debugDraw->DrawPoint(p, 5.0f, b2Color(0.4f, 0.9f, 0.4f));
		debugDraw->DrawSegment(point1, p, b2Color(0.8f, 0.8f, 0.8f));
		b2Vec2 head = p + 0.5f * n;
		debugDraw->DrawSegment(p, head, b2Color(0.9f, 0.9f, 0.4f));
	}*/
	m_angle += 0.25f * b2_pi / 180.0f;
}

Phân tích

  • Dòng 3-21: sự cập nhật của Physics World.
  • Dòng 23-26: xác định điểm đầu, điểm cuối. Điểm đầu là một điểm cho trước, điểm cuối được xác định bởi độ dài Ray Casting với điểm đầu.
  • Dòng 29-53: Đây là lần lượt các Ray Casting được sử dụng.

Tổng kết

Sau khi lần lượt sử dụng các loại Ray Casting ở phần Ví dụ có các kết quả sau:

Loại 1

ss_4

Loại 2

ss_3
IO Stream

IO Stream Co., Ltd

30 Trinh Dinh Thao, Hoa Thanh ward, Tan Phu district, Ho Chi Minh city, Vietnam
+84 28 22 00 11 12
developer@iostream.co

383/1 Quang Trung, ward 10, Go Vap district, Ho Chi Minh city
Business license number: 0311563559 issued by the Department of Planning and Investment of Ho Chi Minh City on February 23, 2012

©IO Stream, 2013 - 2024