UDN
Search public documentation:

DebuggerInterfaceCH
English Translation
日本語訳
한국어

Interested in the Unreal Engine?
Visit the Unreal Technology site.

Looking for jobs and company info?
Check out the Epic games site.

Questions about support via UDN?
Contact the UDN Staff

UE3 主页 > 编辑器 & 工具编程 > UnrealScript 调试器界面
UE3 主页 > 虚幻脚本 > UnrealScript 调试器界面

UnrealScript 调试器界面


概述


让我们了解通常使用的调试会话以及研究它的情况。在您可以调试 UnrealScript 之前,您需要在调试模式中编译您的脚本(通过向命令行添加 "-debug",例如,UDK make -debug)。它会将其他控件和映射信息添加到编译的包中。 只要您具有使用调试信息编译的脚本,您就可以通过两种方法的其中一种在引擎中激活调试器。第一种方法是在处理第一个 UnrealScript 字节码时通过使用会使 UE3 激活脚本调试器的 "-autodebug" 命令行开关。另一种方法,可以在游戏控制台中使用 "toggledebugger" 控制台命令激活调试器并立即中断。

让我们看一下在您激活调试器(使用任意一种方法)时所发生的情况:

  1. 首先,UE3 会加载界面 DLL. 默认情况下,它会加载文件 "DebuggerInterface.dll",但是在 Debugger.ini 文件中可以覆盖该设置。这个 DLL 是将调试命令发送到引擎并返回结果的管道。
  2. 只要加载完成,UE3 就立即调用会创建返回引擎的通信管道的导出函数 SetCallback()。然后,它会清除 3 个变量查看列表(局部、全局和用户查看)。
  3. 最后,它调用到时会显示或激活调试器的导出函数 ShowDllForm。

最初的外部调试器界面是一小的 Borland Delphi 结构 DLL,而且界面还保留了相同的向后兼容性。让我们看一下需要从界面 DLL 中导出的函数。

界面


调试器界面需要执行 19 个函数。它们是:

void SetCallback(void* CallbackFunc)
 它是其中要在 DLL 中进行管理的最重要的函数。在第一次进行激活时,引擎会尝试通过该函数设置回调。您应该存储其中传递的函数指针并且以后使用它与引擎进行通信。稍后我们将会讨论可以传递哪些命令。

void ShowDllForm()
 在调用时,!ShowDLLForm() 需要可以激活调试器的窗口并使其可见。

void BuildHierarchy()
void ClearHierarchy()
在引擎想要发信号给调试器时会调用这两个函数,d over the currently loaded class hierarchy. 在它们之间,UE3 会进行某些 AddClassToHierarchy() 调用,传入已加载类层次结构的树形结构。

void AddClassToHierarchy(const char* ClassName)
该函数用于将一个类添加到调试器的类层次结构。该最初调试器在层次结构树形结构视图中保持了已加载类的列表。 使用以下格式将该信息传递作为字符串: .. 这样,您可能会有以下信息,比如: "core..object"(因为对象没有父代) "engine.object.actor" "utgame.gameinfo.utgame" 调试器处理该信息的方式取决于外部应用程序。

void ClearWatch(int WatchType)
void ClearAWatch(int WatchType)
UE3 会支持 3 种不同的查看类型: 局部、全局和用户查看。会在 UE3 想要清除这些列表中的某一个时调用该函数。它可以支持以下查看类型:

  1. 局部查看
  2. 全局查看
  3. 用户查看
外部调试器主要负责管理和显示查看列表中的数据。 ClearWatch() 是在初始 Delphi 调试器示例中的延期。引擎仍然会调用它,所以需要执行它。我们已经推荐只需将对 ClearWatch() 的调用重定向为您的 ClearAWatch() 处理器。

int AddAWatch(int WatchType, int ParentIndex, const char* VarName, const char* VarValue)
引擎会使用该函数更新调试器查看列表。单独的变量通常会生成多个 AddAWatch() 调用。例如,为向量添加一个查看(具有 3 个子代的结构体)将会产生 4 个 AddAWatch() 调用: 第一个调用的是变量,其他三个调用的是 X、Y 和 Z。 该函数的参数包括:

   • WatchType: 查看类型与 ClearWatch() 中使用的值的类型相同。
   • ParentIndex: 这是是它的父代在当前对象中的唯一索引。当引擎通过对象查找要添加的成员变量用无限递归调试游戏,每一个将会获取唯一索引。
   • VarName: 它是变量的值。
   • VarValue: 它是要显示的值或其他有关这个变量的显示数据。

AddAWatch() 的返回结果必需是在查看列表中描述该特定变量的唯一 ID。

让我们进一步看一下向量示例。 采用以下脚本代码:

function TestFunc()
{
    局部向量 TestVec;
    TestVec.X = 10;
    TestVec.Y=20;
    TestVec.Z=30;
    log(TestVec);
}

如果您在记录声明上设置断点,当调试器中断时,UE3 做的第一件事是使用 ClearWatch(0) 清除局部查看。然后它会尝试为 TestVec 添加查看。它会产生以下 AddAWatch() 调用:

