1.进度条的实现
TypeScript
import { _decorator, Component, Node, Sprite, UITransform, Vec3 } from 'cc';
const { ccclass, property } = _decorator;
@ccclass('ProgressBar')
export class ProgressBar extends Component {
// 进度条填充组件
@property(Sprite)
fill: Sprite = null;
// 草的节点
@property(Node)
grass: Node = null;
// 进度条自身的UI变换组件(用于获取尺寸和位置)
private _barTrans: UITransform = null;
// 进度条左边界的世界坐标
private _leftBoundary: number = 0;
// 进度条右边界的世界坐标
private _rightBoundary: number = 0;
// 当前进度值(0-1范围)
protected _progress: number = 0;
// 组件加载时调用的生命周期函数
protected onLoad(): void {
// 获取进度条自身的UITransform组件
this._barTrans = this.node.getComponent(UITransform);
// 计算进度条的左右边界
this.calculateBoundaries();
// 如果有填充组件,初始化其位置和锚点
if (this.fill) {
const fillTrans = this.fill.node.getComponent(UITransform);
fillTrans.anchorX = 0; // 设置填充区域左对齐(锚点X为0)
// 设置填充组件的初始位置(左边界对齐)
this.fill.node.setPosition(this._leftBoundary - this.node.worldPosition.x, 31.267, 0);
}
// 初始化草的初始位置(左边界)
if (this.grass) {
this.grass.setPosition(this._leftBoundary - this.node.worldPosition.x, 31.267, 0);
}
}
// 计算进度条左右边界的世界坐标
private calculateBoundaries(): void {
// 如果没有UITransform组件则直接返回
if (!this._barTrans) return;
// 获取进度条节点的世界坐标
const barWorldPos = this.node.worldPosition;
// 计算进度条宽度的一半(因为默认锚点在中心)
const halfWidth = this._barTrans.width / 2;
// 左边界 = 节点世界坐标X - 宽度一半
this._leftBoundary = barWorldPos.x - halfWidth;
// 右边界 = 节点世界坐标X + 宽度一半
this._rightBoundary = barWorldPos.x + halfWidth;
}
/**
* 设置进度(外部调用接口)
* @param progress 进度值(0-1范围)
*/
setProgress(progress: number): void {
// 限制进度值在0-1之间
this._progress = Math.max(0, Math.min(progress, 1));
// 更新进度条显示
this._updateBar();
}
/**
* 更新进度条和草的位置显示
*/
protected _updateBar(): void {
// 若关键组件不存在则不执行更新
if (!this.fill || !this.grass || !this._barTrans) return;
// 根据进度计算草在世界坐标系中的X坐标
const grassWorldX = this._leftBoundary + (this._rightBoundary - this._leftBoundary) * this._progress;
// 将草的世界坐标转换为相对于进度条节点的本地坐标
const grassLocalX = grassWorldX - this.node.worldPosition.x;
// 更新草的位置(固定Y坐标为31.267)
this.grass.setPosition(grassLocalX, 31.267, 0);
// 更新填充区域(草坪)的宽度,使其右边界与草对齐
const fillTrans = this.fill.node.getComponent(UITransform);
fillTrans.width = grassLocalX - this.fill.node.position.x;
}
/**
* 获取当前进度值
* @returns 当前进度(0-1范围)
*/
getProgress(): number {
return this._progress;
}
// 组件启动时调用的生命周期函数
start(): void {
// 初始进度设为0
this.setProgress(0);
}
}
TypeScript
import { _decorator, Component, Node } from 'cc';
import { ProgressBar } from './ProgressBar';
const { ccclass, property } = _decorator;
@ccclass('GameStart')
export class GameStart extends Component {
// 进度条组件
@property(ProgressBar)
grassProgressBar: ProgressBar = null;
// 动画持续时间(秒)
@property
duration: number = 5;
// 已流逝的时间(秒)
private _elapsedTime: number = 0;
// 每帧更新函数
update(deltaTime: number): void {
// 若进度条存在且动画未结束
if (this.grassProgressBar && this._elapsedTime < this.duration) {
// 累加流逝时间
this._elapsedTime += deltaTime;
// 计算进度(已用时间/总时间)
const progress = this._elapsedTime / this.duration;
// 更新进度条显示
this.grassProgressBar.setProgress(progress);
}
}
}
整体思路
实现了一个进度条功能,通过ProgressBar类定义进度条的核心逻辑(显示、更新进度),并通过GameStart类控制进度条的动画过程(在指定时间内从 0 到 1 完成进度更新)。主要用于可视化展示某个过程的进度(如加载、任务完成等),其中包含进度填充区域和一个跟随进度移动的 "草" 节点。
关键代码与逻辑解释
- ProgressBar.ts 核心逻辑
该类负责进度条的显示控制,包括边界计算、进度更新和 UI 同步。
-
核心属性
fill: Sprite:进度条的填充区域(如绿色草坪部分),用于可视化进度grass: Node:跟随进度移动的节点(如草的图标)_barTrans: UITransform:进度条自身的尺寸组件,用于计算边界_leftBoundary/_rightBoundary:进度条左右边界的世界坐标,限定进度范围_progress:当前进度(0-1 之间,0 为未开始,1 为完成)
-
初始化逻辑(
onLoad)TypeScriptprotected onLoad(): void { this._barTrans = this.node.getComponent(UITransform); this.calculateBoundaries(); // 计算边界 // 初始化填充区域位置(左对齐) if (this.fill) { const fillTrans = this.fill.node.getComponent(UITransform); fillTrans.anchorX = 0; // 左锚点,确保填充从左侧开始 this.fill.node.setPosition(this._leftBoundary - this.node.worldPosition.x, 31.267, 0); } // 初始化草的位置(左边界) if (this.grass) { this.grass.setPosition(this._leftBoundary - this.node.worldPosition.x, 31.267, 0); } }- 加载时获取进度条尺寸组件,并计算左右边界(基于节点世界坐标和宽度)
- 初始化填充区域和草节点的初始位置(左边界对齐)
-
边界计算(
calculateBoundaries)TypeScriptprivate calculateBoundaries(): void { if (!this._barTrans) return; const barWorldPos = this.node.worldPosition; const halfWidth = this._barTrans.width / 2; this._leftBoundary = barWorldPos.x - halfWidth; // 左边界 = 中心X - 半宽 this._rightBoundary = barWorldPos.x + halfWidth; // 右边界 = 中心X + 半宽 }- 基于进度条节点的世界坐标和宽度,计算左右边界,确定进度的可移动范围
-
进度更新逻辑(
setProgress和_updateBar)TypeScriptsetProgress(progress: number): void { this._progress = Math.max(0, Math.min(progress, 1)); // 限制进度在0-1 this._updateBar(); // 同步UI } protected _updateBar(): void { if (!this.fill || !this.grass || !this._barTrans) return; // 计算草的世界坐标X(根据进度在左右边界间插值) const grassWorldX = this._leftBoundary + (this._rightBoundary - this._leftBoundary) * this._progress; // 转换为本地坐标(相对于进度条节点) const grassLocalX = grassWorldX - this.node.worldPosition.x; // 更新草的位置 this.grass.setPosition(grassLocalX, 31.267, 0); // 更新填充区域宽度(从左边界到草的位置) const fillTrans = this.fill.node.getComponent(UITransform); fillTrans.width = grassLocalX - this.fill.node.position.x; }setProgress:外部调用接口,接收 0-1 的进度值并限制范围,触发 UI 更新_updateBar:根据当前进度计算草的位置和填充区域宽度,实现进度可视化
- GameStart.ts 控制逻辑
该类负责驱动进度条动画,在指定时间内自动更新进度。
-
核心逻辑
TypeScriptupdate(deltaTime: number): void { if (this.grassProgressBar && this._elapsedTime < this.duration) { this._elapsedTime += deltaTime; // 累加每帧时间 const progress = this._elapsedTime / this.duration; // 计算进度(已用时间/总时间) this.grassProgressBar.setProgress(progress); // 更新进度条 } }- 通过
update函数(每帧调用)累计时间,计算当前进度(随时间线性增长) - 调用
ProgressBar的setProgress方法,实现进度条在duration时间内从 0 到 1 的动画效果
- 通过
整体逻辑流程图
GameStart初始化时获取ProgressBar实例- 每帧更新:
GameStart累计时间 → 计算进度 → 调用setProgress ProgressBar接收进度 → 计算草和填充区域的位置 / 宽度 → 同步 UI 显示- 当时间达到
duration时,进度为 1,动画结束
通过分离进度计算(GameStart)和进度显示(ProgressBar),实现了逻辑解耦,便于后续扩展(如修改进度更新方式或 UI 样式)。