☰
Cocos Creator 2D小视图:用Render Texture和摄像机实现局部渲染
2026/10/18 2:43:35 网站建设 项目流程

1. 开篇:一个小视图,能省下多少事

做 Cocos Creator 2D 项目时,大多数人很少真正重视摄像机。按钮、Label、Sprite 往 Canvas 底下一挂,没出问题就从来不碰摄像机。直到某天遇到“为什么这个界面模糊了”“为什么我用对象池回收的节点在某台安卓机上闪了一下”“为什么新手引导的点击穿透了”这类问题时,才会意识到摄像机的设置一直在默默背锅。

我这次要分享的是一个非常基础、但特别容易被忽视的用法——Cocos Creator 2D 摄像机的小视图方案。字面意思很好理解:在一个主场景里,单独开一个小视图,用另一台摄像机渲染某些节点。注意,这里的“小视图”不是 UI 上的一个 Sprite 框,而是实实在在的Render Texture + Camera的组合,把一个场景的画面实时渲染到一张纹理上,再贴到界面某个区域里。

适合谁看?刚接触摄像机体系、被 Stack 和 Visibility 绕晕的 Cocos 开发者,以及需要做小地图、后视镜、弹窗独立渲染、UI 角色展示这类需求的人。下面我把这个 Lv.1 方案从原理到踩坑一步步拆开讲。

2. 摄像机在 Cocos Creator 2D 里的基本盘

在动手之前,先明确两件最基础的事:摄像机怎么工作,以及 2D 项目和 3D 项目在使用上有哪些差异。

2.1 先弄明白 Camera 的渲染逻辑

Cocos Creator 的摄像机本质上是一个“视锥体”加“渲染队列”的组合。场景里的节点要被摄像机看到并画出来,需要同时满足三个条件:

  • 节点存在于摄像机所在的场景层级中;
  • 节点的 Layer 与摄像机的 Visibility(可见性掩码)有交集;
  • 节点在摄像机的渲染范围内(2D 下基本等价于尺寸和位置范围)。

三者缺一不可。这也是很多初学者最容易忽略的点:摄像机的 Visibility 默认是“全部”,一旦你手动改成某几个 Layer,场景里大部分节点立刻消失,连 Canvas 里的 UI 都可能不见。

2.2 2D 和 3D 摄像机虽然同名,但用法差距很大

3D 项目里摄像机涉及近远裁剪面、FOV、透视投影等概念;2D 项目里绝大多数情况用的是正交投影。正交意味着不管物体离摄像机多远,看到的尺寸不变,这也是 2D 游戏不会出现“近大远小”的原因。

Cocos Creator 3.x 的 2D 项目(注意,从 3.0 开始 2D 和 3D 是同一个编辑器体系),摄像机组件挂上后默认是正交模式。这意味着:

  • 摄像机的 Position 的 Z 值决定了它离场景内容多“远”,但在正交投影下,Z 的绝对值只影响深度排序,不影响画面大小;
  • OrthoHeight 决定可见范围的高度,宽度根据 Canvas 的分辨率比例自动推算;
  • 摄像机的 ClearFlag 决定它渲染时是清屏还是叠加在已有画面上。

很多从 Cocos Creator 2.x 迁移过来的老开发者,在 3.x 里第一次碰摄像机时会发现组件参数长得完全不一样。这很正常,因为 3.x 把摄像机的行为统一了,UI 和 3D 场景都可以由同一种摄像机处理,只是参数组合不同而已。

2.3 2D 项目里摄像机的经典用法

Lv.1 阶段,你不必掌握所有参数。先记住三组常用组合:

场景需求摄像机设置说明
主 UI 跟随 CanvasClearFlag 设为 SOLID_COLOR,背景透明即可最常规的做法
3D 场景叠加 2D UI两台摄像机,3D 相机的 ClearFlag 为 SOLID_COLOR,UI 相机为 DONT_CLEAR深度排序靠 Priority 或 Depth
小视图渲染独立场景或独立 Layer + Render Texture这次的核心方案

提示:2D 项目里,如果 Canvas 的 Render Mode 是 Screen Space Overlay 之类传统 UI 模式,可能无法直接实验相机渲染小视图,因为该模式不走摄像机渲染。建议把 UI Canvas 切换为 Camera 模式,或者单独建一个节点挂 Sprite 来承载 Render Texture。

