GameFrameWork框架(Unity3D)使用笔记(七)游戏主流程ProcedureMain——虽然游戏本来没有怪物但是为了使用状态机模块写一个简单怪物AI吧(上)

Unity Game FrameWork—模块使用—数据表 官方说明:可以将游戏数据以表格(如 Microsoft Excel)的形式进行配置后,使用此模块使用这些数据表。数据表的格式是可以自定义的。 阅读详情

目录

前言:

一、怪物需求定制

1、大致功能

2、具体设计

二、状态逻辑设计

1、有哪些状态

 2、状态的转换

(1)追逐状态

(2)攻击状态

  (3)懒散巡视状态

  (4)返回状态

3、画个状态转换图

        ​编辑

三、开始撸代码!

未完待续!


前言:

        状态机是游戏开发中很常用的东西,GF中也有对应的模块FSM(有限状态机)。但是呢,前面我用到的玩家预制体里的控制脚本是一把梭的。。。而且代码是很久以前写的可读性很差。。。(就是说不想再重构了呗),然后在这个游戏里目前并没有AI怪物的需求。。。

        所以为了强行练习使用GF的有限状态机模块,我准备写一个简单的怪物AI,使用FSM来完成。


一、怪物需求定制

1、大致功能

        怪物一开始在一个范围内来回巡视,当它发现玩家的时候,将会追赶和攻击玩家,但是当玩家移动到一定范围之外它就会放弃追逐。

2、具体设计

        如图所示,蓝色、红色范围是相对怪物的范围,绿色、紫色标识的范围是地图上的固定范围;一开始怪物在绿色范围内徘徊,当玩家进入它的蓝色范围的时候,怪物会追赶玩家;若玩家在追赶过程中进入了怪物的红色范围,则怪物进入攻击状态;若在追逐过程中怪物跑出了紫色范围,则怪物停止追逐并且返回绿色范围。


二、状态逻辑设计

        既然我们要用状态机实现,我们就得提前想好有哪些状态,状态转换的条件等。

1、有哪些状态

        由上面的具体设计不难写出一共有四个状态:

 2、状态的转换

        然后继续想想,在每个状态下遇到哪些条件会转换到哪些状态。。。(所以实际上程序员大多数时间是在撑着脑袋盯着屏幕发呆。。。)

(1)追逐状态

        在这个状态下,

        如果检测到玩家进入了攻击范围,则进入攻击状态;

        如果自己超过了追逐范围,则进入返回状态;

(2)攻击状态

        在这个状态下,

        如果检测到玩家跑出了攻击范围,则进入追逐状态;

  (3)懒散巡视状态

        在这个状态下,

        如果检测到玩家进入了发现范围,则进入追逐状态;

  (4)返回状态

        在这个状态下,

        如果检测到玩家进入了发现范围,则进入追逐状态;

        如果返回到了懒散巡视范围,则进入懒散巡视状态;

3、画个状态转换图

        虽然说做到上面的分析就完全可以开始写代码了,但是这里为了更直观展示这个状态机,我画个简陋的图:

        


三、开始撸代码!

        既然怪物的状态及其转换已经设计的差不多啦,那么下面就开始撸状态代码!

        啊慢着慢着,好像怪物实体还没有写出来呢。。。。。。

        所以先来写一下怪物实体!由于之前已经详细写过有关实体的内容,这里就很简略地飞快写一下!

         

         啊怪物预制体就做好了,怪物是这样一个圆形片片绝对不是因为我懒不想画不想花时间太懒了之类的,而是因为游戏设定中怪物就是这......编不下去了,就是我懒。

        然后就是写怪物实体数据类:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace ShadowU
{
    public class EnemyData : EntityData
    {
        /// <summary>
        /// Idle的圆心
        /// </summary>
        public Vector3 Origin
        {
            get; 
            private set;
        }
        /// <summary>
        /// Idle的范围
        /// </summary>
        public float IdleRange
        {
            get;
            private set;
        }
        /// <summary>
        /// 追逐的范围
        /// </summary>
        public float ChasingRange
        {
            get;
            private set;
        }
        /// <summary>
        /// 发现玩家的范围
        /// </summary>
        public float AlarmRange
        {
            get;
            private set;
        }
        /// <summary>
        /// 攻击范围
        /// </summary>
        public float AttackRange
        {
            get;
            private set;
        }
        /// <summary>
        /// 移动速度
        /// </summary>
        public float Speed
        {
            get;
            private set;
        }
        public EnemyData(int entityId, int typeId) : base(entityId, typeId)
        {

        }
    }

}

        逻辑过会儿再写。

        因为怪物也是实体,所以在实体配置表里面加一个,用于加载预制体:

        ​​​​​​​​​​​​​​​​​​​​​

        然后EnemyData的属性也通过数据表进行配置:

        写数据表行类:

 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityGameFramework.Runtime;
