Unity游戏即时模拟

时间:2020-02-10 17:09:13

标签: unity3d

我正在寻找一种模拟我的游戏直到输赢的方法,以用于蒙特卡洛树搜索算法。

我的游戏是基于回合制,基于图块的战术RPG,类似于《最终幻想战术》,《火焰纹章》等。

想法是AI会执行数千次播出(或直到一个阈值),直到他们确定最佳的下一步。

模拟

每个AI和Player代理都会随机进行一次有效动作,直到游戏结束。

为什么要进行MCTS模拟?为什么不选择minmax?

出于以下几个原因,我需要对游戏进行逼真的模拟:

  1. 将游戏状态编码成低维结构是不可能的,因为大多数动作都与诸如Colliders和Rays的Unity结构紧密耦合。
  2. X前进的情况下静态评估游戏状态是相当困难的,甚至是不可能的-在不了解先前动作的情况下。因此,我需要在一个游戏状态下依次执行每个动作,以产生下一个游戏状态,然后才能进行任何评估。

要扩展第2点: 使用简单的minmax方法,并通过查看所有玩家的“当前健康状况”来静态评估游戏状态将是有用的,但并不准确。 由于并非每项措施都会立即改变健康状况。

示例:

在2回合内会产生更高的伤害(最大伤害):

  1. 移动到玩家的前面,攻击->移动到玩家的后面,攻击

OR

  1. 移动玩家前方,使用攻击力->造成4倍伤害

在此示例中,由于minmax方法在2圈内造成了更大的伤害,因此它永远不会导致2nd选项,这是由于其对buff移动的静态评估结果为0,甚至可能为负。

为了使其选择第二个选项,它需要保留对先前操作的了解。就是它需要几乎完美地模拟游戏。

当我们添加其他元素时,例如:舞台陷阱,可破坏的环境和状态效果。使用静态评估几乎变得不可能

我尝试过的事情

Time.timeScale

这使我能够加快物理和其他交互的速度,这正是我所需要的。 但是-这是一个全局属性,因此当AI进行“思考”时,游戏将以超高速运行几分之一秒。

提高NavMesh代理的速度

我所有的动作都在NavMesh上进行-因此,使这些动作“即时”的唯一可察觉的方法是提高速度。这是有问题的,因为运动速度不够快-并且由于速度增加而导致物理问题,有时角色会失控并飞出地图。


作为参考,这是我(正在开发中)游戏的屏幕截图。

enter image description here

问题

我需要一种非常快速地“玩”我的游戏的方法。

我只需要能够在每次AI移动之前快速而有效地运行这些仿真。

我很乐意听取一些有类似经验的人的信息-但任何意见都会不胜感激!

谢谢

1 个答案:

答案 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训练目的。

相关问题