Phaser 3.60 九宫格(Nine Slice)游戏对象实战指南:打造不拉伸边角的自适应 UI 面板
2026/9/19 11:46:50 网站建设 项目流程

Phaser 3.60 九宫格(Nine Slice)游戏对象实战指南:打造不拉伸边角的自适应 UI 面板

【免费下载链接】phaserPhaser is a fun, free and fast 2D game framework for making HTML5 games for desktop and mobile web browsers, supporting Canvas and WebGL rendering.项目地址: https://gitcode.com/gh_mirrors/ph/phaser

Phaser 从 v3.60 起内置了原生 Nine Slice(九宫格)游戏对象,用于解决 UI 面板、按钮等元素在缩放时纹理被整体拉伸导致边角变形的痛点。本文以 changelog/v3/3.60/NineSliceGameObject.md 为主线,结合 NineSlice.js 等源码实现,完整讲解其布局原理、3 Slice / 9 Slice 的创建方式、全部构造参数、尺寸约束与底层顶点批量渲染机制,读完即可在项目中落地使用。

一、为什么需要 Nine Slice:UI 缩放的经典难题

在 HTML5 游戏中,按钮、面板、对话框等 UI 元素往往需要随屏幕分辨率或内容动态伸缩。如果直接把整张纹理等比拉伸,圆角、描边和装饰边角都会跟着变形,视觉上明显失真。

Nine Slice(又称九宫格 / 9-slice scaling)的解决思路是:把纹理拆成 3×3 的九块区域,四角固定不动,只有中间的连接区域参与拉伸。Phaser v3.60 将其实现为原生游戏对象,供 UI 与按钮类元素使用,同时支持一种更简化的「3 Slice」横向变体。该配置概念源自 Pixi 的 NineSlicePlane(见 NineSlice.js 源码注释)。

二、核心原理:9 区域布局结构

Nine Slice 游戏对象所使用的纹理必须遵循以下布局结构:

A B +---+----------------------+---+ C | 1 | 2 | 3 | +---+----------------------+---+ | | | | | 4 | 5 | 6 | | | | | +---+----------------------+---+ D | 7 | 8 | 9 | +---+----------------------+---+

当改变该对象的宽和/或高时,各区域遵循如下伸缩规则:

区域位置缩放行为
1、3、7、9四个角完全不变形(保持原始尺寸)
2、8上、下中段仅水平拉伸
4、6左、右中段仅垂直拉伸
5中心区域水平和垂直同时拉伸

三、3 Slice 变体:仅横向拉伸

如果元素只在水平方向上伸缩(如进度条、血条、横向菜单项),可以创建 3 Slice 游戏对象。它与 9 Slice 原理相似,但只能横向拉伸、不能改变高度,因此配置参数更少:

A B +---+----------------------+---+ | | | | C | 1 | 2 | 3 | | | | | +---+----------------------+---+

改变宽度时:区域 1 和 3 保持不变,区域 2 水平拉伸。

判断方式很直接:只提供leftWidthrightWidth即为 3 Slice;要创建 9 Slice 必须提供全部四个切片参数。在源码中,is3Slice通过(!topHeight && !bottomHeight)判定(NineSlice.js),创建后该属性只读,无法在 3 Slice 与 9 Slice 之间互相转换

四、创建 Nine Slice 游戏对象

4.1 工厂方法 this.add.nineslice

工厂方法在 NineSliceFactory.js 中注册,用法为:

this.add.nineslice(x, y, texture, frame, width, height, leftWidth, rightWidth, topHeight, bottomHeight, tileX, tileY);

完整参数说明(括号内为默认值):

参数类型默认值说明
x/ynumber对象在世界中的中心位置
texturestring | Texture纹理键,或 Texture 实例
framestring | number可选帧名/帧序号
widthnumber256对象宽度,创建后仍可调整
heightnumber256对象高度;3 Slice 对象的高度固定为纹理高度,无法修改
leftWidthnumber10左竖列(A 区域)尺寸
rightWidthnumber10右竖列(B 区域)尺寸
topHeightnumber0顶横行(C 区域)尺寸,置 0 或 undefined 即创建 3 Slice
bottomHeightnumber0底横行(D 区域)尺寸,置 0 或 undefined 即创建 3 Slice
tileXbooleanfalse水平中段改为平铺而非拉伸
tileYbooleanfalse垂直中段改为平铺而非拉伸

典型调用:

// 9 Slice:四角各 20 像素,宽度 300、高度 200 this.add.nineslice(400, 300, 'ui-panel', null, 300, 200, 20, 20, 20, 20); // 3 Slice:左右边角各 24 像素,宽度 240 this.add.nineslice(400, 300, 'ui-bar', null, 240, 24, 24, 24);

4.2 配置对象方式 this.make.nineslice

使用this.make.nineslice(config)可以传入配置对象创建(NineSliceCreator.js),配置字段定义在 NineSliceConfig.js:

this.make.nineslice({ key: 'ui-panel', width: 300, height: 200, leftWidth: 20, rightWidth: 20, topHeight: 20, bottomHeight: 20, add: true // 创建后加入场景 });

4.3 运行时重置:setSlices

setSlices(width, height, leftWidth, rightWidth, topHeight, bottomHeight, skipScale9)可以重置尺寸与切片配置(NineSlice.js),适合切换纹理复用同一对象,但注意它不能改变 3 Slice / 9 Slice 类型。

五、尺寸约束与常用 API

5.1 最小尺寸

  • 最小宽度=leftWidth+rightWidth
  • 最小高度=topHeight+bottomHeight(9 Slice)。

如果需求尺寸小于最小尺寸,应通过setScale/displayWidth缩放实现,而不是继续调小宽高。源码中的widthheightsetter 会强制钳制到下限(NineSlice.js、NineSlice.js),3 Slice 对象修改高度会被忽略。

5.2 常用方法与属性

API作用
setSize(w, h)同时设置宽高,并同步更新命中区域(hitArea)尺寸(NineSlice.js)
setDisplaySize(w, h)按显示尺寸调整scale(NineSlice.js)
displayWidth/displayHeight读取/设置含缩放后的显示尺寸
setOrigin(x, y)设置原点并重建顶点(默认 0.5 居中)
setTint(color)/clearTint()WebGL 下设置/清除着色
setSizeToFrame()恢复为纹理帧尺寸

设置widthheightorigin时,内部会自动调用updateVertices()重建顶点位置,无需手动干预。

六、源码原理:顶点与批量渲染

从源码实现看,Nine Slice 并非多个 Sprite 的组合,而是由内部维护的vertices顶点数组渲染的单一网格对象

  • 每个四边形由 6 个NineSliceVertex顶点构成;
  • 3 Slice 对象包含18 个顶点(3 个四边形),9 Slice 对象包含54 个顶点(9 个四边形);
  • 顶点按“左上、上中、右上、左中、中心、右中、左下、下中、右下”的顺序排列(NineSlice.js)。

渲染时,NineSliceWebGLRenderer.js 逐个四边形把顶点交给 batch 处理器,与场景中其他 Sprite、Graphics 的绘制调用一起合批提交,不产生额外的绘制开销(draw call)。因此:

  • 1 个 3 Slice 对象 ≈ 并排 3 个 Sprite 的性能;
  • 1 个 9 Slice 对象 ≈ 并排 9 个 Sprite 的性能。

WebGL 专属限制

截至 Phaser 3.60,Nine Slice仅支持 WebGL 渲染。在 NineSliceRender.js 中,renderCanvasNOOP(空操作),只有WEBGL_RENDERER构建定义生效时才挂载 WebGL 渲染器;nineslice工厂方法同样只在WEBGL_RENDERER下注册。使用 Canvas 渲染器时该对象无法显示,请将render.type配置为Phaser.AUTOPhaser.WEBGL

七、进阶:平铺模式(tileX / tileY)

对于重复花纹(如齿轮纹理、网格背景),拉伸中段会造成图案变形。此时可启用平铺模式:tileX让水平中段重复铺开,tileY让垂直中段重复铺开(NineSlice.js)。

实现上,updateVertices()会先通过_calcRepeatCount()计算中段能容纳的整数平铺次数,再按平铺数重建顶点数组并更新 UV(NineSlice.js)。注意:每个瓦片仍会被轻微拉伸以凑整整数数量,因此纹理最好是无缝的,以免平铺接缝处出现可见瑕疵。

八、进阶:Texture Packer 7.1 的 scale9 数据

从 Phaser 3.70 起(Frame.js 的Frame.setScale9),Nine Slice 支持直接读取Texture Packer 7.1.0 及以上版本导出的 atlas JSON 中的 scale9 数据

this.add.nineslice(400, 300, 'ui-panel');

当帧带有frame.scale9数据时,构造函数与setSlices自动从scale9Borders读取四个切片尺寸,无需再手动传参;widthheight缺省时也会自动采用帧原始尺寸。需要说明的是,该对象不支持 Texture Packer 的裁剪(trimmed)纹理,因为裁剪会干扰纹理的正确拉伸。

九、测试与验证

仓库中 Nine Slice 相关单元测试位于 tests/gameobjects/nineslice/:

  • NineSliceFactory.test.js:验证工厂模块在 WebGL 构建定义下可正常加载;
  • NineSliceCreator.test.js、NineSliceRender.test.js、NineSliceVertex.test.js:分别覆盖创建器、渲染器与顶点类的基础行为。

由于 NineSlice 依赖完整的 WebGL 场景上下文,纯 Node 环境下的测试以导入冒烟测试为主,实际效果建议在浏览器示例中验证。

小结

Phaser 3.60 的 Nine Slice 游戏对象将九宫格缩放这一 UI 常用能力原生化:通过leftWidth/rightWidth/topHeight/bottomHeight四个参数即可声明边角保护区,配合width/height/setSize动态调整大小而保持四角不变形;3 Slice 变体满足纯横向拉伸场景;tileX/tileY与 Texture Packer scale9 数据则进一步覆盖了平铺与编辑器工作流需求。其全部顶点统一合批渲染,在 WebGL 下与 Sprite、Graphics 共存于显示列表而不产生额外开销,是构建自适应 UI 的理想选择。

【免费下载链接】phaserPhaser is a fun, free and fast 2D game framework for making HTML5 games for desktop and mobile web browsers, supporting Canvas and WebGL rendering.项目地址: https://gitcode.com/gh_mirrors/ph/phaser

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

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

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

立即咨询