3. 小视图方案到底解决什么问题

小视图这个需求,在真实项目里出现的频率远比想象中高。我见过这几类场景:

  • 小地图系统:RPG 或动作游戏里,右上角一个小方块实时显示角色周围的情况。最优雅的实现方式就是用一台跟随角色的摄像机,渲染到 Render Texture,然后贴到 UI 上。
  • 角色立绘/模型展示:抽卡游戏里,点开详情页,一个 3D 模型在 UI 的某个框里旋转展示。做法就是一台只渲染模型的摄像机 + RT。
  • 安全区预览/摄像头的后视画面:某些赛车、驾驶类 2D 游戏的“后视镜”,本质也是另一台摄像机 + RT。
  • 独立 Canvas 局部渲染:某些运营活动页面需要一块区域做特效,但又不想把特效节点混进主 UI 层级导致渲染顺序混乱,用子摄像机渲染到 RT 是一种干净的隔离方案。

Lv.1 的核心不是让你实现小地图全功能,而是打通“第二台摄像机照着指定内容,渲染到 UI 上的一张图”这条链路。

4. 动手前的准备与选型分析

4.1 明确你要渲染的内容

先想清楚:小视图里要显示什么?

  • A.当前场景里的部分节点(比如小地图显示主场景里的障碍物、角色、道具)。这种情况下,你不需要新建场景,只需要设置 Layer。
  • B.完全独立的另一套场景内容(比如角色展示场景,主场景里没有这些模型/UI)。这种情况下,推荐使用独立场景 + 独立摄像机,甚至用 Prefab 动态加载。

两种方式在 Lv.1 里选 A 就好。原因很简单:A 方式实现成本低,理解难度小,适合先跑通流程。

4.2 需要的工程资源

  • 一台主摄像机(已有);
  • 一台子摄像机(新建空节点挂 Camera);
  • 一个 Render Texture 资源;
  • 一张承载 RT 的 Sprite(或 RawImage,取决于你用的 UI 组件)。

在 Cocos Creator 3.x 中,创建 Render Texture 的方法:在资源管理器右键 -> 创建 -> 渲染纹理(Render Texture)。这个资源本质上是一张可以动态更新的纹理,你可以把它拖给 Sprite 的 SpriteFrame 属性。

4.3 Layer 规划:小视图方案的关键钥匙

这一步是整个方案的灵魂。Cocos Creator 里的 Layer 是位掩码,一个节点可以属于多个 Layer。比如主 UI 的 Layer 是 UI_2D,小地图里的角色 Layer 是 Player,摄像机渲染哪些层由 Visibility 决定。

我推荐的规划是这样的:

  • 主摄像机只渲染 UI_2D 和 Default 这两个层;
  • 子摄像机只渲染 Player 和 Map 这两个层;
  • 需要出现在小视图里的节点,勾选对应 Layer;
  • 不需要出现的节点,保持默认层,子摄像机自然看不见它。

为什么用 Layer 而不是直接移动节点?因为移动节点会打断主场景的相对坐标系。Layer 是渲染层面的隔离,不影响节点树结构,是最稳妥的做法。

5. 核心实现:手工搭建小视图

接下来进入实操环节。我用 Cocos Creator 3.x(以 3.8.x 为例)走一遍完整流程。不同小版本界面可能略有差异,但思路完全一致。

5.1 创建 Render Texture 和 Sprite

先在资源管理器里创建 Render Texture,命名为 RT_MiniMap。然后创建 UI 节点结构:

  1. 在 Canvas 下新建一个空节点 MiniMapView;
  2. 给它添加 Sprite 组件;
  3. 把 RT_MiniMap 直接拖到 Sprite 的 SpriteFrame 属性上。

你可能会问:Sprite 的 SpriteFrame 不是应该放图片资源吗?为什么 Render Texture 也能放?因为 RT 本质上也是一张纹理资源,Sprite 组件支持的 type 中有 “Simple” 和 “Sliced” 等形式,RT 默认以 Simple 方式显示即可。

这里要注意一个坑:RT 默认尺寸是 256x256。如果小视图 UI 框的尺寸和这个比例不一致,画面会被拉伸变形。建议先决定好 UI 框的显示尺寸,再在 RT 资源属性里设置相同宽高比,比如 UI 框是 200x200,RT 就用 256x256(还是正方形);如果 UI 框是 400x300,RT 就设成 512x384 之类的同比例尺寸。

