Sử dụng Action Bezier để tạo đường cong trong Cocos2d-x

Vào một lúc nào đó ta cần di chuyển một vật nào đó (có thể là nhân vật,quái thú….) theo đường cong ? Lúc nào đó việc áp dụng các Action MoveTo,MoveBy … có vẻ sẽ gặp nhiều khó khăn ? Vì thường những Action này áp dụng để di chuyển vật theo đường thẳng ! Dưới đây tôi xin giới thiệu các bạn một phương pháp di chuyển theo đường cong đó là dùng Action Bezier trong cocos2d-x ( cocos2d::BezierTo , cocos2d::BezierBy ).

Các bạn copy nhớ ghi nguồn :
Viết bởi : Nguyễn Hoàng Thiên Phước .
Link : https://hoangthienphuoc.blogspot.com/2016/10/su-dung-action-bezier-e-tao-uong-cong.html

 Đường cong Bezier Là Gì ?  

     Về định nghĩa nghiên về mặt lý thuyết chi tiết các bạn có thể tham ở wikipedia . Ở đây tôi chỉ khái quát cho các bạn hiểu , và việc áp dụng nó trong cocos2d-x . Các bạn hãy xem hình bên dưới :
Hình 1

Đây chính là đường cong Bezier ( Bậc 2 ) nó được tạo thành từ 3 điểm P0 , P1 và P2. Nhìn vào hình các bạn sẽ thấy có 1 điểm chạy từ đoạn P0->P1, và điểm thứ 2 chạy từ P1->P2 . Tương ứng với mỗi lần sẽ sinh ra 1 điểm ta gọi là B(t) cái chấm tròn màu đen di chuyển ấy ! cứ liên tục như vậy -> nó sẽ tạo ra đường cong Bezier.

Ghi chú thêm : Nếu muốn đường cong của mình đều,đẹp như nửa hình tròn, hay nửa hình eclipse … thì các bạn nên thiết lập 3 điểm đó, là 3 đỉnh của một tam giác đều , hoặc tam giác cân !

 Cách Thức Sử Dụng Ra Sao ?  

Ok ! Đến đây các bạn có thể hiểu sơ sơ cách thức hoạt động của nó rồi đúng ko !
Vậy việc áp dụng thì sao ? Haha …  Việc sử dụng của nó rất đơn giản , cũng giống như các Action khác thôi !
cocos2d::BezierTo::create(float t, const ccBezierConfig& c);
Nhìn vào hàm create ta thấy nó cần hai tham số :
 Một là tham số t kiểu float là khoảng thời gian (duration ) Action thực hiện.
 Hai là tham số c kiểu ccBezierConfig
Oh ! Vậy ccBezierConfig là kiểu gì ? Các bạn hãy xem cấu trúc sau :
/** @struct Bezier configuration structure
 */
typedef struct _ccBezierConfig {
    //! end position of the bezier
    Vec2 endPosition;
    //! Bezier control point 1
    Vec2 controlPoint_1;
    //! Bezier control point 2
    Vec2 controlPoint_2;
} ccBezierConfig;
☛ Tương ứng với hình 1 :

Hình 2

Ở đây điểm : 
controlPoint_1  tương ứng với điểm P0,
controlPoint_2  tương ứng với điểm P1 ,
endPosition       tương ứng với điểm P2.

Các bạn thấy quen quen rồi đúng không  ?  ◕‿◕ hehe....

OK ! Đọc 100 bài báo lý thuyết suông không bằng ta làm cái demo nhỏ nhỏ ! (••) !

Đến đây ta sẽ bắt đầu thực hành nào ! Đầu tiên tôi sẽ nói cho các bạn biết là mình sẽ làm những gì ?
Đơn giản chỉ là làm đường cong giống như hình 1 và hình 2 là được ! ( Xem hình 3 )
Hình 3

Quy định nhỏ :
Điểm màu đỏ controlPoint_1 (P0)                        -> tọa độ  (200,200)
Điểm màu xanh dương controlPoint_2(P1)     -> tọa độ  (250,300)
Điểm màu xanh lá là điểm endPosition(P2)          -> tọa độ (300,200)
Ở đây 3 điểm của mình sẽ tạo thành tam giác cân(Isosceles Triangle) để cho đường cong của mình nó đẹp xíu!haha….

       cocos2d::ccBezierConfig bezier;
       bezier.controlPoint_1 = Point(200, 200);
       bezier.controlPoint_2 = Point(250, 300);
       bezier.endPosition   = Point(300, 200);
