cocos2d 3.0 box2d 解決移動相機鏡頭(滾屏)問題的方法
最近在研究橫版過關遊戲,原來是用Rect框跟隨主角、怪物來實現碰撞檢測,但覺得不精確,所以想用box2d實現精確的碰撞檢測,但用上box2d後有一個比較嚴重的問題:英雄精靈的座標與PhysicsBody的座標不一樣了。
相機鏡頭(滾屏)原理詳解:
如果Layer是一張長方形的桌子,hero是站在桌子上的一隻螞蟻,我們的遊戲介面視窗是手持的一個DV攝像機,這時我們正通過攝像機的螢幕檢視螞蟻,螞蟻的位置剛好在桌面的左邊界線中間位置(設此位置為x零點位置),而我們長方形的攝像機螢幕的左邊界也正好對準桌面左邊界。此時螞蟻開始向右移動了,不一會螞蟻已經走到攝像機螢幕的中心位置,此時如果螞蟻繼續往右走,它就不會在螢幕的中心位置,但如果這時我們想讓螞蟻一直處於攝像機螢幕的中心位置,那麼我們可以右移動攝像機(方法1)或者左移動桌子(方法2)。
1.我們移動攝像機的步伐跟螞蟻移動的步伐一致,螞蟻一直向右行走,也一直處於螢幕的中心。當螞蟻行走到離右邊邊界只有半個相機螢幕寬度的位置時,螞蟻仍然在螢幕中心,而螢幕的右邊界也正好對準桌子的右邊界,由於我們不想拍攝桌子範圍外的景物,所以此時攝像機不再向右移動,而此時螞蟻仍向右移動,自然螞蟻就不處於攝像機螢幕的中心了,這時就跟開始時一樣,只有螞蟻在移動,但我們仍能拍攝得到。
2.移動桌子,這種方法比較費勁,我們一般不會這樣做。攝像機螢幕的左邊界對準桌子的左邊界,螞蟻向右開始移動,當我們在螢幕上看到螞蟻在螢幕的中間時,我們開始讓桌子向左移動,移動的步伐與螞蟻的一致,此時我們看到的是螞蟻仍在螢幕中間,當我們看到螢幕右邊界正好對準桌面的右邊界時,我們停止移動桌面了,因為我們不想拍攝到桌面以外的景物,此時螞蟻繼續向右移動,自然也離開螢幕中心,它在桌面上行走的動作我們仍然能拍攝到。
一般我們會選擇移動攝像機,即方法1,但如果相機不能移動怎麼辦?那唯有選擇方法一。那又有什麼情況不能移動攝像機呢?我們的遊戲介面視窗就正好是不能移動的(即不能移動攝像機),那麼了為不顯示背景圖片以外的背景,我們選擇了方法2,移動桌子,即移動Layer,幸好移動Layer沒移動桌子費勁。
背景圖與hero圖片都在Layer裡面。假設背景圖的錨點在左邊中間位置,hero的錨點座標在hero的腳下,即hero圖片的下面中間位置。遊戲剛初始化完畢,背景圖位置被設定在與視窗左邊座標一致的地方,即背景圖左邊與視窗左邊重合,而hero也正站在背景左邊中間位置。hero開始向右移動,移動到視窗中間位置,我們就開始讓Layer向左移動,移動的步伐跟hero一致,由於hero是在Layer裡面,所以會隨Layer一起向左移動,但此時他也正以相同的速度向右移動啊,由於運動抵消了,相對於螢幕中心(也即是我們的視點中心)是靜止的,所以我們仍然能看到hero在螢幕的中心。由於跟方法2一樣,所以不再重複,程式碼如下:
void GameLayer::update(float dt)
{
Layer::update(dt);
updatePosition(dt);
}
void GameLayer::updatePosition(float dt)
{
//英雄寬度、高度一半
float side=m_hero->getCenterToSide();
float bottom=m_hero->getCenterToBottom();
//map寬度
float tileMapWidth=m_backMap->getMapSize().width * m_backMap->getTileSize().width;
//限制英雄的移動範圍
float posX=GetMin(tileMapWidth - m_hero->getCenterToSide(),
GetMax(m_hero->getCenterToSide(),m_hero->getDesiredPosition().x));
float posY=GetMin(4* m_backMap->getTileSize().height+m_hero->getCenterToBottom(),
GetMax(m_hero->getCenterToBottom(),m_hero->getDesiredPosition().y));
m_hero->setPosition(ccp(posX,posY));
setViewPointCenter(m_hero->getPosition());
}
void GameLayer::setViewPointCenter(Point position)
{
//視窗大小
Size winSize=Director::getInstance()->getWinSize();
//地圖大小
Size mapSize=CCSizeMake(m_backMap->getMapSize().width * m_backMap->getTileSize().width,
m_backMap->getMapSize().height * m_backMap->getTileSize().height);
//讓hero與mainLayer的左右邊界的相對位置在半個螢幕間不滾動地圖,所以英雄與mainLayer的相對位置在[winSize.w/2,mapSize.w-winSize.w/2]的範圍才發生
//獲得鏡頭移動的範圍(即地圖會滾動的範圍)
float scrollX=GetMax(position.x,winSize.width/2);
scrollX=GetMin(scrollX, mapSize.width - winSize.width/2);
float scrollY=GetMax(position.y,winSize.height/2);
scrollY=GetMin(scrollY, mapSize.height - winSize.height/2);
//鏡頭移動(地圖滾動)時的座標範圍(在未執行是x,y是不確定值,所以算是範圍,當執行時x,y為定值,此時就是鏡頭相對於GameLayer世界移動的距離【如果鏡頭可以移動的話】)
Point cameraNeedMoveSize=ccp(scrollX,scrollY);
//鏡頭相對於Layer的座標位置
Point centerOfView=ccp(CENTER.x,CENTER.y);
//由於鏡頭是靜止的,所以移動Layer等同於移動鏡頭
Point viewPoint=ccpSub(centerOfView,cameraNeedMoveSize);
//設定Layer移動的目標座標
m_mainLayer->setPosition(viewPoint);
}
有問題的效果如圖:
http://my.csdn.net/my/album/detail/1767603
如果不使用cocos2d 3.0的box2d引擎的話我們一般都是使用方法2作為移動鏡頭的方法(雖然說是移動鏡頭,但實際鏡頭沒動,是相對移動,Layer移動了,鏡頭不動,就好比Layer不動,移動鏡頭)。但如果使用了box2d的話就不能使用上述移動Layer的方法了,由於Layer這個Layer世界跟物理引擎的物理世界是分開的,如果Layer世界的原點與物理世界的原點同在某一點處,繫結在hero圖片上的PhysicsBody的座標就完全跟hero圖片的座標一致,但如果只移動Layer世界,hero圖片也跟著移動,但物理世界沒有移動,由於PhysicsBody的座標是物理世界上的座標,所以就會出現hero圖片與PhysicsBody的紅色DebugDraw框脫離的現象。可惜PhysicsWorld暫時沒有移動整個物理世界的方法,不然在移動Layer世界的同時也跟著移動物理世界就可以解決這一問題了。
但現在我們是必須想要用到Box2d,也必須要讓hero圖片與physicsBody的位置同步,該怎麼辦?真的是騎虎難下,幸好還有一種方法。就是用Node::setPosition,由於Node的setPosition裡在設定它自己的位置之餘也設定繫結在它身上的body的位置,所以可以讓它們同步了。
由於上面只設定Layer的座標,hero精靈座標會改變而沒有改變body的座標,所以我們就不能設定Layer的座標,而改直接設定hero精靈的座標,即hero->setPosition,由於直接直接呼叫setPosition是會同時改變body在PhysicsWorld裡的座標的,所以就這種方法可行。
方法如下:
void GameLayer::update(float dt)
{
Layer::update(dt);
updatePosition(dt);
}
void GameLayer::updatePosition(float dt)
{
//獲得hero精靈寬度、高度的一半
float side=m_hero->getCenterToSide();
float bottom=m_hero->getCenterToBottom();
//獲得視窗大小
Size viewSize=Director::getInstance()->getVisibleSize();
//獲得背景地圖大小
Size mapSize=CCSizeMake(m_backMap->getMapSize().width * m_backMap->getTileSize().width,
m_backMap->getMapSize().height * m_backMap->getTileSize().height);
//限制英雄Y座標最高只能移動4格地圖
float map4GridH=m_backMap->getTileSize().height * 4;
//每幀移動的距離
Point subPos=m_hero->getVelocity() * dt;
//渴望到達的位置
Point desiredPos=ccpAdd(m_hero->getPosition(),subPos);
//限制英雄只能在視窗大小中移動
float heroActualX=GetMax(desiredPos.x,0);
heroActualX=GetMin(heroActualX,viewSize.width);
float heroActualY=GetMax(desiredPos.y,0);
heroActualY=GetMin(heroActualY,map4GridH);
m_hero->setPosition(ccp(heroActualX,heroActualY) );
//獲得英雄與地圖的相對位置(如果以地圖為靜止的且相對於世界座標、螢幕座標靜止,那麼此為英雄座標與世界座標原點的距離,如果想讓鏡頭跟隨這個距離,那麼鏡頭也應移動同樣的距離,所以也為相機需要移動的距離點)
Point cameraNeetMovePos=ccpSub(m_hero->getPosition(),m_backMap->getPosition() );
//從得到的鏡頭應移動距離設定給物體座標位置
setViewPointCenter(cameraNeetMovePos,subPos);
}
void GameLayer::setViewPointCenter(Point _carmNeetMovePos,Point _subPos)
{
//視窗大小
Size viewSize=Director::getInstance()->getVisibleSize();
//地圖大小
Size mapSize=CCSizeMake(
m_backMap->getTileSize().width * m_backMap->getMapSize().width,
m_backMap->getTileSize().height * m_backMap->getMapSize().height
);
//限定鏡頭移動的範圍(即限制滾動範圍)
float scrollX=GetMax(_carmNeetMovePos.x,viewSize.width/2);
scrollX=GetMin(scrollX,mapSize.width - viewSize.width/2);
float scrollY=GetMax(_carmNeetMovePos.y,viewSize.height/2);
scrollY=GetMin(scrollY,mapSize.height - viewSize.height/2);
//鏡頭從零點到目標點需要移動的距離(程式未執行時是範圍,執行時x、y都確定,所以為距離),如果鏡頭可移動
Point cameraNeetMoveSize=ccp(scrollX,scrollY);
//現實中鏡頭不能移動,要設定個相對運動,讓地圖實行相反運動,就等同於鏡頭運動(運動是相對的)
Point staticCameraPos=ccp(viewSize.width/2,viewSize.height/2);
//獲得地圖需相對鏡頭移動的實際位置
//這句主要是根據鏡頭的移動範圍限制地圖的移動範圍
//cameraNeetMoveSize為鏡頭相對於GameLayer世界需移動的距離,即鏡頭需移動這個距離才能讓英雄處於中間位置,且這個相對於世界的移動距離受到不露出其他背景的安全限制,因為我們只關注地圖內的事情。
//ccpSub(staticCameraPos,cameraNeetMoveSize)就是獲得認為鏡頭為靜止物,地圖相對於鏡頭的安全移動距離中的實際位置
Point mapActualPos=ccpSub(staticCameraPos,cameraNeetMoveSize);
//x軸滾屏階段
//因為map在cameraNeetMovePos.x<=viewSize.width/2和cameraNeetMovePos.x >=(mapSize.width-viewSize.width/2) 不移動的,由於已經限制了不移動,所以只需關心滾屏範圍。
if (cameraNeetMoveSize.x>viewSize.width/2 && cameraNeetMoveSize.x <(mapSize.width-viewSize.width/2) )
{
//map移動的步伐,傳進來的_subPos為英雄移動的步伐,map移動的步伐與hero的相反,所以乘於-1
Point minusSubPos=ccpMult(_subPos,-1);
//獲得map相對於GameLayer的實際位置
mapActualPos=ccpAdd(m_backMap->getPosition(),minusSubPos);
//由於map.height與視窗一樣大,map不需移動即不需要y軸滾屏,所以相對於GameLayer的Y座標就直接設定為0
mapActualPos=ccp(mapActualPos.x,0);
//要讓英雄處於螢幕中間,如果移動GameLayer的話,map會移動,hero也同時跟著移動,由於不能運動GameLayer,所以英雄需要左移map左移的步伐,以模擬移動整個GameLayer的運動
//因不需要滾縱座標的屏,所以hero也不會受到縱座標的滾屏的力
Point heroSubPos=ccp(minusSubPos.x,0);
m_hero->setPosition(ccpAdd(m_hero->getPosition(),heroSubPos));
}
m_backMap->setPosition(mapActualPos);
}
效果如圖:
相關文章
- 移動端滾動穿透問題解決方案穿透
- 移動端滾動穿透問題完美解決方案穿透
- 初次使用cocos2d 3.0 的box2d引擎詳解
- 解決移動裝置上iframe滾動條的問題
- 移動端頁面滾動--解決方法
- 如何解決移動端滾動穿透問題穿透
- 解決移動端滾動穿透穿透
- 移動端滾動穿透解決方案穿透
- 【前端詞典】滾動穿透問題的解決方案前端穿透
- 解決小程式遮罩層滾動穿透問題遮罩穿透
- 解決移動端複製問題
- ios手機豎屏拍照圖片旋轉90°問題解決方法iOS
- css3實現的相機鏡頭效果CSSS3
- 關於移動端小圖示模糊問題的解決方法教程
- VNC FOR AIX 灰屏的問題解決VNCAI
- Vue 使用 Devextreme框架,下拉框不會隨頁面的滾動而移動的問題解決VuedevREM框架
- 日立開發無鏡頭相機 無鏡頭技術或會用於手機上
- 解決vue移動端適配問題Vue
- 移動APP卡頓問題解決實踐APP
- 移動端適配問題解決方案
- 適配移動端的問題以及解決方案
- 移動端觸屏拖動頁面滾動效果
- 移動端相容性問題解決方案
- 移動端點透問題及其解決方案
- 移動端彈出層滾動穿透問題總結穿透
- listView懶載入解決快速拖動卡屏問題View
- 遇到問題的解決方法
- Ubuntu 16.04 Vysor 破解 和黑屏問題解決+ 閃屏問題解決Ubuntu
- react滾動屏React
- Linux啟動問題解決方法(轉)Linux
- 解決切換Fragment的黑色閃屏問題Fragment
- 工業智慧相機鏡頭選型要點分析
- 移動端相容性問題解決方案(一)
- 移動端彈窗滾動時window窗體也一起滾動的解決辦法
- 英特爾釋出驅動更新,解決Win10藍屏當機問題Win10
- 【移動端相容問題研究】javascript事件機制詳解(涉及移動相容)JavaScript事件
- 移動機器人相機模型:從相機移動到二維影像機器人模型
- SERVICE問題解決方法