Loading...  上一篇: <div class="preview"> <div class="post-inser post box-shadow-wrap-normal"> <a href="https://omo.moe/archives/347/" target="_blank" class="post_inser_a no-external-link"> <div class="inner-image bg" style="background-image: url(https://omo.moe/usr/uploads/2018/11/3225504282.jpg);background-size: cover;"></div> <div class="inner-content" > <p class="inser-title">[Unity 3D新手教程分享】City Builder Demo第六节:创建面板物体管理器</p> <div class="inster-summary text-muted"> 上一篇:第六节 创建面板物体管理器 创建Board脚本。设定scale大小,将地图空间纳入数组,进行空间判断,从而... </div> </div> </a> <!-- .inner-content #####--> </div> <!-- .post-inser ####--> </div> 第七节 建筑部署控制器和界面操作实现 ================== <iframe class="iframe_bilibili_video" src="//player.bilibili.com/player.html?aid=35889456&cid=62997433&page=8" scrolling="yes" border="1" frameborder="yes" framespacing="1" allowfullscreen="true"> </iframe> 创建BuildingHandler脚本 实际创造建筑时,我们一般通过点击鼠标左键部署 这通常采用发射一条射线,将鼠标所在screen空间坐标点投射到world世界坐标系,这就是我们第三节通过图示讲解的ScreenToWorldPoint方法,不太熟悉的话,我们再看一下示意图 检测与世界地图ground碰撞后,获得碰撞点的x.z坐标,从而与上一节的board面板控制器交互,判断此刻是否持有一个建筑,判断资产是否足够创建新的建筑,之后再通过board面板控制发送出来部署的具体坐标位置,通过咱们的buildingHandler脚本进行部署。 好了,我们提取出所需要产生联系的组件: 1.City,我们需要获得City类里面的Cash属性,建造的时候进行判断,造完扣钱也要修改Cash值 同时,我们新建building后,还需要传递buildingcount计数器让buildingcount++。 2.改变cash后,UIController也要通信修改面板cash数值。 3.Board,我们需要传递out出来的hit碰撞点坐标给board控制器,接收board的执行命令是否创建新建筑到ground以及存储部署后的建筑信息到board类里面的building array数组。 于是我们先从City开始吧: ``` [SerializeField] private City city; ``` 同样的,我们需要通过观察面板显示city信息方便调试,于是做一个序列化。 ``` [SerializeField] ``` 再来一个UIController进行后续交互 ``` private UIController uiController; ``` 同样的,序列化后方便调试 ``````````````````` [serialiazedField] ``````````````````` 同UI里面那些button交互后,我们需要创建一个building array得到对应的建筑类型0,1,2,3这些信息,并在观察inspector面板将各个建筑模型预制体和这些建筑按钮进行绑定,因此也来个序列化方便操作吧: ``` [SerializeField] private Building[] buildings; ``` 再创建一个面板方便在放置建筑的时候进行交互判断: `````````````````````````````````````` [SerializeField] private Board board; `````````````````````````````````````` 我们还需要一个选中状态的建筑,等待鼠标左键放置,这个选择中的建筑,将起到和board沟通的作用,为放置建筑的最终步骤。 ``` private Building selectedBuilding; ``` main函数里写一个方法 ````````````````````````````````````````````````````````````````````````````````````````` Public void EnableBuilder(int building) { selectedBuilding = buildings[building]; } ````````````````````````````````````````````````````````````````````````````````````````` 回到 hierarchy面板,将buildinghandler挂载到ground物体上,将4个建筑按钮挂载ground物体上的enableBuilder点击On click事件,并且设置好对应数字,使得我们点击这4个按钮的时候,比如说道路就是0,让按钮上的值int0参数传入enableBuilder方法,将selctedBuilding赋值为buildings[0],这样就相当于获得了对应类型的建筑了. 为了稍后测试效果,我们将EnableBuilder方法写一个debug: `````````` Debug.Log("选择的建筑是:" + selectedBuilding.buildingName); `````````` 接下来是一个核心方法,这节的重点,也是整个游戏系统内交互的重点。与board交互用,在update方法中,我们检测输入信息,比方说我们检测到鼠标左键右键之类的,我们就可以调用这个核心交互方法,效果就是,如果我们选择了一个建筑准备部署,此刻发射一条射线,如果射线碰撞到我们的ground,我们将hit点坐标信息提交给board,通过board组件里面的CalculateGridPosition方法将hit位置的x,z坐标记录进我们的grid网格数组里,通过x,z坐标取整后获得一个最近的网格位置,通过AddBuilding方法申明将此选中的建筑放置到这个网格值对应的坐标位,同时扣除对应资产金额。 这就是这个交互方法的核心功能和流程,我们开始逐一实现。 ``` void InteractWithBoard() { Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); RaycastHit hit; if(Physics.Raycast(ray, out hit)) ``` 这里讲解一下out引用的原因,了解过的,可以跳过这段, 我们的raycasthit方法,如果命中三维物体,会记录第一个碰到的物体信息,保存在hit里,hit里会储存很多信息,包括碰撞点,碰到的表面的法线,射线从原点到碰撞点的距离,碰到的uv纹理坐标,碰到的collider碰撞器还有rigdbody刚体组件等待信息,是一份信息表格,不是简单的数值value,所以我们这里涉及到数值传递问题,值传递和引用, 一般我们操作对象,调用参数都是pass by value居多,传递过来一个值,我们新建的变量会拷贝这个值,形成一个独立的备份数值,修改这个新变量,不会影响原变量或者对象,在内存层面上,也是新开内存地址储存新值。 而引用调用pass by reference则是直接操作原来的对象,不新建一份拷贝,修改新变量直接是通过引用地址直接修改原来的对象,所以这里的hit不能简单传值过来,需要out调取出来引用原hit信息,这就是使用out关键字的原因。 好了,我们继续 ``` Vector3 gridPosition = board.CalculateGridPosition(hit.point); if(!board.IsBuildingHere(gridPosition)) { if (city.Cash >= selectedBuilding.cost) { city.Cash -= selectedBuilding.cost; uiController.UpdateCityData(); city.buildingCounts[selectedBuilding.id]++; board.AddBuilding(selectedBuilding, gridPosition); } } ``` 我们的交互功能主体差不多都实现了,现在只需要在update方法中加入Input信息检测,就可以调用了 ```````````````````````````````````````````````````````````````````````````````````````````````````````````````` if(Input.GetMouseButtonDown(0)&& selectedBuilding != null) { InteractWithBoard(); } ```````````````````````````````````````````````````````````````````````````````````````````````````````````````` 我们可以去hierarchy面板绑定信息进行测试了 之前咱们的Handler是挂载到ground上面的,我们找到这个脚本,buildings属性下size字段填个4吧 我们之前在第二节已经将4个预制体挂载building脚本,如果你之前还未挂载的,记得依次绑定4种建筑预制体一个building脚本,先录入每种建筑的id,cost,buildingName信息,搞定之后,现在就可以将4个预制体模型按照屏幕左下角顺序依次挂载到Element0-4个元素上了。 别忘了将ground上面的board脚本挂载到咱们这个handler脚本里的board上。 City物体上的city脚本挂载到handler脚本的city上, 还有Uicontroller也记得从city物体上直接拖过来挂载到handler脚本上,将handler所有字段属性都匹配完毕 运行游戏,基本的创建建筑放置功能就实现了,下一节我们将讲解如何移除建筑,还有一些细节的修改优化 下一篇: <div class="preview"> <div class="post-inser post box-shadow-wrap-normal"> <a href="https://omo.moe/archives/349/" target="_blank" class="post_inser_a no-external-link"> <div class="inner-image bg" style="background-image: url(https://omo.moe/usr/uploads/2018/11/816411434.jpg);background-size: cover;"></div> <div class="inner-content" > <p class="inser-title">[Unity 3D新手教程分享】City Builder Demo第八节:优化建筑部署操作以及删除建筑功能的实现【结束篇】</p> <div class="inster-summary text-muted"> 上一篇:第八节 优化建筑部署操作以及删除建筑功能的实现 上一节我们基本的部署建筑功能和endturn功能已经实现,... </div> </div> </a> <!-- .inner-content #####--> </div> <!-- .post-inser ####--> </div> [1]: https://omo.moe/usr/uploads/2018/11/3161258991.jpg Last modification:August 12th, 2020 at 06:38 pm © 允许规范转载 Support If you think my article is useful to you, please feel free to appreciate ×Close Appreciate the author Sweeping payments Pay by AliPay Pay by WeChat