Sau đó chúng ta sẽ hiển thị 3 điểm đó lên màn hình ( mục đích để nhận biết thôi chứ không có ý gì khác )  Ở đây tôi dùng một sprite kích cỡ 20x20 pixel ( hoặc có thể khác tùy các bạn ) tên là “point.png”.
auto controlPoint_1 = Sprite::create("point.png");
       controlPoint_1->setColor(Color3B::RED);
       controlPoint_1->setPosition(bezier.controlPoint_1);
       addChild(controlPoint_1);

       auto controlPoint_2 = Sprite::create("point.png");
       controlPoint_2->setColor(Color3B::BLUE);
       controlPoint_2->setPosition(bezier.controlPoint_2);
       addChild(controlPoint_2);

       auto endPosition = Sprite::create("point.png");
       endPosition->setColor(Color3B::GREEN);
       endPosition->setPosition(bezier.endPosition);
       addChild(endPosition);

Đến đây chắc các bạn đã nắm bắt đc vấn đề rồi đúng không ? Ok ! tiếp nào ! Bây giờ vẽ một sprite di chuyển theo đường cong thôi ! Tôi xài một ảnh kích cở 10x10 pixel ( tên là “10x10.png” luôn nha ) !  
       auto sprTest = Sprite::create("10x10.png");
       sprTest->setPosition(100, 100);
       sprTest->runAction(BezierTo::create(2, bezier));
       addChild(sprTest);
Sau khi hiện thực ! Ta được kết quả !
Hình 4

Để làm được đường màu trắng ( Hiển thị lên đường cong cho mình dễ nhận biết ! ) . Các bạn code thêm trong hàm update void HelloWorld::update(float delta) nha ! Tôi dùng một sprite tên là “1x1.png” để vẽ lên !
auto sprTest = (Sprite*) this->getChildByName("sprTest");
//cocos2d::log("x=%f,y=%f", sprTest->getPositionX(), sprTest->getPositionY());
auto point = Sprite::createWithTexture(cocos2d::TextureCache::getInstance()->addImage("1x1.png"));
point->setPosition(sprTest->getPosition());
addChild(point);

Bổ sung thêm sprTest->setName("sprTest"); trên đoạn code mô tả sprTest nha !
Bên file <HelloWorldScene.h> thêm  void update(float delta) override;
Bên file <HelloWorldScene.cpp> thêm scheduleUpdate() trong bool HelloWorld::init() và thêm hàm void HelloWorld::update(float delta){ //.... }

(¬‿¬) Download Source ( Link Github )

 BezierTo Và BezierBy   

Phía trên tôi dùng là BezierTo , có nghĩa là di chuyển mục tiêu của mình theo đường cong Bezier đến một điểm đích xác định !

Ví dụ :
Node của mình ( sprTest ) ban đầu tại vị trí (100,100)Action BezierTo có điểm endPosition tại vị trí (300,200) , sau khi thực hiện hành động nạy node của mình sẽ ở tại vị trí endPosition (300,200). Quá dễ hiểu :v

Tiếp theo , BezierBy khác với BezierTo ra sao ? Nếu BezierTo là di chuyển “đến” -> thì BezierBy nó là di chuyển “thêm” !
Có nghĩa là sao ? Có nghĩa là từ chổ nó đang đứng nó sẽ di chuyển thêm 1 đoạn !
Ví dụ : sprTest mình đang đứng ở chổ (100,100) , controlPoint_1 (200,200) , controlPoint_2 (250,300) , endPosition (300,200) nếu thực hiện Action BezierBy thì :

controlPoint_1 + sprTest = (200,200) + (100,100) = (300,300)
controlPoint_2 + sprTest = (250,300) + (100,100) = (350,400)
endPosition    + sprTest =  (300,200) + (100,100) = (400,300)


Và lúc này điểm mà nó di chuyển đến sẽ là (400,300) ! Sau khi thực hiện Action BezierBy !

Hình 5

Bây giờ mình sẽ dời tọa độ của các điểm RED,GREEN,BLUE ... đến những vị trí mới đã tính toán ?

//.............
controlPoint_1->setPosition(300,300);
//.............
controlPoint_2->setPosition(350,400);
//.............
endPosition->setPosition(400,300);
//.............

sprTest->runAction(BezierBy::create(2, bezier));

Hình 6
Y như mong đợi haha!

 Ứng Dụng Thực Tế  

Mô tả hiệu ứng chết của nhân vật .... 


Character 

ccBezierConfig config;
config.controlPoint_1 = Vec2(60, 50);
config.controlPoint_2 = Vec2(-60, 100);
config.endPosition = Vec2(60, 150);

Sprite *sprite = Sprite::create("character.png");
sprite->setPosition(Vec2(visibleSize.width/2,visibleSize.height/2));
this->addChild(sprite);

sprite->runAction(Spawn::create(BezierBy::create(1.5f, config),FadeOut::create(2.0f),NULL));


Biểu tượng Public
cocos2d-x
Biểu tượng Public
cocos2d
Biểu tượng Public
action bezier cocos2d-x

About Me

Mọi thắc mắc vui lòng liên hệ Nguyễn Hoàng Thiên Phước. Số điện thoại:0122-871-3493