CocosCreator 3.4.0 Graphics组件实战:从涂鸦到自定义图表,一个组件搞定游戏UI绘制
2026/4/27 14:53:33 网站建设 项目流程

CocosCreator 3.4.0 Graphics组件实战:从涂鸦到自定义图表,一个组件搞定游戏UI绘制

在游戏开发中,UI系统往往需要处理大量动态视觉效果。传统解决方案需要设计师产出大量素材,而CocosCreator的Graphics组件则提供了一种更灵活的编程式绘图方案。这个看似简单的2D绘图工具,实际上可以成为游戏UI开发的瑞士军刀——从基础形状到复杂数据可视化,从交互式涂鸦到动态技能指示器,都能通过代码实时生成。本文将带您深入探索Graphics组件在真实游戏场景中的高阶应用,突破基础图形绘制的局限,打造轻量级、高性能的游戏UI系统。

1. 从零构建交互式涂鸦系统

涂鸦功能不仅是休闲游戏的常见玩法,也可用于战斗场景标记、战术板绘制等硬核功能。通过Graphics组件实现涂鸦的核心在于捕捉触摸轨迹:

// 涂鸦画板组件 export class DoodleBoard extends Component { @property(Graphics) private graphics: Graphics = null!; onEnable() { this.node.on(Node.EventType.TOUCH_START, this.onTouchStart, this); this.node.on(Node.EventType.TOUCH_MOVE, this.onTouchMove, this); } private onTouchStart(event: EventTouch) { const pos = this.convertToLocalSpace(event); this.graphics.moveTo(pos.x, pos.y); this.graphics.lineWidth = 5; this.graphics.strokeColor = Color.RED; } private onTouchMove(event: EventTouch) { const pos = this.convertToLocalSpace(event); this.graphics.lineTo(pos.x, pos.y); this.graphics.stroke(); } private convertToLocalSpace(event: EventTouch): Vec2 { const uiPos = event.getUILocation(); return this.node.getComponent(UITransform)! .convertToNodeSpaceAR(v3(uiPos.x, uiPos.y)); } public clear() { this.graphics.clear(); } }

性能优化要点

  • 设置合理的lineWidth避免过度绘制
  • 移动端需要限制采样频率防止卡顿
  • 可添加TOUCH_END事件自动闭合路径

进阶功能可通过以下方式增强体验:

  • 实现笔刷大小动态调整
  • 添加多颜色选择功能
  • 支持撤销/重做操作栈

2. 打造动态游戏HUD元素

游戏中的血条、能量条等HUD元素需要频繁更新,Graphics组件能轻松实现这些动态效果。

2.1 弧形进度条实现

环形进度条非常适合表现技能冷却时间:

// 绘制弧形进度条 drawArcProgress(center: Vec2, radius: number, progress: number) { this.graphics.clear(); this.graphics.lineWidth = 10; this.graphics.strokeColor = Color.GREEN; const startAngle = -Math.PI/2; // 12点钟方向开始 const endAngle = startAngle + (Math.PI * 2 * progress); this.graphics.arc( center.x, center.y, radius, startAngle, endAngle, false ); this.graphics.stroke(); // 添加中心文本 const label = this.node.getComponentInChildren(Label); label.string = `${Math.round(progress * 100)}%`; }

2.2 动态伤害数字效果

结合Graphics与Tween系统可创建生动的伤害数字:

showDamage(value: number, pos: Vec2) { const damageNode = new Node(); this.node.addChild(damageNode); const graphics = damageNode.addComponent(Graphics); graphics.fillColor = Color.RED; graphics.strokeColor = Color.WHITE; graphics.lineWidth = 2; // 绘制数字轮廓 const text = value.toString(); const fontSize = 30; for (let i = 0; i < text.length; i++) { graphics.fillText(text[i], i * fontSize, 0, fontSize); } graphics.stroke(); // 添加动画 tween(damageNode) .to(0.5, { position: v3(pos.x, pos.y + 100, 0) }, { easing: 'sineOut' }) .call(() => damageNode.destroy()) .start(); }

3. 游戏内数据可视化实战

3.1 轻量级雷达图实现

角色属性雷达图是RPG游戏的常见需求:

// 六维雷达图绘制 drawRadarChart(center: Vec2, radius: number, values: number[]) { this.graphics.clear(); const segments = values.length; const angleStep = (Math.PI * 2) / segments; // 绘制网格 this.graphics.lineWidth = 1; this.graphics.strokeColor = Color.GRAY; for (let r = 0.2; r <= 1; r += 0.2) { this.graphics.moveTo( center.x + radius * r * Math.cos(0), center.y + radius * r * Math.sin(0) ); for (let i = 1; i <= segments; i++) { const angle = i * angleStep; this.graphics.lineTo( center.x + radius * r * Math.cos(angle), center.y + radius * r * Math.sin(angle) ); } this.graphics.close(); this.graphics.stroke(); } // 绘制数据区域 this.graphics.fillColor = new Color(255, 0, 0, 100); this.graphics.moveTo( center.x + radius * values[0] * Math.cos(0), center.y + radius * values[0] * Math.sin(0) ); for (let i = 1; i < segments; i++) { const angle = i * angleStep; this.graphics.lineTo( center.x + radius * values[i] * Math.cos(angle), center.y + radius * values[i] * Math.sin(angle) ); } this.graphics.close(); this.graphics.fill(); this.graphics.stroke(); }

3.2 动态折线图优化方案

相比原文的基础实现,我们可以优化性能和数据展示:

// 高性能折线图实现 export class LineChart extends Component { private _data: number[] = []; private _dirty = false; updateData(newData: number[]) { this._data = newData; this._dirty = true; } update() { if (!this._dirty) return; const graphics = this.getComponent(Graphics)!; graphics.clear(); // 计算坐标点 const points = this._data.map((value, i) => { return v2( this.minX + i * this.stepX, this.minY + value * this.scaleY ); }); // 绘制平滑曲线 graphics.lineWidth = 3; graphics.strokeColor = Color.BLUE; this.drawSmoothCurve(graphics, points); this._dirty = false; } private drawSmoothCurve(graphics: Graphics, points: Vec2[]) { // 使用贝塞尔曲线平滑连接点 graphics.moveTo(points[0].x, points[0].y); for (let i = 1; i < points.length - 1; i++) { const cp1 = points[i].clone().add(v2(30, 0)); const cp2 = points[i+1].clone().add(v2(-30, 0)); graphics.bezierCurveTo( cp1.x, cp1.y, cp2.x, cp2.y, points[i+1].x, points[i+1].y ); } graphics.stroke(); } }

4. 高级技巧与性能优化

4.1 批量绘制优化

当需要绘制大量相似图形时,应采用批处理方式:

// 批量绘制性能对比 function drawStarsBad(count: number) { // 错误示范:每次绘制都设置样式 for (let i = 0; i < count; i++) { this.graphics.lineWidth = 2; this.graphics.strokeColor = Color.YELLOW; this.graphics.fillColor = Color.RED; this.drawStar(/*...*/); } } function drawStarsGood(count: number) { // 正确做法:统一设置样式 this.graphics.lineWidth = 2; this.graphics.strokeColor = Color.YELLOW; this.graphics.fillColor = Color.RED; for (let i = 0; i < count; i++) { this.drawStar(/*...*/); } }

4.2 混合模式高级应用

通过设置blendFactor实现特殊效果:

混合模式适用场景示例代码
SRC_OVER默认模式,正常叠加graphics.srcBlendFactor = BlendFactor.SRC_ALPHA
ADDITIVE光效、发光物体graphics.srcBlendFactor = BlendFactor.ONE
MULTIPLY阴影、染色效果graphics.srcBlendFactor = BlendFactor.DST_COLOR
// 创建发光效果 createGlowEffect() { this.graphics.srcBlendFactor = BlendFactor.ONE; this.graphics.dstBlendFactor = BlendFactor.ONE; // 绘制发光体 this.graphics.fillColor = new Color(255, 200, 0, 100); this.graphics.circle(0, 0, 50); this.graphics.fill(); }

4.3 内存优化策略

长期存在的Graphics对象需要注意:

  • 及时调用clear()释放资源
  • 复杂图形考虑转换为SpriteFrame缓存
  • 动态内容使用enabled控制而非反复创建销毁
// 将Graphics内容转为SpriteFrame const texture = new RenderTexture(); const spriteFrame = new SpriteFrame(); spriteFrame.texture = texture; // 渲染到纹理 this.graphics.render(texture); // 后续使用sprite显示 const sprite = this.node.addComponent(Sprite); sprite.spriteFrame = spriteFrame;

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

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

立即咨询