namespace ShadowU
{
    public class DREnemy : DataRowBase
    {
        private int m_Id;
        public override int Id
        {
            get { return m_Id; }
        }
        /// <summary>
        /// Idle的圆心
        /// </summary>
        public Vector3 Origin
        {
            get;
            private set;
        }
        /// <summary>
        /// Idle的范围
        /// </summary>
        public float IdleRange
        {
            get;
            private set;
        }
        /// <summary>
        /// 追逐的范围
        /// </summary>
        public float ChasingRange
        {
            get;
            private set;
        }
        /// <summary>
        /// 发现玩家的范围
        /// </summary>
        public float AlarmRange
        {
            get;
            private set;
        }
        /// <summary>
        /// 攻击范围
        /// </summary>
        public float AttackRange
        {
            get;
            private set;
        }
        /// <summary>
        /// 移动速度
        /// </summary>
        public float Speed
        {
            get;
            private set;
        }
        public override bool ParseDataRow(string dataRowString, object userData)
        {
            string[] columnStrings = dataRowString.Split(DataTableExtension.DataSplitSeparators);
            for (int i = 0; i < columnStrings.Length; i++)
            {
                columnStrings[i] = columnStrings[i].Trim(DataTableExtension.DataTrimSeparators);
                Log.Debug(columnStrings[i]);
                Log.Debug(columnStrings[i].Length);
            }
            int index = 0;
            index++;
            m_Id = int.Parse(columnStrings[index++]);
            index++;
            string[] rowSpawnPosition = columnStrings[index++].Split('|');
            Origin = new Vector3(float.Parse(rowSpawnPosition[0]), float.Parse(rowSpawnPosition[1]), float.Parse(rowSpawnPosition[2]));
            IdleRange = float.Parse(columnStrings[index++]);
            ChasingRange = float.Parse(columnStrings[index++]);
            AlarmRange = float.Parse(columnStrings[index++]);
            AttackRange = float.Parse(columnStrings[index++]);
            Speed = float.Parse(columnStrings[index++]);
            return true;
        }
    }
}

        加载数据表:

把读取数据表配置信息赋值给实体数据的逻辑放在EnemyData的构造函数里:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityGameFramework.Runtime;
using GameFramework.DataTable;
namespace ShadowU
{
    public class EnemyData : EntityData
    {
        /// <summary>
        /// Idle的圆心
        /// </summary>
        public Vector3 Origin
        {
            get; 
            private set;
        }
        /// <summary>
        /// Idle的范围
        /// </summary>
        public float IdleRange
        {
            get;
            private set;
        }
        /// <summary>
        /// 追逐的范围
        /// </summary>
        public float ChasingRange
        {
            get;
            private set;
        }
        /// <summary>
        /// 发现玩家的范围
        /// </summary>
        public float AlarmRange
        {
            get;
            private set;
        }
        /// <summary>
        /// 攻击范围
        /// </summary>
        public float AttackRange
        {
            get;
            private set;
        }
        /// <summary>
        /// 移动速度
        /// </summary>
        public float Speed
        {
            get;
            private set;
        }
        public EnemyData(int entityId, int typeId) : base(entityId, typeId)
        {
            IDataTable<DREnemy> dtEnemy = GameEntry.DataTable.GetDataTable<DREnemy>();
            DREnemy drEnemy = dtEnemy.GetDataRow(typeId);
            Origin = drEnemy.Origin;
            IdleRange = drEnemy.IdleRange;
            ChasingRange = drEnemy.ChasingRange;
            AlarmRange = drEnemy.AlarmRange;
            AttackRange = drEnemy.AttackRange;
            Speed = drEnemy.Speed;
            
            Position = Origin;
        }
    }

}

