Unity实战 RTS3D即时战略游戏开发(十):可建造地点标示

发表于2017-06-01
评论0 2.1k浏览

这是第RTS3D即时战略游戏开发系列的第十篇文章,主要给大家讲解在地图上哪些区域可以建造建筑,哪些地方不可以。

建筑在地形上跟随鼠标,但是并没有标记指示在哪里可以建造,我们希望,如果不能建造就显示红色,如果能建造就显示绿色。

 首先在RTSManager中添加一个特别的函数   ,进入RTSManager并编辑这个脚本,添加一个新的方法:

public bool IsGameObjectSafeToPlace(GameObject go)  
    {  
return true;  
}  

这里暂时不实现这个函数,只是看它的表现;

接着在FindBuildingSite脚本中能让物体显示为红或绿,这里我们需要定义一些变量,来实现功能:如下:

using UnityEngine;  
using System.Collections;  
public class FindbuildingSite : MonoBehaviour {  
    Renderer rend;  //存储Render 这样可以改变颜色并恢复  
    Color Red = new Color (1, 0, 0, 0.5f);  //定义不可建区域红色  
    Color Green = new Color (0, 1, 0, 0.5f); //定义可建区域绿色  
    void Start()  
    {  
        rend = GetComponent ();  
    }  
    // Update is called once per frame  
    void Update () {  
        var tempTarget = RtsManager.Current.ScreenPointToMapPosition (Input.mousePosition);  
        if (tempTarget.HasValue == false)  
            return;  
        transform.position = tempTarget.Value;  
        if (RtsManager.Current.IsGameObjectSafeToPlace (gameObject)) //判断此区域是否可建造建筑  
        {  
            rend.material.color = Green;  
        } else {  
            rend.material.color = Red;  
        }  
    }  
}  


现在返回到RTSManager重新写一下IsGameObjectSafeToPlace这个函数,为了检测是否可建,我要判断每一个顶点看它是不是在可通过的地形上,首先要得到顶点,取得MeshFilter,从中获得mesh,接下来还要取得哪些潜在可能阻碍建造的障碍物列表,例如建筑。代码如下:

public bool IsGameObjectSafeToPlace(GameObject go)  
    {  
        var verts = go.GetComponent ().mesh.vertices;  //获取全部顶点  
        var obstacles = GameObject.FindObjectsOfType ();  //得到障碍物集合  
        var cols = new List ();  
        foreach (var o in obstacles) {  
            if (o.gameObject != go) {  
                cols.Add (o.gameObject.GetComponent ());  
            }  
        }  
        //检测每一个顶点,对于每个顶点都要检测其是否在可通过的地形上,  
        foreach (var v in verts) {  
            NavMeshHit hit;  
            var vReal = go.transform.TransformPoint (v);   //取得顶点的世界位置坐标,  
            NavMesh.SamplePosition (vReal, out hit, 20, NavMesh.AllAreas);  
            //接下来需要检测顶点是不是在核实的XZ平面或碰撞体内,因为不关心离地多高,所以可以忽略Y轴,只关心点在不在障碍物内,  
            bool onXAxis = Mathf.Abs (hit.position.x - vReal.x) < 0.5f;  //如果这两个点的距离小于0.5  我们认为在 X 轴上是可行的  
            bool onZAxis = Mathf.Abs(hit.position.z - vReal.z) < 0.5f;   //如果这两个点的距离小于0.5  我们认为在 Z 轴上是可行的  
            bool hitCollider = cols.Any (c => c.bounds.Contains (vReal));  //如果任何一个碰撞体的范围内有这个顶点,则HitCollider为true  
            if (!onXAxis || !onZAxis || hitCollider) {  
                return false;  
            }  
        }  
        return true;  
    }  

保存代码,运行Unity,则会发现我们把建筑拖拽到山上就变红,拖到平地就会变绿。

现在我们来实现鼠标再次点击时建造建筑,我们找到FindBuildingSite脚本,并修改Start方法,为确保建筑物被认为或意外的销毁后要恢复操作,在Update内判断是否可建

