Lâu lâu lại lười viết bài, vì thật ra cũng càng ngày càng khó, và project cũng dài nữa nên.... Và mình cũng đang bí ở phần áp dụng Box2D body cho Animation SpriteSheet, và Box2D body cho CocoStudio, Spine. Tìm hiểu mãi mà chưa có cách làm. Bạn nào đã từng code với bản 2.x có đoạn code áp cho 1 trong 3 cái trên thì chia sẻ giúp nhé, cám ơn.
Hôm nay mình tìm được 1 game khá thú vị, đưa lên đây cho mọi người cùng tìm hiểu, code khá đơn giản, chủ yếu là ôn tập lại các kiến thức đã học thôi, không phức tạp lắm đâu. Đó là game Đập chuột ( Whack Hole ).
Trong này có 1 -3 chú chuột chũi nhô lên, thụt xuống ở 3 cái lỗ, nhiệm vụ của bạn là đập vào đầu nó để ghi điểm thôi. Trong Part 1 ngày, chúng ta nghiên cứu những vấn đề sau
+ Thay đổi tỉ lệ màn hình với từng loại thiết bị
+ Dựng Scene
+ Action của Chuột
Thế thôi nhỉ, ta bắt đầu!
B1 - Thay đổi tỉ lệ màn hình theo từng loại thiết bị
Bạn tạo mới Project dapchuot, mở file AppDelegate.cpp ra, thêm vào đoạn code sau, bên dưới lệnh director->setAnimationInterval(1.0 / 60);
// Màn hình thiết bị
Size frameSize = glview->getFrameSize();
auto designSize = Size(512, 384); // Khung hình thiết kế
// Độ phân giải thiết kế
glview->setDesignResolutionSize(designSize.width, designSize.height, ResolutionPolicy::NO_BORDER);
// Vector <string> lưu thông tin đường dẫn Resource
std::vector<std::string> searchPaths;
if (frameSize.height > 480) // Nếu màn hình > 480
{
searchPaths.push_back("hd"); // đường dẫn là ("hd");
Size resourceSize = Size(1024, 768); // Đặt kích thước = 1024,768
// Tăng kích thước content để phù hợp với màn hình
director->setContentScaleFactor(MIN(resourceSize.height/designSize.height,
resourceSize.width/designSize.width));
}
else // Chọn sd< 480px
{
searchPaths.push_back("sd");
}
// Chọn Resource
FileUtils::getInstance()->setSearchPaths(searchPaths);
B2 - Dựng màn chơi
Bạn mở file HelloWorldScene.h, thêm vào đoạn code sau
void tryPopMoles(float dt); // 1 hàm rất giống hàm update ( float dt ) đúng ko, thì chức năng cũng giống thế mà, update scene theo thời gian
void popMole(Sprite *mole); // Hàm acction cho chuột
Vector<Sprite*> molesVector; // Vector Sprite lưu các chú chuột
Mở HelloWorldScene.cpp
// Đặt tên cho các file Resource, lưu ý 1 chút với string nó thuộc lớp std nên phải khai báo dạng std::string. Nếu không muốn thế này bạn phải dùng namespace: using namespce std; ở đầu file
// Các dạng file này .pvr.ccz, .plist nhìn có vẻ ghê ghớm nhưng thực ra không có gì cả. File .plist chứa thông tin tọa độ, tên gọi, kích thước của các ảnh png đơn lẻ được pack trong file .pvr.ccz. Còn file .pvr.ccz là 1 dạng nén ảnh để giảm kích thước lưu trữ và bộ nhớ trong game. Bạn dùng phần mềm TexturePacker 3.3.x trong Blog này mình có bài rồi để thực hiện pack nhé
std::string bgSheet = "background.pvr.ccz";
std::string bgPlist = "background.plist";
std::string fgSheet = "foreground.pvr.ccz";
std::string fgPlist = "foreground.plist";
std::string sSheet = "sprites.pvr.ccz";
std::string sPlist = "sprites.plist";
// Nạp background + foreground
SpriteFrameCache::getInstance()->addSpriteFramesWithFile(bgPlist);
SpriteFrameCache::getInstance()->addSpriteFramesWithFile(fgPlist);
// Add background, bg_dirt.png được lấy từ background.pvr.ccz ( dựa trên thông số của background.plist)
Sprite *dirt = Sprite::createWithSpriteFrame(
SpriteFrameCache::getInstance()->getSpriteFrameByName("bg_dirt.png"));
dirt->setScale(2.0); // Phóng to lên
dirt->setPosition(winSize.width/2, winSize.height/2);
this->addChild(dirt, -2);
// Add foreground, grass_lower.png lấy trong foreground.pvr.ccz ( dựa trên thông số của foreground.plist )
// Nửa dưới
Sprite *lower = Sprite::createWithSpriteFrame(
SpriteFrameCache::getInstance()->getSpriteFrameByName("grass_lower.png"));
// Bạn chú ý chỗ này, 1 hình ảnh có nhiều AnchorPoint ( là điểm engine sẽ lấy để đặt trên màn hình vào tọa độ xác định. thường có 9 AnchorPoint hay dùng là các điểm sau: 4 góc hình + 1 tâm + 4 trung điểm 4 cạnh của khung ảnh png
//Như thế này
//Point(0,0) = góc trái-dưới
//Point(0,1) = góc trên-trái
//Point(1,0) = góc phải-dưới
//Point(1,1) = góc trên-phải
//Point(0.5,0.5) = Tâm
//Point(0.5,0) = giữa cạnh dưới
//Point(0,0.5) = giữa cạnh trái
//Point(0.5,1) = giữa cạnh trên
//Point(1,0.5) = giữa cạnh phải
// Với hình đặc biệt trong CocoStudio, Spine, SpriteSheet, ta có thể chỉnh được điểm neo này = tay
// Tùy vào từng trường hợp mà ta set AnchoPoint hợp lý sẽ khiến việc căn đối tượng dễ dàng hơn
lower->setAnchorPoint(Point(0.5, 1));
lower->setPosition(winSize.width/2, winSize.height/2 + 1); // Tách 2 nửa ra 1 chút để nhận biết, trong game bạn xóa + 1 đi
this->addChild(lower, 1);
// Nửa trên, tại sao có nửa dưới và trên ? khi bạn build xong sẽ hiểu, vì nửa trên set index -1, nửa dưới =1 để cho chuột có index ở giữa = 0 sẽ hiện bên trên nửa trên và bị che một phần do nửa dưới. giá trị index có tác dụng sắp sếp đối tượng như thế.
Sprite *upper = Sprite::createWithSpriteFrame(
SpriteFrameCache::getInstance()->getSpriteFrameByName("grass_upper.png"));
upper->setAnchorPoint(Point(0.5, 0));
upper->setPosition(winSize.width/2, winSize.height/2 - 1); // Tách 2 nửa ra 1 chút để nhận biết, trong game bạn xóa - 1 đi
this->addChild(upper, -1);
// Tạo SpriteSheet... lưu chuột
SpriteBatchNode *spriteNode = SpriteBatchNode::create(sSheet);
this->addChild(spriteNode, 0);
SpriteFrameCache::getInstance()->addSpriteFramesWithFile(sPlist);
// Nạp 3 chuột
Sprite *mole1 = Sprite::createWithSpriteFrameName("mole_1.png");
mole1->setPosition(99, winSize.height / 2 - 75);
spriteNode->addChild(mole1); // Thêm vào spritesheet
molesVector.pushBack(mole1); // Thêm vào Vector
Sprite *mole2 = Sprite::createWithSpriteFrameName("mole_1.png");
mole2->setPosition(winSize.width / 2, winSize.height / 2 - 75);
spriteNode->addChild(mole2);
molesVector.pushBack(mole2);
Sprite *mole3 = Sprite::createWithSpriteFrameName("mole_1.png");
mole3->setPosition(winSize.width - 102, winSize.height / 2 - 75);
spriteNode->addChild(mole3);
molesVector.pushBack(mole3);
// Update scene trong thời gian 0.5 giây
this->schedule(schedule_selector(HelloWorld::tryPopMoles), 0.5);
Hàm tryPopMoles như sau
void HelloWorld::tryPopMoles(float dt) // hàm này nhận đối số thời gian giống hàm update (float dt)
{
for (Sprite *mole : molesVector) // duyệt vector
{
int temp = CCRANDOM_0_1()*10000; // Tạo số random
if ( temp % 3 == 0) // chia hết cho 3, thì ....
{
if (mole->getNumberOfRunningActions() == 0) // Nếu ko có Action nào
{
this->popMole(mole); // Thò đầu lên và ăn đập : ))
}
}
}
}
B3 - Hành động của chuột
void HelloWorld::popMole(Sprite *mole)
{
// Lên
auto moveUp = MoveBy::create(0.2f, Point(0, mole->getContentSize().height)); // 1
auto easeMoveUp = EaseInOut::create(moveUp, 3.0f); // 2
auto easeMoveDown = easeMoveUp->reverse(); // 3
auto delay = DelayTime::create(0.5f); // 4
// Thực hiện chuỗi hành động, Lên, đứng đó, Xuống, ko làm gì cả
mole->runAction(Sequence::create(easeMoveUp, delay, easeMoveDown, NULL)); // 5
}
Xong rồi, Build chạy thử xem có ra gì không, ngon rồi!
Vậy là trong bài này chúng ta cùng nhau ôn lại 1 vài kiến thức sau:
+ SpriteSheet
+ Nạp Resource từ file Pvr, plist
+ Vector
+ Action
Đơn giản thế thôi, bài sau chúng ta sẽ thực hiện nốt công việc là, đập chuột, tính điểm.
Dowload Resoucrce, Class
Chào và hẹn gặp lại các bạn ở bài sau!
Bài 27: Học làm game thứ 4 - Game đập chuột ( Part 2)
{ 0 nhận xét... read them below or add one }
Đăng nhận xét