AddAWatch(0, 0, "!TestVec",""); // 应该返回 1
AddAWatch(0,1,"X","10"); // 应该返回 2
AddAWatch(0,1,"Y","20"); // 应该返回 3
AddAWatch(0,1,"Z","20"); // 应该返回 4

void LockList(int WatchList)
void UnlockList(int WatchList)
在 UE3 要对一个给定的查看列表进行更新时会调用这两个函数。调试器应该采取可以尽可能顺利地进行更新所需要的任何步骤,直至调用 UnlockList() 函数。

void AddBreakpoint(const char* ClassName, int LineNo)
void RemoveBreakpoint(const char* ClassName, int LineNo)
当 UE3 需要添加显示它追踪的断点时,它会使用调试器调用 AddBreakPoint 记录它。!ClassName 是存在断点的类的名称, LineNo 是行号。外部调试器主要负责跟踪这些类,并在相关的行上显示断点。 同样, RemoveBreakPoint() 会通知调试器删除现有断点。

void EditorLoadClass(const char* ClassName)
UE3 会使用该函数指导调试器为 ClassName 变量指定的类加载脚本。将 ClassName 传递为 .。调试器将会主要负责分割字符串并加载相应的文件。

void EditorGotoLine(int LineNo, int bHighlight)
UE3 会使用该函数指示该调试器显示当前源代码文件中的特定行。注意: EditorGotoLine() 通常会先于 EditorLoadClass() 调用。

void AddLineToLog(const char* Text)
UE3 会在该函数需要将某些内容输出到调试器的记录文件时调用它。文本就是要添加的字符串。

void CallStackClear()
当 UE3 中断并需要更新调试器中的调用栈时。它要做的第一件事是执行 CallStackClear()。这应该会给调试器发送信号,提示 UE3 想要重新构建调用栈。

void CallStackAdd(const char* CallStackEntry)
中断之后,UE3 会进行一些 CallStackAdd() 调用以便在调试器上可以构建调用栈。该调试器应该会采用进来的数据并在某个位置显示它。!CallStackEntry 是一个包含类名称以及行号的字符串。

void SetCurrentObjectName(const char* ObjectName)
UE3 会使用该函数设置当前调试器中对象的名称。在每个内部更新调用过程中会调用它。您可以按自己的需要处理该信息。

void DebugWindowState(int StateCode)
目前不适用这个函数。

回调


我们已经分析了 UE3 如何与调试器进行通信,现在让我们看一下其他方面。调试器需要通过回调函数与 UE3 进行通信。将界面 DLL 绑定到引擎之后,引擎会记录它的回调函数。通过这个回调函数,您可以传递一些命令并全部传递为字符串。

回调函数的 typedef 如下所示:

typedef void (*CallbackPointer)(const char*);

下面是命令列表:

addbreakpoint

<classname>  <linenumber>

removebreakpoint
<classname>  <linenumber>

使用这些命令通知 UE3 添加/删除断点。 是其中存在断点的调用的名称, 是行号。

addwatch

<varname>

removewatch
<varname>

这些命令会通知 UE3 添加/删除用户定义的查看。您应该对其进行完整说明(例如: pawn.playerreplicationinfo.playername)。

clearwatch
该命令不采用参数,而且会清除所有用户查看。

changestack

<stackid>

该命令会使引擎跳到调用栈中的给定位置。

setdatawatch

<watchtext>

该命令会在给定变量的数据访问上设置断点。

breakonnone

<0=false|1=true>

每当访问没有出现错误时,使用 breakonnone 命令可以指示调试器中断。

break
会通知 UE3 尽快中断。

stopdebugging
该命令会关闭调试器并停止调试会话。

go
stepinto
stepover
stepoutof
这些命令可以控制调试器的执行过程。

但是请等待.. 所以如果我将 "addwatch" 命令发送到 UE3 中,我会获得 AddAWatch() 界面调用吗? 这是对的。将 UE3 看作是事情发生的地点,而将您的界面 DLL 看作是渲染所发生的事情的工具。这样在您发送 "addwatch" 命令时,UE3 首先会验证到这个对象的路径是否有效,接着它会在内部设置有关这个对象的查看,然后当它全部完成后,它会通知界面它正在查看一个给定的对象。就是这么简单.. 对吧 :)

其他有用的信息


在我们结束之前,让我们看一下在您中断调试器之后实际上会发生什么。它应该会为您提供在您的外部应用程序中要寻找什么的建议。 当您将 "break" 命令发送到回调中时,UE3 会启动该程序。它会设置内容为只要执行下一个脚本字节码就会中断的标志。发生这种情况以后,它会执行以下几个步骤。

  1. UE3 会通过对 EditorLoadClass() 和 EditorGotoLine() 的调用通知界面类及其所在行。
  2. 然后它会使用 SetCurrentObjectName 设置 ObjectName。
  3. 下一步,它会刷新所有查看,首先调用 ClearAWatch() 然后多次调用 AddAWatch()。
  4. 接下来它会通过调用 CallStackClear() 接下来多次调用 CallStackAdd() 刷新调用栈。

在其处于“中断”状态时,游戏会静止不动(包括所有计时),但是它会继续渲染和接受窗口消息。