目录
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 -> 选择输出文件夹
- Build 的产物
3.3 命令行运行程序
- 进入终端,cd 到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);