目前测试完成了编译器下的使用,是没问题的,运行时有待测试,并且目前仅支持x64

构建方案参考自Gemini3Pro大模型。

写本文章时,使用的zxing-cpp 仓库提交版本为:zxing-cpp/zxing-cpp at d41fd4189266aaaeff0a15e1df02858084284bbf

准备

CMake:手动编译适用于UE的ZXing.dll

zxing-cpp:zxing库的C++实现源码库:zxing-cpp/zxing-cpp: C++ port of ZXing

Developer PowerShell for VS 2022:需要启用相关支持

编译过程

拉取仓库:

git clone https://github.com/zxing-cpp/zxing-cpp.git
cd zxing-cpp

github仓库依赖了一个字仓库,初始化一下

# 1. 初始化子模块(读取.gitmodules配置,注册子模块路径)
git submodule init
# 2. 拉取所有子模块的实际代码
git submodule update

#或者一步到位
# 初始化 + 拉取所有子模块(包括嵌套子模块)
git submodule update --init --recursive

下面生成干净的 Include 和 Lib (使用 CMake Install)

启动Developer PowerShell for VS 2022 然后切到仓库根目录,在根目录下创建build 目录

mkdir build && cd build

生成cmake工程:这里我们需要指定 CMAKE_INSTALL_PREFIX,告诉 CMake 把整理好的文件放到哪里(比如当前目录下的 output 文件夹)。

cmake .. -G "Visual Studio 17 2022" -A x64 -DCMAKE_CXX_STANDARD=20 -DBUILD_SHARED_LIBS=OFF -DZXING_BUILD_EXAMPLES=OFF -DZXING_BUILD_BLACKBOX_TESTS=OFF -DCMAKE_INSTALL_PREFIX="./output"

编译我们需要的

cmake --build . --config Release --target install

然后打开打开 build/output 文件夹,您会看到完美的结构:

  • include/ZXing/:里面是所有整理好的 .h 头文件。

  • lib/:里面是 ZXing.lib

目录结构如下:

📂 output (D:\Other\zxing-cpp\build\output)
├─ 📂 bin
│  ├─ 📄 ZXingReader.exe (946.00 KB)
│  └─ 📄 ZXingWriter.exe (521.50 KB)
├─ 📂 include
│  └─ 📂 ZXing
│     ├─ 📄 Barcode.h (6.83 KB)
│     ├─ 📄 BarcodeFormat.h (2.93 KB)
│     ├─ 📄 BitHacks.h (6.14 KB)
│     ├─ 📄 BitMatrix.h (5.60 KB)
│     ├─ 📄 BitMatrixIO.h (643 B)
│     ├─ 📄 ByteArray.h (1.44 KB)
│     ├─ 📄 CharacterSet.h (782 B)
│     ├─ 📄 Content.h (2.41 KB)
│     ├─ 📄 DecodeHints.h (245 B)
│     ├─ 📄 Error.h (2.93 KB)
│     ├─ 📄 Flags.h (4.46 KB)
│     ├─ 📄 GTIN.h (1.49 KB)
│     ├─ 📄 ImageView.h (5.09 KB)
│     ├─ 📄 Matrix.h (2.30 KB)
│     ├─ 📄 MultiFormatWriter.h (1.35 KB)
│     ├─ 📄 Point.h (3.39 KB)
│     ├─ 📄 Quadrilateral.h (5.30 KB)
│     ├─ 📄 Range.h (3.79 KB)
│     ├─ 📄 ReadBarcode.h (870 B)
│     ├─ 📄 ReaderOptions.h (7.04 KB)
│     ├─ 📄 Result.h (239 B)
│     ├─ 📄 StructuredAppend.h (249 B)
│     ├─ 📄 TextUtfEncoding.h (566 B)
│     ├─ 📄 Version.h (334 B)
│     ├─ 📄 ZXAlgorithms.h (5.65 KB)
│     ├─ 📄 ZXingCpp.h (496 B)
│     └─ 📄 ZXVersion.h (211 B)
└─ 📂 lib
   ├─ 📂 cmake
   │  └─ 📂 ZXing
   │     ├─ 📄 ZXingConfig.cmake (1.21 KB)
   │     ├─ 📄 ZXingConfigVersion.cmake (2.76 KB)
   │     ├─ 📄 ZXingTargets.cmake (4.58 KB)
   │     └─ 📄 ZXingTargets-release.cmake (850 B)
   └─ 📄 ZXing.lib (15.86 MB)

对应文件已在Github开源,可直接使用

https://github.com/ProgramBase/ZXingForUnreal5

在插件中建立目录结构

假设您的插件目录是 Plugins/MyCustomPlugin。请按以下结构放置文件:

MyProject/
└── Plugins/
    └── MyCustomPlugin/
        ├── Source/
        │   ├── MyCustomPlugin/
        │   │   ├── MyCustomPlugin.Build.cs
        │   │   └── ...
        │   └── ThirdParty/           <-- 在插件Source下新建
        │       └── ZXing/            <-- 新建 ZXing 文件夹
        │           ├── Include/      <-- 将刚才 output/include 下的 "ZXing" 文件夹整个拷进来
        │           │   └── ZXing/    <-- 结构应该是 Include/ZXing/ReadBarcode.h
        │           └── Lib/
        │               └── Win64/    <-- 将 output/lib/ZXing.lib 拷进来

注意:最终的头文件路径应该是 .../ThirdParty/ZXing/Include/ZXing/ReadBarcode.h。这样我们在代码里就可以用标准的 #include "ZXing/ReadBarcode.h" 方式引用了。

配置插件的 Build.cs

在插件环境中,ModuleDirectory 指向的是 Plugins/MyCustomPlugin/Source/MyCustomPlugin。我们需要用它来定位 ThirdParty

打开 Plugins/MyCustomPlugin/Source/MyCustomPlugin/MyCustomPlugin.Build.cs

using UnrealBuildTool;
using System.IO; // 必加

public class MyCustomPlugin : ModuleRules
{
    public MyCustomPlugin(ReadOnlyTargetRules Target) : base(Target)
    {
        PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
        
        // 开启 C++20 (ZXing 需要)
        CppStandard = CppStandardVersion.Cpp20;

        PublicDependencyModuleNames.AddRange(
            new string[]
            {
                "Core",
                "CoreUObject",
                "Engine",
                "Projects", // 插件开发常需要这个
                // ... 其他依赖
            }
        );

        // --- ZXing 集成 ---
        
        // 1. 定位到 ThirdParty 目录
        // ModuleDirectory 是当前 .Build.cs 所在目录
        string ThirdPartyPath = Path.GetFullPath(Path.Combine(ModuleDirectory, "../ThirdParty"));
        
        string ZXingIncludePath = Path.Combine(ThirdPartyPath, "ZXing", "Include");
        string ZXingLibPath = Path.Combine(ThirdPartyPath, "ZXing", "Lib", "Win64", "ZXing.lib");

        // 2. 检查文件是否存在 (良好的习惯,防止路径错误导致莫名其妙的链接失败)
        if (!Directory.Exists(ZXingIncludePath))
        {
            throw new BuildException("ZXing Include path not found: " + ZXingIncludePath);
        }

        // 3. 添加头文件包含路径
        // 这样在代码里就可以写 #include "ZXing/ReadBarcode.h"
        PublicIncludePaths.Add(ZXingIncludePath);

        // 4. 链接静态库
        if (Target.Platform == UnrealTargetPlatform.Win64)
        {
            PublicAdditionalLibraries.Add(ZXingLibPath);
        }
        // --- 集成结束 ---
    }
}

此时就可以在代码中愉快的引用了

// 此时 Include 目录下有 ZXing 子目录,所以前缀是 ZXing/
#include "ZXing/ReadBarcode.h" 
#include "ZXing/ImageView.h"

简易代码

.h

#pragma once
// UE核心头文件(只包含一次)
#include "CoreMinimal.h"
#include "Kismet/BlueprintFunctionLibrary.h"// 自身的generated头文件(放在最后)
#include "CommonUtility.generated.h"
/**
 * 
 */
UCLASS()
class PROJECTPLUGINS_API UCommonUtility: public UBlueprintFunctionLibrary
{
	GENERATED_BODY()
public:
	/**
	 * 生成二维码纹理
	 * @param Content       二维码内容 (URL或文本)
	 * @param Width         生成的图片宽度 (像素)
	 * @param Height        生成的图片高度 (像素)
	 * @param Margin        二维码白边宽度 (通常设为0-4)
	 * @param OutTexture    生成的纹理对象
	 * @return              是否生成成功
	 */
	UFUNCTION(BlueprintCallable, Category = "ZXing|Generation")
	static bool GenerateQRCode(FString Content, int32 Width, int32 Height, int32 Margin, UTexture2D*& OutTexture);
};

.cpp


// 防止 Windows 宏冲突
THIRD_PARTY_INCLUDES_START
#include "ZXing/MultiFormatWriter.h"
#include "ZXing/BitMatrix.h"
#include "ZXing/CharacterSet.h" // <--- 新增这一行
THIRD_PARTY_INCLUDES_END
#include "Engine/Texture2D.h"
#include "TextureResource.h"
bool UCommonUtility::GenerateQRCode(FString Content, int32 Width, int32 Height, int32 Margin, UTexture2D*& OutTexture)
{
    OutTexture = nullptr;

    if (Content.IsEmpty() || Width <= 0 || Height <= 0)
    {
        UE_LOG(LogTemp, Error, TEXT("GenerateQRCode: 内容为空或尺寸无效"));
        return false;
    }

    try
    {
    	// 1. 配置 Writer
    	ZXing::MultiFormatWriter Writer(ZXing::BarcodeFormat::QRCode);
        
    	// --- [关键修复] 设置编码格式为 UTF-8 ---
    	Writer.setEncoding(ZXing::CharacterSet::UTF8);
    	// -------------------------------------

    	Writer.setMargin(Margin);

    	// 2. 编码 (将 FString 转为 std::string UTF8)
    	// TCHAR_TO_UTF8 会将中文转为正确的 UTF-8 字节流
    	std::string TextStr = TCHAR_TO_UTF8(*Content);
        
    	// 生成位矩阵
    	auto Matrix = Writer.encode(TextStr, Width, Height);

        // 获取实际生成的宽高 (zxing 可能会根据内容调整实际大小)
        int32 RealWidth = Matrix.width();
        int32 RealHeight = Matrix.height();

        // 3. 创建 UE5 纹理
        // PF_B8G8R8A8 是 UI 和材质常用的格式
        UTexture2D* NewTexture = UTexture2D::CreateTransient(RealWidth, RealHeight, PF_B8G8R8A8);
        if (!NewTexture) return false;

        // 4. 锁定纹理内存以便写入
        FTexture2DMipMap& Mip = NewTexture->GetPlatformData()->Mips[0];
        void* Data = Mip.BulkData.Lock(LOCK_READ_WRITE);
        
        // 转换为 FColor 指针方便操作像素
        FColor* RawData = static_cast<FColor*>(Data);

        // 5. 将 BitMatrix 转换为像素颜色
        // ZXing Matrix 中 true 为黑(前景),false 为白(背景)
        // 注意:RawData 是由上到下,由左到右排列的数组
        for (int32 y = 0; y < RealHeight; y++)
        {
            for (int32 x = 0; x < RealWidth; x++)
            {
                bool bIsBlack = Matrix.get(x, y);
                
                // 写入颜色:黑色或白色 (Alpha 设为 255)
                // 数组索引 = y * 宽度 + x
                RawData[y * RealWidth + x] = bIsBlack ? FColor::Black : FColor::White;
            }
        }

        // 6. 解锁并更新资源
        Mip.BulkData.Unlock();
        
        // 配置纹理属性以获得清晰的像素化效果 (二维码不应该被模糊/抗锯齿)
        NewTexture->Filter = TextureFilter::TF_Nearest;
        NewTexture->SRGB = true; // 既然是 UI 显示,通常开启 sRGB
        NewTexture->UpdateResource();

        OutTexture = NewTexture;
        return true;
    }
    catch (const std::exception& e)
    {
        // 捕获 ZXing 可能抛出的异常 (例如内容太长导致无法生成)
        UE_LOG(LogTemp, Error, TEXT("ZXing Generation Error: %s"), UTF8_TO_TCHAR(e.what()));
        return false;
    }
}