Unreal Engine 4:如何将第三方库打包到Android应用中

本文详细介绍如何在UE4中整合并使用第三方C++库,包括库的编译、配置及在不同平台上的打包流程,特别针对Android平台的适配。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

一、准备一个UE4工程

二、准备一个简单的C++代码,作为第三方库使用

三、将以上代码编译成库

四、在UE4工程中使用这些库

五、使这些库能在UE4工程中编译并运行

六、在UE4工程中创建组件并配置参数

七、在UE4工程中配置打包Android应用所需要的参数


本文通过一个例子,一步步介绍如何在windows下将第三方库打包到一个Android应用中。

一、准备一个UE4工程

 在这个工程中建一个Widget:BP_Input,并在这个Widget中放入一个TextBox:TextBox_Input。这个TextBox用于显示从第三方库中获取的信息。

并给这个Widget添加一个用于设置TextBox内容的函数:SetInputText

二、准备一个简单的C++代码,作为第三方库使用

share.h

#ifdef HELLO_API_EXPORTS
  #define HELLO_API			    __declspec(dllexport)

#elif defined(HELLO_API_IMPORTS)
  #define HELLO_API			    __declspec(dllimport)

#else
  #define HELLO_API

#endif

Hello.h

#include <string>
#include "share.h"

class HELLO_API Hello
{
public:
	std::string sayHello(std::string msg);
};

Hello.cpp

#include "Hello.h"

std::string Hello::sayHello(std::string msg)
{
    return "Hello " + msg;
};

三、将以上代码编译成库

1、编译成Windows动态库,用于在Windows上做开发和测试用

在编译时,设置编译参数:HELLO_API_EXPORTS,编译后得到如下两个文件:

hello.dll
hello.lib

2、编译成Android上的共享库,用于打包到Android应用中

(1)、安装android-ndk,android-sdk,apache-ant,jdk等

使用C:\Epic Games\UE_4.18\Engine\Extras\AndroidWorks\Win64\CodeWorksforAndroid-1R6u1-windows.exe进行安装。默认都会安装到C:\NVPACK目录下。

(2)、生成toolchain工具

由于是在windows下生成能在Android系统中使用的库,所以需要使用toolchain工具。

可以在C:\NVPACK\android-ndk-r12b\build\tools下找到make_standalone_toolchain.py和make_standalone_toolchain.sh,它们是用来生成toolchain工具的。toolchain工具可以编译C++程序,并生成指定CPU架构的库文件。

make_standalone_toolchain.py --arch arm --api 24 --package-dir C:\NVPACK

--arch             表示指定CPU的架构,这里使用的是arm。如果还要生成其它CPU架构的库可指定其它值。
--api               表示目录Android API的Level
--package-dir 表示toolchain工具生成后存放的目录

关于make_standalone_toolchain.py的详细参数,可以通过make_standalone_toolchain.py --help查到:

make_standalone_toolchain.py --help
usage: make_standalone_toolchain.py [-h] --arch
                                    {arm,arm64,mips,mips64,x86,x86_64}
                                    [--api API]
                                    [--stl {gnustl,libc++,stlport}] [--force]
                                    [-v]
                                    [--package-dir PACKAGE_DIR | --install-dir INSTALL_DIR]

Creates a toolchain installation for a given Android target. The output of
this tool is a more typical cross-compiling toolchain. It is indended to be
used with existing build systems such as autotools.

optional arguments:
  -h, --help            show this help message and exit
  --arch {arm,arm64,mips,mips64,x86,x86_64}
  --api API             Target the given API version (example: "--api 24").
  --stl {gnustl,libc++,stlport}
                        C++ STL to use.
  --force               Remove existing installation directory if it exists.
  -v, --verbose         Increase output verbosity.
  --package-dir PACKAGE_DIR
                        Build a tarball and install it to the given directory.
                        If neither --package-dir nor --install-dir is
                        specified, a tarball will be created and installed to
                        the current directory.
  --install-dir INSTALL_DIR
                        Install toolchain to the given directory instead of
                        packaging.

执行完成后将会生成文件:C:\NVPACK\arm-linux-androideabi.zip。解压后就可以使用它来编译C++程序了。

