Bài 19: Sprite Sheet Animation trong Cocos2dx-3

Người đăng: share-nhungdieuhay on Thứ Tư, 28 tháng 5, 2014

Hi!

Các bài trước chúng ta đã học và làm quen với các Sprite và Action cơ bản. Nhưng các bạn thấy rằng hầu hết các sprite đó trông có vẻ rất đơn điệu, chúng không hề có sự "cử động" - (animation) nào mặc dù chúng vẫn có "hành động" (action). Các bạn cần phân biệt Animation và Action nhé. Animation có thể hiểu ngắn gọn là những cử động của cơ thể nhân vật. Còn Action là những hành động của nhận vật để làm công việc gì đó. Đôi khi 2 khái niệm này cũng khá nhập nhằng. Hix.

Animation trong Cocos2d-x-3 có 2 loại
+ Sprite Sheet Animation: Tạo cử động bằng 1 loạt Sprite ảnh nối tiếp nhau
+ Skeleton Animation: Tạo cử động dạng khung xương

Bài này mình sẽ giới thiệu với mọi người loại Animation thứ nhất: Sprite Sheet Animation. Nội dung bài gồm:

+ Pack ảnh (texture paker) là gì? vì sao lại cần dùng pack ảnh
+ Cách tạo ra 1 Pack ảnh .Plist, prv.ccz
+ Cách import pack ảnh vào game và tạo Animation

Mình bắt đầu luôn đây!

B1- Pack ảnh là gì? dùng để làm gì

Khái niệm: 1 Pack ảnh bao gồm nhiều ảnh đơn gép lại với nhau, và có đi kèm 1 file để lưu thông số của từng ảnh. Thậm chí chỉ gồm có 1 file ( ví dụ pvr )
Dùng để làm gì? Quản lý file ảnh sẽ đơn giản hơn cho 1 project. Thường để nhóm các ảnh của một Animation, hoặc nhóm nhiều ảnh của chương trình lại, tối ưu chương trình, Ngăn chặn việc ăn cắp hình ảnh, và nhiều tác dụng khác.
- SpriteSheet có lẽ là 1 trường hợp riêng của Pack ảnh, gồm những hình được sắp xếp liên tiếp, có thứ tự của 1 chuyển động nào đó.
- Và đôi khi cũng nhập nhăng 2 cái này luôn, gọi chung hết là Sprite Sheet ( cứ hiểu là 1 nhóm ảnh vậy ). Haizzz

* Lưu ý, những định nghĩa hay tác dụng của pack ảnh do mình tự suy diễn nhé. Cũng ko biết phải tìm ở đâu. :-)). Ai biết rõ hơn thì chỉ với.

B2 - Cách tạo ra 1 pack ảnh .Plist, hoặc .Pvr

Bạn sử dụng chương trình TexturePacker ( hỗ trợ tốt cho Cocos2d) để tạo Pack. Do mình ko có license nên Publish ra sẽ báo lỗi. Chán ghê, mua full 2 bản TexturePacker và Physics Editor mất 1,1 triệu VNĐ. Haizzz, chưa kiếm được tiền từ game, mới học làm game mà đã chuẩn bị rút ví rồi.

Ngoài ra có 1 chương trình khác là ShoeBox cũng tạo được TexturePack nhé, các bạn search là thấy, Cài Adobe Air để chạy được phần mềm

Thôi chúng ta đi vào phần chính, giả sử đã tạo được pack ảnh từ TexturePacker nhé. Có khá nhiều định dạng Pack, nhưng bài mình tìm được là dạng .PNG + .PLIST

(Đã tìm được cách dùng FREE 4EVER phần mềm TexturePacker nhé, tuyệt vời ông mặt trời, mình sẽ hướng dẫn ở bài sau) . Đang test 1 thời gian xem lỗi gì ko?

B3 - Import pack ảnh vào game, tạo Animation

- Tạo 1 project mới tên animation nhé, nhớ thêm USING_NS_CC; vào phần #include của file HelloWorldScene.h
- Copy file Resource từ đây vào thư mục Resource
- Bắt đầu nghiên cứu code

* Mở file HelloWorldScene.h, Thêm vào đoạn code sau

public:

    HelloWorld(); // Hàm tạo
    ~HelloWorld(); // Hàm hủy
    virtual void onEnter(); // Hàm chồng ( override, not husband)
    // Bắt sự kiện Touch
    bool onTouchBegan(Touch* touch, Event* event);
    void onTouchMoved(Touch* touch, Event* event);
    void onTouchEnded(Touch* touch, Event* event);
    // Dừng lại
    void bearMoveEnded();

private:
    Sprite *bear; // Sẽ chứa ảnh con Gấu
    Action *walkAction; // Bước đi
    Action *moveAction; // Di chuyển
    bool moving;

* Mở file HelloWorldScene.cpp, Bạn định nghĩa 2 hàm tạo và hàm hủy như sau

HelloWorld::HelloWorld()
{
    moving =false;
}

HelloWorld::~HelloWorld()
{
    if (walkAction)
    {
        walkAction->release(); // Giải phóng con trỏ
        walkAction = NULL;
    }
}

Trong hàm init() xóa hết chỉ trừ return true và đoạn này

    if ( !Layer::init() )
    {
        return false;
    }

// Xóa hết

return true;

Thêm đoạn code sau vào phần đã xóa ở trên

// Bước 1, Nạp file .plist vào bộ đệm SpriteFrameCache, tạo 1 sheet = SpriteBatchNode, spritesheet để nạp 1 loạt các ảnh nằm trong 1 pack nhiều ảnh

