项目实训--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
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值