(3)、使用toolchain编译C++代码

C:\NVPACK\arm-linux-androideabi\bin\arm-linux-androideabi-g++.exe share.h Hello.h Hello.cpp -shared -fPIC -o libhello.so

-shared    表示生成共享库
-fPIC       表示生成与路径无关的库

四、在UE4工程中使用这些库

在AndroidProject目录下创建ThirdParty目录及其子目录,并将C++代码和生成的库文件按如下目录结构存放:

AndroidProject-+
                         |-Source
                         |-ThirdParty-+
                                              |-hello-+
                                                          |-android-+
                                                                          |-libhello.so
                                                          |-include-+
                                                                          |-Hello.h
                                                                          |-share.h
                                                          |-x64-----+
                                                                          |-hello.lib
                                                                          |-hello.dll

修改AndroidProjectGameMode.h和AndroidProjectGameMode.cpp

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/GameModeBase.h"
#include "Blueprint/UserWidget.h"
#include "AndroidProjectGameMode.generated.h"

UCLASS(minimalapi)
class AAndroidProjectGameMode : public AGameModeBase
{
	GENERATED_BODY()

private:

	UPROPERTY()
		UUserWidget* InputWidget;

public:
	AAndroidProjectGameMode();

	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UMG")
	TSubclassOf<UUserWidget> InputWidgetClass;

	void BeginPlay();
};
#include "AndroidProjectGameMode.h"
#include "AndroidProjectCharacter.h"
#include "UObject/ConstructorHelpers.h"
#include "Hello.h"

AAndroidProjectGameMode::AAndroidProjectGameMode()
{
	// set default pawn class to our Blueprinted character
	static ConstructorHelpers::FClassFinder<APawn> PlayerPawnBPClass(TEXT("/Game/ThirdPersonCPP/Blueprints/ThirdPersonCharacter"));
	if (PlayerPawnBPClass.Class != NULL)
	{
		DefaultPawnClass = PlayerPawnBPClass.Class;
	}
}

void AAndroidProjectGameMode::BeginPlay()
{
	Super::BeginPlay();

	InputWidget = CreateWidget<UUserWidget>(GetWorld(), InputWidgetClass);
	if (InputWidget != nullptr)
	{
		UFunction* SetInputText = InputWidget->FindFunction(FName("SetInputText"));
		if (SetInputText) {
			Hello hello;
			std::string msg = hello.sayHello("Tom");
			FString InputText = FString(msg.c_str());

			UE_LOG(LogTemp, Log, TEXT("InputText=%s"), *InputText);

			InputWidget->ProcessEvent(SetInputText, &InputText);
		}

		InputWidget->AddToViewport();
	}
};

可以从代码中看到,使用FindFunction查找InputWidget中定义的函数,使用ProcessEvent调用InputWidget中的函数。同时也调用了第三方库的sayHello方法,并将调用结果显示在InputWidget上。

五、使这些库能在UE4工程中编译并运行

1、修改AndroidProject.Build.cs文件

using UnrealBuildTool;
using System;
using System.IO;

public class AndroidProject : ModuleRules
{
    private string ThirdPartyPath
    {
        get
        {
            return Path.GetFullPath(Path.Combine(ModuleDirectory, "../../ThirdParty"));
        }
    }

    public AndroidProject(ReadOnlyTargetRules Target) : base(Target)
	{
	PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;

	PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "HeadMountedDisplay", "UMG" });

        string helloPath = ThirdPartyPath + "/hello";

        PublicIncludePaths.Add(helloPath + "/include");

        if (Target.Platform == UnrealTargetPlatform.Win64)
        {
            Definitions.AddRange(new string[] { "HELLO_API_IMPORTS" });

            PublicLibraryPaths.Add(helloPath + "/x64");
            PublicAdditionalLibraries.Add("hello.lib");
        }
        else if (Target.Platform == UnrealTargetPlatform.Android)
        {
            AdditionalPropertiesForReceipt.Add(new ReceiptProperty("AndroidPlugin", Path.Combine(helloPath, "My_APL_armv7.xml")));

            PublicLibraryPaths.Add(Path.Combine(helloPath, "android"));
            PublicAdditionalLibraries.Add("hello");
        }
        else if (Target.Platform == UnrealTargetPlatform.IOS)
        {
            PublicLibraryPaths.Add(helloPath + "/IOS");
        }
    }
}

