跑酷游戏开发笔记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 样式)。

相关推荐
前端摸鱼匠1 小时前
Vue 3 的v-bind合并行为:讲解v-bind与普通属性合并的规则
前端·javascript·vue.js·前端框架·ecmascript
REDcker2 小时前
浏览器端Web程序性能分析与优化实战 DevTools指标与工程清单
开发语言·前端·javascript·vue·ecmascript·php·js
OBiO20132 小时前
Cell | 突破AAV载体容量限制!路中华/姜玉武/刘太安团队开发AAVLINK系统实现大基因递送
笔记
智者知已应修善业3 小时前
【51单片机2个按键控制流水灯运行与暂停】2023-9-6
c++·经验分享·笔记·算法·51单片机
Linsk3 小时前
Java和JavaScript的关系真是雷峰和雷峰塔的关系吗?
java·javascript·oracle
当时只道寻常3 小时前
浏览器文本复制到剪贴板:企业级最佳实践
javascript
sakiko_4 小时前
UIKit学习笔记5-使用UITableView制作聊天页面
笔记·学习·swift·uikit
Alice-YUE4 小时前
【js高频八股】防抖与节流
开发语言·前端·javascript·笔记·学习·ecmascript
小陈phd6 小时前
TensorRT 入门完全指南(一)——从核心定义到生态工具全解析
人工智能·笔记
是上好佳佳佳呀6 小时前
【前端(十一)】JavaScript 语法基础笔记(多语言对比)
前端·javascript·笔记