5.2 创建子摄像机

在场景根节点下新建空节点 SubCamera,挂上 Camera 组件。核心参数怎么设:

参数推荐值理由
ProjectionORTHO2D 项目一律正交
ClearFlagSOLID_COLOR保证 RT 背景不透明或半透明可控
ClearColor按需,建议纯黑或纯白避免透明区域导致闪烁
Visibility自定义 Layer先取消全部勾选,再勾选需要渲染的层
Priority小于主摄像机主要是深度排序,如果 RT 独立存在其实影响不大
TargetTextureRT_MiniMap关键:把渲染目标指定为 RT

5.3 分配节点 Layer

这一步是新手最容易遗漏的。场景里有一个角色节点 Player,你想让它出现在小视图里,但主场景正常显示。操作方法:

  1. 选中 Player 节点;
  2. 在属性检查器最上方的 Layer 下拉框里,勾选你自定义的 MiniMapLayer;
  3. 确认 Player 也勾选了 Default 或主摄像机可见的某个层,否则主场景里它会消失。

注意:在 Cocos Creator 中,Layer 是可以多选的。具体操作是点击 Layer 下拉框,在列表里勾选多个层。如果你把 Player 的 Layer 完全改成 MiniMapLayer,主摄像机(不渲染该层)就看不到它了,主场景会直接“丢”了这个角色。所以多选是必须的。

5.4 调整子摄像机位置与大小

子摄像机的位置、OrthoHeight 大小决定了小视图显示什么区域。

在 2D 场景中,子摄像机的 Position 的 X 和 Y 表示它对着场景中的哪个位置;Z 值只要不为 0 即可(正交下 Z 不影响大小,但必须保证摄像机在渲染内容之前被更新)。

OrthoHeight 的含义:屏幕上纵向可见范围的一半(单位:世界坐标单位)。假设你的设计分辨率是 720x1280,默认 OrthoHeight = 640,意味着纵向能看到 1280 单位的高度。如果你想在小视图里显示一个半径 500 单位左右的区域,OrthoHeight 设成 500 就行。

实际操作时,可以先随便设一个值,然后在游戏运行中实时调整,直到小视图里显示的内容范围刚好符合预期。用动态调参代替静态推算,是最快的做法。

5.5 让子摄像机跟随目标(小地图必备)

如果小视图是做“跟随”,那子摄像机必须每帧更新位置。最简单的方式是写一个脚本,在 LateUpdate 里把 SubCamera 的节点位置设置为主场景中某个目标的坐标。

import { _decorator, Component, Node, Camera } from 'cc'; const { ccclass, property } = _decorator; @ccclass('MiniMapCameraFollow') export class MiniMapCameraFollow extends Component { @property(Node) target: Node = null; @property(Camera) subCamera: Camera = null; lateUpdate() { if (!this.target || !this.subCamera) return; this.subCamera.node.setPosition( this.target.node.position.x, this.target.node.position.y, this.subCamera.node.position.z ); } }

使用 LateUpdate 而不是 Update,是为了保证目标节点在 Update 阶段移动之后,摄像机再同步过去,避免一帧的延迟感。

6. 进阶一点:用 Layer 隔离实现“双场景效果”

如果不想把内容放进同一个场景,想用一个独立场景做小视图内容,也有方案。在 Lv.1 阶段,我建议先掌握一种“伪双场景”玩法。

思路:在主场景里创建一个独立的节点容器,所有需要在小视图展示的内容挂在这个容器下,并设置子层 Layer。然后你可以在代码里动态往这个容器添加内容,或者直接拖 Prefab 进去。子摄像机只渲染这个容器所在的 Layer,主摄像机不渲染它。

这样从用户视角看,小视图展示的内容和主场景是“两个世界”,但工程上它们还是同一个场景、同一个节点树。好处是:

  • 不用做复杂的场景切换;
  • 可以方便地在运行时增删内容;
  • 主场景的 Canvas 不需要做任何调整。

这个技巧在做角色抽卡、装备预览这类功能时特别好用。主界面正常玩,弹出一个对话框,对话框里的展示区域挂了一台子摄像机 + RT,RT 显示的是主场景某个隐藏容器里的模型。整个流程可以无缝衔接,不需要切换场景。