然后写Enemy的实体逻辑类(状态机之后写所以这里只写了测试实体数据是否读取成功的逻辑):

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityGameFramework.Runtime;
namespace ShadowU
{
    public class Enemy : EntityLogic
    {
        protected override void OnInit(object userData)
        {
            base.OnInit(userData);
            CachedTransform.position = ((EnemyData)userData).Position;
        }

        protected override void OnShow(object userData)
        {
            base.OnShow(userData);
            EnemyData enemyData = (EnemyData)userData;
            Log.Debug("敌人加载成功");
            Log.Info(enemyData.Origin);
            Log.Info(enemyData.IdleRange);
            Log.Info(enemyData.ChasingRange);
            Log.Info(enemyData.AlarmRange);
            Log.Info(enemyData.AttackRange);
            Log.Info(enemyData.Speed);
        }

        protected override void OnUpdate(float elapseSeconds, float realElapseSeconds)
        {
            base.OnUpdate(elapseSeconds, realElapseSeconds);

        }
    }
}


 然后在实体扩展中扩展一个加载敌人的函数

然后在主流程调用测试一下:

 

 运行!

 

成功! 

未完待续!

     

                

      

【GameFramework框架内置模块】13、流程(Procedure) 流程(Procedure)可以帮助我们管理游戏中的各个阶段。在StarForceProcedure基于FSM状态机的思想,将每个流程看成一个状态,通过切换状态来执行不同的流程。在每个流程中,设置回调函数,可以对流程进行控制和管理,比如下载的操作,等待下载完毕再切换状态。 阅读详情

相关推荐

GameFramework教程✨二十三、Web请求

Web请求

星河造梦坊 1767

GameFrameWork框架(Unity3D)使用笔记(八) 实现场景加载进度条

游戏在转换场景的时候,需要花费时间来加载相关的资源。而这个过程往往因为游戏场景的规模和复杂度以及玩家电脑配置的原因花费一小段时间(虽然这个项目里用不到)。目前大多数游戏在转换关卡这种时候都会有个加载界面,显示加载进度。这样玩家可以对啥时候能加载好有个心理预估(判断要不要因为加载太久浪费时间不如卸载游戏(开个玩笑))。那本篇的任务就是探究如何在UGF框架下实现加载场景的进度条功能。

HowToPause的博客 4699

【Unity3D框架】GameFramework框架完全教程《全网最全》-专栏文章目录

最近准备学习框架,了解到GameFramework框架是国产Unity3D框架,准备学习一下,但是奈何全网关于这个框架的教程实在太少。为了让自己学习方便一些,也为了让后来者乘凉。将框架以自己的理解整理目录,并且补全教程。觉得教程有帮助别忘了一键三连哦。

优快云博客专家,Unity3D高级软件工程师,《Unity 3D从入门到实战》作者。 2万+

GameFrameWork框架(Unity3D)使用笔记(五)游戏主流程ProcedureMain

前面已经完成了UI界面切换等功能,但是真正的游戏内容主体部分还没有开始,下面就来实现游戏的主流程。本篇的内容又用到了GF的一个新模块:Entity。这一次我们主要实现的就是在场景中生成主角。

HowToPause的博客 2545

【GameFramework框架】四、GameFramework框架内置模块

【GameFramework框架内置模块】1、全局配置(Config)【GameFramework框架内置模块】2、数据节点(Data Node)【GameFramework框架内置模块】3、数据表(Data Table)【GameFramework框架内置模块】4、调试器(Debugger)【GameFramework框架内置模块】5、下载(Download)【GameFramework框架内置模块】6、实体(Entity)【GameFramework框架内置模块】7、事件(Event)

优快云博客专家,Unity3D高级软件工程师,《Unity 3D从入门到实战》作者。 5643

GameFramework教程✨六、怎样加载一个3D UI

