***************************************************************************************
時間:2015-04-10
作者:Sharing_Li
轉載註明出處:http://blog.csdn.net/sharing_li/article/details/44980493
***************************************************************************************
最近寫一款家鄉的牌類遊戲,自己玩玩,裏面涉及到撲克牌的翻牌效果,這裏簡單來說一下:
一說到翻轉,我馬上想到了OrbitCamera這個傢伙,雖然很少用,但知道它有這個效果,但代碼寫到一半發現事實情況是這樣的:
我本意是打算將撲克牌背面轉到90°,也就是與屏幕垂直,這個時候,再把撲克牌正面從90°位置轉到0°即正面。但是同樣是轉到90°,各個背面的顯示不一樣。代碼如下:
for (int j = 1; j <= 16; j++) {
m_cardBg.at(j - 1)->runAction(Sequence::create(DelayTime::create(1)
,OrbitCamera::create(0.05 + j / 20.0, 1, 0, 0, 90, 0, 0),NULL));
}
納悶,然後上網查了查文檔,說OrbitCamera是沿屏幕中心進行球面軌跡的旋轉,乍一看不知道說的啥,OrbitCamera::create函數的參數也很多,然後分析了一下,好像知道了個三二一,見下圖:
所謂的旋轉90°也就是垂直球面指向球心。“上有政策,下有對策”,我這裏弄了一個勉強的解決方案,也就是當動畫運行到上圖停止時,隱藏撲克牌背面,讓事先隱藏(先用orbitcamera旋轉90°再隱藏)的撲克牌正面圖片顯示出來,然後向相反的方向調用orbitcamera旋轉90°。看效果圖:
在看看代碼實現:
for (int j = 1; j <= 16; j++) {
m_cardBg.at(j - 1)->runAction(Sequence::create(DelayTime::create(1)
,OrbitCamera::create(0.05 + j / 20.0, 1, 0, 0, 90, 0, 0)
,Hide::create()
,CallFunc::create([=]
{
m_cardVec.at(j - 1)->runAction(Sequence::create(
Show::create(),
OrbitCamera::create(0.05 + j / 20.0, 1, 0, 90, -90, 0, 0), NULL));
}),NULL));
m_cardVec.at(j - 1)->runAction(OrbitCamera::create(0.02, 1, 0, 0, 90, 0, 0));
}
基本上有模有樣了,這就是第一種方法。第二種方法其實也是用的orbitcamera實現的,勉強湊個數。先看下效果圖:
可以感覺的到,效果比之前的更流暢。並且只需在之前的代碼基礎上加上一行代碼。第一種方法弄好後,我總感覺可以再優化,剛開始打算這樣弄,以中間的一張圖爲比較對象,將每一張圖的中點,球心,和中間一張圖的中心,構成三角形,比較tan值,從而設置其要旋轉的角度,但是最後失敗了,效果不佳,放棄了。最後,我打算進入到OrbitCamera的源代碼看看。最後看到OrbitCamera::update函數中調用了一個setEye函數,然後突然想到之前第二張分析圖裏的箭頭都指向球心,感覺就像許多眼睛都盯着球心那個地方一樣。然後繼續跟蹤:setEye—》ActionCamera::updateTransform—》Node::setAdditionalTransform,然後發現這句代碼:_transformUpdated = _transformDirty = _inverseDirty =true;這個跟對象的繪製有關,然後在Node.cpp裏找到visit函數,發現跟director在搞鬼,然後繼續跳到director,然後真相大白了。見如下代碼:
/**
* @brief Possible OpenGL projections used by director
*/
enum class Projection
{
/// Sets a 2D projection (orthogonal projection).
_2D,
/// Sets a 3D projection with a fovy=60, znear=0.5f and zfar=1500.
_3D,
/// It calls "updateProjection" on the projection delegate.
CUSTOM,
/// Default projection is 3D projection.
DEFAULT = _3D,
};
void Director::setProjection(Projection projection)
{
Size size = _winSizeInPoints;
setViewport();
switch (projection)
{
case Projection::_2D:
{
loadIdentityMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION);
#if CC_TARGET_PLATFORM == CC_PLATFORM_WP8
if(getOpenGLView() != nullptr)
{
multiplyMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION, getOpenGLView()->getOrientationMatrix());
}
#endif
Mat4 orthoMatrix;
Mat4::createOrthographicOffCenter(0, size.width, 0, size.height, -1024, 1024, &orthoMatrix);
multiplyMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION, orthoMatrix);
loadIdentityMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
break;
}
case Projection::_3D:
{
float zeye = this->getZEye();
Mat4 matrixPerspective, matrixLookup;
loadIdentityMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION);
#if CC_TARGET_PLATFORM == CC_PLATFORM_WP8
//if needed, we need to add a rotation for Landscape orientations on Windows Phone 8 since it is always in Portrait Mode
GLView* view = getOpenGLView();
if(getOpenGLView() != nullptr)
{
multiplyMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION, getOpenGLView()->getOrientationMatrix());
}
#endif
// issue #1334
Mat4::createPerspective(60, (GLfloat)size.width/size.height, 10, zeye+size.height/2, &matrixPerspective);
multiplyMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION, matrixPerspective);
Vec3 eye(size.width/2, size.height/2, zeye), center(size.width/2, size.height/2, 0.0f), up(0.0f, 1.0f, 0.0f);
Mat4::createLookAt(eye, center, up, &matrixLookup);
multiplyMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION, matrixLookup);
loadIdentityMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
break;
}
case Projection::CUSTOM:
// Projection Delegate is no longer needed
// since the event "PROJECTION CHANGED" is emitted
break;
default:
CCLOG("cocos2d: Director: unrecognized projection");
break;
}
_projection = projection;
GL::setProjectionMatrixDirty();
_eventDispatcher->dispatchEvent(_eventProjectionChanged);
}
然後我在初始化的時候加入下面的代碼,問題就解決了:Director::getInstance()->setProjection(cocos2d::DisplayLinkDirector::Projection::_2D);
記得動畫播放完之後,要還原到默認值,也就是_3D,以免對其他部分的顯示造成影響。最後看看第三種方法,用到了ScaleTo,大家可能疑惑爲什麼這個縮放函數可以進行旋轉,當我們對精靈進行如下操作的時候,setScaleX(-1),作用是以Y軸爲對稱軸反轉,爲什麼呢,因爲-1就是從相反的方向進行縮放,如果是-2,就是從相反的方向縮放兩倍。那麼同理,ScaleTo就可以有翻牌的效果啦。看如下用ScaleTo實現的效果圖:
代碼如下:
for (int j = 1; j <= 16; j++) {
m_cardBg.at(j - 1)->runAction(Sequence::create(DelayTime::create(1)
,ScaleTo::create(0.05 + j / 20.0, -1,1),
Hide::create()
,CallFunc::create([=]
{
m_cardVec.at(j - 1)->runAction(Sequence::create(
Show::create(),
ScaleTo::create(0.05 + j / 20.0, -1,1), NULL));
}),NULL));
m_cardVec.at(j - 1)->setFlippedX(true);
}
到這裏結束了,下載資源