Unity如何制作3D人物残影——实现黑客帝国的躲子弹镜头

在这里插入图片描述

public abstract class AfterImage3D : MonoBehaviour
{
    public abstract void Play();
    //我想了三种方式
    //1.获取当前动画以及播放了多长时间,然后实例化一个仅有渲染功能的预制体,让他播放这一帧并暂停动画
    //2.直接克隆当前游戏对象,再清除他与渲染无关的组件并改变材质
    //3.直接调用全局绘制网格的方法
}
public class AdventurerController : MonoBehaviour
{
    private Animator animator;
    private AfterImage3D afterImage3D;

    private void Awake()
    {
        animator = GetComponent<Animator>();
        afterImage3D = GetComponent<AfterImage3D>();
    }

    // Update is called once per frame
    void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            animator.Play("swordStrike1");
            afterImage3D.Play();
        }
    }
}

方式1:

public class AfterImage3DByAnimation : AfterImage3D
{
    public AfterImageObject afterImagePrefab;
	//在一段时间呢隔一段时间生成一个残影
    public float Duration = 5;
    public float Interval = 0.2f;

    private float mTime = 5;

    private Animator animator;
    private List<AfterImageObject> afterImageObjects = new List<AfterImageObject>();

    private void Awake()
    {
        animator = GetComponent<Animator>();
    }

    public override void Play()
    {
        if (GetComponentsInChildren<Renderer>().Length <= 0)
        {
            return;
        }

        mTime = Duration;
        StartCoroutine(AddAfterImage());
    }

    IEnumerator AddAfterImage()
    {
        while (mTime > 0)
        {
            CreateImage();
            yield return new WaitForSeconds(Interval);
            mTime -= Interval;
        }
        yield return null;
    }

    private void CreateImage()
    {
        if (animator.GetCurrentAnimatorClipInfoCount(0) > 0) 
        {
            string currentAnimationName = animator.GetCurrentAnimatorClipInfo(0)[0].clip.name;
            float currentAnimationTime = animator.GetCurrentAnimatorStateInfo(0).normalizedTime;
            for (int i = 0; i < afterImageObjects.Count; i++)
            {
                if (!afterImageObjects[i].gameObject.activeSelf)
                {
                    afterImageObjects[i].Play(currentAnimationName, currentAnimationTime);
                    return;
                }
            }
            AfterImageObject clone = Instantiate(afterImagePrefab, transform.position, transform.rotation);
            clone.Play(currentAnimationName, currentAnimationTime);
            afterImageObjects.Add(clone);
        }
        
    }
}

public class AfterImageObject : MonoBehaviour
{
    private Animator animator;
	//所有要生成残影的渲染组件
    public Renderer[] renderers;
	//对应的透明材质
    public Material[] materials;
	//渐隐时长
    public float FadeoutTime = 1;
	
    private Material[] tempMaterials;

    private float time = 1;
	//改变动画速度来制作更细节的残影
    public bool useDelay;
    [Range(0, 1)]
    public float delaySpeed;
    
    public void Play(string animName, float normalizeTime) 
    {
        if (animator == null) 
        {
            animator = GetComponent<Animator>();
            tempMaterials = new Material[renderers.Length];
            for (int i = 0; i < renderers.Length; i++)
            {
                tempMaterials[i] = new Material(materials[i]);
                renderers[i].material = tempMaterials[i];
                renderers[i].shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off;
            }
        }
        gameObject.SetActive(true);
        animator.Play(animName, 0, normalizeTime);
        animator.speed = useDelay ? delaySpeed : 0;
        time = FadeoutTime;
    }

