本文主要是介绍RTS游戏关于建筑物放置的测试Demo(Unity),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
一.准备一个建筑物
1,创建一个立方体Cube,改名为Mesh
2,创建一个空物体,起名叫Building
3,将Mesh放到Building下,将Mesh的BoxCollider组件删除,在Building的组件中新加BoxCollider和Rigidbody组件
4,创建Building.cs,将脚本挂载到Building下
注意点:因为BoxCollider没有挂载到Mesh上,所以在调整碰撞器大小时,应该调整到和Mesh原来BoxCollider大小位置一样 (使用导入资源如果涉及到旋转等,注意做好调整)
5,在Assets创建Resources文件夹,在Resources文件夹下创建Prefabs文件夹,在Prefabs文件夹下创建Buildings文件夹,将Building拖入保存为预制件
二.配置环境
1,创建一个地形Terrain(Layer为第八层,可以自己设置,不过在Globals 里面自己改)
2,创建一个空物体,改名为Game,创建一个BuildingPlacer.cs,将脚本挂载到Game上
3,在Resources文件夹下创建Materials文件夹,然后创建2个Material,将名字改为下面的,然后将颜色改一下
三.创建建筑物数据和脚本
public class BuildingData
{private string _name;private int _hp;public BuildingData(string name, int hp){_name = name;_hp = hp;}public string Name { get { return _name; } }public int HP { get { return _hp; } }
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class BuildingPlacer : MonoBehaviour
{//作为建筑放置类,手里必须拿着建筑private Building _building;private Ray _ray;private RaycastHit _raycastHit;//这个点的作用主要就是为了判断是否鼠标发生了移动,这个时候就可以更改值了private Vector3 SavePoint;private void Start(){_prepareBuilding(0);}private void Update(){if (_building != null){//如果按下esc就取消建筑物if (Input.GetKeyDown(KeyCode.Escape)){_cancelBuilding();return;}_ray = Camera.main.ScreenPointToRay(Input.mousePosition);if(Physics.Raycast(_ray,out _raycastHit, 1000f, Globals.TERRAIN_LAYER_MASK)){_building.gameObject.transform.position = _raycastHit.point;//只要一动就检查建筑的状态,随时进行切换if(SavePoint != _raycastHit.point){_building.ChangeState();}SavePoint = _raycastHit.point;if (_building.HasValidPlace && Input.GetMouseButtonDown(0)){_PlaceBuilding();}}}}/// <summary>/// 准备建筑物/// </summary>public void _prepareBuilding(int index){if(_building != null){Destroy(_building.gameObject);}else{GameObject _gameObject = (GameObject)GameObject.Instantiate(Resources.Load($"Prefabs/Buildings/{Globals.buildingDatas[index].Name}"));_building = _gameObject.GetComponent<Building>();_building.setvaults(Globals.buildingDatas[index]);}SavePoint = Vector3.zero;}/// <summary>/// 取消建筑物/// </summary>void _cancelBuilding(){Destroy(_building.gameObject);_building = null;}/// <summary>/// 放置建筑物/// </summary>void _PlaceBuilding(){_building.place();_building = null;_prepareBuilding(0);}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public enum BuildingPlacementState
{VALID,INVALID,FIXED
};[RequireComponent(typeof(BoxCollider))]
public class Building : MonoBehaviour
{private BuildingData _buildingData;private int currentHp; private BuildingPlacementState _state;private List<Material> _materials;private int _nCollisions = 0;public void setvaults(BuildingData buildingData){this._buildingData = buildingData;this.currentHp = buildingData.HP;}public string Name { get { return _buildingData.Name; } }public int CurrentHp { get { return currentHp; } }public int Hp { get { return _buildingData.HP; } }public int DataIndex{get{for (int i = 0; i < Globals.buildingDatas.Length; i++){if (Globals.buildingDatas[i].Name == _buildingData.Name){return i;}}return -1;}}private void Awake(){_materials = new List<Material>();_state = BuildingPlacementState.VALID;foreach (Material material in gameObject.transform.Find("Mesh").GetComponent<Renderer>().materials){_materials.Add(new Material(material));}SetMaterials(_state);}/// <summary>/// 根据状态改变颜色/// </summary>/// <param name="buildingState"></param>public void SetMaterials(BuildingPlacementState buildingState){List<Material> materials;if (buildingState == BuildingPlacementState.VALID){materials = new List<Material>();for (int i = 0; i < _materials.Count; i++){materials.Add(Globals.Valid);}}else if (buildingState == BuildingPlacementState.INVALID){materials = new List<Material>();for (int i = 0; i < _materials.Count; i++){materials.Add(Globals.Invalid);}}else if (buildingState == BuildingPlacementState.FIXED){materials = _materials;}else{return;}gameObject.transform.Find("Mesh").GetComponent<Renderer>().materials = materials.ToArray();}/// <summary>/// 触发进入/// </summary>/// <param name="other"></param>private void OnTriggerEnter(Collider other){if (other.tag == "Terrain") return;_nCollisions++;ChangeState();}/// <summary>/// 触发离开/// </summary>/// <param name="other"></param>private void OnTriggerExit(Collider other){if (other.tag == "Terrain") return;_nCollisions--;ChangeState();}/// <summary>/// 根据自身的触发检测来改变状态/// </summary>public void ChangeState(){if (_state == BuildingPlacementState.FIXED){return;};if (_nCollisions > 0){_state = BuildingPlacementState.INVALID;SetMaterials(_state);}else{_state = BuildingPlacementState.VALID;SetMaterials(_state);}}/// <summary>/// 放置建筑物,改变建筑物状态和恢复颜色/// </summary>public void place(){_state = BuildingPlacementState.FIXED;SetMaterials(_state);gameObject.GetComponent<BoxCollider>().isTrigger = false;}public bool HasValidPlace{ get => _state == BuildingPlacementState.VALID; }}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class Globals : MonoBehaviour
{public static BuildingData[] buildingDatas = new BuildingData[]{new BuildingData("Building",100)};//这里只保留地面的检测public static int TERRAIN_LAYER_MASK = 1 << 8;public static Material Valid = Resources.Load("Materials/Valid") as Material;public static Material Invalid = Resources.Load("Materials/Invalid") as Material;
}
4.总结
项目运行起来后,就可以进行Building实体的放置了,当放置时碰到已经放置的实体时会显示红色,点击鼠标左键也是没有效果的,如果是绿色,则可以直接左键放置。本博客只是一个小Demo,只是提供一个学习思路。
这篇关于RTS游戏关于建筑物放置的测试Demo(Unity)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!