复述所需功能及其概念
进入草丛隐身。
像英雄联盟的草丛机制一样。
其中(自己与敌人的可见性)包括如下几种情况:
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;
}
}
总结
两种隐身功能,采用两个机制,互不冲突又可搭配使用。