7. 深水区:关于 ClearFlag 和透明背景的坑

用 RT 渲染 UI 内容时,如果你的内容背景是透明的,比如小视图里只想显示一个角色图标,背景要透明,这时候 ClearFlag 的设置要特别注意。

我建议:ClearFlag 用 SOLID_COLOR,并把 ClearColor 的 Alpha 设为 0。

为什么不用 DONT_CLEAR?因为如果你上一帧在 RT 上画了什么,DONT_CLEAR 不会清理画面,会导致残影、拖影问题。除非你确定内容每帧全屏覆盖,否则不要用 DONT_CLEAR。

Alpha 为 0 的 SOLID_COLOR 实际上会让 RT 的初始颜色完全透明,再渲染内容上去,最终 Sprite 的显示效果就是“只有内容,没有底”。听起来很理想,但有一个坑:有些低端安卓设备上,RT 的透明通道处理有兼容性问题,可能导致背景变成黑色或白色噪点。

遇到这种兼容性问题的替代方案有两个:

  1. 在 RT 上渲染时,内容区域加一个不透明底色(如深色半透明底),让 RT 永远不是完全透明的;
  2. 不用 RT,改用“摄像机 + 视口分割”实现小视图,这个方式后面再展开,Lv.1 不做要求。

8. 常见问题与排查实录

我整理了自己反复踩过的几个典型问题,做成速查表,希望对你有直接帮助。

现象可能原因解决方案
小视图黑屏/白屏,没有内容子摄像机 Visibility 没有勾选目标内容的 Layer检查 Layer 位掩码
主场景节点消失节点 Layer 被修改后主摄像机不渲染它节点 Layer 同时勾选主摄像机可见的层
小视图画面变形RT 宽高比与 Sprite 显示框不一致调整 RT 尺寸或 Sprite 尺寸,保持同比例
小视图有残影ClearFlag 设为 DONT_CLEAR改回 SOLID_COLOR,或手动清理
子摄像机渲染了不想要的元素Visibility 勾选范围过宽用独立的 Layer 专门给子摄像机内容
RT 在部分安卓机型上闪烁透明通道兼容性加不透明底色,或转用视口方案
子摄像机看不到任何东西,但参数都对摄像机 position 的 Z 值可能是 0确保 Z 值不为 0

8.1 小视图和主 UI 重叠时,点击穿透问题

这个点很多人做完显示才发现。小视图如果只是一张 Sprite,它属于 UI 的一部分,点击事件会正常处理。但如果你希望点击小视图触发某些逻辑(比如点击小地图跳转),你必须在小视图 Sprite 上挂一个 Button 组件(或自己监听触摸事件),而不是直接监听子摄像机的内容。

因为子摄像机的渲染结果本质是一张贴图,里面的物体不会响应 UI 点击。这是 RT 方案的天然限制,也是区分“显示渲染”和“交互逻辑”的重要边界。

8.2 为什么我的 RT 在浏览器里首次加载是黑的?

RT 在首次加载时,GPU 可能还没有完成纹理初始化,直接显示会是黑片。常见处理方式:在 main.ts 或场景初始化时,主动让子摄像机先渲染一帧,或者把 RT 关联的 Sprite 在场景启用时设置一张默认图,等第一帧渲染结束后再切换为 RT 显示。

一个小技巧:在 onLoad 里执行this.subCamera.node.active = true并手动调用一次this.subCamera.visibility = ...来强制刷新。但更省事的做法是,给 RT 关联的 Sprite 设置一张和 RT 同尺寸的占位图,等画面真正渲染出来后再隐藏占位图。

8.3 摄像机排序:Priority 还是 Depth?

在 Cocos Creator 3.x 中,摄像机的排序由 Depth 属性决定,数字越大越晚渲染、越靠上层。Priority 是 2.x 时代的叫法,如果你看到的教程说 Priority,注意它是否对应 3.x 的 Depth。

小视图子摄像机只需要渲染到 RT,和主摄像机的叠加顺序无关,所以 Depth 设什么其实影响不大。但如果你的子摄像机没有指定 TargetTexture,而是直接渲染到屏幕,那 Depth 就重要了——它决定了子摄像机画面是罩在主画面之上还是之下。建议:子摄像机一定指定 TargetTexture,否则会出现“第二个全屏画面盖住主游戏”的经典失误。

