yande.re 338979 sample dress kin'iro_mosaic kujou_karen no_bra shiratama shiratamaco summer_dress.jpg
上一篇:


第八节 优化建筑部署操作以及删除建筑功能的实现



上一节我们基本的部署建筑功能和endturn功能已经实现,这破游戏差不多可以玩了,不过还有一些细节可以改进
比如我们背景在ground的时候,点击button的时候,因为检测出

if(Input.GetMouseButtonDown(0)&& selectedBuilding != null)
        {
            InteractWithBoard();
        }

从而直接放置建筑了,这需要加一个检测,那就是如果点击的是UI界面,则不执行interactWithBOard,我们只需要再加一个&&检测条件即可
还有shift拖动批量建造功能
以及删除建筑,返还部分建造资金功能
甚至是部署时添加一个旋转建筑自身朝向的功能按钮
或者是增加建筑升级功能,
添加移动单位,哨塔,怪物等等,
别激动,这是以后咱们的发展之路,作为新人,一步一个脚印,将UI,Board面板,Camera控制,city属性存储,局部单位building构造,以及之间的交互逻辑初步掌握并加以灵活运用,才是新手应该掌握的重点,我们接下来选择性优化一些部署建筑操作,差不多这个教程就告一段落了,希望能给大家一些启发,收获一点灵感,那就很好了
先处理UI界面不放置建筑:InteractWithBoard方法中判定加入一个条件:

if(!UnityEngine.EventSystems.EventSystem.current.IsPointerOverGameObject()&& selectedBuilding != null)

这样就不会在点击building button的时候建造到按钮后方的ground上了
接下来增加shift拖动批量建造功能
在buildingHandler脚本里,update方法中加入一个新的按键触发

else if(Input.GetMouseButton(0)&&Input.GetKey(KeyCode.LeftShift)&& selectedBuilding != null)
{
 InteractWithBoard();
 }

即表示按住鼠标左键和左shift键,并且持有选中建筑的时候,就执行建造功能
我们进游戏继续测试,发现建造道路或者其他建筑的时候,边缘大概0.5单位也会腾空建造建筑,这应该是跟我们的Mathf.round方法取整方式有关,我们应该尝试修改一下
先来到Board脚本中,我们找到计算grid的方法分析:

public Vector3 CalculateGridPosition(Vector3 position)
    {
        return new Vector3(Mathf.Round(position.x),0.5f,Mathf.Round(position.z));
    }
}

我们在边缘0.5个单位以内的坐标,会执行取整放到架空的那个整数格子去,可以通过对鼠标坐标进行数值筛选范围,符合范围的坐标后,再执行这个计算网格方法,即可解决问题:
回到buildhandler里面interactWithboard方法,调用addbuiding方法前,做一个范围约束,比较x,z坐标和地图边界大小,超过0.5距离,代表不在边界附近架空格,可以建造。

if (gridPosition.x - 0.5f > 0.01f && 99.5 - gridPosition.x > 0.01f 
   &&gridPosition.z - 0.5f > 0.01f && 99.5 - gridPosition.z > 0.01f)
      {
       board.AddBuilding(selectedBuilding, gridPosition);
      }

为了增加更多测试,我们去city脚本start方法里将初始cash改成50000吧,方便测试。
保存后进入游戏看看效果。
建筑摆放位置判定差不多ok了,我们初始右下角citydata显示还是默认初始录入的数值,我们要将其和实际citydata一致,这个简单,只需要进入UIController 在start方法update下相关信息即可:

UpdateDayCount();
UpdateCityData();

保存后测试,发现开局是第0天,有点尴尬,去city脚本start方法里增加Day=1,再看看
好了,我们最后演示下如何增加一个删除建筑的功能,交互功能都放在Board脚本里方便管理,直接新建一个RemoveBuilding方法吧:

public void RemoveBuilding(Vector3 position)
    {
        Destroy(buildings[(int)position.x, (int)position.z].gameObject);
      buildings[(int)position.x, (int)position.z]=null;
    }

解释一下,我们传入鼠标所在position坐标,通过取整获取到该建筑对应buliding array索引,然后删除该索引下的gameobject物体,同时将该索引对应存储信息清空,方便今后放置建筑时判定IsBuildingHere正确执行。这个命名buildings自行从脚本开头定义的Building[]数组名查询,需要一致。
删除功能搞定,我们还需要做一个返回建造资金功能
这样在BuildingHandler脚本interact交互方法中,我们需要新增一个判定,判断按鼠标左键执行新建建造,按鼠标右键执行删除建筑,返还资金功能可以在第二个判断中实现,但是我们需要传入对应建筑类型。如何方便获得需要删除的建筑类型呢?
总之我们先做出删除建筑判断具体实现吧。
我们如果想在一个interact方法中实现两种按键判断,那么就需要设置参数,不同按键传入不同参数,interaction方法里执行对应参数需要的判断,我们简单设置一个int 参数,命名action好了
我们首先将两类操作需要调用的action分好类。update方法里,

void Update()
{
    if(Input.GetMouseButtonDown(0)&& selectedBuilding != null)
    {
        InteractWithBoard(0);//归类为action 0
    }
    else if (Input.GetMouseButton(0) && Input.GetKey(KeyCode.LeftShift) && selectedBuilding != null)
    {
        InteractWithBoard(0);//归类为action 0
    }

}

我们将建造功能判定都归位0类
新建删除按键功能归类1:

if (Input.GetMouseButtonDown(1))

{
    InteractWithBoard(1);
}

好了,接下来我们重新调整下interact方法的逻辑结构:
由于两种按键行为都需要先执行判定不在UI按钮界面执行,我们可以将大前提提取到最外面的if里

