Cocos2d-x 3.2 Final

Cập nhật lên phiên bản Cocos2d-x 3.2. Bổ sung 3D sprite, 3D animation, game controller, fast Tilemap,..v...v... Cập nhật thường xuyên nhé

Cocos2d-x 3.0 Final

Cocos2d-x là Engine lập trình game đa nền tảng phổ biến nhất trên thế giới. Tính năng mạnh mẽ, dễ sử dụng, miễn phí, mã nguồn mở.

Cocos2d-x V3.0 Final

Hướng dẫn chi tiết, đầy đủ bằng Video, Blog. Cộng đồng hỗ trợ rộng lớn. Sản phẩm phong phú mọi nền tảng

Game Badland

Một trong những game nổi tiếng nhất được tạo ra bởi Engine Cocos2d-x.

Cocos2d-x V3.1

Cập nhật phiên bản Engine Cocos2d-x V3.1 bổ sung thêm nhiều tính năng. Sửa các lỗi nhỏ của các phiên bản trước

Contra Evolution - KONAMI

1 Game rất hay không thể bỏ qua, 1 vé trở về với tuổi thơ.

Saturday, May 17, 2014

Bài 12: Làm game đầu tiên - GameOver, tính điểm, thêm âm thanh cho game sôi động (Part 4)

Hello all!

Vậy là chúng ta đã đi được 3/4 chặng đường cho project đầu tiên này rồi. Tuy rằng khá đơn giản, nhưng công việc viết ( thực ra là đang áp dụng ) code sao cho đúng và dễ kiểm tra sửa lỗi cũng hoa mắt ghê.

Chỉ cần nốt 1 bài này thôi là chúng ta có 1 game đầu tiên để chơi rồi, hehe. Trong phần cuối này chúng ta sẽ cùng làm nốt những công việc sau:

+ Kiểm tra game Over khi quái lao vào nhân vật
+ Thêm âm thanh khi bắn đạn và thêm chút nhạc nền cho hấp dẫn nhỉ.
+ Mình bỏ phần Tính điểm nha, hơi phức tạp 1 tí, hẹn bài sau. Bài này để mức đơn giản thôi.

Bắt đầu nào!!!!!

B1 - Thêm âm thanh vào game

Các bạn Download resource ở đây, chép vào trong thư mục Resource của Project
Sau đó mở file HelloWordScene.cpp, trong hàm init() thêm vào dòng lệnh sau

CocosDenshion::SimpleAudioEngine::getInstance()->preloadBackgroundMusic("background-music-aac.wav");
CocosDenshion::SimpleAudioEngine::getInstance()->playBackgroundMusic("background-music-aac.wav",true); // True = lặp lại vô hạn


Tìm tới hàm onTouchEnded(), thêm vào dòng lệnh sau


CocosDenshion::SimpleAudioEngine::getInstance()->preloadEffect("pew-pew-lei.wav");

CocosDenshion::SimpleAudioEngine::getInstance()->playEffect("pew-pew-lei.wav");

Thêm vào 1 lệnh #include"SimpleAudioEngine.h" ở sau dòng lệnh #include" " đầu tiên nhé.

B1 - Tạo màn GameOver

Bạn tạo thêm 1 Scene mới tên là GameOverScene.h và GameOverScene.cpp như sau, Down file Class


Chúng ta ngó qua 2 file này chút nhé:

* Mở file GameOverScene.h khai báo 2 Class GameOverLayer, GameOverScene, cùng với các hàm tạo hàm hủy, hàm khởi tạo init(), không quá phức tạp phải không?

Tuy nhiên, nên chú ý 1 chút tới 2 lệnh này

CC_SYNTHESIZE_READONLY(cocos2d::LabelTTF*, _label, Label); (1)

CC_SYNTHESIZE_READONLY(GameOverLayer*, _layer, Layer); (2)

Nhìn có vẻ khó hiểu nhỉ, nhưng bạn có thể hiểu thế này

(1) tạo ra 1 biến _label có kiểu LabelTTF* thông qua 1 phương thức của lớp Label

(2) tạo ra 1 biến _layer có kiểu GameOverLayer* thông qua 1 phương thức của lớp Layer

đơn giản thế thôi, đừng phức tạp hóa lên quá, Bạn có thể tra cứu thêm về các hàm Inline ở đây

* Mở file GameOverScene.cpp, hàm hủy (có dấu ~ ) thì dễ hiểu rồi nhé, đơn giản là giải phóng cho các con trỏ của lớp để giải phóng bộ nhớ.

Xét các hàm Init()
GameOverScene::init(), tạo ra 1 Scene mới và 1 Layer mới
GameOverLayer::init(), tạo 1 layer màu trắng Color4B(255,255,255,255), có ai thắc mắc Color4B, Color3B là gì không?

Color3B tạo ra màu RGB từ 3 tham số vào. Color4B tạo ra màu RGBA từ 4 tham số vào, trong đó tham số cuối điều chỉnh độ opacity ( đục ).

Đoạn này 

this->runAction( Sequence::create(
                                DelayTime::create(3),
                                CallFunc::create(this, 
                                callfunc_selector(GameOverLayer::gameOverDone)),
                                NULL));

Là tạo 2 Action liên tiếp nhau
+ Làm trễ 3 giây  DelayTime::create(3)
+Tiếp theo CallFunc::create(this, callfunc_selector(GameOverLayer::gameOverDone)), // Gọi hàm game OverDone

Hàm gameOverDone() làm mỗi việc là thay thế cái Scene GameOverScene bằng 1 Game Scene mới để tiếp tục chơi

void GameOverLayer::gameOverDone()
{
    Director::getInstance()->replaceScene(HelloWorld::createScene()); // Tạo 1 Game Scene mới
}


Bạn mở file HelloWordScene.cpp tìm tới hàm onContactBegin(), bên trong khối lệnh