SpriteFrameCache::getInstance()->addSpriteFramesWithFile("AnimBear.plist");
auto spriteSheet = SpriteBatchNode::create("AnimBear.png");
this->addChild(spriteSheet);

// Bước 2, Nạp frame từng frame từ bộ đệm SpriteFrameCache vào 1 Vector ( giống mảng)

Vector<SpriteFrame*> aniframe(15); // Khai báo 1 vector kiểu SpriteFrame, với size = 15

char str[50]={0}; // chuỗi trung gian để đọc tên ảnh trong pack

for(int i =1;i<9;i++) // Lặp để đọc 8 ảnh trong pak
{
sprintf(str,"bear%d.png",i); // Đọc vào chuỗi str tên file thứ i

// Tạo 1 khung, lấy ra từ bộ đệm SpriteFrameCache với tên = str
auto frame = SpriteFrameCache::getInstance()->getSpriteFrameByName(str);

aniframe.pushBack(frame); // Nhét vào vector

}

// Bước 3, Tạo Animation từ Vector SPriteFrame

// Tạo khung hình animation từ vector SpriteFrame
auto animation = Animation::createWithSpriteFrames(aniframe,0.1f);
// Tạo ảnh 1con gấu
bear = Sprite::createWithSpriteFrameName("bear1.png");
// Đặt vị trí giữa màn hình thôi
bear->setPosition(Point(visibleSize.width/2, visibleSize.height/2));

// Tạo Action Animate ( hoạt họa ) bằng cách gọi hàm create của lớp Animate, Hãy tưởng tượng thế này, bạn có 8 cái hình ảnh nằm trên 8 trang giấy, lật nhanh 8 trang => ảnh chuyển động của nhân vật. Cái hàm create của lớp Animate có tác dụng "lật trang"gần giống thế, sẽ duyệt qua các khung hình của animation tạo ra ở trên

walkAction = RepeatForever::create(Animate::create(animation));
walkAction->retain(); // Hàm này chưa hiểu ý lắm
spriteSheet->addChild(bear); // Thêm ảnh con gấu tạo ở trên vào spritesheet

+ Dựng hàm onEnter()

void HelloWorld::onEnter()
{
Layer::onEnter();  //  Phải gọi hàm onEnter của Layer, lớp cha của HelloWorld

// Đặt Listener khi vào game
auto touchListener = EventListenerTouchOneByOne::create();
touchListener->setSwallowTouches(true);

touchListener->onTouchBegan= CC_CALLBACK_2(HelloWorld::onTouchBegan,this);
touchListener->onTouchMoved=CC_CALLBACK_2(HelloWorld::onTouchMoved,this);
touchListener->onTouchEnded=CC_CALLBACK_2(HelloWorld::onTouchEnded,this);
_eventDispatcher->addEventListenerWithSceneGraphPriority(touchListener,this);


}
+ Xây dựng các hàm Touch

bool HelloWorld::onTouchBegan(Touch* touch, Event* event)
{
    return true;
}

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

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

// Lấy điểm Touch
auto touchPoint = touch->getLocation();
touchPoint = this->convertToNodeSpace(touchPoint);

//Vận tốc= 480px / 3 giây;
float bearVelocity = 480.0/3.0;

// Khoảng di chuyển
Point moveDifference = touchPoint - bear->getPosition();
float distanceToMove = moveDifference.getLength(); // Khoảng cách thực
float moveDuration = distanceToMove / bearVelocity; // Thời gian di chuyển

// Quay đầu tùy theo khoảng cách âm hay dương
if (moveDifference.x < 0)
{
bear->setFlippedX(false); // Quay đầu
}
else
{
bear->setFlippedX(true); // Quay đầu

bear->stopAction(walkAction);
bear->runAction(walkAction); // Thực hiện cái Animate cử động nhân vật
// Di chuyển tới điểm Touch, trong khi vẫn thực hiện Animate
moveAction = Sequence::create(MoveTo::create(moveDuration,touchPoint),
CallFuncN::create(CC_CALLBACK_0(HelloWorld::bearMoveEnded, this)),NULL);
bear->runAction(moveAction);
moving = true;

}

+ Hàm

void HelloWorld::bearMoveEnded()
{
bear->stopAction(walkAction); // Dừng việc bước đi
moving = false;
}

Build ra và run




Vậy là chúng ta đã kết thúc bài 19 khá dài, Trong bài này chúng ta đã biết cách
+ Tạo khung hình với Vector SpriteFrame
+ Tạo animation từ SpriteSheet

Download file nguồn

Sprite Sheet tạo ra Animation khá hay và đơn giản nhưng nó có một nhược điểm khá lớn đó là sẽ tốn bộ nhớ để load các ảnh spritesheet.

Và chắc sẽ có bạn thắc mắc tạo khung physic body cho các nhân vật chuyển động như thế nào.? Các bài sau sẽ trả lời cho bạn nhé.

P/S: Trong bài này có 1 bug: là khi di chuyển con Gấu tới 1 điểm, nếu ta lick đúp sẽ thấy có lúc con gấu sẽ không bước chân mà chỉ trượt đi. Mọi người tìm cách fix lỗi giúp nhé.

Xin chào và hẹn gặp lại!

Bài 20: Học làm game thứ 3: Sushi Crush - Like Candy Crush or Bejewer ( Part 1)

{ 0 nhận xét... read them below or add one }

Đăng nhận xét