Hi, Rảnh rỗi tranh thủ viết cho xong game Space Ship này.
Bài trước chúng ta đã thiết kế sơ bộ xong phần màn chơi, song còn thiếu một số phần quan trọng trong game nên có mà chúng ta sẽ bổ sung ngay sau đây:
+ Bắn đạn khi Touch màn hình
+ Bắt sự kiện va chạm giữa đạn và thiên thạch
+ Tính điểm
+ Game Over
Đơn giản có thế thôi, chúng ta sẽ lướt nhanh!
B1: Bắn đạn khi Touch màn hình
Bạn mở file HelloWorldScene.h thêm vào dòng lệnh sau, trong public
// Hàm bắt sự kiện touch, dùng multiTouch, hoặc Touch thôi cũng được
void HelloWorld::onTouchesBegan(const std::vector<Touch*>& touches, Event *event)
Tiếp đó trong HelloWorldScene.cpp ta thiết kế hàm này như sau
void HelloWorld::onTouchesBegan(const std::vector<Touch*>& touches, Event *event)
{
SimpleAudioEngine::getInstance()->playEffect("laser_ship.wav"); // Âm thanh
Size winSize = Director::getInstance()->getWinSize();
Sprite *shipLaser = (Sprite *) _shipLasers->at(_nextShipLaser++);
if ( _nextShipLaser >=_shipLasers->size()) // Reset index laser
_nextShipLaser = 0;
// Đặt vị trí ở phía mũi tàu, và cho hiện lên
shipLaser->setPosition(Point(_ship->getPosition().x + shipLaser->getContentSize().width/2, _ship->getPosition().y));
shipLaser->setVisible(true);
// set body
auto laserbody = PhysicsBody::createBox(shipLaser->getContentSize()/2);
laserbody->setContactTestBitmask(0xf);
laserbody->setDynamic(true);
shipLaser->setPhysicsBody(laserbody);
shipLaser->stopAllActions();
shipLaser->runAction(Sequence::create(
MoveBy::create(0.5,Point(winSize.width, 0)),
CallFuncN::create(this, callfuncN_selector(HelloWorld::setInvisible)),
NULL
));
}
B2: Bắt sự kiện va chạm
Thêm hàm sau vào file HelloWorldScene.h
bool onContactBegin(const PhysicsContact &contact);
Và xây dựng nó trong file HelloWorldScene.cpp như sau
bool HelloWorld::onContactBegin(const PhysicsContact& contact)
{
auto laser = (Sprite*)contact.getShapeA()->getBody()->getNode();
int Tag1 = -1;
if(laser) {
Tag1 = laser->getTag();
auto asteroid = (Sprite*)contact.getShapeB()->getBody()->getNode();
int Tag2 = -1;
if(asteroid) Tag2 = asteroid->getTag();
if((Tag1==KLASER&Tag2==KASTEROID)||(Tag2==KLASER&Tag1==KASTEROID))
{
SimpleAudioEngine::sharedEngine()->playEffect("explosion_large.wav");
_world->removeBody(laser->getPhysicsBody());
laser->setVisible(false);
_world->removeBody(asteroid->getPhysicsBody());
asteroid->setVisible(false);
}
if((Tag1==KSHIP&Tag2==KASTEROID)||(Tag2==KSHIP&Tag1==KASTEROID))
{
_lives--;
}
}
return true;
}
Và không được quên đoạn code Listener ở init()
auto contactListener = EventListenerPhysicsContact::create();
contactListener->onContactBegin = CC_CALLBACK_1(HelloWorld::onContactBegin, this);
_eventDispatcher->addEventListenerWithSceneGraphPriority(contactListener, this);
OK, giờ là tới phần Tính điểm và GameOver
B3: Tính điểm và GameOver
Trong hàm update() bạn thêm vào 1 đoạn sau đây
if (_lives <= 0) { // Kiểm tra không còn mạng nào thì game Over
_ship->stopAllActions();
_ship->setVisible(false);
_world->removeBody(_ship->getPhysicsBody());
this->endScene(KENDREASONLOSE); // Game Over
}
void HelloWorld::endScene( EndReason endReason ) {
if (_gameOver) // trạng thái game
return;
_gameOver = true;
Size winSize = Director::getInstance()->getWinSize();
char message[10] = "";
if ( endReason == KENDREASONLOSE)
strcpy(message,"You Lose");
LabelBMFont * label ;
label = LabelBMFont::create(message, "Arial.fnt");
label->setScale(0.1);
label->setPosition(Point(winSize.width/2 , winSize.height*0.6));
this->addChild(label);
LabelBMFont * restartLabel;
strcpy(message,"Restart");
restartLabel = LabelBMFont::create(message, "Arial.fnt");
MenuItemLabel *restartItem = MenuItemLabel::create(restartLabel,CC_CALLBACK_1(HelloWorld::resetGame,this));
restartItem->setScale(0.1);
restartItem->setPosition( Point(winSize.width/2, winSize.height*0.4));
Menu *menu = Menu::create(restartItem, NULL);
menu->setPosition(Point::ZERO);
this->addChild(menu);
restartItem->runAction(ScaleTo::create(0.5, 1.0));
label ->runAction(ScaleTo::create(0.5, 1.0));
this->unscheduleUpdate(); // dừng update Scene
}
Bổ sung hàm endScene() và resetGame() vào trong lớp HelloWorld, và hàm resetGame như sau
void HelloWorld::resetGame(Ref* pSender) {
auto scene = HelloWorld::createScene();
Director::getInstance()->replaceScene(TransitionZoomFlipY::create(0.5, scene));
Size winSize = Director::getInstance()->getVisibleSize();
}
Bạn thêm 1 thuộc tính int score, LabelBMFont * _scoreDisplay;vào lớp HelloWorldScene, và khi khởi tạo thêm đoạn code này
_scoreDisplay = LabelBMFont::create("Score: 0", "Arial.fnt",
visibaleSize.width * 0.3f);
_scoreDisplay->setAnchorPoint(Point(1, 0.5));
_scoreDisplay->setPosition(
Point(visibaleSize.width * 0.8f, visibaleSize.height * 0.94f));
this->addChild(_scoreDisplay);
Trong hàm kiểm tra va chạm chúng ta sẽ tính điểm bằng đoạn code nhỏ như thế này
score+=10;
char szValue[100] = { 0 }; // Lấy ra điểm qua mảng đệm char
sprintf(szValue, "Score: %i", score); // Chuyển sang chuỗi => chuỗi
_scoreDisplay->setString(szValue); // Hiện điểm lên
OK, Build thử xem kết quả thế nào nhé, cũng không tệ với 1 game "tự tui".
Và sau đây mình làm thêm 1 bước Bonus nữa là
Bonus: Điều khiển Ship bằng Accelerometer - gia tốc kế
Trước hết bạn copy 2 file VisibleRect.h, .cpp trong bài cpp-tests vào Class của chúng ta. sau đó trong phần init() thêm đoạn code này vào
#define FIX_POS(_pos, _min, _max) \
if (_pos < _min) \
_pos = _min; \
else if (_pos > _max) \
_pos = _max;
auto listener = EventListenerAcceleration::create([=](Acceleration* acc, Event* event){
auto shipSize = _ship->getContentSize();
auto ptNow = _ship->getPosition();
log("acc: x = %lf, y = %lf", acc->x, acc->y);
ptNow.x += acc->x * 9.81f;
ptNow.y += acc->y * 9.81f;
FIX_POS(ptNow.x, (VisibleRect::left().x+shipSize.width / 2.0), (VisibleRect::right().x - shipSize.width / 2.0));
FIX_POS(ptNow.y, (VisibleRect::bottom().y+shipSize.height / 2.0), (VisibleRect::top().y - shipSize.height / 2.0));
_ship->setPosition(ptNow);
});
auto dispathcher = Director::getInstance()->getEventDispatcher();
dispathcher->addEventListenerWithSceneGraphPriority(listener, this);
Kết thúc bài này, chúng ta cùng nghiên cứu 1 số vấn đề sau
+ Bắn đạn = Touche, duyệt vector
+ Va chạm
+ Tính điểm, game Over
+ Di chuyển Ship bằng gia tốc kế
Download Code
Mình dừng bài học ở đây nhé
Bài 31: Làm game gì bây giờ?
anh có thể làm 1 ví dụ về sliding menu grid không ạ?
ReplyDeleteSự khác nhau giữa hàm schedule() và scheduleUpdate() Mình thấy cách sử dụng hàm schedule() sẽ thường có tham số là một selector(...) khi muốn update một cái gì đó (vị trí,...) Thế còn hàm scheduleUpdate() cách hoạt động nó ra sao bạn? nó sẽ tự gọi hàm update(float dt) cho mình luôn hay sao có phải khi mình dùng this->scheduleUpdate() thì nó sẽ tự cập nhật những gì trong hàm update(float dt) có phải không và thời gian nó thực hiện update là bao lâu khi mình gọi this->scheduleUpdate()
ReplyDeleteBạn có thể vào wiki để tra về các loại schedule luôn
Deletehttp://www.cocos2d-x.org/reference/native-cpp/V3.2/index.html
Theo mình hiểu thì
scheduleUpdate() ngầm định gọi tới update(float dt) ( dt xác định ở file Appdelegate
schedule() thì ko có tham số ngầm định, bạn phải chỉ rõ hàm nào ( có dạng tham số float dt ) thì sẽ gọi tới hàm đó, dt mình tự phải truyền vào
Đơn giản vậy thôi
Là một người chân ướt chân ráo nhưng đã vào mò vào bài cuối của series này... Cám ơn các bạn ( các anh , chị ) đã truyền nhiệt huyết cho mình. Xin các bậc tiền bối chỉ bảo... và đừng ngạc nhiên khi mình cmt vào những Post đầu với những câu hỏi rất ngu nhé vì mình rất muốn học hỏi...
ReplyDeletebạn ơi cho mình hỏi ở phần gia tốc kế, mình muốn cho ship của mình khi lắc màn hình về bên phải thì nó di chuyển về phía bên phải và bên trái thì di chuyển về bên trái thì làm như thế nào. ở đây, là ship di chuyển lên trên và phía dưới màn hình đúng ko
ReplyDeleteXin lỗi vì mình không biết đặt câu hỏi ở đâu nên đành đặt câu hỏi ở đây vậy:
ReplyDeleteMình cần chia 1 bức ảnh làm 9 phần bằng nhau và Sắp Xếp Ngẫu Nhiên 9 phần này trong ô vuông 3x3.
Mình đã chia được bức ảnh làm 9 phần bằng nhau rồi:
auto spriteItem1 = Sprite::create(text, Rect(0, 0, 200, 200));
auto spriteItem2 = Sprite::create(text, Rect(200, 0, 200, 200));
auto spriteItem3 = Sprite::create(text, Rect(400, 0, 200, 200));
auto spriteItem4 = Sprite::create(text, Rect(0, 200, 200, 200));
auto spriteItem5 = Sprite::create(text, Rect(200, 200, 200, 200));
auto spriteItem6 = Sprite::create(text, Rect(400, 200, 200, 200));
auto spriteItem7 = Sprite::create(text, Rect(0, 400, 200, 200));
auto spriteItem8 = Sprite::create(text, Rect(200, 400, 200, 200));
auto spriteItem9 = Sprite::create(text, Rect(400, 400, 200, 200));
Các bạn chỉ cho mình cách sắp xếp ngẫu nhiên các ảnh này vào ô vuông 3x3 với. Mình cảm ơn rất nhiều.
This comment has been removed by the author.
Deletevector sprites = {spriteItem1, spriteItem2, spriteItem3, spriteItem4, spriteItem5, spriteItem6, spriteItem7, spriteItem8, spriteItem9};
Deletestatic int count;
static Vec2 pos;
for (int i = 0; i < (int)sprites.size(); i++) {
int index = (int)sprites.size() - count;
index = rand() % index;
Sprite * sprite = (Sprite*)sprites.at(index);
float width = sprite->getContentSize().width;
float height = sprite->getContentSize().height;
pos.x = i * width;
pos.y = (i==0) ? height : ((i+1)%3==0) ? (i+1)/3 * height : pos.y;
sprite->setPosition(pos);
sprites.earse(index);
count++;
}
@Hieu Trinh
DeleteRất cảm ơn bạn đã dành thời gian chỉ bảo giúp mình. :D
This comment has been removed by the author.
DeleteThis comment has been removed by the author.
Deleteusing namespace std;
Delete...
vector sprites = {spriteItem1, spriteItem2, spriteItem3, spriteItem4, spriteItem5, spriteItem6, spriteItem7, spriteItem8, spriteItem9};
static int count;
static Vec2 pos;
for (int i = 0; i < (int)sprites.size(); i++) {
int index = (int)sprites.size() - count;
index = rand() % index;
Sprite * sprite = (Sprite*)sprites.at(index);
float width = sprite->getContentSize().width;
float height = sprite->getContentSize().height;
pos.x = i * width;
pos.y = (i==0) ? height : ((i+1)%3==0) ? (i+1)/3 * height : pos.y;
sprite->setPosition(pos);
sprites.earse(index); // Lệnh này viết đúng không bạn? "erase" hay là "earse" vậy :(
count++;
}
Sorry, sai chính tả, dòng đó đúng ra phải là
Deletesprites.erase(index);
bạn sửa lại "earse" thành "erase" nhé.
Như thế vẫn có hiện ra lỗi bạn ạ. Hình như bạn chưa test thử đoạn code đó thì phải.
DeleteBạn dùng "vector" trong câu lệnh:
vector sprites = {spriteItem1, spriteItem2, spriteItem3, spriteItem4, spriteItem5, spriteItem6, spriteItem7, spriteItem8, spriteItem9};
thì câu lệnh sprites.erase(index) không sử dụng được?
mình thay bằng :
Vector sprites;
sprites.pushBack(spriteItem1);
sprites.pushBack(spriteItem2);
sprites.pushBack(spriteItem3);
sprites.pushBack(spriteItem4);
sprites.pushBack(spriteItem5);
sprites.pushBack(spriteItem6);
sprites.pushBack(spriteItem7);
sprites.pushBack(spriteItem8);
sprites.pushBack(spriteItem9);
thì sử dụng được.
Nhưng kết quả không nằm trong ô vuông 3X3 mà nằm trên 1 hàng ngang.
Bạn xem lại giùm mình với.
Sorry, mình pase nhầm công thức tính X và Y cho bạn, bạn sử dụng Vector của cocos2d-x cũng được, còn vector kia là std::vector của C++. Vậy đây là code hoàn chỉnh cho bạn.
DeleteVector sprites;
sprites.pushBack(spriteItem1);
sprites.pushBack(spriteItem2);
sprites.pushBack(spriteItem3);
sprites.pushBack(spriteItem4);
sprites.pushBack(spriteItem5);
sprites.pushBack(spriteItem6);
sprites.pushBack(spriteItem7);
sprites.pushBack(spriteItem8);
sprites.pushBack(spriteItem9);
static int count;
static Vec2 pos;
for (int i = 0; i < (int)sprites.size(); i++) {
int index = (int)sprites.size() - count;
index = rand() % index;
Sprite * sprite = (Sprite*)sprites.at(index);
float width = sprite->getContentSize().width;
float height = sprite->getContentSize().height;
pos.x =(i%3==0) ? 0 : ((i+1)%3==0) ? 400 : i%3 * 200;
pos.y = (i<3) ? 0 : (i<6) ? 200 : 400;
sprite->setPosition(pos);
sprites.erase(index);
count++;
}
This comment has been removed by the author.
ReplyDelete@ Chè Thái Nguyên
ReplyDeleteLàm cái hướng dẫn về Design Pattern trong lập trình game bằng Cocos2d-x đê. Mình sẽ chuyển code mẫu và support cho.
Nếu quan tâm thì email cho mình hieut@creative-game.com
Mình có một câu hỏi:
ReplyDeleteCó thể dùng update(float dt) và schedule ở một hàm khác ko ?
@Sĩ Luận: Cái đó còn tuỳ vào hàm của bạn có phải là 1 phương thức nằm trong 1 lớp được kế thừa từ lớp Ref của cocos2d-x hay không.
ReplyDeleteBạn có thể tham khảo thêm thông tin ở đây: http://www.cocos2d-x.org/reference/native-cpp/V3.2/dc/d82/classcocos2d_1_1_scheduler.html#ad86f74fb8869d343245fabbd206ddbf8
@Hieu Trinh: Rất cảm ơn bạn đã dành thời gian xem xét và giúp mình giải quyết bài toán sắp xếp ngẫu nhiên 9 hình ảnh được cắt ra từ 1 hình ảnh trong ô vuông 3x3.
ReplyDeleteNhưng có vẻ đoạn code sau khi được gửi lên blog thì bị thiếu 1 số phần.
Mong bạn có thể gửi đoạn code của bạn vào gmail buithanhtung.mrtest@gmail.com hoặc skyper: youinmyeyes@outlook.com giúp mình với.
Và giúp mình giải thích ý nghĩa của 2 câu code:
pos.x =(i%3==0) ? 0 : ((i+1)%3==0) ? 400 : i%3 * 200;
pos.y = (i<3) ? 0 : (i<6) ? 200 : 400;
Mình xin được cảm ơn rất nhiều.
@Tùng Bùi Thanh
ReplyDeleteĐoạn code mình gửi là đầy đủ và đã được test chính xác rồi đó, tuy nhiên đây chỉ là đoạn code xử lý giải thuật cho yêu cầu của bạn còn thực chạy sau khi ghép vào trong project của bạn thế nào thì bạn phải tự debug thôi. Mình có thể giải nghĩa cho bạn chi tiết các đoạn code như sau:
// Đầu tiên khai báo 1 biến sprites là Vector kiểu con trỏ đối tượng "sprite"
Vector sprites; // Không chạy được chắc vì thiếu cái định kiểu ''
// Chèn 9 đối tượng sprite cần sắp xếp vào trong vector này.
sprites.pushBack(spriteItem1);
sprites.pushBack(spriteItem2);
sprites.pushBack(spriteItem3);
sprites.pushBack(spriteItem4);
sprites.pushBack(spriteItem5);
sprites.pushBack(spriteItem6);
sprites.pushBack(spriteItem7);
sprites.pushBack(spriteItem8);
sprites.pushBack(spriteItem9);
// Khai báo biến đếm count kiểu int tĩnh và biến pos kiểu Vec2 tĩnh
static int count;
static Vec2 pos;
// Chạy vòng lặp duyệt qua toàn bộ 9 phần tử của vector sprites
for (int i = 0; i < (int)sprites.size(); i++) {
// Với mỗi vòng lặp xử lý như sau:
int index = (int)sprites.size() - count; // Tính toán số phần tử còn lại trong vector
index = rand() % index; // Lấy ngẫu nhiên vị trí 1 phần tử trong số còn lại đó
Sprite * sprite = (Sprite*)sprites.at(index); // Lấy ra phần tử tại vị trí ngẫu nhiên trên
float width = sprite->getContentSize().width; // Lấy chiều rộng của phần tử sprite
float height = sprite->getContentSize().height; // Lấy chiều cao của phần tử sprite
// Và đây là 2 dòng code sắp xếp quan trọng nhất mà bạn hỏi.
// Tính toán toạ độ trục tung X của sprite thứ i (pos.x)
// X = nếu vị trí phần tử đang xét là thứ i chia hết cho 3 thì nó có toạ độ X = 0
// Còn ngược lại, kiểm tra xem nếu vị trí kế cận nó chia hết cho 3 thì X của nó sẽ là 400
// Còn không thì X của nó sẽ bằng phần dư của i chia 3 nhân với 200.
pos.x =(i%3==0) ? 0 : ((i+1)%3==0) ? 400 : i%3 * 200;
// Tương tự với tọ độ trục hoành Y của phần tử có vị trí thứ i
// Nếu nó nhỏ hơn 3 thì Y của nó sẽ là 0
// Còn lại kiểm tra nếu nó nhỏ hơn 6 (tương đương nằm ở giữa) thì Y cuả nó sẽ là 200
// Cuối cùng thì còn lại là nó nằm trên cùng tương đương với Y của nó là 400
pos.y = (i<3) ? 0 : (i<6) ? 200 : 400;
// Bạn sẽ thắc mắc tại sao lại là 400 và 200 đúng không. Mình sẽ giải thích như sau:
// Thật ra đúng theo giải thuật thì 400 thực chất sẽ bằng width * (max số phần tử của hàng hoặc cột - 1) và theo như dữ liệu của bạn thì ở đây sẽ là 200 * (3-1) = 200 * 2 = 400
// Và 200 ở đây thực chất chính là width hay height sprite (hình vuông) của bạn.
// Vậy thật ra ở đây ta có công thức tổng quát cho tính toán toạ độ X, Y để sắp xếp các phần tử thành 1 hình vuông có độ rộng n phần tử sẽ là:
// X[i] = (i%n==0) ? 0 : ((i+1)%n==0) ? (width*n-1) : (i%n * width);
// Y[i] = (i < n) ? 0 : (i/n * height) :
// Đặt phần tử sprite đang xét vào vị trí đã được tính toán ra bên trên
sprite->setPosition(pos);
// Xoá phần tử đã được sắp xếp ra khỏi vector những phần tử chưa được sắp xêp
sprites.erase(index);
// Cộng biến đếm lên thêm 1 nghĩa là đã xử lý xong thêm 1 phần tử
count++;
}
Trên đây mình đã giải thích tỉ mỉ và tường tận giải thuật sắp xếp hình vuông cho bạn. Bạn lưu ý là trong trao đổi lập trình mọi người chủ yếu chỉ trao đổi giải thuật và code mẫu còn việc áp dụng cụ thể vào project của mình thế nào thì mỗi người sẽ phải tự có sự chủ động vì toàn bộ code của ai thì chỉ có người đó nắm rõ nhất.
@Hieu Trinh
DeleteCảm ơn bạn nhiều nha. Bạn giải thích rất tường tận dễ hiểu.
Mình sẽ cố gắng để áp dụng vào PJ mình đang làm.
@Hieu Trinh: Ban có thể giúp mình phần loại trừ các trường hợp không sắp xếp được về vệ trí đúng của hình ảnh không ? Mình còn bước này là hoàn thành cái game xếp hình rồi mà mình tìm cách làm cả tuần nay không ra rồi :(
ReplyDelete@Tùng Bùi Thanh: Mình không hiểu ý của bạn vì mình không biết game của bạn chơi như thế nào. Nhưng theo quy tắc cơ bản của những game ráp hình thì thông thường sẽ có 2 vector. Vector 1 để lưu toàn bộ các mảnh ghép cùng thông số vị trí chính xác của chúng trên hình dùng để đối chiếu với kết quả sau khi ghép của người chơi. Vector thứ 2 chứa các mảnh ghép cùng tọa độ vị trí của nó mà đã được người chơi ghép. Sau cùng là duyệt vector 2 để đối chiếu vị trí của các mảnh ghép với các giá trị tọa độ chính xác của nó bên vector 1, mảnh nào đúng thì bỏ qua mảnh nào sai thì thực hiện trả nó về vị trí chính xác theo thông số tọa độ của nó lưu trong vector 1, chỉ vậy thôi.
ReplyDelete@Hieu Trinh: Ok bạn. Mình tìm ra thuật toán rồi. Đang tìm cách triển khai thuật toán đó đây.
Deleteauto spriteItem1 = Sprite::create(text, Rect(0, 0, 200, 200));
ReplyDeleteauto spriteItem2 = Sprite::create(text, Rect(200, 0, 200, 200));
auto spriteItem3 = Sprite::create(text, Rect(400, 0, 200, 200));
auto spriteItem4 = Sprite::create(text, Rect(0, 200, 200, 200));
auto spriteItem5 = Sprite::create(text, Rect(200, 200, 200, 200));
auto spriteItem6 = Sprite::create(text, Rect(400, 200, 200, 200));
auto spriteItem7 = Sprite::create(text, Rect(0, 400, 200, 200));
auto spriteItem8 = Sprite::create(text, Rect(200, 400, 200, 200));
auto spriteItem9 = Sprite::create(text, Rect(400, 400, 200, 200));
mình muốn gán giá trị cho spriteItem1 = 1; spriteItem2 = 2; spriteItem3 = 3
để chuyển về bài toán xếp hình 8Pluzzle thì làm thế nào?
Các bạn chỉ cho mình với.
Có bài nào hướng dẫn cách xài CCSwipeGestureRecognizer ko ad
ReplyDeleteMọi người cho mình hỏi chút. Phần Điều khiển Ship bằng Accelerometer - gia tốc kế.
ReplyDeleteKhi build trên máy mọi người chpwi có mượt k ?
Trên máy mình chơi lần đầu tiên khi mở ứng dụng thì rất mượt. Nhưng từ lần tiếp theo ( sau khi die Resert lại) thì chơi không ổn định. Có lúc nghiêng máy mà tàu không di chuyển, hoạc di chuyển rất nhanh. Ai đã fix lỗi này rùi thì chỉ mình với nhé. Thank mọi người
chào các anh chị,em có ý tưởng thế này,thay các thiên thạch bằng 1 con tàu địch và các con tàu địch có thể bắn được ta(vừa bay vừa bắn),nhưng em ko biết làm thế nào để có thể làm cho tàu địch vừa bay vừa bắn,em là newbie còn chưa hiểu biết gì nhiều mong các anh chị chỉ giáo
ReplyDeleteví dụ e có đoạn mã này:
ReplyDeletehapieu0.png
aliases
spriteOffset
{0,0}
spriteSize
{47,51}
spriteSourceSize
{47,51}
textureRect
{{621,226},{47,51}}
textureRotated
Dãy số {{621,226},{47,51}} có ý nghĩa gì vậy ad?
cảm ơn anh, cả khoá của anh toàn post chất lượng <3
ReplyDeleteCảm ơn chủ thread.
ReplyDeleteChuyển qua giải thích sourcecode các game lớn khác cho các mem học thêm đi chủ thread