怎样加载一个3D UI

星河造梦坊 1583

GameFramework——Procedure

前言 结束了鹅厂三个多月的实习后,回到学校,当然不能忘记学习GF,今天就来盘一盘GF中的精华——流程 流程,是贯穿游戏运行时整个生命周期的有限状态机。通过流程,将不同的游戏状态进行解耦将是一个非常好的习惯。对于网络游戏,你可能需要如检查资源流程、更新资源流程、检查服务器列表流程、选择服务器流程、登录服务器流程、创建角色流程等流程,而对于单机游戏,你可能需要在游戏选择菜单流程和游戏实际玩法流程之间做切换。如果想增加流程,只要派生自 ProcedureBase 类并实现自己的流程类即可使用。 看到上面的类图我

白小飞 635

究竟什么是虚拟同步机???虚拟同步机巨简单理解

同步发电机定子铁芯的内圆均匀分布着定子槽,槽内按一定规律嵌放着对称的三相绕组U1-U2、V1-V2、W1-W2,如图1所示。工作原理转子铁芯上装有制成一定形状的成对磁极,磁极上绕有励磁绕组。励磁绕组上通直流电时,会在电机的气隙中形成极性相间的分布磁场,称为励磁磁场。当原动机拖动转子以恒定速度旋转时,励磁磁场随转轴一起旋转并顺次切割定子各相绕组。定子电枢绕组中将会感应出大小和方向周期性变化的三相对称正弦感应电动势。各相电动势大小相等,相位各差120°。

weixin_53126442的博客 2607

Unity GameFramework StarForce 流程介绍

根据提供的案例参考即可实现自己的组件。

zzzsss123333的博客 1558

R以及Rstudio超详细安装教程(包括安装过程中出现的问题)

R安装及Rstudio安装,以及安装中出现的问题

2301_76908257的博客 1万+

解放双手!利用海康VM全局脚本+通讯管理打造自动化视觉控制系统

本文深入探讨了如何利用海康VisionMaster(VM)的全局脚本和通讯管理功能,打造高效自动化视觉控制系统。通过全局变量实现数据共享与动态调整,结合多协议通讯管理,提升系统响应速度与智能化水平,适用于工业4.0环境下的复杂视觉检测与决策场景。

weixin_28720573的博客 424

GameFrameWork框架(Unity3D)使用笔记(七)游戏主流程ProcedureMain——虽然游戏本来没有怪物但是为了使用状态机模块写一个简单怪物AI吧(下)

这篇直接接着上一篇开始写啦。使用GF的状态机的话,我们要做的主要是实现一下各个状态类。虽然有些细节需要注意但是总体来说按照设计填代码就好了。

HowToPause的博客 1137

瑞萨CS+仿真器高级配置指南:从原理到实战优化RX系列MCU调试

在嵌入式系统开发中,仿真器是进行早期验证和深度调试的核心工具。它通过精确模拟目标微控制器的指令集、内存和外设行为,构建了一个虚拟的硬件沙盒环境。其工作原理在于对CPU核心、时钟系统和外设接口的软件模拟,使开发者能在无物理硬件时进行代码逻辑验证、内存布局规划和时序分析。这项技术的核心价值在于大幅降低开发成本、加速问题定位,并支持复杂场景的复现与测试。在瑞萨RX系列MCU开发中,CS+集成开发环境内置的仿真器功能尤为强大。本文聚焦于**指令解码缓存**和**外设时钟比率**等关键配置的深度解析,阐述如何通过优化

cmff98425的博客 1121

Unity Game FrameWork—模块使用—有限状态机

如果用if else来判断则太过复杂不便于梳理流程,以及一堆条件变量控制,当状态改变或触发条件改变,整个流程的维护是很复杂的,我们不能专注当前状态要做的事,需要去看前后关联的触发条件是否会有问题,这些条件变量在其他状态是怎样的,会不会由于条件变量的改变导致其它问题,因此诞生了状态机。状态机所解决的问题与流程需要完成的事情是完全吻合的。创建ProcedureFSM流程类,在流程类中创建英雄实体并添加逻辑类FSM_Hero,英雄逻辑类中创建四种状态类,随后在四个状态类中完成触发及状态切换等工作。

