void PlayLayer::update(float dt) { if (m_timeBar->getPercent() <= 0) { sendMsg(); Director::getInstance()->replaceScene(GameOverLayer::createScene()); } // check if animationing,m_isAnimationing(true) if (m_isAnimationing) { // init with false m_isAnimationing = false; for (int i = 0; i < m_height * m_width; i++) { SushiSprite *sushi = m_matrix[i]; if (sushi && sushi->getNumberOfRunningActions() > 0) {//getNumberOfRunningActions()方法可以获取sushi正在执行中的动作个数 m_isAnimationing = true; break; } } } // if sushi is moving, ignore use touch event m_isTouchEnable = !m_isAnimationing; if (!m_isAnimationing) { // checkAndRemoveChain(); } }
void PlayLayer::getColChain(SushiSprite *sushi, std::list<SushiSprite *> &chainList) {//横向检测每一列 chainList.push_back(sushi);// 插入第一个寿司精灵 //向前检测相同图标值(ImgIndex)的寿司 int neighborCol = sushi->getCol() - 1;//sushi前一列寿司所在列数值 while (neighborCol >= 0) { SushiSprite *neighborSushi = m_matrix[sushi->getRow() * m_width + neighborCol]; if (neighborSushi && (neighborSushi->getImgIndex() == sushi->getImgIndex())) { chainList.push_back(neighborSushi);//插入该“邻居”寿司 neighborCol--;//继续向前 } else { break; } } //向后检测相同图标值(ImgIndex)的寿司 neighborCol = sushi->getCol() + 1; while (neighborCol < m_width) { SushiSprite *neighborSushi = m_matrix[sushi->getRow() * m_width + neighborCol]; if (neighborSushi && (neighborSushi->getImgIndex() == sushi->getImgIndex())) { chainList.push_back(neighborSushi); neighborCol++; } else { break; } } }
void PlayLayer::getRowChain(SushiSprite *sushi, std::list<SushiSprite *> &chainList) { chainList.push_back(sushi);// add first sushi int neighborRow = sushi->getRow() - 1; while (neighborRow >= 0) { SushiSprite *neighborSushi = m_matrix[neighborRow * m_width + sushi->getCol()]; if (neighborSushi && (neighborSushi->getImgIndex() == sushi->getImgIndex())) { chainList.push_back(neighborSushi); neighborRow--; } else { break; } } neighborRow = sushi->getRow() + 1; while (neighborRow < m_height) { SushiSprite *neighborSushi = m_matrix[neighborRow * m_width + sushi->getCol()]; if (neighborSushi && (neighborSushi->getImgIndex() == sushi->getImgIndex())) { chainList.push_back(neighborSushi); neighborRow++; } else { break; } } }
bool PlayLayer::onTouchBegan(Touch *touch, Event *unused_event) { if (m_isTouchEnable) { SushiSprite *sushi = nullptr; for (int i = 0; i < m_width*m_height; i++) { sushi = m_matrix[i]; if (sushi) { //将触点坐标, 转换为相对节点sushi的, 相对坐标 auto point = sushi->convertTouchToNodeSpace(touch); //构造sushi的尺寸矩形 Size size = sushi->getContentSize(); Rect rect = Rect(0, 0, size.width, size.height); //判断触点是否触摸到sp内部 if (rect.containsPoint(point)) { m_srcSushi = sushi; } } } } checkAndRemoveChain(); return m_isTouchEnable; }
void PlayLayer::actionEndCallback(Node *node) { // Loại bỏ Sushi khỏi ma trận và Layer SushiSprite *sushi = (SushiSprite *)node; m_matrix[sushi->getRow() * m_width + sushi->getCol()] = NULL; sushi->removeFromParent(); }
void PlayLayer::fillVacancies() { Size size = CCDirector::getInstance()->getWinSize(); // Chỗ này nhìn có vẻ phức tạp nhưng chẳng có gì đâu, chỉ là khai báo con trỏ, cấp phát bộ nhớ cho nó thôi, dùng như mảng 1 chiều int *colEmptyInfo = (int *)malloc(sizeof(int) * m_width); memset((void *)colEmptyInfo, 0, sizeof(int) * m_width); // set giá trị là 0 hết // Rơi Sushi đang có xuống khoảng trống SushiSprite *sushi = NULL; // Tạo 1 con trỏ Sushi = Null, // Duyệt ma trận. Lưu ý ở đây 1 chút, chúng ta thường duyệt mảng 2 chiều theo thứ tự hàng, rồi đến cột, nhưng ở đây, hơi ngược 1 tý là cột rồi đến hàng. Và lưu ý rằng Cột 0, và Hàng 0 nằm ở vị trí bên Dưới phía Trái nhé. khi tạo ma trận ta cho viên Sushi 0,0 rơi xuống trước tiên mà for (int col = 0; col < m_width; col++) { // Duyệt theo cột, từ trái sang phải int removedSushiOfCol = 0; // Duyệt theo hàng, từ dưới lên trên for (int row = 0; row < m_height; row++) { sushi = m_matrix[row * m_width + col]; // Sushi tại vị trí hàng, cột if (NULL == sushi) { // Nếu rỗng removedSushiOfCol++; // Đếm số Sushi đã bị "ăn" } else { // Nếu ko rỗng if (removedSushiOfCol > 0) { // Nếu bên dưới nó có ô trống = số Sushi bị ăn // Làm rơi xuống int newRow = row - removedSushiOfCol; //Vị trí hàng mới ( giảm xuống ) // Trong ma trận ta bỏ sushi ở hàng row, và chuyển nó xuống dưới qua removedSushiOfCol ô rỗng m_matrix[newRow * m_width + col] = sushi; m_matrix[row * m_width + col] = NULL; //Di chuyển Point startPosition = sushi->getPosition(); Point endPosition = positionOfItem(newRow, col); float speed = (startPosition.y - endPosition.y) / size.height; // Tốc độ sushi->stopAllActions(); // Dừng mọi chuyển động trước đó của Sushi sushi->runAction(MoveTo::create(speed, endPosition)); // Di chuyển = rơi xuống // set hàng mới cho Sushi tại vị trí mới này sushi->setRow(newRow); } } } // Mảng lưu trữ số lượng Sushi bị ăn tại vị trí Cột xác định colEmptyInfo[col] = removedSushiOfCol; } // 2. Tạo mới và làm rơi các Sushi xuống khoảng trống , lấp đầy ma trận for (int col = 0; col < m_width; col++) { // Duyệt cột từ trái sang phải // Duyệt hàng, chỉ xét từ vị trí rỗng trở lên for (int row = m_height - colEmptyInfo[col]; row < m_height; row++) { createAndDropSushi(row, col); // Tạo Sushi và rơi xuống vị trí Row, Col } } m_movingVertical = true; m_isAnimationing = true; free(colEmptyInfo); // Giải phóng con trỏ }
SushiSprite *SushiSprite::create(int row, int col) { SushiSprite *sushi = new SushiSprite(); sushi->m_row = row; sushi->m_col = col; sushi->m_imgIndex = rand()% TOTAL_SUSHI; sushi->initWithSpriteFrameName(sushiNormal[sushi->m_imgIndex]); sushi->autorelease(); return sushi; }
SushiSprite * SushiSprite::create(int x, int y) { SushiSprite *sushi = new SushiSprite(); sushi->setX(x); sushi->setY(y); sushi->init(); sushi->autorelease(); return sushi; }
void PlayLayer::getRowChain(SushiSprite *sushi, std::list<SushiSprite *> &chainList) { chainList.push_back(sushi); int neighborRow = sushi->getRow() - 1; // Xét sushi bên dưới while (neighborRow >= 0) { SushiSprite *neighborSushi = m_matrix[neighborRow * m_width + sushi->getCol()]; if (neighborSushi && (neighborSushi->getImgIndex() == sushi->getImgIndex()) && !neighborSushi->getIsNeedRemove() && !neighborSushi->getIgnoreCheck()) { chainList.push_back(neighborSushi); neighborRow--; } else { break; } } neighborRow = sushi->getRow() + 1; // Xét sushi bên trên while (neighborRow < m_height) { SushiSprite *neighborSushi = m_matrix[neighborRow * m_width + sushi->getCol()]; if (neighborSushi && (neighborSushi->getImgIndex() == sushi->getImgIndex()) && !neighborSushi->getIsNeedRemove() && !neighborSushi->getIgnoreCheck()) { chainList.push_back(neighborSushi); neighborRow++; } else { break; } } }
SushiSprite *SushiSprite::create(int row, int col) { SushiSprite *sushi = new SushiSprite(); sushi->m_row = row; sushi->m_col = col; #if (CC_TARGET_PLATFORM == CC_PLATFORM_WIN32) srand(rand() + GetTickCount()); #else srand(rand() + 1199); #endif sushi->m_imgIndex = rand() % TOTAL_SUSHI; sushi->initWithSpriteFrameName(sushiNormal[sushi->m_imgIndex]); sushi->autorelease(); return sushi; }
void PlayLayer::fillVacancies() { Size size = CCDirector::getInstance()->getWinSize(); int *colEmptyInfo = (int *)malloc(sizeof(int) * m_width); memset((void *)colEmptyInfo, 0, sizeof(int) * m_width); // 1. drop exist sushi SushiSprite *sushi = NULL; for (int col = 0; col < m_width; col++) { int removedSushiOfCol = 0; // from buttom to top for (int row = 0; row < m_height; row++) { sushi = m_matrix[row * m_width + col]; if (NULL == sushi) { removedSushiOfCol++; } else { if (removedSushiOfCol > 0) { // evey item have its own drop distance int newRow = row - removedSushiOfCol; // switch in matrix m_matrix[newRow * m_width + col] = sushi; m_matrix[row * m_width + col] = NULL; // move to new position Point startPosition = sushi->getPosition(); Point endPosition = positionOfItem(newRow, col); float speed = (startPosition.y - endPosition.y) / size.height; sushi->stopAllActions();// must stop pre drop action sushi->runAction(CCMoveTo::create(speed, endPosition)); // set the new row to item sushi->setRow(newRow); } } } // record empty info colEmptyInfo[col] = removedSushiOfCol; } // 2. create new item and drop down. for (int col = 0; col < m_width; col++) { for (int row = m_height - colEmptyInfo[col]; row < m_height; row++) { createAndDropSushi(row, col); } } free(colEmptyInfo); }
void PlayLayer::checkAndRemoveChain() { //log("checkAndRemoveChain"); SushiSprite *sushi = m_srcSushi; if (!sushi) { return; } std::list<SushiSprite*> sameList; FloodSeedFill(sushi->getRow(),sushi->getCol(),sushi); for (int i = 0; i < m_width*m_height; i++) { if (m_matrix[i]->getIgnoreCheck()) { sameList.push_back(m_matrix[i]); } } m_srcSushi = nullptr;//不然有上个源结点的bug 可以弄个新的功能 if (sameList.size()>2) { removeSushi(sameList); } return; /* // start count chain std::list<SushiSprite *> colChainList; getColChain(sushi, colChainList); std::list<SushiSprite *> rowChainList; getRowChain(sushi, rowChainList); std::list<SushiSprite *> &longerList = colChainList.size() > rowChainList.size() ? colChainList : rowChainList; if (longerList.size() == 3) { removeSushi(longerList); return; } if (longerList.size() > 3) { //TODO: make a special sushi can clean a line, and remove others removeSushi(longerList); return; }*/ }
void PlayLayer::removeSushi(std::list<SushiSprite *> &sushiList) { // make sequence remove m_isAnimationing = true; std::list<SushiSprite *>::iterator itList; for (itList = sushiList.begin(); itList != sushiList.end(); itList++) { SushiSprite *sushi = (SushiSprite *)*itList; // remove sushi from the metrix m_matrix[sushi->getRow() * m_width + sushi->getCol()] = NULL; explodeSushi(sushi); } // drop to fill empty fillVacancies(); }
/*void PlayLayer::checkAndRemoveChain() { //log("checkAndRemoveChain"); for (int i = 0; i < m_height * m_width; i++) { SushiSprite *sushi = m_matrix[i]; if (!sushi) { continue; } // start count chain std::list<SushiSprite *> colChainList; getColChain(sushi, colChainList); std::list<SushiSprite *> rowChainList; getRowChain(sushi, rowChainList); std::list<SushiSprite *> &longerList = colChainList.size() > rowChainList.size() ? colChainList : rowChainList; if (longerList.size() == 3) { removeSushi(longerList); return; } if (longerList.size() > 3) { //TODO: make a special sushi can clean a line, and remove others removeSushi(longerList); return; } } }*/ void PlayLayer::FloodSeedFill(int x, int y, SushiSprite* sushi) { if (x >= 0 && x < m_height && y >= 0 && y < m_width) { SushiSprite *neighborSushi = m_matrix[x * m_width + y]; if (neighborSushi->getImgIndex() == sushi->getImgIndex()) { if (neighborSushi->getIgnoreCheck())return; for (int i = 0; i < 4; i++) { neighborSushi->setIgnoreCheck(true); FloodSeedFill(x + direction_4[i].x_offset, y + direction_4[i].y_offset, sushi); } } } }
//创建SushiSprite,并下落到指定位置 void PlayLayer::createAndDropSushi(int row, int col) { Size size = Director::getInstance()->getWinSize(); SushiSprite *sushi = SushiSprite::create(row, col); // 创建并执行下落动画 Point endPosition = positionOfItem(row, col); Point startPosition = Point(endPosition.x, endPosition.y + size.height /2); sushi->setPosition(startPosition); float speed = startPosition.y / (2 * size.height); sushi->runAction(MoveTo::create(speed, endPosition)); //将寿司添加到精灵表单里。注意,如果没有添加到精灵表单里,而是添加到层里的话,就不会得到性能的优化。 spriteSheet->addChild(sushi); //给指定位置的数组赋值 m_matrix[row * m_width + col] = sushi; }
void PlayLayer::removeSushi() // Không cần truyền tham số { m_isAnimationing = true; // Duyệt toàn ma trận for (int i = 0; i < m_height * m_width; i++) { SushiSprite *sushi = m_matrix[i]; if (!sushi) { // Bỏ qua Sushi rỗng continue; } if (sushi->getIsNeedRemove()) { // Sushi cần xóa bỏ m_isNeedFillVacancies = true; // Cần điền đầy // Nổ các Sushi đặc biệt if(sushi->getDisplayMode() == DISPLAY_MODE_HORIZONTAL) // Loại Sushi sọc ngang { explodeSpecialH(sushi->getPosition()); // Gọi hàm nổ theo chiều ngang } else if (sushi->getDisplayMode() == DISPLAY_MODE_VERTICAL) // Loại Sushi sọc dọc { explodeSpecialV(sushi->getPosition()); // Gọi hàm nổ theo chiều dọc } explodeSushi(sushi); // Nổ sushi bình thường } } }
void PlayLayer::createAndDropSushi(int row, int col) { Size size = Director::getInstance()->getWinSize(); SushiSprite *sushi = SushiSprite::create(row, col); // Gọi đến hàm tạo ra Sushi của lớp SushiSprite // Tạo animation, or Action? Point endPosition = positionOfItem(row, col); // Lấy tọa độ Point từ row, col truyền vào Point startPosition = Point(endPosition.x, endPosition.y + size.height / 2); // (y) Điểm đầu = Điểm Cuối + 1 khoảng nửa màn hình sushi->setPosition(startPosition); float speed = startPosition.y / (2 * size.height); // tốc độ sushi->runAction(MoveTo::create(speed, endPosition)); // Di chuyển rơi xuống // Thêm vào Spritesheet spriteSheet->addChild(sushi); // Thêm sushi vào mảng, chỗ này là cách quy mảng 2 chiều về mảng 1 chiều nhé, a[i][j] = a[i*COL + j] m_matrix[row * m_width + col] = sushi; }
void PlayLayer::update(float dt) { // Kiểm tra giá trị lần đầu của m_isAnimationing, mỗi bước thời gian dt, sẽ lại kiểm tra m_isAnimationing là true hay flase if (m_isAnimationing) { // nếu True // Gán = false m_isAnimationing = false; // Duyệt trong toàn ma trận for (int i = 0; i < m_height * m_width; i++) { SushiSprite *sushi = m_matrix[i]; // Nếu tồn tại 1 Sushi mà đang có "Action" thì m_isAnimationing = true, và thoát vòng lặp if (sushi && sushi->getNumberOfRunningActions() > 0) { m_isAnimationing = true; break; } } } // Đến khi không có Action nào của Sushi tại bước thời gian dt nào đó, thì kiểm tra việc "Ăn" dãy Sushi nếu tồn tại // Thiết lập cờ cho phép Touch khi không còn chuyển động, và ngược lại m_isTouchEnable = !m_isAnimationing; //Nếu ko có chuyển động if (!m_isAnimationing) { // Xét xem phải điền đầy ô trống không if (m_isNeedFillVacancies) { fillVacancies(); // điền đầy m_isNeedFillVacancies = false; } else { checkAndRemoveChain(); // Kiểm tra và ăn các chuỗi } } }
SushiSprite *PlayLayer::sushiOfPoint(Point *point) { SushiSprite *sushi = NULL; Rect rect = Rect(0, 0, 0, 0); // Hình chữ nhật kích thước 0,0 tại Point 0,0 // Duyệt ma trận Sushi for (int i = 0; i < m_height * m_width; i++) { sushi = m_matrix[i]; // Tính kích thước hình chữ nhật bao quanh Sushi if (sushi) { rect.origin.x = sushi->getPositionX() - (sushi->getContentSize().width / 2); rect.origin.y = sushi->getPositionY() - (sushi->getContentSize().height / 2); rect.size = sushi->getContentSize(); // Nếu hình chữ nhật đó chứa Point ( chắc là point của điểm Touch ) if (rect.containsPoint(*point)) { return sushi; // trả lại Sushi } } } return NULL; // Trả lại Null nếu Touch ra ngoài ma trận, điểm Touch ko thuộc 1 Sushi nào }
void PlayLayer::getColChain(SushiSprite *sushi, std::list<SushiSprite *> &chainList) { chainList.push_back(sushi); // Thêm vào dãy Sushi đầu tiên, tại vị trí thứ i đang xét trong vòng lặp FOR của hàm checkAndRemoveChain int neighborCol = sushi->getCol() - 1; // Xét cột bên trái while (neighborCol >= 0) { // Tồn tại cột bên trái // Tạo 1 pointer Sushi "bên trái" trỏ vào Sushi tại vị trí (Hàng * width + neighborCol ), đây là cách quy ma trận cấp 2 về mảng 1 chiều nhé SushiSprite *neighborSushi = m_matrix[sushi->getRow() * m_width + neighborCol]; // Nếu tồn tại sushi bên trái và cùng imgIndex (cùng loại Sushi) với sushi đang xét thì.. if (neighborSushi && (neighborSushi->getImgIndex() == sushi->getImgIndex()) && !neighborSushi->getIsNeedRemove() && !neighborSushi->getIgnoreCheck()) { // Thêm sushi trái này vào list chainList.push_back(neighborSushi); neighborCol--; // Xét tiếp Sushi bên trái đến khi ko còn Sushi nào, cột 0 } else { break; // Ko thỏa mãn đk if ở trên, Phá vòng while } } neighborCol = sushi->getCol() + 1; // Xét Sushi bên phải while (neighborCol < m_width) { // Xét đến cột cuối cùng, cột cuối = m_width - nhé // Tương tự trên tìm ông sushi cùng loại bên trái SushiSprite *neighborSushi = m_matrix[sushi->getRow() * m_width + neighborCol]; if (neighborSushi && (neighborSushi->getImgIndex() == sushi->getImgIndex()) && !neighborSushi->getIsNeedRemove() && !neighborSushi->getIgnoreCheck()) { chainList.push_back(neighborSushi); // Nhét vào List neighborCol++; } else { break; // Phá vòng while } } }
void PlayLayer::markRemove(SushiSprite *sushi) { if (sushi->getIsNeedRemove()) { return; } if (sushi->getIgnoreCheck()) { return; } // Set true sushi->setIsNeedRemove(true); // Các sushi loại sọc dọc if (sushi->getDisplayMode() == DISPLAY_MODE_VERTICAL) { for (int row = 0; row < m_height; row++) { SushiSprite *tmp = m_matrix[row * m_width + sushi->getCol()]; if (!tmp || tmp == sushi) { continue; //Bỏ qua loại sọc dọc } if (tmp->getDisplayMode() == DISPLAY_MODE_NORMAL) { tmp->setIsNeedRemove(true); // Đánh dấu loại Sushi thường } else { markRemove(tmp); // Đệ quy, } } // Các sushi loại sọc ngang, tương tự } else if (sushi->getDisplayMode() == DISPLAY_MODE_HORIZONTAL) { for (int col = 0; col < m_width; col++) { SushiSprite *tmp = m_matrix[sushi->getRow() * m_width + col]; if (!tmp || tmp == sushi) { continue; } if (tmp->getDisplayMode() == DISPLAY_MODE_NORMAL) { tmp->setIsNeedRemove(true); } else { markRemove(tmp); } } } }
void PlayLayer::checkAndRemoveChain() { SushiSprite *sushi; // Thiết lập cờ IgnoreCheck = false for (int i = 0; i < m_height * m_width; i++) { sushi = m_matrix[i]; if (!sushi) { continue; } sushi->setIgnoreCheck(false); } // 2. Kiểm lại for (int i = 0; i < m_height * m_width; i++) { sushi = m_matrix[i]; if (!sushi) { continue; } if (sushi->getIsNeedRemove()) { continue; // Bỏ qua Sushi đã gắn cờ "cần loại bỏ" } if (sushi->getIgnoreCheck()) { continue; // Bỏ qua Sushi đã gắn cờ "bỏ qua kiểm tra" } // Đếm cuỗi std::list<SushiSprite *> colChainList; getColChain(sushi, colChainList); std::list<SushiSprite *> rowChainList; getRowChain(sushi, rowChainList); std::list<SushiSprite *> &longerList = colChainList.size() > rowChainList.size() ? colChainList : rowChainList; if (longerList.size() < 3) { continue;// Bỏ qua } std::list<SushiSprite *>::iterator itList; bool isSetedIgnoreCheck = false; for (itList = longerList.begin(); itList != longerList.end(); itList++) { sushi = (SushiSprite *)*itList; if (!sushi) { continue; } if (longerList.size() > 3) { // Sushi đặc biệt khi chuỗi có 4 hoặc 5 Sushi if (sushi == m_srcSushi || sushi == m_destSushi) { isSetedIgnoreCheck = true; sushi->setIgnoreCheck(true); sushi->setIsNeedRemove(false); // Tùy theo hướng di chuyển mà tạo ra loại Sushi sọc dọc hay ngang sushi->setDisplayMode(m_movingVertical ? DISPLAY_MODE_VERTICAL : DISPLAY_MODE_HORIZONTAL); continue; } } markRemove(sushi); // Đánh dấu cần loại bỏ sushi } // Chuỗi đặc biệt, khi Sushi rơi, sinh ra tự nhiên if (!isSetedIgnoreCheck && longerList.size() > 3) { sushi->setIgnoreCheck(true); sushi->setIsNeedRemove(false); sushi->setDisplayMode(m_movingVertical ? DISPLAY_MODE_VERTICAL : DISPLAY_MODE_HORIZONTAL); } } // 3.Loại bỏ removeSushi(); }