小伙伴说这个撕胶带游戏很火很解压,于是我连夜做了一个Cocos教程...

image

引言

哈喽大家好,我是亿元程序员。

有个小伙伴发给我一段视频,是一个解压游戏:一个方方正正的快递盒,上面贴着几条封箱胶带,点哪条,哪条就从边上卷起来消失:

我当时就觉得这个效果,挺有意思!

然后我就打开了Cocos Creator……

然后就踩了不少坑。

不过最终效果还挺不错的,今天就带大家完整走一遍,看看一个3D撕胶带效果是怎么在Cocos从零搭起来的

本文源工程在文末获取,小伙伴们自行前往。

先看效果

效果就是这样:

  • 拖拽可以360°旋转正方体
  • 胶带可以跨越多个面(比如从正面绕到侧面)
  • 点击胶带,从你点的那头开始卷,带着一个小圆柱滚轴
  • 卷完就消失

话不多说,直接开干。

第一步:搭场景

在Cocos Creator里新建一个3D项目,场景很简单,核心层级只有这几个节点:

Scene
├── Main Light       ← 打一盏平行光,别让正方体全黑
├── Main Camera      ← 斜上方俯视,位置 (-10, 10, 10)
└── CubeRoot         ← 关键!所有旋转都作用在这里
    ├── Cube         ← 内置正方体,scale 改成 (3,3,3)
    └── TapeStrips   ← 所有胶带的容器节点

这里有个小细节值得说一下:

为什么要多一个 CubeRoot,不直接在 Cube 上旋转?

因为胶带也要跟着转。

如果直接旋转 Cube,胶带节点不是 Cube 的子节点,就会留在原地不动,立方体转走了,胶带还飘在空中,很尴尬。

Cube 和所有胶带都挂在 CubeRoot 下,旋转 CubeRoot,全部一起动,省心。

第二步:拖拽旋转(踩坑警告)

新建 CubeRotate.ts,挂在 CubeRoot 上。

思路很直接:监听鼠标拖拽 → 计算每帧位移 → 转成旋转叠加上去。

第一版我这么写的:

image

跑起来一看……

斜向拖的时候,感觉不对,像在跟自己较劲。

原因是:左右拖死死绑在世界Y轴,上下拖死死绑在世界X轴,完全无视当前正方体的朝向。

正确的旋转方式叫Trackball(轨迹球)。

想象你的屏幕后面有个大球,手指贴在球面上,往哪拨球就往哪转——旋转轴始终垂直于你的拖拽方向

推导也不复杂:

拖拽向量:(dx, dy)
垂直方向:(-dy, dx)   ← 数学上逆时针旋转90°

映射到世界空间:
旋转轴 = -dy × 相机右方向 + dx × 相机上方向

代码改成这样:

image

改完之后:

流畅多了,斜拖也完全正常。

小知识:四元数左乘 vs 右乘

next = rot × cur(左乘)→ 在世界空间叠加旋转,轴不随物体转动

next = cur × rot(右乘)→ 在局部空间叠加旋转,轴跟着物体走

做Trackball用左乘,因为旋转轴是相机空间的,属于"世界视角"。

第三步:画胶带

这是整个项目最有意思的部分。

胶带不是一张图贴上去,而是用代码生成的 3D 网格

1.胶带的本质:Quad Strip

每条胶带在代码里先定义一条"中线"(若干个控制点,不知道大家是否还记得绳子纹理那期),然后向两侧各展开半个宽度,得到左右两列顶点,连成三角形条带:

中线:  P0 ──── P1 ──── P2

展开:  L0      L1      L2    ← 左边缘
        R0      R1      R2    ← 右边缘

三角形:(L0,R0,L1), (L1,R0,R1), ...

关键是怎么算"向两侧展开的方向":

image

这里normal是胶带所在面的朝外法线,_dir是沿中线走向的切线。叉积得到的向量一定在面的平面内,且垂直于走向——这就是胶带的宽度方向。

2.跨面胶带怎么处理?

跨面的胶带是这个项目最难的几何问题。

比如这条从正面绕到右面的胶带,我在棱的位置手动加了两个"转折点",同时切换法线方向:

