好的,我这里发生了一个非常奇怪的情况。首先我需要介绍一些背景。我正在为在 XNA 引擎上制作的游戏创建 AI 代理。事情的设置方式,人们应该使用代理的框架来生成一个.dll,然后游戏在运行时使用它来加载代理。
我可以访问游戏的代码(所以我可以看到发生了什么),此时我正在使用其他人的代理作为我自己的起点。最近,游戏(以及随之而来的框架)发生了一些变化,主要是类和接口的名称,这意味着我必须让代理跟上速度。因此,在我进行了所有必要的更新以便能够使用新版本的框架编译代理之后,我遇到了一个问题。这是游戏加载.dll的代码
// dynamically load assembly from file GeometryFriendsAgents.dll
Assembly agentsDLL = Assembly.LoadFile(path);
// get type of classes BallAgent and SquareAgent from just loaded Assembly
Type circleType = AgentsDLL.GetType("GeometryFriendsAgents.CircleAgent");
Type rectangleType = AgentsDLL.GetType("GeometryFriendsAgents.RectangleAgent");
try {
// create instances of classes BallAgent and SquareAgent
npcCircle = (ICircleAgent)Activator.CreateInstance(circleType);
npcRectangle = (IRectangleAgent)Activator.CreateInstance(rectangleType);
}catch(TargetInvocationException e){
throw e.InnerException;
}
我可以确认路径是正确的。当我尝试运行游戏时,try/catch 中的行将抛出 TargetInvocationException(这将自动加载代理)。我添加了 try/catch 以查看内部异常,即 FormatException,VisualStudio 提供输入字符串格式不正确的附加信息。
我不知道代理代码的哪一部分与此相关,但我还没有谈到奇怪的部分。在我使用的实现中,代理使用了一个 LearningCenter 类。这个类本质上是读写代理的学习文件。在课程开始时,它存储学习文件的路径:
protected const string path = @"..\..\..\..\Agents\";
所以这就是事情变得奇怪的地方。这是学习文件的正确路径。当我早些时候犯了一个错误时,我有这条路径(之前在整个代码中重复了很多次)作为
protected const string path = @"..\..\..\..\Agents";
当我使用不正确的路径构建 .dll 时,我可以成功加载代理并运行游戏。问题是路径不正确,当学习中心尝试写入学习文件时,它显然会失败并出现 DirectoryNotFoundException。有问题的方法是:
public void EndGame(float knownStatesRatio) {
if (_toSave) {
FileStream fileStream = new FileStream(path + _learningFolder + "\\Ratios.csv", FileMode.Append);
StreamWriter sw = new StreamWriter(fileStream);
sw.WriteLine(knownStatesRatio);
sw.Close();
fileStream.Close();
fileStream = new FileStream(path + _learningFolder + "\\IntraPlatformLearning.csv", FileMode.Create);
DumpLearning(fileStream, _intraplatformPlayedStates);
fileStream.Close();
if (interPlatform) {
fileStream = new FileStream(path + _learningFolder + "\\InterPlatformLearning.csv", FileMode.Create);
DumpLearning(fileStream, _interplatformPlayedStates);
fileStream.Close();
}
}
}
创建新文件流时立即发生异常。我已经尝试将缺失值转移\
到_learningFolder
变量中,但是当我这样做时,它又回到了第一个问题。只要路径不正确,我就可以运行游戏...
我还应该提到,在此之前,我最初在同一位置遇到了另一个 TargetInvocationException。当时,通过将代理类的可见性更改为公共来解决问题。
我意识到带有路径的东西可能隐藏了实际问题,但我只是不知道下一步该往哪里看。
编辑:这是第一个问题的堆栈跟踪
GeometryFriends.exe!GeometryFriends.AI.AgentsManager.LoadAgents() Line 396
GeometryFriends.exe!GeometryFriends.Levels.SinglePlayerLevel.LoadLevelContent() Line 78
GeometryFriends.exe!GeometryFriends.Levels.Level.LoadContent() Line 262
GeometryFriends.exe!GeometryFriends.ScreenSystem.ScreenManager.LoadContent() Line 253
Microsoft.Xna.Framework.Game.dll!Microsoft.Xna.Framework.DrawableGameComponent.Initialize()
GeometryFriends.exe!GeometryFriends.ScreenSystem.ScreenManager.Initialize() Line 221
Microsoft.Xna.Framework.Game.dll!Microsoft.Xna.Framework.Game.Initialize()
GeometryFriends.exe!GeometryFriends.Engine.Initialize() Line 203
Microsoft.Xna.Framework.Game.dll!Microsoft.Xna.Framework.Game.RunGame(bool useBlockingRun)
Microsoft.Xna.Framework.Game.dll!Microsoft.Xna.Framework.Game.Run()
GeometryFriends.exe!GeometryFriends.Program.Main(string[] args) Line 16
首先失败的代理是 CircleAgent,这是构造函数:
public CircleAgent() {
//Change flag if agent is not to be used
SetImplementedAgent(true);
lastMoveTime = DateTime.Now;
lastRefreshTime = DateTime.Now;
currentAction = 0;
rnd = new Random(DateTime.Now.Millisecond);
model = new CircleWorldModel(this);
learningCenter = new CircleLearningCenter(model);
learningCenter.InitializeLearning();
startTime = DateTime.Now;
}
编辑 2:好的,我设法在 FormatException 的来源上进行了分区。CircleLearningCenter的这个方法出现错误(第一个if中的语句):
public override void addStateMovementValue(string[] lineSplit, string stateId, ref Dictionary<string, Dictionary<int, double>> lessons) {
if (!lineSplit[1].Equals("0")) {
lessons[stateId].Add(Moves.ROLL_LEFT, double.Parse(lineSplit[1]));
}
if (!lineSplit[2].Equals("0")) {
lessons[stateId].Add(Moves.ROLL_RIGHT, double.Parse(lineSplit[2]));
}
if (!lineSplit[3].Equals("0")) {
lessons[stateId].Add(Moves.JUMP, double.Parse(lineSplit[3]));
}
}
在 LearningCenter 中通过此方法调用:
private void createLearningFromFile(FileStream fileStream, ref Dictionary<string, Dictionary<int, double>> lessons) {
lessons = new Dictionary<string, Dictionary<int, double>>();
StreamReader sr = new StreamReader(fileStream);
string line;
while ((line = sr.ReadLine()) != null) {
string[] lineSplit = line.Split(',');
string stateId = lineSplit[0];
lessons.Add(stateId, new Dictionary<int, double>());
addStateMovementValue(lineSplit, stateId, ref lessons);
}
}
依次由该方法调用(在圆的构造函数中调用):
public void InitializeLearning() {
if (File.Exists(Path.Combine(Path.Combine(path, _learningFolder), "IntraPlatformLearning.csv"))) {
FileStream fileStream = new FileStream(Path.Combine(Path.Combine(path, _learningFolder),"IntraPlatformLearning.csv"), FileMode.Open);
createLearningFromFile(fileStream, ref _intraplatformLessonsLearnt);
fileStream.Close();
} else {
createEmptyLearning(ref _intraplatformLessonsLearnt);
}
if (File.Exists(Path.Combine(Path.Combine(path, _learningFolder), "InterPlatformLearning.csv"))) {
FileStream fileStream = new FileStream(Path.Combine(Path.Combine(path, _learningFolder), "InterPlatformLearning.csv"), FileMode.Open);
createLearningFromFile(fileStream, ref _interplatformLessonsLearnt);
fileStream.Close();
} else {
createEmptyLearning(ref _interplatformLessonsLearnt);
}
}
如果不明显,CircleLearningCenter 是 LearningCenter 的子类。另外,很抱歉文字墙,但我无能为力。