我们有个游戏需要在特定的角度下进行。但游戏的特效非常的多和复杂,例如其中一些小道具的dc就达到6个。
其中一个优化方法当然是从源头优化,就是美术制作时就减少,但这样有几个问题,第一是美术可能需要重新做,时间上不可控。第二就是减少也很难,而且可能有些特效是公用的,这个情况下合并多个特效一起也并不是好事。
粒子特效的特点就是透明度粒子比较普通,同时发射得比较集中,所以overdraw普遍比较高。
如果不改变其他得前提下要优化粒子就是把所有同一材质和mesh的放在同一个“Order in layer”,当然要注意这个值不要跟其他mesh重叠了,不然会打乱批次。
这可能是最简单的优化方案了。但是他是基于我们设计上也清楚这个数值作用的前提下。
一开始已经说明了是固定视角,说明他的角度不变。这个情况下有个特点,就是你走到哪,怎么玩,看到的一种特效都是恒定的循环表现。这个做法不适合随机特效和拖尾特效等不确定的特效
那么我们可以考虑把他做成序列帧的方式播放,分两个阶段:
1.录制阶段:录制阶段是创建摄像机,把特效每帧渲染到摄像机的targetrender上,然后这个rt保存起来,形成了rt列表。
2.播放阶段:隐藏特效,播放rt列表。
录制状态大体代码:
public class RecordFxState : BaseState
{
private List<RenderTexture> mFxRTs;
private int cameraLayer;
private const float currentFrameRate = 60;
private int realDuration = 0;
public override void Init(FxRTData data)
{
base.Init(data);
//currentFrameRate = Application.targetFrameRate;
realDuration = (int)(mFxrtData.duration* currentFrameRate);
cameraLayer = data.CameraLayer;
modifyCameraProperty();
//SfxMgr.instance.ReleaseFxCamRT();
}
public override void OnUpdate()
{
base.OnUpdate();
if (!FxRTData.HasRT(mFxrtData.FXName) && mFxrtData.RootTransform)
{
mFxRTs = FxRTData.GetRTs(mFxrtData.FXName);
if (mFxRTs.Count < realDuration)
{
RenderTexture rt = RenderTexture.GetTemporary(fxCam.targetTexture.descriptor);
fxCam.targetTexture = rt;
mFxRTs.Add(rt);
}
else
{
ReleaseFxCamRT();
AddDicBool(FxRTData.FxHasRTList, mFxrtData.FXName, true);
ALUGUICommon.setGameObjDisable(mFxrtData.RootTransform.gameObject);
}
}
}
private void AddDicBool(Dictionary<string, bool> dic, string str, bool val)
{
if (dic.ContainsKey(str))
{
dic[str] = val;
}
else
{
dic.Add(str, val);
}
}
private Camera fxCam = null;
public void ReleaseFxCamRT()
{
if (fxCam != null)
{
//RenderTexture.ReleaseTemporary(fxCam.targetTexture);
ALUGUICommon.setGameObjDisable(fxCam.gameObject);
GameObject.Destroy(fxCam.gameObject);
}
}
private void CreateCamera(Transform parent)
{
if (fxCam != null)
{
return;
}
GameObject go = new GameObject("fxCamera");
fxCam = go.AddComponent<Camera>();
if (cameraLayer == 0)
{
cameraLayer = LayerMask.NameToLayer("Item");
}
fxCam.cullingMask = 1 << cameraLayer;
fxCam.farClipPlane = 10;
fxCam.nearClipPlane = -5;
fxCam.orthographic = true;
fxCam.clearFlags = CameraClearFlags.SolidColor;
fxCam.backgroundColor = new Color(0, 0, 0, 0);
go.transform.SetParent(parent);
go.transform.localRotation = Quaternion.Euler(mFxrtData.CameraRotation);
RenderTexture rt = RenderTexture.GetTemporary((int)mFxrtData.RTSize.x, (int)mFxrtData.RTSize.y, 1, RenderTextureFormat.ARGB32);
rt.useMipMap = false;
rt.autoGenerateMips = false;
fxCam.targetTexture = rt;
}
private void modifyCameraProperty()
{
if (FxRTData.HasRT(mFxrtData.FXName))
{
return;
}
CreateCamera(mFxrtData.RootTransform);
fxCam.orthographicSize = mFxrtData.CameraOrthographicSize;
fxCam.rect = mFxrtData.CameraViewPortRect;
fxCam.transform.localPosition = mFxrtData.CameraPosition;
}
}
播放状态大体代码:
using UnityEngine;
public class PlayRTState : BaseState
{
private bool canPlay = false;
private int index = 0;
protected MeshRenderer mMeshRenderer = null;
private bool hasHideItem = false;
private void CreateQuad()
{
if (mMeshRenderer != null)
{
return;
}
GameObject go = GameObject.CreatePrimitive(PrimitiveType.Quad);
go.name = "fx_rt";
Component.Destroy(go.GetComponent<MeshCollider>());
go.transform.SetParent(mFxrtData.RootTransform);
//var pos = fxCam.transform.localPosition;
//go.transform.localPosition = new Vector3(-pos.x, -pos.y, -pos.z);
go.transform.localPosition = mFxrtData.QuadPosition;
go.transform.localRotation = Quaternion.Euler(mFxrtData.QuadRotation);
go.transform.localScale = mFxrtData.QuadScale;//new Vector3(4, 4, 1);
mMeshRenderer = go.GetComponent<MeshRenderer>();
if (FxRTData.FxMaterialsList.ContainsKey(mFxrtData.FXName))
{
mMeshRenderer.sharedMaterial = FxRTData.FxMaterialsList[mFxrtData.FXName];
mMeshRenderer.sortingOrder = FxRTData.FxMeshDepthList[mFxrtData.FXName];
}
else
{
mMeshRenderer.sharedMaterial = new Material(Shader.Find("D_Shader/D_Shader_RT_Particle"));
mMeshRenderer.sharedMaterial.enableInstancing = true;
FxRTData.FxMaterialsList.Add(mFxrtData.FXName, mMeshRenderer.sharedMaterial);
FxRTData.FxMeshDepthList.Add(mFxrtData.FXName, FxRTData.FxMeshDepth);
mMeshRenderer.sortingOrder = FxRTData.FxMeshDepth;
FxRTData.FxMeshDepth++;
}
mMeshRenderer.sharedMaterial.mainTexture = null;
mFxrtData.mMeshRenderer = mMeshRenderer;
}
public override void Init(FxRTData data)
{
base.Init(data);
if (mFxrtData.FxRTs != null &&
mFxrtData.FxRTs.Count > 0)
{
canPlay = true;
}
if (!canPlay)
{
return;
}
CreateQuad();
if (!hasHideItem)
{
hasHideItem = true;
if (mFxrtData.childTransforms.Length >= 2)
{
for (int i = 1; i < mFxrtData.childTransforms.Length; i++)
{
bool isSetDisable = true;
var thisName = mFxrtData.childTransforms[i].name;
for (int j = 0; j < FxRTData.ExceptFXs.Length; j++)
{
string eFx = FxRTData.ExceptFXs[j];
if (thisName.Equals(eFx))
{
mFxrtData.childTransforms[i].SetParent(mFxrtData.RootTransform);
mFxrtData.childTransforms[i].localPosition = mFxrtData.QuadPosition;
isSetDisable = false;
break;
}
}
if (isSetDisable)
ALUGUICommon.setGameObjDisable(mFxrtData.childTransforms[i].gameObject);
}
}
}
index = 0;
}
int cuTime = 0;
const int tTime = 2;
public override void OnUpdate()
{
base.OnUpdate();
if (!canPlay)
{
return;
}
cuTime++;
if (cuTime >= tTime)
{
cuTime = 0;
return;
}
var rts = mFxrtData.FxRTs;
var count = rts.Count;
if (index < count)
{
mFxrtData.FxMPR.SetTexture("_MainTex", rts[index]);
mFxrtData.mMeshRenderer.SetPropertyBlock(mFxrtData.FxMPR);
//mFxrtData.mMeshRenderer.material.mainTexture = rts[index];
}
mFxrtData.RootTransform.localRotation = Quaternion.identity;
//mFxrtData.RootTransform.transform.localScale = vec;
index++;
if (index >= rts.Count - 1)
{
index = 1;
}
}
}
最后效果是没什么区别的,如果不介意像素,还可以减少rt的像素,减少内存消耗。
然后dc在rt下当然就只有一个了,而且overdraw也没了。而且我们把gpuinstance打开也可以把所有同类型的合成一个。