if (!UnityEngine.EventSystems.EventSystem.current.IsPointerOverGameObject())
    if( !board.IsBuildingHere(gridPosition))
            {
                if (action==0&&city.Cash >= selectedBuilding.cost) //引入action分类条件限制
                {
                    city.Cash -= selectedBuilding.cost;
                    uiController.UpdateCityData();
                    city.buildingCounts[selectedBuilding.id]++;
                    board.AddBuilding(selectedBuilding, gridPosition);
                }
            }
    else if (action==1&&board.IsBuildingHere(gridPosition)//按下鼠标右键同时鼠标点位存在建筑时
    {
        borad.RemoveBuilding(gridpositon);//删除该坐标位对应的建筑。
        city.cash+=gridposition所对应的building下.cost花费

这就出现一个问题了,我们现在这个ridposition只是一个三维坐标,我们需要调取对应的建筑
同时,我们注意到上面的判断条件下,city.cash调用一个建筑叫selectedBuilding,那个方法是创建一个新建筑时候,通过EnableBuilder方法赋值给selctedbuilding的

 public void EnableBuilder(int building)
    {
        selectedBuilding = buildings[building];
        Debug.Log("选择的建筑是:" + selectedBuilding.buildingName);
    }

我们现在删除地图上任意建筑,可没有对应的字段存储该建筑。唯一和当前选中建筑有关联的,只有board.IsBuildingHere(gridPosition) 这个方法,但是我们目前这个方法返回的只是一个bool值,ture或者false而已。
思考一下,目前这个方法,功能是告诉建造或者删除建筑的时候,这里是否有建筑,那么通知是否有建筑的方法,可以是告诉true 或者false,也可以返回一个建筑,如果存在建筑,建筑!=null,也可以代表有建筑了,建筑=null,代表没有建筑,也可行的。
这样我们在删除建筑的时候,就可以拿到该建筑信息了,同时,我们的删除返回资金所需要检索引入的建筑信息也得到解决,那么,我们从Borad脚本ISBuildingHere方法开始修改:

 public bool IsBuildingHere(Vector3 position)
    {
        return buildings[(int)position.x, (int)position.z] != null;
    }

改为

 public Building BuildingStateHere(Vector3 position)
    {
        return buildings[(int)position.x, (int)position.z] ;
    }

因为返还的是一个建筑了,所以改下方法名以免歧义。
此方法原来的作用是在BuildingHandler脚本中进行位置是否为空判定,我们再回到handler里面看看如何修改:

if (!UnityEngine.EventSystems.EventSystem.current.IsPointerOverGameObject())
    if( board.BuildingStateHere(gridPosition)==null)//返还空建筑,即可表明位置是空的,可以建造
            {
                if (action==0&&city.Cash >= selectedBuilding.cost)               
        {
                    city.Cash -= selectedBuilding.cost;
                    uiController.UpdateCityData();
                    city.buildingCounts[selectedBuilding.id]++;
                    board.AddBuilding(selectedBuilding, gridPosition);
                }
            }
    else if (action==1&&board.BuildingStateHere(gridPosition)!=null)//返还存在建筑,我们准备删除它,同时直接引用此建筑从中计算返还资金数值。
        {
        borad.RemoveBuilding(gridPosition);
        city.cash+=board.BuildignStateHere(gridPosition).cost/2;//我们可以按自己喜好设置返还多少金额。
        uiController.UpdateCityData;
        }

这样,我们的功能差不多就完成了,注意到资金有增减两种状态,我们可以在city脚本里新定义一个资金管理方法

public void DepositCash(int cash)
{
    Cash+=cash;
}

这里面今后还可以扩充借贷功能,方便管理资金变动情况。
接下来我们将目前仅有的两种资金调取方法改成这个
handler下面将

 city.Cash -= selectedBuilding.cost;

改成

city.DepositCash(-selectedBuilding.cost);

将摧毁建筑判断中的

city.cash+=board.BuildignStateHere(gridPosition).cost/2;

修改为

city.DepositCash(board.BuildignStateHere(gridPosition).cost/2);

最后成品:

if (!UnityEngine.EventSystems.EventSystem.current.IsPointerOverGameObject() )
                if(action==0&& board.BuildingStateHere(gridPosition)==null)
                {
                if (city.Cash >= selectedBuilding.cost)
                    {
                    city.DepositCash(-selectedBuilding.cost);
                    uiController.UpdateCityData();
                    city.buildingCounts[selectedBuilding.id]++;
                    board.AddBuilding(selectedBuilding, gridPosition);
                    }
                }
                else if (action == 1 && board.BuildingStateHere(gridPosition) != null)

                {
                    board.RemoveBuilding(gridPosition);
                    city.DepositCash(board.BuildingStateHere(gridPosition).cost / 2);
                    uiController.UpdateCityData();
                }

保存后测试,发现金钱没有加上,原来是这之前我们先摧毁建筑了,这个建筑已经不存在了,就无法获取信息,自然返还不了资金了,我们调整下返还资金和摧毁建筑位置,测试发现ok
然后还有很多小问题可以继续优化,比方说我鼠标指向建筑模型的时候,进行售卖操作,而不是现在的基于ground网格位置进行操作,这会来的更符合玩家习惯。这些东西你可以自己慢慢研究,目前来说,将初始资金改成500,基本上可以玩了
本教程算是一个基础框架分享,主要是用来养成一个基本的编程思维,希望能对大家有所帮助,我也是新人一枚,目前阶段个人能力和表达水准有限,如有错漏,欢迎大佬指正。

Last modification:August 6, 2020
If you think my article is useful to you, please feel free to appreciate