前沿

  • 看了一下,距离上一篇文章的输出已经时过半年了,很久没有输出了。了解的朋友们可能知道宗宝闲余时间再尝试一个新的挑战。
  • 今天宗宝给大家分享一个**3D文本【Lable3D】**的实现,这也是3D游戏开发中比较常用的功能之一

效果展示

  • 先给大家看看效果吧

实现原理

1.出发点

  • 首先确定的了,咱们的目标是想办法实现3D文本的显示功能;那么我们先需要考虑一下问题

  • 2d文本,也就是咱们常用的Label组件的实现原理是什么

  • 我们在输入文本之后到渲染显示在屏幕上都进过了那些操作,

  • 这些操作后最终得到的结构是什么形式的,我们是否可以直接拿来用

2.了解Label实现

抱着上面的疑问,我们的首要任务就是去了解Lable的源码实现,这就是开源引擎最大好多,随时随地可以查看源码,

  • 那我们就开始看源码,在看源码的时候,我们会看见下边这个函数
protected _applyFontTexture () {this.markForUpdateRenderData();const font = this._font;if (font instanceof BitmapFont) {const spriteFrame = font.spriteFrame;if (spriteFrame && spriteFrame.texture) {this._texture = spriteFrame;if (this.renderData) {this.renderData.textureDirty = true;}this.changeMaterialForDefine();if (this._assembler) {this._assembler.updateRenderData(this);}}} else {if (this.cacheMode === CacheMode.CHAR) {this._letterTexture = this._assembler!.getAssemblerData();this._texture = this._letterTexture;} else if (!this._ttfSpriteFrame) {this._ttfSpriteFrame = new SpriteFrame();this._assemblerData = this._assembler!.getAssemblerData();const image = new ImageAsset(this._assemblerData!.canvas);const texture = new Texture2D();texture.image = image;this._ttfSpriteFrame.texture = texture;}if (this.cacheMode !== CacheMode.CHAR) {// this._frame._refreshTexture(this._texture);this._texture = this._ttfSpriteFrame;}this.changeMaterialForDefine();}}

我们会发现这个函数最终的输出结果会得到一个SpriteFrame(_texture),正常我们理解SpriteFrame不就是一张图片嘛,有了图片我们不久可以随便操作了嘛

3.论证

有了上边的想法,我们就开始先做一个最基础的操作

  • 创建一个Label组件
  • 创建一个3d的panel节点
  • 将Label组件中的texture赋值给panel材质中的贴图
    简单写一段测试代码
import { _decorator, Component, Label, MeshRenderer, Texture2D } from 'cc';
const { ccclass, property, executeInEditMode } = _decorator;
@ccclass('NewComponent')
@executeInEditMode
export class NewComponent extends Component {@property({ type: Label })label: Label = null!;@property({ type: MeshRenderer })meshRender: MeshRenderer = null!;start() {}update(deltaTime: number) {let spriteFrame: any = this.label.spriteFrame!;let texture: Texture2D = spriteFrame.texture;this.meshRender.material?.setProperty("mainTexture", texture);}
}


既然看到了效果,那么说明上边猜想是可行的,通过改变label中的字体后,我们可以拿到它本身所生成的纹理然后渲染到任意的3d物体上;

注意:3d物体的材质因该选择使用透明材质,同时开启mainTexture对应的宏定义

4.进阶

  • 你以为得到了结果就结束了嘛,答案肯定不是的,因为这种方式使用起来会有一个明显的问题,我的3d文本必须对应一个2d的Label组件,是不是很麻烦;同时我们还有一个问题没有去得到答案:Label的实现原理是什么,具体有那些操作;
  • 带着这个问题我们继续深入源码,我们会在ttfUtils.ts和bmfont.ts脚本中找到答案
  • 下边主要是以ttfUtils为主进行展开,因为宗宝主要研究了一下系统字体的实现

在代码中会发现,系统字体的显示主要是使用CanvasRenderingContext2D,通过CanvasRenderingContext2D将文本渲染到canvas上,然后通过canvas生成一张纹理图,最终渲染到屏幕上;

既然了解了大概的原理,那么咱们的Label3D也就轻松的出来了,

 /*** 刷新渲染*/
private updateRenderData(): void {if (!this._assemblerData) return;this._context = this._assemblerData.context;this._canvas = this._assemblerData.canvas;this.initTexture2D();this.updateFontFormatting();this.updateFontCanvasSize();this.updateRenderMesh();this.updateFontRenderingStyle();this.updateTexture();this.updateMaterial();this.resetRenderData();
}

大致需要一下几个步骤:

1.0 updateFontFormatting 文本格式化

  private updateFontFormatting(): void {if (!this._context) return;let strs: string[] = this._string.split("\\n");this._splitStrings = strs;for (let i = 0; i < strs.length; i++) {//获取文本的宽度let len: number = this._context.measureText(strs[i]).width;if (len > this._canvasSize.width) {this._canvasSize.width = len;}}this._canvasSize.height = strs.length * this.getLineHeight() + BASELINE_RATIO * this.getLineHeight();}

以’\n’ 作为换行符,格式化文本,并且计算文本显示的size

2.0 updateFontCanvasSize 设置canvse

private updateFontCanvasSize(): void {this._canvasSize.width = Math.min(this._canvasSize.width, MAX_SIZE);this._canvasSize.height = Math.min(this._canvasSize.height, MAX_SIZE);if (this._canvas.width != this._canvasSize.width) {this._canvas.width = this._canvasSize.width;}if (this._canvas.height != this._canvasSize.height) {this._canvas.height = this._canvasSize.height;}this._context.font = this.getFontDesc();
}

通过文字显示所需的宽高,更新canvas的的size

3.updateRenderMesh

private updateRenderMesh(): void {let rate: number = this._canvas.width / this._canvas.height;this._positions = [];this._positions.push(-0.5 * rate, -0.5, 0);this._positions.push(0.5 * rate, -0.5, 0);this._positions.push(-0.5 * rate, 0.5, 0);this._positions.push(-0.5 * rate, 0.5, 0);this._positions.push(0.5 * rate, -0.5, 0);this._positions.push(0.5 * rate, 0.5, 0);// this._meshRender.mesh?.updateSubMesh(0, {//     positions: new Float32Array(this._positions),//     minPos: { x: -0.5 * rate, y: -0.5, z: 0 },//     maxPos: { x: 0.5 * rate, y: 0.5, z: 0 }// });this._meshRender.mesh = utils.MeshUtils.createMesh({positions: this._positions,uvs: this._uvs,minPos: { x: -0.5, y: -0.5, z: 0 },maxPos: { x: 0.5, y: 0.5, z: 0 }});this._meshRender.model?.updateWorldBound();this.updateMeshRenderMaterial();
}

根据canvas 的宽高比例更新显示所需网格的顶点数据,这一步主要是保证生成的贴图显示在网格上的时候文字的宽高不会被压缩

4.updateTexture 生成贴图

private updateTexture(): void {if (!this._context || !this._canvas) return;this._context.clearRect(0, 0, this._canvas.width, this._canvas.height);let textPosX: number = 0;let textPosY: number = 0;for (let i = 0; i < this._splitStrings.length; i++) {textPosY = this._startPosition.y + (i + 1) * this.getLineHeight();let len: number = this._context.measureText(this._splitStrings[i]).width;textPosX = (this._canvas.width - len) / 2;this._context.fillText(this._splitStrings[i], textPosX, textPosY);}let uploadAgain: boolean = this._canvas.width !== 0 && this._canvas.height !== 0;if (uploadAgain) {this._texture.reset({width: this._canvas.width,height: this._canvas.height,mipmapLevel: 1,});this._texture.uploadData(this._canvas);this._texture.setWrapMode(RenderTexture.WrapMode.CLAMP_TO_EDGE, RenderTexture.WrapMode.CLAMP_TO_EDGE);}
}

主要代码来了,将文本渲染到canvas中,然后通过canvas生成贴图

5.updateMaterial 更新材质贴图

private updateMaterial(): void {if (!this._texture) return;if (!this._meshRender) return;if (!this._material) return;let material: Material = this._meshRender.getMaterialInstance(0)!;material.setProperty("mainTexture", this._texture);
}

将生成的贴图显示到咱们的网格上

总结

上边就是宗宝参考引擎代码,实现Label3D的大概思路以及部分代码,希望能给大家带来帮助;宗宝的实现中还有很多不足,比如对齐模式,倾斜,加速,等等,由于时间关系都没有实现,大家都可以自由的扩展奥;

  • 如需完整代码:关注公众号:“搬砖小菜鸟”,回复"label3D"即可

Cocos Creator 3.x : 你们要的Label3D来了相关推荐

  1. Cocos Creator 预制的使用模板(一般用于UI)

    Cocos Creator里绑定properties @property(cc.Prefab)XXUIPrefab: cc.Prefab = null; 在预制上挂脚本 import { Consta ...

  2. Cocos Creator 的 动作(Action)系统:moveBy的使用

    Cocos Creator 快速上手:制作第一个游戏 可以在这里感受一下这款游戏的完成形态: http://fbdemos.leanapp.cn/star-catcher/ 准备项目和资源 我们已经为 ...

  3. cocos creator 安卓原生平台环境_竞技对抗小游戏单挑篮球开发历程 | Cocos技术派第12期...

    本文来自于"Cocos 荣耀讲师"征稿活动第1期,最先发表于 Cocos 中文社区,作者 ID:蟹老板,2017年加入社区,文章作品包括<猎头专家的开发历程>等. Co ...

  4. asp.net js函数弹出登录窗口_JS基础 | Cocos Creator 开发环境搭建

    编程并不只是简单地写代码,而是要将编写的代码运行在指定平台环境上,在此之前我们还需要搭建生产代码的环境. 一. 软件准备 Chrome:浏览器,用于预览.调试我们的游戏 VSCode:代码编辑器,用于 ...

  5. Cocos Creator—最佳构建部署实践

    这篇文章主要是我们团队在使用Cocos Creator过程中的一些关于部署方面的实践总结,标题党了一回,严格来说,应该是<快看漫画游戏研发团队使用Cocos Creator构建部署最佳实践> ...

  6. Cocos Creator快速开通联网服务教程

    继集成Egret编辑器工作流后,在最新的Cocos Creator v2.0.7 版本中, Creator服务面板也集成了游戏服务器引擎Matchvs的联网服务.现附上开通教程,方便大家更快上手. 1 ...

  7. 5弹出阴影遮罩_千文详述Cocos Creator弹出式对话框实现技术,着实硬核

    正文 在Cocos Creator游戏开发中,经常需要使用到弹出式对话框,下面我们就一起来封装下自己的弹出式对话框. 一.弹出式对话框原理解析 1:对话框的结构: 1. `根节点 -->`2. ...

  8. cocos creator 获取当前时间_前端开发者入门 Creator 必读吧

    写在前面 因为公司的业务需求,近期学习了Cocos Creator这款游戏引擎的开发,也基于此上线了一款游戏,因此写这系列文章记录一下我从入门到项目发布的学习过程. 相对于 web 开发,像Cocos ...

  9. Cocos Creator学习目录

    目录 安装和启动 文件结构 编辑器基础 基本概念 (场景树 节点 坐标 组件 ) Cocos Creator 脚本简介 Cocos Creator调试 节点 cc.Node 组件开发cc.Compon ...

最新文章

  1. LaTex文章中插入Visio及Matlab矢量图
  2. python学习(1)
  3. java char 打印_Java中char[]输出不是内存地址的原因详解
  4. 明天放假,我放价!一个国庆假期教你学会数学建模
  5. 局域网物理机怎么访问虚拟机
  6. python 列表间隔取值_python list数据等间隔抽取并新建list存储的例子
  7. 转 node.js和 android中java加密解密一致性问题;
  8. Java JSON转Excel工具类
  9. Python 将中文大写数字转为阿拉伯数字
  10. UDID 和 UUID 的问题
  11. 农民伯伯android,Android3.1r1API中文文档——ImageView(cnmahj+农民伯伯).doc.doc
  12. 大数据运维工作(Linux,OGG,链路监控,Hadoop运维等)
  13. 免费开源的工程师项目管理系统
  14. 免费拿和平精英模拟器
  15. 2023年全国最新交安安全员精选真题及答案4
  16. 汽车之家精选论坛图片下载
  17. 那些与三维激光扫描有关的建模
  18. jQuery元素操作-遍历元素
  19. Android集成极光推送
  20. 傅立叶级数和傅立叶变换是什么关系?

热门文章

  1. WebRTC:P2P 连接过程完全解析
  2. 纵横CW大鹏无人机地面站航线规划方法
  3. mysql date_format
  4. 【IEEE CIM 2023】基于多目标进化算法的抗菌肽设计方法
  5. MapBar简单实用
  6. c语言连续生成不同随机数_C语言随机数生成教程,C语言rand和srand用法详解
  7. 模拟战役题解(dfs,联通块,贪心)
  8. Ant Design + react-resizable实现列表页可拖拽宽度变化
  9. 全卷积神经网络FCN
  10. 服务器inode满了怎样删除文件,linux系统inode占满故障处理