using UnityEngine;  
using System.Collections;  
public class FindbuildingSite : MonoBehaviour {  
    public float Cost = 200;   //建造费用  
    public float MaxBuildDistance = 30;  //建造距离  
    public GameObject BuildingPrefab;    //建筑预设  
    public PlayerSetupDefinition Info;   //玩家信息  
    public Transform Source;           //资源的位置坐标,用来判断是否可建造  
    Renderer rend;  
    Color Red = new Color (1, 0, 0, 0.5f);  
    Color Green = new Color (0, 1, 0, 0.5f);  
    void Start()  
    {  
        MouseManager.Current.enabled = false;  
        rend = GetComponent ();  
    }  
    // Update is called once per frame  
    void Update () {  
        var tempTarget = RtsManager.Current.ScreenPointToMapPosition (Input.mousePosition);  
        if (tempTarget.HasValue == false)  
            return;  
        transform.position = tempTarget.Value;  
        if (Vector3.Distance (transform.position, Source.position) > MaxBuildDistance)   //判断和无人机的位置,如果太远不可建  
        {  
            rend.material.color = Red;  
            return;  
        }  
        if (RtsManager.Current.IsGameObjectSafeToPlace (gameObject)) {  
            rend.material.color = Green;  
            if (Input.GetMouseButtonDown (0)) //鼠标再次点击时建造建筑  
            {     
                var go = GameObject.Instantiate (BuildingPrefab);  
                go.transform.position = transform.position;  
                Info.Credits -= Cost;  //扣除费用  
                go.AddComponent ().Info = Info;  
                Destroy (this.gameObject);  
            }  
        } else {  
            rend.material.color = Red;  
        }  
    }  
    void OnDestroy()  
    {  
        MouseManager.Current.enabled = true;  
    }  
}  

返回Unity修改CreateBuildingAction脚本,如下:
using UnityEngine;  
using System.Collections;  
public class CreateBuildingAction : ActionBehavior {  
    public float Cost = 0;     //建筑花费   
    public GameObject BuildingPrefab;//建筑预设  
    public float MaxBuildDistance = 30; //最远可建造距离  
    public GameObject GhostBuildingPrefab;  
    private GameObject active = null;  
    public override System.Action GetClickAction ()  
    {  
        return delegate() {  
            var player = GetComponent().Info;  
            if (player.Credits < Cost)  
            {  
                Debug.Log("Not enough, this costs " + Cost);  
                return;  
            }  
            //建造建筑逻辑处理  
            var go = GameObject.Instantiate(GhostBuildingPrefab);  
            var finder = go.AddComponent();  
            finder.BuildingPrefab = BuildingPrefab;  
            finder.MaxBuildDistance = MaxBuildDistance;  
            finder.Info = player;  
            finder.Source = transform;  
            active = go;  
        };  
    }  
    void Update()  
    {  
        if (active == null)  
            return;  
        if (Input.GetKeyDown (KeyCode.Escape)) //取消建造建筑  
            GameObject.Destroy (active);  
    }  
    void OnDestroy()  
    {  
        if (active == null)  
            return;  
        Destroy (active);  
    }  
}  

接下来我们来为建筑添加一个可以生产金币的功能,新建Earning脚本:
using UnityEngine;  
using System.Collections;  
public class Earnings : MonoBehaviour {  
    public float CreditsPerSecond = 1;  //每秒生产金币数量   
    private PlayerSetupDefinition player;  //所归属玩家信息    
    // Use this for initialization  
    void Start () {  
        player = GetComponent ().Info;   //得到玩家信息  
    }  
    // Update is called once per frame  
    void Update () {  
        player.Credits += CreditsPerSecond * Time.deltaTime;  //生产金币  
    }  
}

  

Unity实战RTS3D即时战略游戏开发系列:

  Unity实战 RTS3D即时战略游戏开发(一):场景

  Unity实战 RTS3D即时战略游戏开发(二):玩家状态显示

  Unity实战 RTS3D即时战略游戏开发(三):单位选中处理

  Unity实战 RTS3D即时战略游戏开发(四):鼠标管理器

  Unity实战 RTS3D即时战略游戏开发(五):NavigationMesh自动寻路

  Unity实战 RTS3D即时战略游戏开发(六):信息显示

  Unity实战 RTS3D即时战略游戏开发(七):HUD的使用小地图显示

  Unity实战 RTS3D即时战略游戏开发(八):HUD的使用单位信息显示

  Unity实战 RTS3D即时战略游戏开发(九):行为管理器Action的使用

  Unity实战 RTS3D即时战略游戏开发(十一)AiController

  Unity实战 RTS3D即时战略游戏开发(十二):建造AI、生产AI

  Unity实战 RTS3D即时战略游戏开发(十三):战斗AI的控制

  Unity实战 RTS3D即时战略游戏开发(十四)伤害更新信息显示、销毁单位、爆炸效果

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引