由于使用了Widget,所以要在PublicDependencyModuleNames中添加UMG。
上述代码根据编译目标平台(Target.Platform)的不同设置不同的配置:
(1)Windows平台下,由于导入DLL时需要使用__declspec(dllimport),所以添加HELLO_API_IMPORTS参数。添加导出库hello.lib用作编译。
(2)Android平台下,添加库名hello,toolchain在编译时会自动加上前缀lib和后缀.so,最终生成的库名为libhello.so;如果此处直接使用库名libhello.so而不用库名hello,编译时反而会找不到库。

My_APL_armv7.xml放在AndroidProject\ThirdParty\hello目录下,文件的内容如下:

<?xml version="1.0" encoding="utf-8"?>
<!-- steps to add to build additions -->
<root xmlns:android="http://schemas.android.com/apk/res/android">
	<!-- init section is always evaluated once per architecture -->
	<init>
	    <setBool result="bSupported" value="false"/>
	        <isArch arch="armeabi-v7a">
	            <setBool result="bSupported" value="true"/>
	        </isArch>
	</init>

	<!-- optional files or directories to copy to Intermediate/Android/APK -->
	<resourceCopies>
	    <isArch arch="armeabi-v7a">
	        <copyFile src="$S(BuildDir)/../../../ThirdParty/hello/android/libhello.so"
	                  dst="$S(BuildDir)/libs/armeabi-v7a/libhello.so" />
		</isArch>
	</resourceCopies>

	<!-- optional libraries to load in GameActivity.java before libUE4.so -->
	<soLoadLibrary>
	    <if condition="bSupported">
	        <true>
			<loadLibrary name="hello" failmsg="Failed to load myso library" />
		</true>
	    </if>
	</soLoadLibrary>
</root>

 它的作用是:
(1)在打包Android应用时,将AndroidProject/ThirdParty/hello/android/libhello.so文件拷贝到AndroidProject/Intermediate/Android/APK/libs/armeabi-v7a/目录下。这样就不用手动拷贝了。
(2)在AndroidProject\Intermediate\Android\APK\src\com\epicgames\ue4\GameActivity.java文件中生成加载hello库的代码:

try
{
	System.loadLibrary("hello");
}
catch (java.lang.UnsatisfiedLinkError e)
{
	Log.debug(e.toString());
	Log.debug("Failed to load hello library");
}

2、将hello.dll拷贝到AndroidProject\Binaries\Win64目录下。这样就可以从VS中启动UE4了。

六、在UE4工程中创建组件并配置参数

1、启动UE4后,创建Blueprint

在AndroidProjectGameMode上点右键,创建AndroidProjectGameMode对应的Blueprint:BP_AndroidProjectGameMode,打开BP_AndroidProjectGameMode后,设置Input Widget Class:

2、配置GameMode

将GameModeOverride配置成BP_AndroidProjectGameMode

3、查看运行结果

点击“Play”后,就可以看到界面右上方文本框中正确显示了调用第三方库函数后返回的结果。但这只是说明了在Windows上调用第三方库运行正常,接下来要将第三方库打包到Android应用中去。

七、在UE4工程中配置打包Android应用所需要的参数

1、打开Editor -> Project Settings,其中必须的配置如下:

其它可选的优化配置:

Project    -> Packaging    -> Packaging         -> Create compressed cooked packages  (Enable)
Project    -> Packaging    -> Packaging         -> Full Rebuild           (Enable)
Project    -> Packaging    -> Packaging         -> Exclude editor content when cooking  (Enable)

Engine     -> Network      -> Libcurl              -> Verify peer   (Disable)
Engine     -> Input        -> Mouse Properties  ->  Use Mouse for touch   (Disable)
Engine     -> Input        -> Moblie                 ->  Always show touch interface (Disable)
Engine     -> Rendering    -> Moblie            ->  Max Dynamic Point Lights = 0