image

两个转折点坐标只差了一个 D(0.002),中间生成一个极短的过渡quad,视觉上完全看不出缝隙。

3.最终效果如下

第四步:点击卷起(最硬核的部分)

胶带是自定义网格,引擎没有现成的点击事件,需要自己做点击检测。

1.怎么知道点到哪条胶带了?

用射线-三角形求交。

原理:从相机位置,穿过鼠标点击的屏幕坐标,发出一根射线,看这根射线和哪条胶带的三角形相交。

三步走:

第一步,用相机把屏幕坐标转成世界空间射线:

image

第二步,把射线变换到 CubeRoot 的局部空间。

**为什么要变换?**因为胶带顶点是在 CubeRoot 局部空间定义的,正方体旋转后,世界坐标变了,但局部坐标没变。射线和三角形必须在同一个坐标空间里才能做运算。

image

第三步,用算法对每个三角形做射线求交:

image

遍历所有三角形,有一个相交就判定命中。

2.从哪头开始卷?

靠近哪头就从哪头卷。

计算点击射线到胶带两个端点的距离:

image

哪头更近就从哪头卷:

image

3.让胶带逐渐变短

每帧根据动画进度,截取剩余的中线,重新生成网格:

image

4.卷轴圆柱

圆柱要跟着剥离点走,并且越卷越大。

圆柱网格只创建一次,用 setScale 来控制实际尺寸。

这样每帧只需改scaleposition,不需要重建网格:

最终效果

所有问题解决之后,效果如下:

旋转流畅、跨面胶带自然、点近头从近头卷、圆柱跟随移动。

代码结构回顾

整个项目只有 4 个脚本文件,职责非常清晰:

文件 职责 一句话说清楚
CubeRotate.ts 输入 → 旋转 监听拖拽,Trackball算法旋转CubeRoot
TapeGeometry.ts 数据 → 几何 纯函数,中线控制点 → 三角形顶点数组
TapeManager.ts 配置 → 场景 定义每条胶带的形状和颜色,创建节点
TapeStrip.ts 交互 → 动画 点击检测 + 卷起动画 + 圆柱滚轴

整体数据流:

结语

不忘初心,保持更新。

这个Demo虽然功能简单,但涉及到了3D游戏开发中几个非常通用的技术点:

  • Trackball旋转(任何需要拖拽旋转3D物体的场景都能用)
  • 程序化Mesh生成(胶带、绳子、轨迹线、流体表面都是这个思路)
  • 射线-三角形求交3D点击检测的通用方案)
  • 坐标空间变换(这是理解3D编程的核心概念)

那么问题来了:如果要做成完整的游戏,纸皮效果、关卡编辑,应该怎么做? 欢迎评论区聊聊。

本文完整工程源码可通过点赞后私信发送"撕胶带"获取。


我是"亿元程序员",一位有着8年游戏行业经验的主程。在游戏开发中,希望能给到您帮助,也希望通过您能帮助到大家。

实不相瞒,想要个在看!请把该文章分享给你觉得有需要的其他小伙伴,谢谢!

推荐文章:

亿元Cocos小游戏实战合集2.0

亿元Cocos小游戏实战合集1.0

老板说最近这款游戏很火让我抄,可是我连玩都玩不明白…

这款值68亿的游戏,你不实战一下吗?安排!

小伙伴说我的拼图游戏用Mask不能合批…

俄罗斯方块谁不会做…啊?流沙版?

最近很火的一个拼图游戏,老板让我用Cocos3.8做一个…

老板说拼图游戏太卷了,让我用Cocos做个3d版本的…

敢不敢挑战用Cocos3.8复刻曾经很火的割绳子游戏?

8赞

第一!!!!

昨天刚看到公众号文章

强死了!!

看到这个标题,就猜到是你了

撕胶带 插个眼

高产!!!

吊吊吊!!!

人们请求我,“拜托,亿元先生,你拆解了这么多项目,我们都受不了了”。“我们的cocos论坛再次赢了。事实上,我们赢得太多,多到都不知道该怎么办”。

有必要支持你一下

绝了!!!!

在一些不规则物体上 是不难度有点高 :joy: