cocos2dx实现翻书效果。
因为项目需求,需要使用cocos实现3d翻书的效果,刚开始确实没有什么思路,cocos2d做3d的效果这不是开玩笑吗。但是,再难也得做啊,没办法。
开始查资料,在百度,google上搜索了好几天,基本把所有的文章都翻遍了,根本没有人实现过,好吧,我承认有点虚了,这可咋办。
后来想到cocos2dx的例子里有个类似的效果,不过那个是个action,后来看了看感觉还可以,只是效果和我的需求有点差异,但终归是找到了实现的思路,于是开始看cocos2dx的源码,主要用到的是cocos的网格动画,涉及到以下几个文件:
CCGrid.h /cpp :网格数据及渲染,包括基本网格数据和3D网格数据,这是数据的基础。
CCActionGrid.h /cpp :网格基本动画,这是动画的基础。
CCActionGrid3D.h/cpp: 3D网格基本动画,这是3D网格动画的基础。
CCActionTiledGrid.h / cpp :网格衍生动画,这是最终的特效实现。
咱们这里主要用到CCGrid.h进行渲染,其他三个是action的实现。
这里插一句,在此之前,我曾经还想用遮罩去实现类似的翻书效果,因为android上类似效果的实现就是实用遮罩的,可是在cocos中这种方法行不通,最终放弃了。
看CCActionPageTurn3d这个类,cocos的翻页效果的实现就是在这个类中实现的,在这个类中有个update()函数:
1 /* 2 3 * Update each tick 4 5 * Time is the percentage of the way through the duration 6 7 */ 8 9 voidCCPageTurn3D::update(float time) 10 11 { 12 13 float tt =MAX(0, time -0.25f); 14 15 float deltaAy = (tt * tt *500); 16 17 float ay = -100 - deltaAy; 18 19 20 21 float deltaTheta = - (float)M_PI_2 *sqrtf( time) ; 22 23 float theta =/*0.01f */ + (float)M_PI_2 +deltaTheta; 24 25 26 27 float sinTheta =sinf(theta); 28 29 float cosTheta =cosf(theta); 30 31 32 33 for (int i =0; i <=m_sGridSize.width; ++i) 34 35 { 36 37 for (int j =0; j <=m_sGridSize.height; ++j) 38 39 { 40 41 // Get original vertex 42 43 ccVertex3F p = originalVertex(ccp(i ,j)); 44 45 46 47 float R = sqrtf((p.x * p.x) + ((p.y - ay) * (p.y - ay))); 48 49 float r = R * sinTheta; 50 51 float alpha = asinf( p.x / R ); 52 53 float beta = alpha / sinTheta; 54 55 float cosBeta = cosf( beta ); 56 57 58 59 // If beta > PI then we've wrapped around the cone 60 61 // Reduce the radius to stop these points interfering with others 62 63 if (beta <= M_PI) 64 65 { 66 67 p.x = ( r *sinf(beta)); 68 69 } 70 71 else 72 73 { 74 75 // Force X = 0 to stop wrapped 76 77 // points 78 79 p.x =0; 80 81 } 82 83 84 85 p.y = ( R + ay - ( r * (1 - cosBeta) * sinTheta)); 86 87 88 89 // We scale z here to avoid the animation being 90 91 // too much bigger than the screen due to perspective transform 92 93 p.z = (r * (1 - cosBeta ) * cosTheta) /7;// "100" didn't work for 94 95 96 97 // Stop z coord from dropping beneath underlying page in a transition 98 99 // issue #751 100 101 if( p.z < 0.5f ) 102 103 { 104 105 p.z =0.5f; 106 107 } 108 109 110 111 // Set new coords 112 113 setVertex(ccp(i, j), p); 114 115 116 117 } 118 119 } 120 121 }
刚开始看这个,完全不知所云,一堆三角函数,,简直了,后来找了好久,找到一篇论文
正好把源码解释得一清二楚,论文的链接为:http://www.parc.com/content/attachments/turning-pages-3D.pdf |
看完之后才明白,原来是建立了一个数学模型,通过数学模型去计算每个顶点在变换过程中的位置,明白了。到现在才理解大学时老师说的那句话,数学对编程来说很重要。
回到主题,我们要实现的是类似翻书的效果,通过上面的算法,得到的效果和我的需求有点差异,既然他是想象成一个锥体的运到,我们可以想象成一个圆柱体的运到,通过改变圆柱体轴心的位置,来实现翻动的效果,不多说了,直接上源码:
1 voidPageTurn::calculateHorizontalVertexPoints(float offsetX) 2 3 { 4 5 float theta = (GLfloat)(M_PI /6.0f); 6 7 float R =50; 8 9 float b = (m_pBgSprite->getContentSize().width - offsetX *1.4f) *sinf(theta); 10 11 12 13 14 15 16 17 for (int i =0; i <=m_sGridSize.width; ++i) 18 19 { 20 21 for (int j =0; j <=m_sGridSize.height; ++j) 22 23 { 24 25 // Get original vertex 26 27 ccVertex3F p =originalVertex(ccp(i ,j),m_pForeSprite); 28 29 30 31 float x = (p.y + b) / tanf(theta); 32 33 34 35 float pivotX = x + (p.x - x) * cosf(theta) * cosf(theta); 36 37 float pivotY = pivotX * tanf(theta) - b; 38 39 40 41 float l = (p.x - pivotX) / sinf(theta); 42 43 float alpha = l / R; 44 45 if (l >= 0) { 46 47 if (alpha > M_PI) { 48 49 p.x = (GLfloat)(mHOffsetX + pivotX - R * (alpha -M_PI) *sinf(theta)); 50 51 p.y = (GLfloat)(mHOffsetY + pivotY + R * (alpha -M_PI) *cosf(theta)); 52 53 p.z = (GLfloat)(2 * R /9); 54 55 } 56 57 else if (alpha <= M_PI) 58 59 { 60 61 p.x = (GLfloat)(mHOffsetX + pivotX + R *sinf(alpha) *sinf(theta)); 62 63 p.y = (GLfloat)(mHOffsetY + pivotY - R *sinf(alpha) *cosf(theta)); 64 65 p.z = (GLfloat)((R - R *cosf(alpha))/9); 66 67 } 68 69 } 70 71 else 72 73 { 74 75 p.x +=mHOffsetX; 76 77 p.y +=mHOffsetY; 78 79 } 80 81 82 83 // Set new coords 84 85 setVertex(ccp(i, j), p,m_pForeSprite); 86 87 88 89 90 91 } 92 93 } 94 95 96 97 for (int i =0; i <=m_sGridSize.width; ++i) 98 99 { 100 101 for (int j =0; j <=m_sGridSize.height; ++j) 102 103 { 104 105 // Get original vertex 106 107 ccVertex3F p =originalVertex(ccp(i ,j),m_pBgSprite); 108 109 float x = (p.y + b) / tanf(theta); 110 111 112 113 float pivotX = x + (p.x - x) * cosf(theta) * cosf(theta); 114 115 float pivotY = pivotX * tanf(theta) - b; 116 117 118 119 float l = (p.x - pivotX) / sinf(theta); 120 121 float alpha = l / R; 122 123 if (l >= 0) { 124 125 if (alpha > M_PI) { 126 127 p.x = (GLfloat)(mHOffsetX + pivotX - R * (alpha -M_PI) *sinf(theta)); 128 129 p.y = (GLfloat)(mHOffsetY + pivotY + R * (alpha -M_PI) *cosf(theta)); 130 131 p.z = (GLfloat)(2 * R /9); 132 133 } 134 135 else if (alpha <= M_PI) 136 137 { 138 139 p.x = (GLfloat)(mHOffsetX + pivotX + R *sinf(alpha) *sinf(theta)); 140 141 p.y = (GLfloat)(mHOffsetY + pivotY - R *sinf(alpha) *cosf(theta)); 142 143 p.z = (GLfloat)((R - R *cosf(alpha))/9); 144 145 } 146 147 } 148 149 else 150 151 { 152 153 p.x +=mHOffsetX; 154 155 p.y +=mHOffsetY; 156 157 } 158 159 160 161 setVertex(ccp(i, j), p,m_pBgSprite); 162 163 164 165 166 167 168 169 } 170 171 } 172 173 174 175 // float R2 = 50; 176 177 // float offsetX2 = mTouchBegin.x - pTouch->getLocation().x; 178 179 // float pivotX2 = m_pForeSpriteVertical->getContentSize().height - offsetX2; 180 181 // 182 183 // 184 185 // for (int i = 0; i <= m_sGridSize.width; ++i) 186 187 // { 188 189 // for (int j = 0; j <= m_sGridSize.height; ++j) 190 191 // { 192 193 // // Get original vertex 194 195 // ccVertex3F p = originalVertex(ccp(i ,j),m_pForeSpriteVertical); 196 197 // float l = p.x - pivotX2; 198 199 // float alpha = l / R2; 200 201 // if (l >= 0) { 202 203 // if (alpha > M_PI) { 204 205 // p.x = mVOffsetX + pivotX2 - R2 * (alpha - M_PI); 206 207 // p.z = 2 * R2 / 9; 208 209 // p.y = p.y + mVOffsetY; 210 211 // } 212 213 // else if (alpha <= M_PI) 214 215 // { 216 217 // p.x = mVOffsetX + pivotX2 + R2 * sinf(alpha); 218 219 // p.z = (R2 - R2 * cosf(alpha))/9; 220 221 // p.y = p.y + mVOffsetY; 222 223 // } 224 225 // } 226 227 // else 228 229 // { 230 231 // p.x = p.x + mVOffsetX; 232 233 // p.y = p.y + mVOffsetY; 234 235 // } 236 237 // 238 239 // 240 241 // // Set new coords 242 243 // setVertex(ccp(i, j), p,m_pForeSpriteVertical); 244 245 // 246 247 // 248 249 // } 250 251 // } 252 253 // 254 255 // for (int i = 0; i <= m_sGridSize.width; ++i) 256 257 // { 258 259 // for (int j = 0; j <= m_sGridSize.height; ++j) 260 261 // { 262 263 // // Get original vertex 264 265 // ccVertex3F p = originalVertex(ccp(i ,j),m_pBgSpriteVertical); 266 267 // float l = p.x - pivotX2; 268 269 // float alpha = l / R2; 270 271 // if (l >= 0) { 272 273 // if (alpha > M_PI) { 274 275 // p.x = mVOffsetX + pivotX2 - R2 * (alpha - M_PI); 276 277 // p.z = 2 * R2 / 9; 278 279 // p.y = p.y + mVOffsetY; 280 281 // } 282 283 // else if (alpha <= M_PI) 284 285 // { 286 287 // p.x = mVOffsetX + pivotX2 + R2 * sinf(alpha); 288 289 // p.z = (R2 - R2 * cosf(alpha))/9; 290 291 // p.y = p.y + mVOffsetY; 292 293 // } 294 295 // } 296 297 // else 298 299 // { 300 301 // p.x = p.x + mVOffsetX; 302 303 // p.y = p.y + mVOffsetY; 304 305 // } 306 307 // 308 309 // // Set new coords 310 311 // setVertex(ccp(i, j), p,m_pBgSpriteVertical); 312 313 // 314 315 // 316 317 // 318 319 // } 320 321 // } 322 323 }
3d节点的渲染我用的是CCGridBase的子类CCGrid3D,只需要把CCGrid3DAction中渲染的部分摘出来即可,代码在cocos中都有(补充下,我用的是cocos2dx2.2.6),以下是最终效果: