You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何构建不将C#脚本编译为DLL的Unity游戏?支持玩家编辑脚本

解决Unity脚本编译为DLL的问题,实现玩家可编辑C#脚本机制

首先明确:Unity在构建项目或进入Play模式时,默认会将Assets目录(含Packages内代码)下的C#脚本编译为DLL。要实现玩家可编辑脚本并动态生效的核心机制,无法通过阻止Unity编译脚本实现,而是要采用运行时动态编译外部脚本的方案,具体步骤如下:

1. 将玩家编辑脚本放在Unity编译目录外

Unity仅编译Assets目录内的脚本,因此让玩家编辑的脚本存放在独立于Assets的外部文件夹(比如项目根目录下的PlayerScripts文件夹),从根源避免Unity将其打包进项目DLL。

2. 用C# Roslyn/CodeDOM实现运行时编译

在游戏中通过代码读取玩家编辑的脚本文件,使用C#编译API动态编译为程序集后加载执行逻辑。示例代码(基于Roslyn,需在Unity中添加Microsoft.CodeAnalysis相关包):

using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using System.IO;
using System.Reflection;
using UnityEngine;

public class PlayerScriptCompiler : MonoBehaviour
{
    public string playerScriptDir = "PlayerScripts";
    public string targetScriptName = "CustomPlayerLogic.cs";

    public void CompileLoadScript()
    {
        string fullPath = Path.Combine(Application.dataPath, "../", playerScriptDir, targetScriptName);
        if (!File.Exists(fullPath))
        {
            Debug.LogError("玩家脚本不存在");
            return;
        }

        // 读取脚本内容
        string scriptCode = File.ReadAllText(fullPath);

        // 设置编译依赖,引入Unity核心程序集
        var references = new MetadataReference[]
        {
            MetadataReference.CreateFromFile(typeof(object).Assembly.Location),
            MetadataReference.CreateFromFile(typeof(MonoBehaviour).Assembly.Location),
            MetadataReference.CreateFromFile(typeof(GameObject).Assembly.Location)
        };

        // 构建编译任务
        var syntaxTree = CSharpSyntaxTree.ParseText(scriptCode);
        var compilation = CSharpCompilation.Create("DynamicPlayerScript")
            .WithOptions(new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary))
            .AddReferences(references)
            .AddSyntaxTrees(syntaxTree);

        // 执行编译
        using var stream = new MemoryStream();
        var compileResult = compilation.Emit(stream);

        if (!compileResult.Success)
        {
            // 输出编译错误给玩家查看
            foreach (var error in compileResult.Diagnostics)
            {
                Debug.LogError($"脚本编译错误:{error.GetMessage()}");
            }
            return;
        }

        // 加载编译后的程序集并实例化逻辑类
        stream.Seek(0, SeekOrigin.Begin);
        Assembly assembly = Assembly.Load(stream.ToArray());
        Type logicType = assembly.GetType("CustomPlayerLogic");
        
        if (logicType != null && typeof(MonoBehaviour).IsAssignableFrom(logicType))
        {
            GameObject logicObj = new GameObject("PlayerScriptInstance");
            logicObj.AddComponent(logicType);
        }
    }
}

3. 自定义Assembly Definition的局限性

若需隔离项目内部分脚本,可创建.asmdef文件,但自定义Assembly仍会被Unity编译为DLL,仅能实现脚本分组,无法阻止编译,因此不适用于玩家编辑的外部脚本场景。

重要注意事项

  • 需做安全限制:玩家脚本可能存在恶意代码,要通过API白名单、沙箱环境等方式限制脚本权限。
  • 跨平台适配:移动端需额外配置文件读取权限,确保游戏能访问外部脚本文件夹。

内容的提问来源于stack exchange,提问作者Amatorii

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 10:33:10