我正在寻找一种模拟我的游戏直到输赢的方法,以用于蒙特卡洛树搜索算法。
我的游戏是基于回合制,基于图块的战术RPG,类似于《最终幻想战术》,《火焰纹章》等。
想法是AI会执行数千次播出(或直到一个阈值),直到他们确定最佳的下一步。
每个AI和Player代理都会随机进行一次有效动作,直到游戏结束。
为什么要进行MCTS模拟?为什么不选择minmax?
出于以下几个原因,我需要对游戏进行逼真的模拟:
X
前进的情况下静态评估游戏状态是相当困难的,甚至是不可能的-在不了解先前动作的情况下。因此,我需要在一个游戏状态下依次执行每个动作,以产生下一个游戏状态,然后才能进行任何评估。要扩展第2点: 使用简单的minmax方法,并通过查看所有玩家的“当前健康状况”来静态评估游戏状态将是有用的,但并不准确。 由于并非每项措施都会立即改变健康状况。
示例:
在2回合内会产生更高的伤害(最大伤害):
OR
在此示例中,由于minmax方法在2圈内造成了更大的伤害,因此它永远不会导致2nd选项,这是由于其对buff移动的静态评估结果为0,甚至可能为负。
为了使其选择第二个选项,它需要保留对先前操作的了解。就是它需要几乎完美地模拟游戏。
当我们添加其他元素时,例如:舞台陷阱,可破坏的环境和状态效果。使用静态评估几乎变得不可能
Time.timeScale
这使我能够加快物理和其他交互的速度,这正是我所需要的。 但是-这是一个全局属性,因此当AI进行“思考”时,游戏将以超高速运行几分之一秒。
提高NavMesh代理的速度
我所有的动作都在NavMesh上进行-因此,使这些动作“即时”的唯一可察觉的方法是提高速度。这是有问题的,因为运动速度不够快-并且由于速度增加而导致物理问题,有时角色会失控并飞出地图。
作为参考,这是我(正在开发中)游戏的屏幕截图。
我需要一种非常快速地“玩”我的游戏的方法。
我只需要能够在每次AI移动之前快速而有效地运行这些仿真。
我很乐意听取一些有类似经验的人的信息-但任何意见都会不胜感激!
谢谢
答案 0 :(得分:2)
要使某项内容能够快速运行,我们需要使其简单-这意味着将游戏还原到其基本机制上,并(仅)表示该内容。
那是什么意思?好吧,首先我们有一个基于图块的世界。一个简单的表示就是一个Tile对象的2D数组,如下所示:
/// <summary>
/// This gameworld is 20x20 tiles
/// </summary>
public Tile[,] Tiles = new Tile[20,20];
世界上无法触及的部分-例如因为房间不是矩形的,或者是非交互式的家具-在此数组中可以只是空值。
接下来,在这些图块上会移动一些字符。每个角色也都健康:
public class Character {
/// <summary>
/// The tile this character is on.
/// </summary>
private Tile _currentLocation;
/// <summary>
/// The tile this character is on.
/// </summary>
public Tile CurrentLocation{
get{
return _currentLocation;
}
set{
if(_currentLocation != null)
{
_currentLocation.Occupier = null;
}
_currentLocation = value;
_currentLocation.Occupier = this;
}
}
/// <summary>
/// Its HP
/// </summary>
public float Hitpoints = 100f;
public void SetLocation(Tile newLocation){
CurrentLocation = newLocation;
}
}
我们可以制作更具特色的Character变体,例如具有特殊属性的class Hero : Character
,但我们暂时将其忽略。
上面的CurrentLocation也也更新了图块-为了提高速度,它有助于知道哪个角色正站在特定图块上,因此这里要记住一个图块类:
public class Tile{
public Character Occupier;
public int X;
public int Y;
}
好吧,让我们以一个世界为例-为了简单起见,这个世界是矩形的,因此所有图块都已填充:
for(var x=0;x<20;x++){
for(var y=0;y<20;y++){
Tiles[x,y] = new Tile(){X = x, Y = y};
}
}
我们将在特定位置放两个字符:
var badGuy = new Character();
badGuy.CurrentLocation = Tiles[3,4];
var hero = new Character();
hero.CurrentLocation = Tiles[9,9];
从这一点来看,我们有一个非常简单的世界模型。例如,我们可以通过检查Tiles[x,y].Occupier
来询问位于x,y的位置。从这里开始,将您的机制添加到模型中-例如,可以使用某种aTile.Attack(AttackType.IceBlast)
方法:
public void Attack(AttackType type, Character attackedBy){
// impacts type.Range tiles around this one, dealing damage to each Occupier.
// Get a neighbouring tile via e.g. yourReferenceToTheTilesArray[X+1, Y].
}
重要的是,然后将游戏修改为仅是模型的一种反映-这样,模型始终是准确的,因为它可以驱动游戏,并且是例如损坏金额的真正来源。您可以向其中添加事件,因此,当调用上述Attack方法时,将触发某些OnAttack处理程序,然后在整个游戏运行时触发动画。
这样的模型可以完全在Unity之外作为独立的C#程序运行,并能够每秒生成数百万个回合用于测试或AI训练目的。