잔상을 남기려는 오브젝트의 Renderer가 SkinnedMeshRenderer인 경우에 가능한 방법입니다.
단계는 아래와 같습니다.
1. SkinnedMeshRenderer.BakeMesh( Mesh mesh ) 를 이용하여 오브젝트의 현재 상태를 저장한다.
2. 저장한 Mesh를 잔상으로 이용한다.
// 잔상 오브젝트에 필요한 것들
SkinnedMeshRenderer smr = gameObject.Getcomponent<SkinnedMeshRenderer>();
Material afterImageMaterial;
// 잔상 생성
Mesh mesh = new Mesh();
smr.BakeMesh( mesh );
GameObject afterImageObj = new GameObject("AfterImage");
MeshFilter mf = afterImageObj.AddComponent<MeshFilter>();
mf.mesh = mesh;
MeshRenderer mr = afterImageObj.AddComponent<MeshRenderer>();
mr.material = afterImageMaterial;
afterImageObj.transform.position = gameObject.transform.position;
afterImageObj.transform.rotation = gameObject.transform.rotation;
Destroy( afterImageObj, 1f );
위의 코드는 잔상을 생성하는 과정을 쉽게 보여드리기 위한 코드입니다.
위와 같이 짜게 된다면 성능이 꽝인 것은 물론 잔상을 사라지게 하는 등 관리가 불편합니다.
[ AfterImage.cs ]
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Afterimage : MonoBehaviour
{
Material m;
MeshFilter mf = null;
// GameObject afterImageObj = null;
Coroutine fadeoutCoroutine = null;
float originAlpha = 0f;
public Mesh mesh { get { return mf.mesh; } }
public void InitAfterImage(Material material)
{
// afterImageObj = new GameObject();
MeshRenderer mr = gameObject.AddComponent<MeshRenderer>();
m = new Material(material);
originAlpha = m.color.a;
mr.material = m;
mf = gameObject.AddComponent<MeshFilter>();
gameObject.SetActive(false);
}
public void CreateAfterImage(Vector3 position, Quaternion rot, float time)
{
if ( fadeoutCoroutine == null )
{
gameObject.SetActive(true);
gameObject.transform.position = position;
gameObject.transform.rotation = rot;
mf.mesh = mesh;
fadeoutCoroutine = StartCoroutine(FadeOut(time));
}
}
IEnumerator FadeOut(float time)
{
while(time > 0f)
{
time -= Time.deltaTime;
m.color = new Color(m.color.r, m.color.g, m.color.b, originAlpha * time);
yield return null;
}
gameObject.SetActive(false);
fadeoutCoroutine = null;
}
}
[ SMRAfterImageCreator.cs ]
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SMRAfterImageCreator : MonoBehaviour
{
[SerializeField]
Material afterImageMaterial;
SkinnedMeshRenderer smr;
Afterimage[] afterImages;
int afterImageCount;
int currentAfterImageIndex;
float remainAfterImageTime;
float createAfterImagedelay;
Coroutine createAfterImageCoroutine = null;
bool isCreating = false;
public void Setup(SkinnedMeshRenderer smr, int maxNumber, float remainTime)
{
this.smr = smr;
afterImageCount = maxNumber;
remainAfterImageTime = remainTime;
createAfterImagedelay = remainAfterImageTime / (float)afterImageCount + 0.1f;
CreateAfterImages();
}
void CreateAfterImages()
{
afterImages = new Afterimage[afterImageCount];
for (int i = 0; i < afterImages.Length; ++i)
{
GameObject newObj = new GameObject();
afterImages[i] = newObj.AddComponent<Afterimage>();
afterImages[i].InitAfterImage(afterImageMaterial);
}
}
public void Create(bool flag)
{
this.isCreating = flag;
if ( flag )
{
if ( createAfterImageCoroutine == null )
createAfterImageCoroutine = StartCoroutine(CreateAfterImageCoroution());
}
}
IEnumerator CreateAfterImageCoroution()
{
float t = 0f;
while ( isCreating )
{
t += Time.deltaTime;
if (t >= createAfterImagedelay)
{
smr.BakeMesh(afterImages[currentAfterImageIndex].mesh);
afterImages[currentAfterImageIndex].CreateAfterImage(transform.position, transform.rotation, remainAfterImageTime);
currentAfterImageIndex = (currentAfterImageIndex + 1) % afterImageCount;
t -= createAfterImagedelay;
}
yield return null;
}
createAfterImageCoroutine = null;
}
}
[ 사용법 ]
SMRAfterImageCreator aic;
void CreateAfterImages()
{
aic = GetComponent<SMRAfterImageCreator>();
aic.Setup(transform.GetComponentInChildren<SkinnedMeshRenderer>(), 5, 1.4f);
actualSlowMotionCharge = maxSlowMotionCharge;
}
SMRAfterImageCreator를 컴포넌트로 추가해주신 뒤, 위의 함수와 같이 셋업해주면 됩니다.
Setup의 인자로는 각각 SkinnedMeshRenderer, 생성할 총 잔상의 수, 잔상이 남아있을 시간 입니다.
셋업을 해두었다면, 잔상을 생성하고 싶을 때 aic.Create(true)를, 생성을 멈추고 싶다면 aic.Create(false)를 호출하면
됩니다.
[ 주의 사항 ]
SkinnedMeshRenderer.BakeMesh( Mesh mesh ) 는 성능적으로 무거운 함수라고 되어 있습니다.
잔상을 생성하려면 런 타임에 BakeMesh를 지속적으로 호출 해주어야 하기 때문에 너무 많은 잔상은 성능에
무리가 많이갈 수 있을 것 같습니다.
저는 5개 정도를 사용하고 있는데요, 제 핸드폰 갤럭시 S9+에서는 프레임 드랍없이 잘 돌아가는 것을 확인했습니다.
'Unity 공부시작 > Unity3D 연습' 카테고리의 다른 글
총알 피격 파티클 발생시키기 ( Raycast, RaycastHit ) (0) | 2019.03.21 |
---|