chore:文档补全
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
# KRxGwTZ0miRVMMk0fjPcIVv7nge
|
||||
|
||||
Source: https://dianchukeji.feishu.cn/wiki/KRxGwTZ0miRVMMk0fjPcIVv7nge
|
||||
|
||||
|
||||
# 一.工作内容概述
|
||||
- 碎片收集系统数据框架、UI初步搭建
|
||||
- 实现碎片拼图效果绘制逻辑
|
||||
- 基于昨日进度,继续尝试GPT-Image2生图
|
||||
# 二.碎片拼图效果实现
|
||||
## 预期效果
|
||||
- 碎片海报应当呈现接近真实拼图的效果,海报外边缘保持平直,内部碎片边缘呈现凹凸互补,而不是简单的矩形切图。
|
||||
- 每张海报会根据配置被切分成不同数量的拼图碎片,例如 4x6、5x6、9x15 等。
|
||||
- 不同切分数量下,碎片形状可以不同,但海报内容必须保持连续。同一张图片在同一归一化位置上的采样内容应保持一致,不会因为碎片数量变化导致整张图案整体偏移或每片独立拉伸。
|
||||
- 轮廓线只用于表达“未收集区域”的拼图形状,不干扰已经收集到的海报内容。
|
||||
- 已填充碎片不显示轮廓线,避免完整海报画面被线条切碎。
|
||||
- 玩家已经收集到的区域应当更像真实图片,而不是一堆被描边的小块。未填充碎片继续显示拼图轮廓,用来提示剩余空位的位置和形状。
|
||||
- 轮廓线应当粗细稳定、透明度一致。相邻未填充碎片之间的共享边只绘制一次,避免左右或上下两个碎片重复描边,导致线条叠加变粗、变亮。
|
||||
- 新获得并分配的碎片不会直接瞬间出现在海报上,而是通过飞入动画表现“获得并填入”的过程。
|
||||
- 动画开始前,目标位置仍然显示为空位轮廓。系统会创建一个临时飞行碎片,让它从碎片入口或广告按钮附近飞向目标海报位置。
|
||||
- 飞行中的碎片使用与目标位置一致的拼图形状和海报贴图内容,避免出现飞行时和落地后视觉不一致的问题。当碎片飞到目标位置后,临时飞行节点会被移除,目标位置正式显示为已收集碎片,同时该位置的轮廓线消失。
|
||||
- 整体流程为:碎片飞向海报 → 填入空位 → 海报内容恢复
|
||||
## 实现思路
|
||||
### 1. 拼图视觉表现
|
||||
拼图渲染的核心由 PuzzlePosterRenderer 和 PuzzleShapeGenerator 配合完成。
|
||||
- PuzzleShapeGenerator 负责生成碎片形状。系统会根据海报的行列数和 seed 生成一张边缘表。海报外边界固定为平边,内部边缘随机生成凸起或凹槽。相邻碎片读取同一条边,但方向相反,因此可以保证凹凸关系互补。
|
||||
- 每个碎片最终会被转换成一组多边形点。平边直接连接,凸起和凹槽则通过曲线采样生成。为了交给 Mesh 稳定渲染,系统还会把这些拼图多边形拆成三角形索引,再交给 Mesh 渲染。为了避免大量节点,已收集碎片不会每块创建一个节点,而是会遍历整张海报的所有碎片,只处理已经收集并且当前没有隐藏的碎片。每个碎片会贡献三类数据:
|
||||
- 顶点位置:决定碎片画在海报中的哪里。
|
||||
- UV 坐标:决定这个顶点从海报原图的哪里取颜色。
|
||||
- 三角形索引:决定这些顶点如何组成可绘制的面。
|
||||
所有已收集碎片的数据会被合并进同一个 Mesh。这样一张海报只需要一个主要 MeshRenderer,就能显示多个碎片,避免碎片节点过多可能带来的性能问题。
|
||||
之前曾按“每块碎片自己的外接范围”做归一化,也就是每片碎片都把自己的形状单独铺满一个格子的贴图区域。这个方案会导致每片都被独立拉伸,文字和线条很容易错位。
|
||||
现在改为按海报全局位置计算 UV。可以理解为:先确定当前碎片顶点在整张海报网格中的真实位置,再换算到原图上的对应位置。
|
||||
核心思路类似:
|
||||
- col / row 表示当前碎片位于第几列、第几行。
|
||||
- sourceCellW / sourceCellH 表示原图中每个格子占多少宽高。
|
||||
- point.x / point.y 表示碎片边缘上的这个点相对格子中心偏移了多少。
|
||||
- 合起来就是这个点在整张海报原图中的真实采样位置。
|
||||
这样做以后,凸起部分如果几何上伸到相邻区域,就会采到原图中对应位置的内容,而不是被强行压回自己格子内部。文字、线条和图案因此可以跨碎片保持连续。
|
||||
### 2. 轮廓线表现
|
||||
- 轮廓线由 PuzzlePosterRenderer._drawOutline() 负责绘制,它和已收集碎片 Mesh 是分开的:海报图片内容是一层,轮廓线是另一层。这样系统可以独立决定哪些碎片显示图片,哪些位置显示轮廓。
|
||||
- 当前轮廓规则是:如果某个位置已经收集并且不是动画隐藏状态,就不画轮廓;如果未收集,或者新分配碎片正在飞入、目标位置还处于隐藏状态,就继续画轮廓。这样可以保证已收集区域尽量干净,未收集区域仍然有空位提示。
|
||||
- 如果对每个未收集碎片完整画一圈,相邻两个未收集碎片之间的共享边会被画两次。因为轮廓是半透明颜色,两次叠加后就会变粗、变亮。为了解决这个问题,PuzzleShapeGenerator 增加了按边生成轮廓点的能力。系统可以拿到某个碎片的上、右、下、左四条边,而不是只能拿到完整闭合轮廓。绘制时采用统一规则:
|
||||
- 每个需要显示轮廓的碎片固定画上边和左边。
|
||||
- 右边只有在右侧不是未收集碎片时才画。
|
||||
- 下边只有在下方不是未收集碎片时才画。
|
||||
### 3. 飞入动画
|
||||
飞入动画由 PuzzleCollectionPanelCtrl协调,PuzzlePosterRenderer提供目标位置和临时碎片节点。
|
||||
- 当系统分配到新碎片后,会得到一组分配结果,里面包含目标海报、行、列和对应 key。面板控制器会先把这些 key 放进_hiddenAllocatedKeys。这个隐藏集合的作用是:数据上这些碎片已经分配了,但在动画完成前,主海报 Mesh 暂时不画它们。这样目标位置仍然显示为空位轮廓,给飞入动画留出视觉空间。
|
||||
- 随后 PuzzleCollectionPanelCtrl._playAllocationAnimation() 会按一定间隔播放多个碎片飞行动画。每个碎片由_playOneAllocationFly() 处理。
|
||||
- 单个飞行动画大致分为几步:
|
||||
- createFlyingPieceNode()方法,不是创建一个普通方块,也不是创建一张简化图片,而是复用正式渲染中的碎片形状和UV 逻辑,为单个碎片单独构建一个小 Mesh。因此飞行中的碎片和最终落地后的碎片具备一致的:
|
||||
- 拼图轮廓形状
|
||||
- 海报采样位置
|
||||
- 贴图内容
|
||||
- seed 生成规则
|
||||
这就避免了“飞行时看起来是一块,落地后变成另一块”的割裂感。
|
||||
- 动画结束时,临时飞行节点会被销毁,目标 key 从隐藏集合中移除。随后主海报刷新,正式 Mesh 开始绘制这块碎片,同时轮廓线规则也会把该位置视为已填充,不再绘制空位轮廓。
|
||||
整个过程流程如下:
|
||||
Reference in New Issue
Block a user