// Nhân vật bị quái đụng vào
if((tag==1&tag1==2)||(tag==2&tag1==1))
 {
// Xử lý GameOver
// Tính điểm 
 }

Thêm vào đoạn code sau đây

auto gameOverScene = GameOverScene::create(); // Tạo 1 Scene Over của lớp GameOverScene
gameOverScene->getLayer()->getLabel()->setString("You Lose :["); // Đặt 1 dòng thông báo lên màn hình
Director::getInstance()->replaceScene(gameOverScene); // Thay thế game Scene =  game Over Scene 

Nhớ thêm #include"GameOverScene.h" ở phần #include nhé.

Trước khi biên dịch chạy thử, bạn cần làm 1 việc sau

+ Build Android, hãy mở file Android.mk trong đường dẫn firstgame\proj.android\jni\Android.mk thêm vào dòng ../../Classes/GameOverScene.cpp bên dưới dòng ../../Classes/HelloWorldScene.cpp
+ Build Win32: hãy mở file firstgame.vcxproj theo đường dẫn firstgame\proj.win32\firstgame.vcxproj thêm vào 2 dòng

<ClCompile Include="..\Classes\GameOverScene.h" /> dưới dòng <ClCompile Include="..\Classes\HelloWorldScene.h" />

<ClCompile Include="..\Classes\GameOverScene.cpp"/>dưới dòng <ClCompile Include="..\Classes\HelloWorldScene.cpp" />

Build và chạy thử nhé, 

Ặc, lại lỗi tè le, và ko hiểu là lỗi gì luôn, thông báo lỗi loằng ngoằng quá. Nếu như bạn không đọc cái chú ý của mình ( phần chữ đỏ ) ở Bài 11 chắc chắn sẽ chẳng thể tìm được cách sửa lỗi này đâu, cá đó ( Tinh vi tí thôi, mò mãi cũng ra thôi, chả nhớ ngày trước tìm được cách sửa ở đâu).

Túm lại lỗi này sửa thế này thêm USING_NS_CC; vào dưới phàn #include trong file GameOverScene.h là xong ( mọi file .h nên thêm vào, mất gì đâu ).

Xong, Build lại và chạy là OK ngay, ko OK mình xóa Blog này luôn nhé! Hứa đấy
























Vậy là chúng ta đã cùng nhau hoàn thành xong Project Game đầu tiên rồi đấy, cũng không khó phải không?
Trong Project này chúng ta đã nắm được các phần cơ bản sau đây:

+ Tạo nhân vật bằng Sprite
+ Bắt sự kiện Touch - Chạm màn hình
+ Bắt sự kiện Contact - va chạm vật lý
+ Xử lý âm thanh
+ Xử lý game Over

- Hi vọng ở những bài sau chúng ta sẽ cùng nhau làm được nhiều Project phức tạp hơn, hay hơn, đẹp hơn nhé.

Chào và hẹn gặp lại các bạn ở bài sau!


Wednesday, May 14, 2014

Bài 11: Làm game đầu tiên - Phát hiện va chạm vật lý ( Part 3 )

Hix!

Không ngờ ra 3 bài này nhanh thật! Mà các bài trong Blog toàn là viết ngẫu hứng, viết 1 lần không cần nháp. Chả bù ngày đi học viết thư cho gái nháp nát cả mấy tờ A4 rồi mới chép lại để gửi ( thảo nào giờ viết cũng có chút lên tay ) : )

Trong 2 phần trước, chúng ta đã xây dựng được 1 nửa project rồi đó, tuy nhiên có 1 phần quan trong nhất cần phải làm trong project này là: tiêu diệt lũ quái bằng những viên đạn bắn ra. Phải làm thế nào đây? Với các phiên bản Engine 1.x, 2.x việc xây dựng các va chạm vật lý giữa các đối tượng là "Hơi bị khoai" do việc tính toán tọa độ, mảng, rồi véc tơ đủ kiểu, rất đau đầu. Tuy nhiên giờ đây trong bản 3 này công việc đó lại rất là đơn giản với chỉ vài dòng lệnh, Nào chúng ta bắt đầu thôi.

Sơ lược công việc trong phần 3 này:
+ Thiết lập thuộc tính vật lý cho các đối tượng ( nhân vật, đạn và quái )
+ Bắt các sự kiện va chạm vật lý xảy ra.
+ Xây dựng hàm xử lý va chạm đó.
Thế thôi nhỉ.

Go Go Go!

B1 - Thiết lập thuộc tính vật lý cho các đối tượng

Các bạn mở file HelloWorldScene.cpp lên, làm các công việc đơn giản sau

Trong hàm createScene(), ta sửa lại lệnh
auto scene = Scene::createScene() thành
auto scene = Scene::createWithPhysics();
// Tạm hiểu là tạo ra 1 Scene - 1 thế giới thu nhỏ có các đăc tính vật lý trong có chứa các đối tượng vật lý.

Thêm 1 lệnh sau
// Lệnh debug này cho phép nhìn thấy các khung body vật lý áp dụng vào các đối tượng ( đường viền đỏ bao quanh đối tượng)
scene->getPhysicsWorld()->setDebugDrawMask(PhysicsWorld::DEBUGDRAW_ALL);
//Thiết lập gia tốc trọng lực bằng 0, để các đối tượng của chúng ta ko rơi xuống đáy màn hình
scene->getPhysicsWorld()->setGravity(Vect(0.0f,0.0f));

1/Nhân vật

Bạn tìm đến dòng this->addChild(player) trong hàm init()

Trên dòng lệnh đó bạn thêm vào đoạn sau

//Tạo 1 bộ khung body vật lý dạng hình tròn
auto playerBody= PhysicsBody::createCircle(player->getContentSize().width / 2);
//Đặt cờ = 1, để kiểm tra đối tượng khi va chạm sau này
player->setTag(1);
//Lệnh này ko hiểu lắm nhưng thực sự ko thể thiếu, bỏ đi sẽ ko có gì xuất hiện khi va chạm
playerBody->setContactTestBitmask(0x1);
//Đặt bộ khung vật lý vào nhân vật
player->setPhysicsBody(playerBody);

