unity游戏开发-3-读入命令行参数、文件写入、条件编译、日志工具

0. 速览

0.1 关键语法

  • 读取命令行参数
    System.Environment.GetCommandLineArgs()

  • 字符串

    • int 转 字符串
      调用 int 变量的 .ToString() 函数

    • 风格化字符串: 以$开头

$"logFilePath: {logFilePath}"
  • 写入文件
StreamWriter writer = new StreamWriter(logFilePath, true);
writer.WriteLine("...");
writer.Close();
  • 条件编译 (通过预处理指令控制代码段编译生效)
#if XXX
// do something
#elif XXX
// do something
#else
// do something
#endif

1. 前置说明

1.1 基础环境准备

  • 引擎、vscode 详见前两篇文章。
  • 这里使用的 c#脚本 是在 unity Editor 界面中新建的,该脚本被赋予在一个 GameObject 上(这个 GameObject 实例也是通过 unity Editor 界面新建的)。
  • 运行:在 unity Editor 界面中点击运行按钮。

1.2 命令行参数是什么

在 Unity 中,当你点击运行按钮时,Unity 编辑器会启动一个运行时环境来运行你的项目,而 System.Environment.GetCommandLineArgs() 获取的是启动这个运行时环境时传递的命令行参数。
(注:以上回答通过 Kimi 的大语言模型生成)

2. 在日志中打印命令行参数

2.1 代码

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

public class main : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        // 从命令行获取参数
        string[] cmdArgs = System.Environment.GetCommandLineArgs();

        if (cmdArgs.Length <= 1)
        {
            Debug.Log("Hello world!");
            return;
        }

        for (int i = 0; i < cmdArgs.Length; ++i)
        {
            Debug.Log("arg " + i.ToString() + ": " + cmdArgs[i]);
        }
    }

    // Update is called once per frame
    void Update()
    {}
}

2.2 运行结果

在 unity Editor 界面的日志中,直接打印出了 11 个参数日志打印命令行参数

3. 在 命令行 带参数启动程序(windows)

3.1 代码:写到日志文件中

        StreamWriter writer = new StreamWriter(logFilePath, true);

        // 从命令行获取参数
        string[] cmdArgs = System.Environment.GetCommandLineArgs();

        for (int i = 0; i < cmdArgs.Length; ++i)
        {
            string msg = "arg " + i.ToString() + ": " + cmdArgs[i];
            writer.WriteLine(msg);
        }

        writer.Close();

3.2 将程序打包成 exe

  • 打包方式
    • 在 unity Editor 界面
    • 进入 File -> Build Settings
    • 选择相应操作系统 -> Build -> 选择输出文件夹
      打包成 exe
  • Build 的产物
    打包产物

3.3 命令行运行程序

  • 进入终端,cd 到exe所在文件夹,带参数执行:
    命令行执行程序
  • 执行结果
    带参运行 exe 的日志文件内容

4. 补充:条件编译

4.1 含义与用途

  • 含义
    在编程中,通过 #if、#else、#elif 和 #endif 等预处理器指令来控制代码是否生效的操作被称为 条件编译(Conditional Compilation)。
  • 用途
    条件编译是一种编译时的代码控制机制,它允许开发者根据特定的条件(通常是宏定义)来决定是否编译某段代码。这在以下场景中非常有用:
    • 平台特定代码:根据不同的操作系统或平台编译不同的代码。
    • 调试与发布模式:在调试模式下启用调试代码,而在发布模式下禁用。
    • 功能开关:通过宏定义启用或禁用某些功能。
    • 版本控制:根据不同的版本编译不同的代码。

4.2 操作方法

  • 步骤1:在 Unity 编辑器中,选择 Edit > Project Settings > Player。
  • 步骤2:在 Other Settings 中找到 Scripting Define Symbols。
  • 步骤3:添加自定义的符号,例如 MY_CUSTOM_ARG,点击 Apply。
  • 步骤4:在代码中通过 #if 指令检查这些符号。

4.2.1 界面操作

设置自定义符号

4.2.2 代码示例

#if MY_CUSTOM_ARG
        Debug.Log("defined MY_CUSTOM_ARG");