    void LateUpdate()
    {
        time -= Time.deltaTime;
        for (int i = 0; i < tempMaterials.Length; i++)
        {
            if (tempMaterials[i].HasProperty("_Color"))
            {
                Color color = Color.white;
                color.a = Mathf.Max(0, time / FadeoutTime);
                tempMaterials[i].SetColor("_Color", color);
            }
        }

        if (time <= 0) 
        {
            for (int i = 0; i < tempMaterials.Length; i++)
            {
                if (tempMaterials[i].HasProperty("_Color"))
                {
                    tempMaterials[i].SetColor("_Color", materials[i].GetColor("_Color"));
                }
            }
            gameObject.SetActive(false);
        }
    }
}

方式三:

public class AfterImage3DByCombine : AfterImage3D
{
    public class AfterImange
    {
        public Mesh mesh;
        public Material material;
        public Matrix4x4 matrix;
        public float duration;
        public float time;
    }

    protected SkinnedMeshRenderer[] skinRenderers;
    protected MeshFilter[] filters;
    protected int filtersCount = 0;

    public bool IncludeMeshFilter = true;
    public Material EffectMaterial;

    public float Duration = 5;
    public float Interval = 0.2f;
    public float FadeoutTime = 1;

    private float mTime = 5;
    private List<AfterImange> mAfterImageList = new List<AfterImange>();

    protected virtual void Awake()
    {
        skinRenderers = GetComponentsInChildren<SkinnedMeshRenderer>();
        if (IncludeMeshFilter)
        {
            filters = GetComponentsInChildren<MeshFilter>();
            filtersCount = filters.Length;
        }
    }

    public override void Play()
    {
        if (skinRenderers.Length + filtersCount <= 0)
        {
            return;
        }

        mTime = Duration;
        StartCoroutine(AddAfterImage());
    }

    IEnumerator AddAfterImage()
    {
        while (mTime > 0)
        {
            CreateImage();
            yield return new WaitForSeconds(Interval);
            mTime -= Interval;
        }
        yield return null;
    }

    void CreateImage()
    {
        CombineInstance[] combineInstances = new CombineInstance[skinRenderers.Length + filtersCount];

        int index = 0;
        for (int i = 0; i < skinRenderers.Length; i++)
        {
            var render = skinRenderers[i];
            var mesh = new Mesh();
            render.BakeMesh(mesh);
            combineInstances[index] = new CombineInstance
            {
                mesh = mesh,
                transform = render.gameObject.transform.localToWorldMatrix,
                subMeshIndex = 0
            };

            index++;
        }

        for (int i = 0; i < filtersCount; i++)
        {
            var render = filters[i];
            var temp = (render.sharedMesh != null) ? render.sharedMesh : render.mesh;
            var mesh = (Mesh)Instantiate(temp);
            combineInstances[index] = new CombineInstance
            {
                mesh = mesh,
                transform = render.gameObject.transform.localToWorldMatrix,
                subMeshIndex = 0
            };

            index++;
        }

        Mesh combinedMesh = new Mesh();
        combinedMesh.CombineMeshes(combineInstances, true, true);

        mAfterImageList.Add(new AfterImange
        {
            mesh = combinedMesh,
            material = new Material(EffectMaterial),
            time = FadeoutTime,
            duration = FadeoutTime,
        });
    }

    void LateUpdate()
    {
        bool needRemove = false;
        foreach (var image in mAfterImageList)
        {
            image.time -= Time.deltaTime;
            if (image.material.HasProperty("_Color"))
            {
                Color color = Color.white;
                color.a = Mathf.Max(0, image.time / image.duration);
                image.material.SetColor("_Color", color);
            }
            //public static void DrawMesh(Mesh mesh, Matrix4x4 matrix, Material material, int layer, Camera camera, int submeshIndex, MaterialPropertyBlock properties, ShadowCastingMode castShadows);
            Graphics.DrawMesh(image.mesh, Matrix4x4.identity, image.material, gameObject.layer, null, 0, null, false);
            if (image.time <= 0)
            {
                needRemove = true;
            }
        }

        if (needRemove)
        {
            mAfterImageList.RemoveAll(x => x.time <= 0);
        }
    }


}

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码

)">
< <上一篇
下一篇>>