2/ Quái

Bạn tìm đến dòng this->addChild(target); trong hàm addTarget()

Trên dòng lệnh đó bạn thêm vào đoạn sau:
// Giải thích giống phần Nhân vật
auto targetBody = PhysicsBody::createCircle(target->getContentSize().width / 2);
target->setTag(2);
targetBody->setContactTestBitmask(0x1);
target->setPhysicsBody(targetBody);

3/ Viên đạn

Trong hàm onTouchEnded bạn tìm dòng projectile->setPosition( Point(20, winSize.height/2) );, 

Thêm vào bên dưới nó đoạn sau, cách giải thích như 2 phần trên

auto projectileBody = PhysicsBody::createCircle(projectile->getContentSize().width / 2);
projectile->setTag(3);
projectileBody->setContactTestBitmask(0x1);
projectile->setPhysicsBody(projectileBody);

Vậy là đã xong việc xây dựng bộ khung vật lý cho các đối tượng

B2 - Bắt sự kiện va chạm giữa các đối tượng

Để bắt sự kiện va chạm này chúng ta chũng tạo ra 1 listener lắng nghe việc va chạm, rồi truyền tới hàm xử lý va chạm thông qua 1 bộ truyền tải, thể hiện bằng code như sau

Trước lệnh return true; của hàm init() thêm vào đoạn code dưới đây:

//Tạo đối tượng lắng nghe va chạm nếu xảy ra
auto contactListener = EventListenerPhysicsContact::create();
//Khi có va chạm sẽ gọi hàm onContactBegin để xử lý va chạm đó, chú ý dòng CC_CALLBACK_1, nhiều tại liệu là CC_CALLBACK_2 sẽ báo lỗi ko chạy
contactListener->onContactBegin = CC_CALLBACK_1(HelloWorld::onContactBegin, this);
//Bộ truyền tải kết nối với đối tượng bắt va chạm
_eventDispatcher->addEventListenerWithSceneGraphPriority(contactListener, this);

Các bạn chú ý biến _eventDispatcher, biến này có 2 điểm đặc biệt là
+ Chưa từng được khai báo ở đâu ( kể cả trong file tiêu đề HelloWorldScene.h) đáng lẽ phải báo lỗi chứ nhỉ??
+ Biến đó có 1 gạch ở phía đầu, nghĩa là sao??

Bạn hỏi tôi trả lời => Đó là 1 biến đặc biệt có sẵn thuộc 1 lớp ( hình như là lớp Dispatcher ) trong thư viện, nên việc lôi nó ra dùng nó là hoàn toàn bình thường. Còn nếu ko dùng thì bạn phải khai báo 1 thằng khác cũng có chức năng như thế. Chú ý các biến có gạch đầu nhé, thường là biến có sẵn của các lớp.

B3 - Hàm xử lý va chạm

Trong file HelloWorldScene.h thêm vào 1 nguyên mẫu hàm 

//Nhớ là phải khai báo chuẩn như thế này, ko được sai 1 dấu chấm phảy nào nhé ( vì khai báo hàm chồng lên hàm của lớp cha)
bool onContactBegin(const PhysicsContact& contact);

Sau đó ta định nghĩa hàm đó trong HelloWorldScene.cpp như sau

bool HelloWorld::onContactBegin(const PhysicsContact& contact)
{
//Lấy đối tượng va chạm thứ nhất, ép kiểu con trỏ Sprite*
auto bullet = (Sprite*)contact.getShapeA()->getBody()->getNode();
//Lấy giá trị cờ để xét xem đối tượng nào ( đạn, quái, hay nhân vật)
int tag = bullet->getTag();

//Lấy đối tượng va chạm thứ hai, ép kiểu con trỏ Sprite*
auto target = (Sprite*)contact.getShapeB()->getBody()->getNode();
//Lấy giá trị cờ để xét xem đối tượng nào ( đạn, quái, hay nhân vật)
int tag1 = target->getTag();
//Nếu va chạm xảy ra giữa đạn và quái thì xử lý xóa cả đạn và quái khỏi Layer trong Scene ( biến mất khỏi màn)
if((tag==2&tag1==3)||(tag==3&tag1==2))
    {

this->removeChild(bullet,true); // Xóa đạn

this->removeChild(target,true); // Xóa quái
}
// Nếu va chạm xảy ra giữa quái và nhân vật thì NV lăn ra chết , rồi GameOver, rồi tính điểm, cái này để bài sau
if((tag==1&tag1==2)||(tag==2&tag1==1))
        {
// Xử lý GameOver
// Tính điểm
}
    return true; // Phải trả lại giá trị true
}

Vậy là xong, Hãy build, chạy thử và xem kết quả có như mong đợi không nhé.
Kết quả đê, Lỗi luôn mới kinh, mặc dù Code không sai 1 dấu phẩy nào, mình khằng định thế


Nói thêm ở đây, có lẽ mình nên kết luận là 1 Bug nhỏ của Engine. Vì về mặt code không hề lỗi, đã lục tung trên mạng cũng không thấy bài nào hướng dẫn sửa. Trước gặp bài physic này đúng là ức chế vãi. Nhưng cuối cùng cũng tìm được cách, đó là kiếm 1 bài tut Physics rồi so code, thì phát hiện ra file HelloWorldScene.h thiếu đúng 1 dòng lệnh:

USING_NS_CC; ( các bạn copy paste dưới dòng #include "cocos2d.h" trong file HelloWorldScene.h)

Ngay từ khi tạo NewProject thì file HelloWorldScene.h đã ko có dòng này, về sau các bạn tạo Project mới nhớ thêm vào kẻo không biết vì sao lỗi, và không biết sửa ở đâu nhé. và các file ( .h ) các bạn tạo mới cũng nên điền thêm vào nhé.

Kết quả sau khi thêm dòng trên vào, là đây



Kết thúc bài 11 ở đây: Và chúng ta đã biết thêm cách:

+ Tạo 1 Scene chứa các đối tượng có thể tương tác vật lý
+ Tạo khung body vật lý cho các đối tượng
+ Bắt sự kiện va chạm bằng Listener Physics
+ Xây dựng hàm xử lý va chạm onContactBegin

Download file nguồn cho bác nào lười, trong này đã full comment của dòng lệnh

Hẹn gặp lại các bạn trong bài sau


Tuesday, May 13, 2014

Bài 10: Làm game đầu tiên - Sự kiện chạm màn hình và bắn đạn (Part 2)

Hi!

Đi làm ngồi rảnh quá, tranh thủ post bài vậy. Trong bài trước chúng ta đang triển khai dang dở 1 Project game đầu tiên nhỉ? Hoành tráng chưa. Cũng bình thường mà, Project nào chẳng bắt đầu từ sự đơn giản, sau đó phức tạp hóa lên dần dần.

Ở phần đầu chúng ta đã học được cách làm thế nào để tạo 1 nhân vật, 1 đám quái từ những hình ảnh thông qua lớp Sprite. Và trong phần thêm quái, bạn chú ý 1 đoạn tính toán vị trí xuất hiện, và tốc độ di chuyển cũng khá hay nhỉ. Chỉ là tính toán thôi mà, cũng chưa phải thuật toán gì ghê ghớm cả, nhưng thực sự nó là thuật toán ( đơn giản ) mà.

Trong phần tiếp theo này, chúng ta sẽ làm công việc sau:

+ Bắt sự kiện khi chạm màn hình
+ Khi chạm vào màn hình, nhân vật của ta sẽ bắn ra đạn
+ Xử lý viên đạn bay theo hướng của điểm chạm trên màn hình

Lets GOOOOÔ!

B1 - Sự kiện chạm màn hình và cách phát hiện

Các bạn mở file HelloWorldScene.cpp lên , trong hàm init() tìm đến cuối hàm trước lệnh return true; thêm vào đoạn lệnh sau:

//Tạo đối tượng truyền tải thông tin của các sự kiện
auto dispatcher = Director::getInstance()->getEventDispatcher();
//Tạo 1 đối tượng lắng nghe sự kiện Chạm màn hình theo cách One by One, xử lý 1 chạm tại 1 thời điểm
auto listener1 = EventListenerTouchOneByOne::create();
//Thiết lập "nuốt" sự kiện Touch khi xảy ra, ngăn ko cho các đối tượng Bắt sự kiện khác sử dụng event này
listener1->setSwallowTouches(true);

//Bắt sự kiện Touch, khi xảy ra sự kiện Touch nào thì sẽ gọi đến hàm tương ứng của lớp HelloWorld
listener1->onTouchBegan = CC_CALLBACK_2(HelloWorld::onTouchBegan, this);
listener1->onTouchMoved = CC_CALLBACK_2(HelloWorld::onTouchMoved, this);
listener1->onTouchEnded = CC_CALLBACK_2(HelloWorld::onTouchEnded, this);

//Gửi cho dispatcher xử lý
dispatcher->addEventListenerWithSceneGraphPriority(listener1, this);

return true;

* Lưu ý: dù rằng ko dùng onTouchMoved, nhưng chúng ta cũng nen cho vào để sự kiện Touch được xử lý đầy đủ.

B2 - Xử lý sự kiện chạm - tạo khả năng bắn đạn cho nhân vật

Bạn mở file HelloWorldScene.h, thêm vào 3 nguyên mẫu hàm:

void onTouchEnded (cocos2d::Touch* touches, cocos2d::Event* event);
void onTouchMoved (cocos2d::Touch* touches, cocos2d::Event* event);
//Chú ý hàm onTouchBegan phải trả về bool
bool onTouchBegan (cocos2d::Touch* touches, cocos2d::Event* event);

Sau đó mở file HelloWorldScene.cpp để định nghĩa 3 hàm này

Mặc dù chúng ta ko dùng hết cả 3 hàm nhưng để sự kiện Touch được xử lý đầy đủ bạn phải khai báo cả 3 hàm như sau


bool HelloWorld::onTouchBegan(Touch* touch, Event* event)

{  

return true; // Phải trả về True

}



void HelloWorld::onTouchMoved(Touch* touch, Event* event)
{  

// Không xử lý gì ở đây

}

void HelloWorld::onTouchEnded (Touch* touches, Event* event){
   
// Lấy tọa độ của điểm chạm
Point location =  touches->getLocationInView();
location = Director::getInstance()->convertToGL(location);

Size winSize = Director::getInstance()->getWinSize();

//Tạo viên đạn là 1 Sprite, đặt vị trí đầu tiên gần nhân vật chính
auto projectile = Sprite::create("Projectile.png");
projectile->setPosition( Point(20, winSize.height/2) );

// Đoạn này tính toán điểm cuối cùng của viên đạn thông qua vị trí đầu và vị trí Touch, hình ảnh bên dưới sẽ minh họa cho điều này. Ở đây áp dụng 1 vài công thức toán học rất cơ bản thôi nhé. Không phức tạp lắm

// Lấy tọa độ điểm chạm trừ đi tọa độ đầu của viên đạn (offX, offY)
int offX = location.x - projectile->getPosition().x;
int offY = location.y - projectile->getPosition().y;

// Không cho phép bắn ngược và bắn thẳng đứng xuống dưới ( bên dưới nhân vật )

if (offX <= 0) return;

// Thỏa mãn điều trên thì tạo hình viên đạn trên màn
this->addChild(projectile,1);

//Tính toán tọa độ điểm cuối thông qua toa độ điểm đầu và khoảng offX, offY
// Tọa độ tuyệt đối realX = chiều rộng màn hình + 1/2 chiều rộng viên đạn, vừa lúc bay ra khỏi màn hình 
int realX = winSize.width  + (projectile->getContentSize().width/2); 

// Tỷ lệ giữa offY và offX
float ratio = (float)offY / (float)offX;

// Tọa độ tuyệt đối realY tính dựa trên realX và tỷ lệ trên + thêm tọa độ Y ban đầu của đạn ( tính theo Talet trong tam giác, hoặc theo tính tang 1 góc)

int realY = (realX * ratio) + projectile->getPosition().y; // Chỗ này theo mình là chưa đúng, đúng ra phải thế này int realY = ((realX-projectile->getPosition().x) * ratio) + projectile->getPosition().y; (realX-projectile->getPosition().x mới đúng là chiều dài từ điểm đầu tới điểm cuối trên trục X

//Tọa độ điểm cuối
auto realDest = Point(realX, realY);

//Chiều dài đường đi của viên đạn, tính theo Pitago a*a = b*b + c*c, a là cạnh huyền tam giác vuông
int offRealX = realX - projectile->getPosition().x;
int offRealY = realY - projectile->getPosition().y;
float length = sqrtf((offRealX * offRealX)  + (offRealY*offRealY));

// Thiết lập vận tốc 480pixels/1giây
float velocity = 480/1;

// Thời gian bay của đạn = quãng đường đạn bay chia vận tốc ở trên
float realMoveDuration = length/velocity;

// Di chuyển viên đạn tới điểm cuối với thời gian, và tọa độ đã tính ở trên. Khi qua viền màn hình thì biến mất
projectile->runAction( Sequence::create(
MoveTo::create(realMoveDuration, realDest),
CallFuncN::create(CC_CALLBACK_1(HelloWorld::spriteMoveFinished,this)), NULL) );

}

Đoạn code runAction nhìn có vẻ phức tạp nhưng nếu viết tường minh nó sẽ thế này

//Di chuyể đạn với thời gian và tọa độ tính toán ở trên
auto move= MoveTo::create(realMoveDuration,realDest);

//Khi move tới điểm cuối xong, bạn sẽ gọi hàm spriteMoveFinished để xóa bỏ hình ảnh viên đạn, nếu không xóa bỏ đi, thì viên đạn bay ra ngoài vẫn nằm trong Layer, ngày càng nhiều, xử lý ngày càng nặng. Đoạn này sẽ trả về 1 Action*
auto finish=CallFuncN::create(CC_CALLBACK1(HelloWorld::spriteMoveFinished,this));

//Thực hiện tuần tự 2 việc, Move sau đó Xóa 
auto run = Sequence::create(move,finish,NULL);

//Thực hiện công việc xử lý hàm Sequence
projectile->runAction(run);

Bạn nên xem lại bài về Các Action cơ bản của Sprite

Ảnh minh họa cho việc tính toán đường bay của đạn



Phần này như vậy là đã OK rồi nhé, tìm hiểu kỹ thì cũng không có gì phức tạp cả phải không nào. Tóm tắt lại công việc như sau

+ Bắt sự kiện chạm màn hình bằng các Listener
+ Xây dựng hàm xử lý khi có sự kiện Chạm màn hình, bắn ra viên đạn, tính toán đường bay, tọa độ, tốc độ viên đạn, có dính tí toán học, vật lý học nè
+ Tạo Action di chuyển viên đạn với tốc độ, tọa độ đã tính toán

Các bạn có thể down file nguồn ở đây, nếu ngại copy và kiểm tra code ở phía trên

Ảnh Demo chút


Tuy nhiên bạn sẽ thấy đạn bắn "xuyên táo" con quái luôn, hehe. Ở bài sau chúng ta sẽ thiết lập sự kiện va chạm cho viên đạn với quái nhé. 


Monday, May 12, 2014

Tản mạn chuyện học game - làm game

Hi!

Công nhận là học lập trình, đặc biệt là lâp trình game khá vất vả, nhưng đầy thú vị. Không biết cảm giác của mọi người thế nào chứ mình thấy công việc này làm cho mình giống như một vị Chúa với đầy đủ quyền năng, sáng tạo ra hẳn 1 thế giới mới tươi đẹp và hấp dẫn.

Vui vậy thôi, chứ mệt ghê đấy, phải luôn chăm chỉ, tìm tòi và sáng tạo nữa. HIX

Buồn buồn lang thang trên mạng đọc được mấy bài báo về Học làm game, chủ yếu là Quảng cáo cho mấy trung tâm, đại học dạy làm game thôi các bạn ạ. Đọc nghe hoàng tráng lắm ( QC mà lị ), nào là:

+ Cơ hội việc làm, thăng tiến, lương cao
+ Đội ngũ chuyên gia về game : online, ofline, 2D, 3D, các thể loại
+ Công nghệ tiên tiến mới nhất của thế giới
+ Hợp tác đầu tư với các doanh nghiệp Game đầu ngành: Gameloft, FPT,vv
+ Vân vân và vân vân....
+ Học phí có 20 -60 triệu 1 khóa từ 1- 2 năm, tính ra cũng 2-3 triệu/tháng. :((

Thực hư không biết thế nào, nhưng có mấy điều chúng ta phải nhìn nhận vào thực tế là:
+ Chất lượng giáo dục nói chung, ngành Game nói riêng của Việt Nam ta còn yếu, nặng về thành tích, số lượng, điểm trác.
+ Thực trạng kinh doanh giáo dục, lấy lợi nhuận đặt lên hàng đầu chứ không lấy chất lượng của học sinh, sinh viên làm trọng tâm.
+ 100% học song ra có việc làm, xin lỗi chỉ là Quảng Cáo, ở bất cứ đâu cũng vậy, giỏi thực sự bạn sẽ được săn đón, còn chỉ làng nhàng trung bình khá thì hãy tự bơi nhé, dù có chết chìm cũng ko ai vớt đâu chứ đừng nói cứu. Vì thế con số này nếu có chỉ là 1-2 % thôi.
+ Các Sudio Game hàng đầu của Việt Nam giờ ở đâu, và ra sao. bạn cứ tìm hiểu là biết, hầu hết đều đang thoi thóp sau 1 thời gian mọc lên theo phong trào. Có sống thì một vài năm mới ra được 1 -2 game chìm ngỉm trong muôn ngàn game nước ngoài. Haiizzz!

Vậy thì: Học làm game có nên chăng, học như thế nào, học để làm làm gì? Thực là chính mình cũng băn khoăn lắm. vì hiện tại làm game ko phải nghề của mình chỉ là thú vui, là đam mê, là sự tìm tòi, học hỏi , vậy thôi. 

Còn những ai có tư tưởng học làm game theo kiểu phong trào, hoặc làm game để lấy le với gái, hoặc để nổi tiếng như NHĐ thì xác định là thôi khỏi học làm gì, chẳng tới đâu và tốn thời gian tâm sức lắm.

Tản mạn đôi chút cùng chính mình, không biết có ai đọc không!?

Bài tham khảo ( 1 dạng QC trá hình, đọc tại đây: http://game.thanhnien.com.vn/print/2014/03/07/ban-tre-lam-game-hoc-gi-o-dau.1124.html )



Bài 9 - Làm game đầu tiên - Tạo nhân vật. (Part 1)

Chào mọi người!

Cũng nhanh nhỉ, Blog đã được 14-15 bài rồi đấy. Chắc các bạn cũng đã nắm được một vài kỹ thuật cơ bản về Engine Cocos2d-x V3 này rồi. Với 1 chút kiến thức ấy liệu chúng ta có thể làm được game không. Tất nhiên là được chứ, tuy chỉ là game đơn giản thôi. 

Sau đây mình sẽ hướng dẫn các bạn làm 1 game đơn giản nhất nhé, game này hồi mình mới tìm hiểu về Cocos2dx cũng đã học và làm theo ( bài này nằm trong phần learn, các bạn có thể tìm trên trang chủ, không biết đã được cập nhật chưa. Bài hướng dẫn đó đã cũ rồi, nếu bạn copy code vào V3.0 chắc chắn sẽ ko chạy. Ngoài ra 1 lỗi nữa đó là phần phát hiện Physics quá phức tạp trong bản cũ. V3 sẽ cực kỳ đơn giản. Mình sẽ trình bày lại trong bài của mình nha).

Bài gốc cũ ở đây ( cho Cocos2d-x V2.2.3)

Mô tả 1 chút về game này như sau: Chúng ta có 1 Nhân vật chính và 1 đám quái, Nhân vật chính sẽ tiêu diệt đám quái bằng cách xả đạn về phía quái. Trò chơi có tính điểm đơn giản (mỗi chú nằm xuống được 1 điểm), và có cả màn GameOver, có thể chèn nhạc nền nếu muốn.

GOooooooooooooooooo!

B1 - Tạo Project, thêm Resource ảnh, âm thanh + v..v

Đầu tiên, bạn phải tạo 1 Project mới (bằng cmd nhé). Có một số bạn hỏi sao không tạo = VS2012, hay Eclipse cho trực quan. Mình cũng tìm hiểu rồi, hình như là ko có ( V3.0). Mà thôi, cách quái nào chả được. Dùng dòng lệnh thì trên hệ máy nào ( WIN, MAC, LINUX) cũng đều giống nhau nhé.

>cocos new firstgame -p com.vn.firstgame -l cpp -d f:/android/project

Bạn cần download thê Resource ( file ảnh ) rồi chép đè vào Resource của Project firstgame nhé. À, nếu bạn build cho Android thì chép vào Resource trong thư mục proj.android của firstgame. LINK đây

B2 - Tạo nhân vật

Nghe thì có vẻ ghê ghớm nhỉ, nhưng không có gì ngoài công việc thêm 1 Sprite hình ảnh vào trong Layer của chúng ta thôi. hehe. Các bạn làm như sau

1/ Nhân vật chính
Trong hàm bool HelloWorld::init(), các bạn xóa hết đi, chỉ chừa lại đoạn sau

    if ( !Layer::init() )
    {
        return false;
    }
    
----Xóa
----Xóa

return true;

Sau đó thêm vào trước lệnh return true; đoạn code sau đây ( mình giải thích ý nghĩa luôn )
// Lấy kích thước màn hình
Size winSize = Director::getInstance()->getWinSize(); 
// Tạo 1 Sprite, nhân vật của chúng ta
auto player = Sprite::create("Player.png");
// Đặt lên màn hình ở phía trái
player->setPosition( Point(player->getContentSize().width/2, winSize.height/2) );
// Thêm vào layer nằm trong Scene game
this->addChild(player,1);
// Gọi tới hàm gameLogic , hàm này có nhiệm vụ tạo ra đám quái với thời gian 1 giây 1 quái
this->schedule( schedule_selector(HelloWorld::gameLogic), 1.0 );

2/ Tạo Quái

Bạn mở file HelloWorld.h ra thêm vào đó 3 nguyên mẫu hàm sau

void addTarget();  
void gameLogic(float dt);
void spriteMoveFinished(cocos2d::Node* sender);

Sau đó bạn phải khai báo các hàm này trong HelloWorld.cpp như sau:

*
void HelloWorld::gameLogic(float dt)
{
    this->addTarget();
}

**

// Hàm này tạo ra Quái và di chuyển chúng nè
void HelloWorld::addTarget()
{
    auto target = Sprite::create("Target.png");
    Size winSize = Director::getInstance()->getWinSize();
// Đoạn này tính toán vùng xuất hiện quái sao cho ko bị khuất quái vào viền màn hình

    int minY = target->getContentSize().height/2;
    int maxY = winSize.height
                          -  target->getContentSize().height/2;
    int rangeY = maxY - minY;
    int actualY = ( rand() % rangeY ) + minY;
//
// Đặt quái vào khoảng vị trí trên actualY (random)
    target->setPosition(Point(winSize.width + (target->getContentSize().width/2),actualY));
    this->addChild(target,1);

 //Tính toán tốc độ di chuyển của quái
    int minDuration = (int)2.0;
    int maxDuration = (int)4.0;
    int rangeDuration = maxDuration - minDuration;
    int actualDuration = ( rand() % rangeDuration )
                                        + minDuration;
// Di chuyển quái với 1 tốc độ nằm trong khoảng actualDuration , từ điềm xuất hiện tới điểm Point(0,y)

auto actionMove =  MoveTo::create( (float)actualDuration, Point(0 - target->getContentSize().width/2, actualY) );

// Kết thúc việc di chuyển của quái khi đã tới điểm cuối
 auto actionMoveDone =   CallFuncN::create(CC_CALLBACK_1(HelloWorld::spriteMoveFinished,this));
/
// Chạy 2 Action trên 1 cách tuần tự = lệnh Sequence sau
 target->runAction( Sequence::create(actionMove, actionMoveDone, NULL) );
}

Bạn hãy tham khảo bài về Sprite và các lệnh Action cơ bản ở bài trước nhé.

***
void HelloWorld::spriteMoveFinished(Node* sender)
{
// Hàm này có mỗi công việc là loại bỏ Target ( đang là Sprite) ra khỏi layer của game
// Ép kiểu Contrỏ Sprite của 1 Node*
  auto sprite = (Sprite *)sender;
  this->removeChild(sprite, true);    
}

B3 - Chạy thử
OK men, bây giờ các bạn có thể build và chạy trên window với lệnh sau

>cocos run -s f:android/project/firstgame -p win32

Kết quả đê, ra được thế này là thành công Part 1 nhé




Bạn hoàn toàn có thể build ra apk rồi cài trên Android nhé

>cocos compile -s f:android/project/firstgame -p android --ap 16 ( cho Android 4.1.2 trở lên)

Vậy là hết phần 1 rồi. Đơn giản quá phải không mọi người. Toàn là kiến thức cơ bản thôi mà. Ở bài sau chúng ta sẽ nghiên cứu xem làm thế nào để nhân vật chính bắn ra được viên đạn khi ta chạm vào màn hình, việc phát hiện ra sự kiện chạm màn hình sẽ phải làm như thế nào. Hẹn ở phần sau nhé, sẽ rất thú vị đây


Sunday, May 11, 2014

Sprite và những Action cơ bản trong Cocos2d-x V3.0

Hi!

Chủ nhật ta lại một mình, buồn buồn ngồi viết Blog chơi, viết đi viết lại buồn ơi vẫn buồn

Đây là 1 bài về Sprite và một số Action trong Cocos2d-x V3 các bạn nhé, phần Tut này có Video hướng dẫn của nước ngoài tại Đây. Mình tổng hợp lại dưới dạng Text, tiện cho việc tra cứu.

Tạo 1 dự án mới tên Tutorial, hoặc helloworld, hoặc abc ...., trong hàm init() của nó

SPRITE:

Tạo Sprite, Đặt vị trí trên màn hình, thêm vào Layer

auto sprite = Sprite::create("sprite.png");
sprite->setPosition(Point(x,y));
this->addChild(sprite,1);


ACTION

1/ MoveBy (di chuyển 1 sprite tới 1 điểm với 1 khoảng cho trước)
static MoveBy* create ( float duration, const Point & deltaPosition )
Ex:
auto action = MoveBy::create(3,Point(100,100);
sprite->runAction(action);

2/ MoveTo (di chuyển 1 sprite tới 1 điểm cho trước)
static MoveTo* create ( float duration, const Point & position )
Ex:
auto action = MoveTo::create(3,Point(100,100);
sprite->runAction(action);

3/ JumpBy (Nhảy tới 1 điểm với 1 khoảng cho trước, cùng với chiều cao và bước nhảy)
static JumpBy* create ( float duration, const Point & position, float height, int jumps )
Ex:
auto action =  JumpBy::create(3,Point(100,100),50,5);
sprite->runAction(action);

4/ JumpTo (Nhảy tới 1 điểm cho trước, cùng với chiều cao và bước nhảy)
static JumpTo* create ( float duration, const Point & position, float height, int jumps )
auto action =  JumpTo:create(3,Point(100,100),50,5);
sprite->runAction(action);

* Lưu ý: trong các hàm trên tuy cùng 1 Point(100,100), nhưng hoàn toàn khác nhau, 1 cái thì là điểm tọa độ trong loại hàm Func_TO, 1 loại là đoạn (x,y) trong loại hàm Func_BY

5/ BezierBy ( Di chuyển sprite theo 1 đường "cong" tạo bởi 3 điểm đầu, giữa và cuối )
static BezierBy * create (float t, const ccBezierConfig &c)
ccBezierConfig  là 1 cấu trúc 3 điểm định nghĩa như sau:
struct ccBezierConfig {
Point controlPoint_1;
Point controlPoint_2;
Point endPosition;
};

Ex:
ccBezierConfig  bezier;
bezier.controlPoint_1 = Point(100,100);
bezier.controlPoint_2 = Point(200,-200);
bezier.endPosition = Point(40,400);
auto action = BezierBy::create(3,bezier);
sprite->runAction(action);

6/ BezierTo  ( Di chuyển sprite theo 1 đường "cong" tạo bởi 3 điểm đầu, giữa và cuối )
static BezierTo * create (float t, const ccBezierConfig &c)

Ex:
ccBezierConfig  bezier;
bezier.controlPoint_1 = Point(100,100);
bezier.controlPoint_2 = Point(200,-200);
bezier.endPosition = Point(40,400);
auto action = BezierBy::create(3,bezier);
sprite->runAction(action);

* Lưu ý: Dù cùng 1 ccBezierConfig, nhưng BezierTo và BezierBy di chuyển trên màn hình sẽ khác nhau, do tọa độ Point trong ccBezierConfig là khác nhau về bản chất. Các bạn cần lưu ý điều này.

7/ Place (Đặt Sprite tại 1 tọa độ xác định trên màn hình
static Place* create(const Point & pos)
EX:
auto action = Place::create(Point(100,100));
sprite->runAction(action);

8/ ScaleBy (Thay đổi kích thước sprite theo 1 tỷ lệ xác định)
static ScaleBy* create ( float duration, float s )
static ScaleBy* create ( float duration, float sx, float sy )
static ScaleBy* create ( float duration, float sx, float sy, float sz )

EX:
auto action = ScaleBy::create(4,3); sprite->runAction(action);
auto action = ScaleBy::create(4,3,5); sprite->runAction(action);

9/ ScaleTo ( Thay đổi kích thước sprite theo 1 tỷ lệ xác định);
static ScaleTo * create ( float duration, float s )
static ScaleTo * create ( float duration, float sx, float sy )
static ScaleTo * create ( float duration, float sx, float sy, float sz )

EX:
auto action = ScaleTo ::create(4,3); sprite->runAction(action);
auto action = ScaleTo ::create(4,3,5); sprite->runAction(action);

* 2 lệnh ScaleTo, ScaleBy, kết quả khá giống nhau

10/ RotateBy ( Quay Sprite theo 1 góc xác định)
static RotateBy* create ( float duration, float deltaAngle )
static RotateBy* create ( float duration, float deltaAngleZ_X, float deltaAngleZ_Y )
Ex:
auto action = RotateBy::create(3,45); sprite->runAction(action);

11/ RotateTo ( Quay Sprite theo 1 góc xác định)
static RotateTo* create ( float duration, float deltaAngle )
static RotateTo* create ( float duration, float deltaAngleZ_X, float deltaAngleZ_Y )
Ex:
auto action = RotateTo::create(3,45); sprite->runAction(action);

12/ TintBy (Chuyển màu của Sprite sang 1 màu nhất định)
static TintBy* create ( float duration, GLshort deltaRed, GLshort deltaGreen, GLshort deltaBlue )
Ex:
auto action = TinBy::create(3, 255,200,100); sprite->runAction(action);
* Cách này chuyển màu khá nhanh và không mượt như cách dưới

13/ TintTo (Chuyển màu của Sprite sang 1 màu nhất định)
static TintTo* create ( float duration, GLshort deltaRed, GLshort deltaGreen, GLshort deltaBlue )
Ex:
auto action = TintTo::create(3, 255,200,100); sprite->runAction(action);
* Màu biến thiên mượt hơn cách trên

14/ FadeTo ( Tạo hiệu ứng Fade cho sprite với độ Opacity nhất định)
static FadeTo* create ( float duration, GLubyte opacity )
Ex:
auto action = FadeTo::create(3,55); sprite->runAction(action)
Opacity từ 0 tới ~ 255 ??

15/ FadeIn ( Tạo Fade - hiện dần 1 sprite)
static FadeIn * create (float d)
Ex:
sprite->setOpacity(0);
auto action = FadeIn::create(5);sprite->runAction(action);

16/ FadeOut ( Làm mờ dần 1 sprite )
static FadeOut * create (float d)
Ex:
auto action = FadeOut ::create(5);sprite->runAction(action);

17/ Repeat ( Nhắc lại 1 Action với số lần xác định)
static Repeat* create ( FiniteTimeAction * action, unsigned int times )
Ex:
auto action = Repeat:create(RotateBy::create(2,45),8); sprite->runAction(action);

18/ RepeatForever ( Nhắc lại 1 Action với số lần vô hạn)
static RepeatForever * create (ActionInterval *action)
Ex:
auto action = RepeatForever :create(RotateBy::create(2,45)); sprite->runAction(action);

Các bạn có thể tra cứu Hàm cocos2d-x V3 ở đây nhé

TOBE CONTINUED (2)...

Thursday, May 8, 2014

Thêm 1 Kênh Video hướng dẫn Cocos2d-x V3.0 rất hữu ích trên Youtube nhé

Hi cả nhà!

Lâu lâu không viết Tut mới, cơ bản là cũng đang nghiên cứu. Nhưng các bạn yên tâm, hiện đã có nhiều nhóm viết Tut = Video trên Youtube rồi, rất tường tận và tỉ mỉ.

Các bạn có thể tham khảo tại link sau


Đây là của Tut của nước ngoài nhé, chỉ có 1 chút tiếng Anh thôi, nhưng mà toàn chủ yếu xem code thôi. Các bài Tút rất rõ ràng dễ hiểu, cơ bản là ra rất nhanh nữa, mới 1 tuần mà ra được 35 bài rồi ( bài ngắn thôi ).



Trên Blog này có khá nhiều hạn chế, không tập trung nên mình mới Create 1 cái fanpage để mọi người vào trao đổi và thảo luận nhé.

https://www.facebook.com/mycocos2dx?ref=hl

Thursday, May 1, 2014

Video - Hướng dẫn cài đặt Cocos2d-x V3.0 FINAL - Install Cocos2d-x v3.0 Final - 100% Working

Lần đầu làm Video, có gì sai sót, mọi người bỏ qua. Quay đi quay lại 3-4 lần thôi,

Các bạn chú ý theo dõi kỹ nhé

Đoạn cuối có Bonus 1 tí, Các bạn xem và ủng hộ



Các Video lần sau mình sẽ làm công phu hơn