#endif

#if MY_CUSTOM_ARG2
        Debug.Log("defined MY_CUSTOM_ARG2");
#endif

4.2.3 运行结果

显然,上述示例代码只会打印 “defined MY_CUSTOM_ARG”
通过条件编译控制代码段生效

5. 思考题:写一个日志工具

5.1 要求

用上述技巧实现一个写入日志的工具,要求是:
(1)文件大小超过 10M 后新建一个日志文件;
(2)文件个数超过 7 个后保留最新的 7 个文件;
(3)在开发过程中 通过 条件 USE_LOG 来使日志工具生效,在最终生成程序时,使得日志工具失效;

5.2 参考答案

using UnityEngine;
using System.IO;
using System.Text;

#if USE_LOG
public static class LogUtility
{
    private static readonly string LogDirectory = Application.dataPath + "/Logs";
    private static readonly int MaxLogFiles = 7; // 最大保留 7 个日志文件
    private static readonly long MaxFileSize = 10 * 1024 * 1024; // 每个文件最大的大小: 10 MB

    public static void WriteLog(string message, LogType logType = LogType.Log)
    {
        string logFilePath = GetLogFilePath();
        if (!Directory.Exists(LogDirectory))
        {
            Directory.CreateDirectory(LogDirectory);
        }

        // 检查文件大小
        if (File.Exists(logFilePath) && new FileInfo(logFilePath).Length > MaxFileSize)
        {
            // 将旧日志文件重命名为 log_YYYYMMDD_HHMMSS.txt
            string bakLogFilePath = GetBakLogFilePath();
            File.Move(logFilePath, bakLogFilePath);
        }

        // 写入日志
        string formattedMessage = $"{System.DateTime.Now} [{logType}] {message}";
        using (StreamWriter writer = new StreamWriter(logFilePath, true, Encoding.UTF8))
        {
            writer.WriteLine(formattedMessage);
        }

        // 删除旧的日志文件
        CleanOldLogs();
    }

    private static string GetLogFilePath()
    {
        return Path.Combine(LogDirectory, $"log_{System.DateTime.Now:yyyyMMdd}_cur.txt");
    }

    private static string GetBakLogFilePath()
    {
        string aimFileTag = $"log_{System.DateTime.Now:yyyyMMdd_HHmmss}";
        string[] logFiles = Directory.GetFiles(LogDirectory, $"{aimFileTag}*.txt");
        if (logFiles.Length == 0)
        {
            return Path.Combine(LogDirectory, $"{aimFileTag}_000.txt");
        }

        System.Array.Sort(logFiles);
        string lastLogFile = logFiles[logFiles.Length - 1];
        int fileId = GetLogFileId(lastLogFile) + 1;
        string fileIdStr = fileId.ToString().PadLeft(3, '0');
        return Path.Combine(LogDirectory, $"{aimFileTag}_{fileIdStr}.txt");
    }

    private static int GetLogFileId(string logFilePath)
    {
        // 按下划线分割字符串
        string[] parts = logFilePath.Split('_');

        // 获取最后一个部分(编号和扩展名)
        string lastPart = parts[parts.Length - 1];

        // 去掉扩展名,提取编号
        string numberStr = lastPart.Split('.')[0];

        // 将字符串转换为 int
        if (int.TryParse(numberStr, out int number))
        {
            return number;
        }
        else
        {
            Debug.LogError("Failed to convert the extracted string to an integer.");
            return -1;
        }
    }

    private static void CleanOldLogs()
    {
        string[] logFiles = Directory.GetFiles(LogDirectory, "log_*.txt");
        if (logFiles.Length > MaxLogFiles)
        {
            System.Array.Sort(logFiles);
            for (int i = 0; i < logFiles.Length - MaxLogFiles; i++)
            {
                Debug.Log("delete: " + logFiles[i]);
                File.Delete(logFiles[i]);
            }
        }
    }
}

#else

public static class LogUtility
{
    public static void WriteLog(string message, LogType logType = LogType.Log) {}
}

#endif

5.3 用法

LogUtility.WriteLog(msg);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值