9. 性能考量与优化提示

RT 方案有一个不可回避的问题:多一台摄像机渲染,就多一份 Draw Call 和 GPU 开销。Lv.1 阶段,如果你的小视图是常驻 UI,那要留意分辨率不能贪大。

经验数据:一张 512x512 的 RT,色深 32 位,每帧渲染一次,对绝大多数移动设备的 GPU 压力可以忽略。但如果是 2048x2048 的 RT 且每一帧都完整渲染场景里的所有内容,掉帧是必然的。

给几条优化思路:

  • 小地图类不需要每帧都向 RT 渲染。可以降低更新频率,比如每 3 帧更新一次,或者只在玩家移动超过某个阈值时更新;
  • 如果 RT 永远显示静态或低频变化的内容,可以手动把子摄像机的渲染频率降下来(通过 disable/enable);
  • 避免 RT 尺寸超过 UI 框本身的数倍。小 UI 框挂超大 RT,等于白白浪费 GPU 带宽;
  • 尽量让子摄像机的内容 Layer 按需裁剪,不要渲染整棵场景树。

9.1 和 Canvas 的关系:RT 内容如何参与 UI 层级?

注意,RT 本身是一张纹理,放在 Canvas 下用 Sprite 展示。因此它参与 UI 渲染队列,遵循 UI 一样的层级排序。你可以随意调整 Sprite 在 Canvas 下的兄弟节点顺序,来控制小视图显示在主 UI 的上方还是下方。

有一种比较隐蔽的现象:如果你在做 OS 级“断网重连”弹窗,弹窗里放置了 RT 小视图,而弹窗本身是动态生成的,要注意 Sprite 的 UITransform 尺寸是否在弹窗打开后才赋值。如果 UITransform 的 ContentSize 是 0,RT 画面可能显示不出来或显示成 0 大小。解决方式:在弹窗 show 之前,先给 UITransform 的 ContentSize 赋值。

10. 扩展思路:小视图还能怎么玩

Lv.1 方案虽然基础,但掌握了摄像机 + RT 的组合,后面能延伸出不少实用功能。

  • 对象池 + 多路小视图:如果你需要同时显示多个角色的头像画面,可以创建多个 RT,每个 RT 对应一台子摄像机。只要子摄像机数量不多(一般不超过 4 个),性能和内存都在可控范围。
  • 战斗回放/技能展示:战斗中单独录制一段技能演出到 RT,用于结算回放或战报展示,但这已经有点接近“录制”概念,需要额外处理帧同步;
  • 画中画:某些剧情对话中,主场景在战斗,角落里小视图播另一段动画情景,本质上就是独立的摄像机 + RT,且该 RT 的 ClearFlag 要配合透明背景;
  • 3D 模型展示:2D 主场景里嵌入一个 3D 模型的实时渲染,只需要把子摄像机的 Projection 改成 PERSPECTIVE,其他不变。

11. 最后一个建议:从哪里开始验证

如果你看完这篇,想立刻在项目里验证,建议不要急着做完整的小地图。先做一个最简单的实验:

  1. 新建一个场景,放一个 Sprite(比如一张测试图);
  2. 创建一个 RT 并拖给新 Sprite;
  3. 新建一台子摄像机,把 TargetTexture 设为 RT;
  4. 把测试 Sprite 的 Layer 设为子摄像机可见的层;
  5. 运行,观察 RT 里是否实时显示测试 Sprite。

这个十几分钟的小实验跑通,你对摄像机的 Visibility、Layer、ClearFlag、RT 的认识就会有一个质的提升。之后再上小地图、角色展示,都是顺着这条链路的自然延伸。

我自己在第一次做小地图时,就是因为没搞清楚 Layer 的关系,子摄像机看到的是整个世界,而不是我期望的区域,排错排了好几个小时。所以强烈建议你动手前先花两分钟画一遍“谁渲染谁”的清单:主摄像机渲染哪些层,子摄像机渲染哪些层,内容节点属于哪些层,心里有数再动手,效率会高很多。

这个 Lv.1 小视图方案,就是一场“给渲染做减法”的练习。别急着堆高级功能,先把一台摄像机、一张 RT、一组 Layer 的关系彻底吃透,后续各种花哨玩法都不会再难住你。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询