跑酷游戏开发笔记3 && 游戏开始场景 cocos 3.8.7

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 完成进度更新)。主要用于可视化展示某个过程的进度(如加载、任务完成等),其中包含进度填充区域和一个跟随进度移动的 "草" 节点。

关键代码与逻辑解释

  1. ProgressBar.ts 核心逻辑

该类负责进度条的显示控制,包括边界计算、进度更新和 UI 同步。

  • 核心属性

    • fill: Sprite:进度条的填充区域(如绿色草坪部分),用于可视化进度
    • grass: Node:跟随进度移动的节点(如草的图标)
    • _barTrans: UITransform:进度条自身的尺寸组件,用于计算边界
    • _leftBoundary/_rightBoundary:进度条左右边界的世界坐标,限定进度范围
    • _progress:当前进度(0-1 之间,0 为未开始,1 为完成)
  • 初始化逻辑(onLoad

    TypeScript 复制代码
    protected 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

    TypeScript 复制代码
    private 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

    TypeScript 复制代码
    setProgress(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:根据当前进度计算草的位置和填充区域宽度,实现进度可视化
  1. GameStart.ts 控制逻辑

该类负责驱动进度条动画,在指定时间内自动更新进度。

  • 核心逻辑

    TypeScript 复制代码
    update(deltaTime: number): void {
        if (this.grassProgressBar && this._elapsedTime < this.duration) {
            this._elapsedTime += deltaTime; // 累加每帧时间
            const progress = this._elapsedTime / this.duration; // 计算进度(已用时间/总时间)
            this.grassProgressBar.setProgress(progress); // 更新进度条
        }
    }
    • 通过update函数(每帧调用)累计时间,计算当前进度(随时间线性增长)
    • 调用ProgressBarsetProgress方法,实现进度条在duration时间内从 0 到 1 的动画效果

整体逻辑流程图

  1. GameStart初始化时获取ProgressBar实例
  2. 每帧更新:GameStart累计时间 → 计算进度 → 调用setProgress
  3. ProgressBar接收进度 → 计算草和填充区域的位置 / 宽度 → 同步 UI 显示
  4. 当时间达到duration时,进度为 1,动画结束

通过分离进度计算(GameStart)和进度显示(ProgressBar),实现了逻辑解耦,便于后续扩展(如修改进度更新方式或 UI 样式)。

相关推荐
受之以蒙2 小时前
智能目标检测:用 Rust + dora-rs + yolo 构建“机器之眼”
人工智能·笔记·rust
EniacCheng2 小时前
【RUST】学习笔记-环境搭建
笔记·学习·rust
MoonBit月兔2 小时前
海外开发者实践分享:用 MoonBit 开发 SQLC 插件(其二)
开发语言·javascript·数据库·redis·mysql·moonbit
前端李易安2 小时前
ERROR in ./node_modules/vue-router/dist/vue-router.mjs 被报错折磨半天?真相竟是……
前端·javascript·vue.js
monkey_slh2 小时前
JS逆向实战——最新某东cfe滑块
开发语言·前端·javascript
d111111111d2 小时前
STM32编码电机闭环PID调节教程。
笔记·stm32·单片机·嵌入式硬件·学习·面试
2503_928411562 小时前
12.17 vue递归组件
前端·javascript·vue.js
如果你好2 小时前
🔥 手撕call/apply/bind:从ES6用法到手写实现,吃透this指向核心
前端·javascript
@大迁世界2 小时前
React 以惨痛的方式重新吸取了 25 年前 RCE 的一个教训
前端·javascript·react.js·前端框架·ecmascript