qq_37619255的博客 1266

3D缺陷检测实战:从点云处理到工业质检优化

本文深入探讨了3D缺陷检测在工业质检中的实战应用,详细阐述了从点云处理到深度学习模型部署的完整技术链路。文章通过PCB焊点检测等实际案例,分析了3D视觉相比2D检测在识别深度、形状等缺陷方面的优势,并分享了点云滤波、分割、特征提取以及利用YOLOv5等模型进行高效检测的优化心得与落地挑战。

weixin_28358083的博客 337

单节锂电池充电管理芯片应用原理阐述及应用领域

当电池电压超过2.9V时,采用恒流模式对电池充电,充电电流由PROG 管脚和GND之间的电阻RPROG确定。充电电压固定于4.2V,而充电电流可通过一个电阻器进行外部设置当充电电流在达到最终浮充电压之后降至设定值1/10时,WD4054将自动终止充电循环。在待机模式中,WD4054 对 BAT 引脚电压进行监控,只有当 BAT 引脚电压低于再充电阈值电压4.1V时(对应电池容量80%~90%),才会开始新的充电循环,重新对电池进行充电,这就避免了对电池进行不必要的反复充电,有效延长电池的使用寿命。

weixin_41141180的博客 943

GameFramework篇:新项目搭建GF框架需知

1.AB包相关配置 GF资源管理实属牛批,但要使用它,并不是全自动的,还是要配置一下 创建一个静态类,类名随意,注意里面至少要包含以下三个属性,后面的地址所指就是打AB包所必须的三个配置文件(这个地址可以自己更改,注意改变路径就行) [AssetBundleBuilderConfigPath] public static string AssetBundleBuilderC...

设计师养成记 7106

BRep实战指南:从理论到文件格式的全面解析

本文全面解析BRep(边界表示法)从基础概念到文件格式的实战应用。详细介绍了BRep的数学定义、工业应用场景,以及STEP、IGES等主流文件格式的对比与使用技巧,帮助读者掌握三维建模核心技术并优化性能。

weixin_29235923的博客 407

Unity开发日记【第七天】——怪物的移动和动画及类的实现

怪物的移动和动画及通过主类Class的创建,使得所有敌人调取动画效果

qq_50688324的博客 2686

前言:为什么要学习爬虫和逆向,该如何学习?

学习爬虫和逆向技术是当今数字化时代的重要技能,尤其在数据分析、安全研究、自动化工具开发等领域有广泛应用。以下是详细的学习理由、路径和方法:

数据知道的博客 1万+

FLAnalyzer:一款基于Node.js的竞彩足球赛事数据采集、分析程序

FLAnalyzerFLAnalyzer 是一款基于Node.js的竞彩足球赛事数据爬取、分析工具。安装依赖在命令行下进入 FLAnalyzer 所在目录,执行:npm install配置数据库在爬取或查看数据前,请确认 MongoDB 处于运行状态。默认数据库为 mongodb://localhost/FLAnalyzer,可在 ./configs/database.js 中修改。爬取数据在命令行下进入 FLAnalyzer 所在目录,执行:node spiderFLAnalyzer 将自动爬取所有数据,首次爬取大约需要12-18小时,再次运行时将只会爬取有变动的部分。爬取内容基本信息:比赛时间、联赛名称、双方队名等亚盘赔率:BET365,澳门欧盘赔率:平均欧赔、立博、BET365、澳门、伟德、SNAI必发盈亏:彩客网(310win)的必发盈亏模拟数据竞

初识GameFrameWork(二)有限状态机FSM篇

FSM(Finite State Machine)有限状态机,主要用于管理一些线性的流程,例如怪物的AI,游戏的流程控制等等。本篇将着重介绍GF层的FSM模块以及在Unity中的使用。

m0_74936198的博客 1745

Delphi 13.2控件之EhLib 13.0 Build 13.0.021 Professional Edition - Full Source.7z

Delphi 13.2控件之EhLib 13.0 Build 13.0.021 Professional Edition - Full Source.7z

