【发布时间】:2015-05-25 10:28:43
【问题描述】:
我已经研究过这个问题,并尝试按照之前几个问题 (such as this one) 中的说明修复代码,但它仍然无法正常工作。虽然我必须说我检查的所有答案都是从 2009 年到 2010 年,所以它们可能已经过时了。
这是罪魁祸首代码:
foreach(Entity player in players)
{
if(player.actions.Count > 0)
{
Entity temp = player;
player.isDoingAction = true;
Debug.Log(player.name + " started action");
player.actions.Dequeue().Execute(() => { temp.isDoingAction = false; Debug.Log(temp.name + " finished"); });
}
}
这将打印以下内容:
Player1 started action
Player2 started action
Player2 finished
Player2 finished
何时打印:
Player1 started action
Player2 started action
Player1 finished
Player2 finished
或类似的东西。
此代码在 Unity 协程函数中运行。
代码稍大一点:
GameManager.cs
private IEnumerator RunTurn()
{
...
...
...
for(int i = 0; i < phases; i++)
{
//Assign action to each player
foreach(Entity player in players)
{
if(player.actions.Count > 0)
{
Entity temp = player;
player.isDoingAction = true;
Debug.Log(player.name + " started action");
player.actions.Dequeue().Execute(() => { temp.isDoingAction = false; Debug.Log(temp.name + " finished"); });
}
}
//Wait for each player to finish action
foreach(Entity player in players)
{
while(player.isDoingAction == true)
{
Debug.Log("Waiting for " + player.name);
yield return null;
}
}
}
...
...
...
}
Action.cs
public override void Execute(System.Action callback)
{
Move(callback);
}
private void Move(System.Action callback)
{
...
...
...
//Move target entity
target.MoveToPosition(newPosition, mSpeed, callback);
target.location = newLocation;
...
...
...
}
Entity.cs
public void MoveToPosition(Vector3 position, float speed, System.Action callback)
{
StartCoroutine(CoMoveToPosition(position, speed, callback));
}
//Move to position
private IEnumerator CoMoveToPosition(Vector3 position, float speed, System.Action callback)
{
while(position != transform.position)
{
transform.position = Vector3.MoveTowards(transform.position, position, speed * Time.deltaTime);
yield return null;
}
//Move finished so use callback
callback();
}
解决方案
事实证明 Unity 中存在协程和匿名 lambda 回调的错误。查看this链接了解更多信息。
工作代码:
foreach(Entity player in players)
{
if(player.actions.Count > 0)
{
player.isDoingAction = true;
Debug.Log(player.name + " started action");
System.Func<Entity, System.Action> action = new System.Func<Entity,System.Action>(p =>
new System.Action(() => { p.isDoingAction = false; Debug.Log(p.name + " finished"); }));
player.actions.Dequeue().Execute(action(player));
}
}
【问题讨论】:
-
有些事情没有意义。使用本地
temp是“捕获”(闭包样式)变量并防止它在下一次迭代中更改的标准方法。代码中还有其他我们没有看到的内容吗? -
@Amit 我在整个循环结构中进行了编辑。这真的没有意义,这就是我在这里发布这个的原因。
-
如果您捕获名称本身会发生什么(我认为这不是您真正想要的,但可能有助于理解)?:
string tempName = player.name;...Debug.Log(tempName... -
试试这个:
Action a = () => Debug.Log(temp.name); a(); player.actions.Dequeue().Execute(a);这个想法是在进入Execute调用之前测试 lambda 是否捕获了temp.name,以及之后是否以某种方式发生了变化。 -
毫无意义:-)。看起来你的回调被覆盖了,问题不在于捕获或 lambda。最终验证:
Action a = (player.name == 'player1') ? (() => Debug.Log("Player1")) : (() => Debug.Log("not Player1"));。不再使用捕获。如果失败,那肯定是回调被重置了。