Platforms  -> Android      -> APKPackaging      ->  Enable Gradle instead of Ant    (Disable)
Platforms  -> Android      -> APKPackaging      ->  Package game data inside apk?   (Enable)

Plugins    -> OculusVR     -> Splash Screen     ->  Auto Enable    (Disable)
Plugins    -> TCP Messaging-> Transport         ->  Enable Transport    (Disable)
Plugins    -> UDP Messaging-> Transport         ->  Enable Transport    (Disable)

 2、然后进行打包操作。选择ETC1是因为所有Android设备都支持它:

 3、最后会在AndroidProject\Android_ETC1下生成:AndroidProject-armv7-es2.apk。这就是最终可以放到Android设置安装并运行的文件。

4、如果想知道在打包过程中执行了哪些命令,这此命令都的哪些参数的话,可以在C:\Epic Games\UE_4.18\Engine\Source\Programs\UnrealBuildTool\System\ActionGraph.cs文件中的如下方法中

public static bool ExecuteActions(BuildConfiguration BuildConfiguration, List<Action> ActionsToExecute, 
bool bIsRemoteCompile, out string ExecutorName, string TargetInfoForTelemetry, EHotReload HotReload)

对ActionsToExecute加上打印语句:

using (StreamWriter writer = new StreamWriter("D:/Temp/log.txt", true))
{
	foreach (Action action in ActionsToExecute)
	{
		writer.WriteLine("action=" + action.ToString());
	}

	writer.Close();
}

 就可以查看到如下日志:

action=C:/NVPACK/android-ndk-r12b\toolchains/llvm\prebuilt/windows-x86_64\bin/clang++.exe - @"D:\projects\AndroidProject\Intermediate\Build\Android\AndroidProject\Development\AndroidProject\AndroidProjectGameMode.cpp-armv7-es2.o_37AAC25F.rsp"
action=C:/NVPACK/android-ndk-r12b\toolchains/llvm\prebuilt/windows-x86_64\bin/clang++.exe -  -nostdlib -Wl,-shared,-Bsymbolic -Wl,--no-undefined -Wl,-gc-sections -target armv7-none-linux-androideabi --sysroot="C:/NVPACK/android-ndk-r12b/platforms/android-24/arch-arm" -gcc-toolchain "C:/NVPACK/android-ndk-r12b/toolchains/arm-linux-androideabi-4.9/prebuilt/windows-x86_64" -march=armv7-a -Wl,--fix-cortex-a8 -Wl,-soname,libUE4.so -o "D:/projects/AndroidProject/Binaries/Android/AndroidProject-armv7-es2.so" @"D:/projects/AndroidProject/Intermediate/Build/Android/AndroidProject/Development/AndroidProject-armv7-es2.so.response"
action=C:\Microsoft\Visual Studio\2017\Professional\VC\Tools\MSVC\14.15.26726\bin\HostX64\x64\cl.exe -  @"D:\projects\AndroidProject\Intermediate\Build\Win64\UE4Editor\Development\AndroidProject\AndroidProjectGameMode.cpp.obj.response"
action=C:\Microsoft\Visual Studio\2017\Professional\VC\Tools\MSVC\14.15.26726\bin\HostX64\x64\link.exe - @"D:\projects\AndroidProject\Plugins\tanx\Intermediate\Build\Win64\UE4Editor\Development\UE4Editor-tanx-4779.dll.response"
action=C:\Microsoft\Visual Studio\2017\Professional\VC\Tools\MSVC\14.15.26726\bin\HostX64\x64\link.exe - @"D:\projects\AndroidProject\Intermediate\Build\Win64\UE4Editor\Development\UE4Editor-AndroidProject-7604.dll.response"
action=C:\Microsoft\Visual Studio\2017\Professional\VC\Tools\MSVC\14.15.26726\bin\HostX64\x64\lib.exe - @"D:\projects\AndroidProject\Plugins\tanx\Intermediate\Build\Win64\UE4Editor\Development\UE4Editor-tanx-4779.lib.response"
action=C:\Microsoft\Visual Studio\2017\Professional\VC\Tools\MSVC\14.15.26726\bin\HostX64\x64\lib.exe - @"D:\projects\AndroidProject\Intermediate\Build\Win64\UE4Editor\Development\UE4Editor-AndroidProject-7604.lib.response"
action=C:\Microsoft\Visual Studio\2017\Professional\VC\Tools\MSVC\14.15.26726\bin\HostX64\x64\link.exe - @"D:\projects\AndroidProject\Intermediate\Build\Win64\UE4Editor\Development\UE4Editor-AndroidProject.dll.response"

 其中,D:\projects\AndroidProject\Intermediate\Build\Android\AndroidProject\Development\AndroidProject\AndroidProjectGameMode.cpp-armv7-es2.o_37AAC25F.rsp中存放编译参数:

 -target armv7-none-linux-androideabi --sysroot="C:/NVPACK/android-ndk-r12b/platforms/android-24/arch-arm" -gcc-toolchain "C:/NVPACK/android-ndk-r12b/toolchains/arm-linux-androideabi-4.9/prebuilt/windows-x86_64" -c -fdiagnostics-format=msvc -Wall -Wdelete-non-virtual-dtor -Wno-unused-variable -Wno-unused-function -Wno-switch -Wno-tautological-compare -Wno-unused-private-field -Wno-local-type-template-args -Wno-return-type-c-linkage -Wno-reorder -Wno-unknown-pragmas -Wno-invalid-offsetof -Wno-logical-op-parentheses -Wshadow -Wno-error=shadow -Wundef -Wno-error=undef -Wno-undefined-bool-conversion -Wno-gnu-string-literal-operator-template -Wno-unused-local-typedef -Wno-inconsistent-missing-override -g2 -gdwarf-4 -O3 -fno-exceptions -fno-rtti -funwind-tables -fstack-protector -fno-strict-aliasing -fpic -fno-short-enums -march=armv7-a -mfloat-abi=softfp -mfpu=vfpv3-d16 -ffunction-sections -fdata-sections -fsigned-char -Werror -DPLATFORM_64BITS=0 -DPLATFORM_ANDROID_ARM=1 -I"C:/NVPACK/android-ndk-r12b/sources/cxx-stl/gnu-libstdc++/4.9/include" -I"C:/NVPACK/android-ndk-r12b/sources/cxx-stl/gnu-libstdc++/4.9/libs/armeabi-v7a/include" -I"C:/NVPACK/android-ndk-r12b/sources/android/native_app_glue" -I"C:/NVPACK/android-ndk-r12b/sources/android/cpufeatures" -I"C:/Epic Games/UE_4.18/Engine/Source" -I"D:/projects/AndroidProject/ThirdParty/hello/include" -I"D:/projects/AndroidProject/Source" -I"D:/projects/AndroidProject/Intermediate/Build/Android/UE4/Inc/AndroidProject" -I"D:/projects/AndroidProject/Source/AndroidProject" -I"Runtime/Core/Public" -I"Runtime/Core/Public/Internationalization" -I"Runtime/Core/Public/Async"

。。。。。。。

 -x c++ -std=c++14 -O3 -include "C:/Epic Games/UE_4.18/Engine/Intermediate/Build/Android/UE4/Development/Engine/SharedPCH.Engine-armv7-es2.h" -include "D:/projects/AndroidProject/Intermediate/Build/Android/AndroidProject/Development/AndroidProject/Definitions.AndroidProject.h" -o "D:/projects/AndroidProject/Intermediate/Build/Android/AndroidProject/Development/AndroidProject/AndroidProjectGameMode.cpp-armv7-es2.o" "D:/projects/AndroidProject/Source/AndroidProject/AndroidProjectGameMode.cpp"

 D:/projects/AndroidProject/Intermediate/Build/Android/AndroidProject/Development/AndroidProject-armv7-es2.so.response中存放链接参数:

"AndroidProject/AndroidProject.cpp-armv7-es2.o"
"AndroidProject/AndroidProjectCharacter.cpp-armv7-es2.o"
"AndroidProject/AndroidProjectGameMode.cpp-armv7-es2.o"
"AndroidProject/AndroidProject.init.gen.cpp-armv7-es2.o"
"AndroidProject/AndroidProjectCharacter.gen.cpp-armv7-es2.o"
"AndroidProject/AndroidProjectGameMode.gen.cpp-armv7-es2.o"
"tanx/Module.tanx.cpp-armv7-es2.o"
"UELinkerFixups/UELinkerFixups.cpp-armv7-es2.o"
 -L"C:/NVPACK/android-ndk-r12b/sources/cxx-stl/gnu-libstdc++/4.9/libs/armeabi-v7a" -L"C:/Epic Games/UE_4.18/Engine/Source/ThirdParty/Android/cxa_demangle/armeabi-v7a" -L"C:/Epic Games/UE_4.18/Engine/Source/ThirdParty/ICU/icu4c-53_1/Android/ARMv7/lib" -L"C:/Epic Games/UE_4.18/Engine/Source/ThirdParty/FreeType2/FreeType2-2.4.12/Lib/Android/ARMv7" -L"C:/Epic Games/UE_4.18/Engine/Source/ThirdParty/HarfBuzz/harfbuzz-1.2.4/Android/ARMv7/Release/" -L"C:/Epic Games/UE_4.18/Engine/Source/ThirdParty/libPNG/libPNG-1.5.27/lib/Android/ARMv7" -L"C:/Epic Games/UE_4.18/Engine/Source/ThirdParty/PhysX/Lib/Android/ARMv7" -L"C:/Epic Games/UE_4.18/Engine/Source/ThirdParty/libcurl/lib/Android/ARMv7" -L"C:/Epic Games/UE_4.18/Engine/Source/ThirdParty/Ogg/libogg-1.2.2/lib/Android/ARMv7" -L"C:/Epic Games/UE_4.18/Engine/Source/ThirdParty/Vorbis/libvorbis-1.3.2/lib/Android/ARMv7" -L"C:/Epic Games/UE_4.18/Engine/Source/ThirdParty/Vorbis/libvorbis-1.3.2/Lib/Android/ARMv7" -L"C:/Epic Games/UE_4.18/Engine/Source/ThirdParty/GooglePlay/gpg-cpp-sdk.v2.3/gpg-cpp-sdk/android/lib/gnustl/armeabi-v7a/" -L"C:/Epic Games/UE_4.18/Engine/Source/ThirdParty/libOpus/opus-1.1/Android/ARMv7/" -L"C:/Epic Games/UE_4.18/Engine/Source/ThirdParty/Oculus/OVRPlugin/OVRPlugin/Lib/armeabi-v7a/" -L"C:/Epic Games/UE_4.18/Engine/Source/ThirdParty/Oculus/OVRPlugin/OVRPlugin/ExtLibs/" -L"C:/Epic Games/UE_4.18/Engine/Source/ThirdParty/PhysX/Lib/Android/ARMv7" -L"D:/unreal_projects/AndroidProject/ThirdParty/hello/android" -Wl,--start-group "-lgnustl_shared" "-lgcc" "-lz" "-lc" "-lm" "-llog" "-ldl" "-lGLESv2" "-lEGL" "-lOpenSLES" "-landroid" "-lcxa_demangle" "-lz" "-licudata" "-licuuc" "-licui18n" "-licule" "-liculx" "-licuio" "-lfreetype2412" "-lharfbuzz" "-lpng" "-lPhysX3PROFILE" "-lPhysX3ExtensionsPROFILE" "-lPhysX3CookingPROFILE" "-lPhysX3CommonPROFILE" "-lPxFoundationPROFILE" "-lPxPvdSDKPROFILE" "-lPsFastXmlPROFILE" "-lcurl" "-logg" "-lvorbis" "-lvorbisfile" "-lgpg" "-lopus" "-lspeex_resampler" "-lOVRPlugin" "-lvrapi" "-lPhysX3VehiclePROFILE" "-lPhysX3CookingPROFILE" "-lhello" "C:/Epic Games/UE_4.18/Engine/Binaries/Android/UE4-Launch-armv7-es2.a" 

。。。。。。

-Wl,--end-group 

 在链接参数中可以看到,使用的都是不含前缀lib和后缀.so的库名。

 

参考文档

How to add a shared library (.so) in android project
Packaging Android Projects
Reducing APK Package Size
Android Development Reference
Android SO文件的兼容和适配
Packaging Projects

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值