GameFrameWork框架(Unity)学习资料汇总(持续更新)

学习GameFrameWork框架时对我帮助很大的一些资料。

HowToPause的博客 5929

libpng15-1.5.30-7.el8.x86_64.rpm

官方离线安装包,亲测可用

高校学生成绩预测系统:联邦学习Python实现+Streamlit可视化界面+多算法对比实验数据

一套开箱即用的高校学生成绩预测开发资源,基于联邦学习框架实现隐私保护下的分布式模型训练,支持FedRep、SCAFFOLD、Ditto、APFL、L2GD、MTL、FedProx、本地训练等多种算法配置。代码使用PyTorch构建,包含完整训练/测试流程(train_utils.py、test.py)、网络定义(Nets.py)、通信辅助模块(comm_helpers.py)及数据采样逻辑(sampling.py),适配多客户端模拟场景。配套两个真实结构的学生成绩数据集(data-JSJfb1.csv、样本数据.csv),以及MNIST风格模拟实验的损失与准确率记录文件(如losses_fedrep_mnist5.csv、accs_fedrep_mnist3.csv等),便于复现与横向对比。通过Streamlit快速搭建交互式可视化平台,可实时查看混淆矩阵(confusion_matrix.png)、训练曲线、参数配置与预测结果。项目已完整运行验证,支持直接执行main_xxx.py启动对应算法,附带详细README.md说明运行步骤与参数含义。适用于课程设计、毕业设计、科研入门或联邦学习教学演示,无需深度调参即可观察不同算法在成绩预测任务上的收敛性与泛化表现。

Unity Game FrameWork—框架学习—流程对状态机封装

IFsm:接口功能,获取有限状态机(名称、持有者、状态数量、是否正常运行、持续时间、当前状态,所有状态、数据(设置、移除、获取))、开始有限状态机、是否存在有限状态机状态。m_Fsms中的流程类是在ProduceManager流程管理器的初始化方法Initialize中创建状态,foreach流程轮询会判断流程是否销毁,销毁即跳过,只执行当前流程的Update。定义了流程的初始化、进入、轮询、离开、销毁生命周期方法。分析完代码,流程的本质其实是状态机,创建流程就是创建状态机,流程是对状态机进行了一层封装。

qq_37619255的博客 681

UnityGameFramework

资源工程 Game Framework文档,项目地址(GitHub),下载资源包,操作步骤 GameFramework简介 预制体包含了框架的基本组件(如图一),各个组件的含义下面有介绍(如图二)。 图一图二...

Until 2477

简易版GameFramework游戏框架搭建教程(一)前言

GameFramework(以下简称GF),是由Ellan开发的一款开源Unity游戏框架,以下是来自官网的简介:Game Framework 是一个基于 Unity 5.3+ 引擎的游戏框架,主要对游戏开发过程中常用模块进行了封装,很大程度地规范开发过程、加快开发速度并保证产品质量。在最新的 Game Framework 版本中,包含以下 17 个内置模块,后续还将开发更多的扩展模块供开发者使用...

道友请留步 1万+

Unity Game FrameWork—模块使用—数据结点

根节点可以添加任意子节点,子节点也可添加子节点。每个节点只能存储一个数据,这个数据可以是任意类型,根节点不能添加数据。如果多次设置数据,则只会保存最后一次的数据及其类型。Node03.GetOrAddChild(“所属组件名”).SetData(“UIComponent”);官方说明:将任意类型的数据以树状结构的形式进行保存,用于管理游戏运行时的各种数据。数据结点存储的是游戏运行时数据,不会保存到本地。这一块功能,Demo里面没有使用。下面看下数据结点相关API调用。

qq_37619255的博客 732
上一篇: GameFrameWork框架(Unity3D)使用笔记(六)游戏主流程ProcedureMain——从数据表加载出所需实体
下一篇: GameFrameWork框架(Unity3D)使用笔记(七)游戏主流程ProcedureMain——虽然游戏本来没有怪物但是为了使用状态机模块写一个简单怪物AI吧(下)
咖啡咖_CoffCa
博客等级 码龄6年 116粉丝 58原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

咖啡咖_CoffCa

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值