项目实训--Unity多人游戏开发(十六、草丛隐身与道具隐身)

复述所需功能及其概念

进入草丛隐身。

像英雄联盟的草丛机制一样。
其中(自己与敌人的可见性)包括如下几种情况:

1.都不在草丛中,互相可见
2.敌人在草丛中,自己在草丛外,自己无法看到敌人
3.自己在草丛中,敌人在草丛外,自己能看到敌人,敌人无法看到自己
4.都在草丛中,互相都可见

emm好吧,其中2.和3.是一回事。。。

道具技能隐身:

emm打个比方就像英雄联盟里小丑或者老鼠的“隐身”。

一个问题

值得考虑的是,隐身技能结束后,要执行“现身”,但是如果隐身进入了草丛,此时也应该直接显示吗。(倘若敌人不在草丛里,那我应该还是处于隐身才对);亦或者出草丛也会“现身”,但我在草丛中使用隐身技能后出草,这时是不应该“现身”的。

或许能直接思考到一种解决:多设置几个变量。
在草丛中时为true。如果此时隐身技能结束,只有在这个变量为false时才执行“现身”。
还要另一个变量:技能隐身时为true,当出草时只有此变量为false时才执行“现身”。

这个解决方式没毛病。
但是还要单独维护两个变量并且使用Photon多人化时也需要进行网络同步。


不妨先思考一下Unity中有几种方式可以实现“隐身”呢?
emm目前就想到3种:

1.粗暴简单又直接的SetActive(false),但是这样会导致脚本也不再执行。
2.物体身上的渲染组件,当关闭时即不再渲染,屏幕上就不会出现。
3.摄像机中有一个设置,捕获哪些layer中的物体,若一个物体不属于这些ayer,则不会被摄像机捕获并显示。
4…maybe more

如果采用不同原理实现这两种隐身。那么好像就不再需要额外的变量了。
其中前两点其实还是有点关系的,具有一定的交集关系。
所以采取2和3比较好。原理上不冲突又互不相交。


实际开发

本项目草丛隐身机制采取开关物体身上的渲染器组件。
道具技能隐身采取摄像机layer捕获机制。(可能术语不是那么专业)

草丛

两个不同的草丛之间是没有关系的。
也就是敌人在草丛里,我在不在另一个草丛对于是否能看到敌人是没有关系的。
于是一个草丛一个脚本。必要时可以给脚本编号(此项目用不到)。
草丛脚本里维护一个列表存储在草丛中的人。
BigBush.cs

	public class BigBush : MonoBehaviour
    {
        public int bushId;
        public bool isIinBush;//我是否在草丛
        private List<GameObject> playersInBush = new List<GameObject>();

        public void OnTriggerEnter(Collider other)
        {
            if (other.gameObject.tag.Equals("Player"))//加入列表
            {
                //Debug.Log("有人进草了"+bushId+":"+ other.gameObject.GetComponent<PhotonView>().Owner.NickName);
                playersInBush.Add(other.gameObject);
                //自己进入草丛
                if (other.gameObject.GetComponent<PhotonView>().IsMine)
                {
                    EnterByThisClient();
                    isIinBush = true;
                }
                else//别人进入
                {
                    if (isIinBush)//自己在草里
                    {
                        //nothing
                    }
                    else//隐身别人
                    {
                        SetInvisible(other.gameObject);
                    }
                }
            }
        }
        public void OnTriggerExit(Collider other)
        {
            if (other.gameObject.tag.Equals("Player"))//加入列表
            {
                //Debug.Log("有人出草了" + bushId + ":" + other.gameObject.GetComponent<PhotonView>().Owner.NickName);
                playersInBush.Remove(other.gameObject);
                if (other.gameObject.GetComponent<PhotonView>().IsMine)
                {
                    isIinBush = false;
                    //隐藏草中人
                    ExitByThisClient();
                }
                else//别人离开草
                {
                    //无论如何可以执行一遍显形,如果自己在草里当然是白调用
                    SetVisible(other.gameObject);
                }
            }
        }

        //自己进入草,显示全部在草里的
        public void EnterByThisClient()
        {
            playersInBush.ForEach(item => SetVisible(item));
        }

        //自己出草,隐藏其他人
        public void ExitByThisClient()
        {
            playersInBush.ForEach(item => SetInvisible(item));
        }

        public void SetInvisible(GameObject go)
        {
            go.GetComponent<PropsPlayerController>().SetInvisible();
        }
        public void SetVisible(GameObject go)
        {
            go.GetComponent<PropsPlayerController>().SetVisible();
        }
    }

道具

修改人物的layer就好了。
有个问题是自己的客户端也看不到自己的角色了,这比较尴尬。
那么可以进行判断,如果是自己的客户端,则不需要隐身,实现一个材质的半透明效果就好了。
动态修改材质(材质中的color的alpha属性)代码

		[PunRPC]
        public void Cloak()//隐身
        {
            AudioManager.instance.Cloak(transform.position);
            //他端隐藏3秒
            //自己半透明3秒
            if (photonView.IsMine)//自己电脑上肯定还是要看到自己的
            {
                //身体各个部位半透明
                SetMaterialRenderingMode(transform.GetChild(0).GetComponent<SkinnedMeshRenderer>().material, RenderingMode.Transparent);
                SetMaterialRenderingMode(transform.GetChild(2).GetComponent<SkinnedMeshRenderer>().material, RenderingMode.Transparent);
                Invoke("RestoreVisibleFromCloakWithMaterial", 3f);
            }
            else
            {
                //考虑到在隐身途中进入到了草里结束隐身时,所以与草不同要用不同的“隐身”逻辑以规避和草丛隐身的bug
                transform.GetChild(0).gameObject.layer = noCameraLayer;
                transform.GetChild(2).gameObject.layer = noCameraLayer;
                transform.GetChild(5).gameObject.layer = noCameraLayer;
                Invoke("RestoreLayer", 3f);
            }
        }
动态修改材质的代码
		private void RestoreLayer()
        {
            transform.GetChild(0).gameObject.layer = 0;
            transform.GetChild(2).gameObject.layer = 0;
            transform.GetChild(5).gameObject.layer = 0;
        }


        private void RestoreVisibleFromCloakWithMaterial()
        {
            SetMaterialRenderingMode(transform.GetChild(0).GetComponent<SkinnedMeshRenderer>().material, RenderingMode.Opaque);
            SetMaterialRenderingMode(transform.GetChild(2).GetComponent<SkinnedMeshRenderer>().material, RenderingMode.Opaque);
        }

//		动态修改材质,原理不懂,看起来材质可能有许多属性都是键值对
        private void SetMaterialRenderingMode(Material material, RenderingMode renderingMode)
        {
            switch (renderingMode)
            {
                case RenderingMode.Opaque:
                    material.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.One);
                    material.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.Zero);
                    material.SetInt("_ZWrite", 1);
                    material.DisableKeyword("_ALPHATEST_ON");
                    material.DisableKeyword("_ALPHABLEND_ON");
                    material.DisableKeyword("_ALPHAPREMULTIPLY_ON");
                    material.renderQueue = -1;
                    break;

                case RenderingMode.Transparent:
                    material.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.One);
                    material.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha);
                    material.SetInt("_ZWrite", 0);
                    material.DisableKeyword("_ALPHATEST_ON");
                    material.DisableKeyword("_ALPHABLEND_ON");
                    material.EnableKeyword("_ALPHAPREMULTIPLY_ON");
                    material.renderQueue = 3000;
                    break;
            }
        }

总结

两种隐身功能,采用两个机制,互不冲突又可搭配使用。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值