github传送门:https://github.com/dongzizhu/unity3DLearning
视频传送门:https://space.bilibili.com/472759319
游戏智能
所谓游戏智能,其实是一种根据状态的决策模型。这种模型小到可以是根据规则下井字棋、玩贪吃蛇,大到可以利用强化学习训练处一个决策模型。
因为电子游戏可以方便地与代码交互,所以用电子游戏来训练人工智能或者启发人工智能的研究,成为了一个常用的手段。这里所谓“交互”,其实就是决策-->环境给出反馈-->状态发生变化-->决策如此循环地一个过程,我们可以简化地将游戏过程看作是一个状态机转化的过程,然后可以用人为给定的方式或者强化学习自主训练的方式来得到一个决策模型。
这里用之前完成的牧师与魔鬼游戏来简单地实现一个游戏智能。
牧师与魔鬼
根据游戏规则,我们可以列出状态转移图(这里我借鉴了前辈的博客中画的图,只做了一点点小的改动)。
上图中采用fromCoast上面的人物个数以及船的位置来表示状态(比如3P3DB代表fromCoast上有3个牧师和3个魔鬼,船也在fromCoast这边),状态转移则用两个或一个字母来表示穿上承载的人;省略号所在的方框代表游戏输了,左下角0P0D的方框代表游戏胜利;红线标出的路径就是正确的路径,当程序每次做决策的时候只要沿着路径的方向走就好了。可以看到在3P3DB以及0P1D的位置有两个分叉,我们可以用一个随机函数来实现随机性。
首先我们在baseCode中加入我们需要的新的结构和函数。
如Boataction结构来帮助我们记录当前船要进行的操作。
public enum Boataction { P, D, PP, DD, PD }
或者在coastController中加入获取指定类型的人物的函数。
public MyCharacterController getP(){
for(int i = 0; i < passengerPlaner.Length; i++){
if(passengerPlaner[i]==null)
continue;
if(passengerPlaner[i].getType()==0)
return passengerPlaner[i];
}
return null;
}
public MyCharacterController getD(){
for(int i = 0; i < passengerPlaner.Length; i++){
if(passengerPlaner[i]==null)
continue;
if(passengerPlaner[i].getType()==1)
return passengerPlaner[i];
}
return null;
}
完成以上准备工作后,我们就可以在firstController中实现所需要的功能了。
注意这里的autoNext调用StartCoroutine函数开启了一个协程,这样做的目的是通过调用WaitForSeconds来让进程暂停。因为我们要保证人物移动到穿上之后再开船,所以这里要等待个0.5秒。
IEnumerator moveWithPause() {
Boataction nextAction = getNext();
autoMoveCharacterOnBoat(nextAction);
yield return new WaitForSeconds(0.5f);
moveBoat();
yield return new WaitForSeconds(0.5f);
autoMoveCharacterOffBoat();
yield return new WaitForSeconds(0.5f);
}
public void autoNext(){
if(userGUI.status != 0)
return;
StartCoroutine(moveWithPause());
}
至于其中每一步调用的函数就没有什么可太赘述得了。就是单纯的根据fromCoast的状态决定下一步的操作,然后移动相应的人到船上,等人在船上后开船,最后再将人物移动上岸。
private int randomValue() {
float num = Random.Range(0f, 1f);
if (num <= 0.5f) return 1;
else return 2;
}
public Boataction getNext(){
int pNum = fromCoast.getPNum();
int dNum = fromCoast.getDNum();
// to->-1; from->1
if(pNum == 3 && dNum == 3 && boat.get_to_or_from() == 1){
return randomValue()==1 ? Boataction.DD : Boataction.PD;
}
else if(pNum == 0 && dNum == 2 && boat.get_to_or_from() == 1){
return Boataction.DD;
}
else if(pNum == 1 && dNum == 1 && boat.get_to_or_from() == 1){
return Boataction.PD;
}
else if(pNum == 0 && dNum == 1 && boat.get_to_or_from() == -1){
return randomValue()==1 ? Boataction.D : Boataction.P;
}
else if(pNum == 2 && dNum == 1 && boat.get_to_or_from() == 1){
return Boataction.PP;
}
else if(pNum == 0 && dNum == 3 && boat.get_to_or_from() == 1){
return Boataction.DD;
}
else if(pNum == 0 && dNum == 2 && boat.get_to_or_from() == -1){
return Boataction.D;
}
else if(pNum == 2 && dNum == 2 && boat.get_to_or_from() == 1){
return Boataction.PP;
}
else if(pNum == 1 && dNum == 1 && boat.get_to_or_from() == -1){
return Boataction.PD;
}
else if(pNum == 3 && dNum == 1 && boat.get_to_or_from() == 1){
return Boataction.PP;
}
else if(pNum == 3 && dNum == 0 && boat.get_to_or_from() == -1){
return Boataction.D;
}
else if(pNum == 3 && dNum == 2 && boat.get_to_or_from() == 1){
return Boataction.DD;
}
else if(pNum == 3 && dNum == 1 && boat.get_to_or_from() == -1){
return Boataction.D;
}
else if(pNum == 2 && dNum == 2 && boat.get_to_or_from() == -1){
return Boataction.P;
}
else if(pNum == 3 && dNum == 2 && boat.get_to_or_from() == -1){
return Boataction.D;
}
Debug.Log("out of control!");
return Boataction.D;
}
public void autoMoveCharacterOnBoat(Boataction nextAction){
if(nextAction==Boataction.P){
if(boat.get_to_or_from()==toCoast.get_to_or_from())
characterIsClicked(toCoast.getP());
else
characterIsClicked(fromCoast.getP());
}
else if(nextAction==Boataction.D){
if(boat.get_to_or_from()==toCoast.get_to_or_from())
characterIsClicked(toCoast.getD());
else
characterIsClicked(fromCoast.getD());
}
else if(nextAction==Boataction.DD){
if(boat.get_to_or_from()==toCoast.get_to_or_from()){
characterIsClicked(toCoast.getD());
characterIsClicked(toCoast.getD());
}
else{
characterIsClicked(fromCoast.getD());
characterIsClicked(fromCoast.getD());
}
}
else if(nextAction==Boataction.PD){
if(boat.get_to_or_from()==toCoast.get_to_or_from()){
characterIsClicked(toCoast.getP());
characterIsClicked(toCoast.getD());
}
else{
characterIsClicked(fromCoast.getP());
characterIsClicked(fromCoast.getD());
}
}
else{
if(boat.get_to_or_from()==toCoast.get_to_or_from()){
characterIsClicked(toCoast.getP());
characterIsClicked(toCoast.getP());
}
else{
characterIsClicked(fromCoast.getP());
characterIsClicked(fromCoast.getP());
}
}
}
public void autoMoveCharacterOffBoat(){
for(int i = 0; i < boat.getCharacters().Length; i++){
if(boat.getCharacters()[i]==null)
return;
characterIsClicked(boat.getCharacters()[i]);
}
}
除了决策外,操作的时候调用之前已经写好的函数即可。
完整项目请见github。