DestroyImmediate() : 에디터용 Destroy()
Destroy() vs DestroyImmediate()| 항목 | Destroy() | DestroyImmediate() |
|---|---|---|
| 실행 시점 | 다음 프레임의 끝에서 파괴 | 즉시 파괴 |
| 사용 위치 | 런타임 주로 사용 | 에디터 스크립트 전용 |
| 위험성 | 상대적으로 안전 | 높은 위험 (Undo 불가, 에디터 충돌 가능) |
| 사용 시기 | 대부분의 경우 | 특수 상황 (커스텀 에디터 등) |
DestroyImmediate()는 런타임에서 사용하지 말 것.보통은 Mesh Renderer가 Mesh Filter를 분석하고 알아서 low level 명령으로 바꿔준다.
엄청 많은 오브젝트를 직접 batching하거나 커스텀 효과가 필요할 때 사용될 수 있음.
| 항목 | 레벨 | 목적 | 주요 특징 |
|---|---|---|---|
| GL | 초저수준 | 임시 그리기 | OpenGL immediate-style, 성능 낮음 |
| Graphics.DrawMesh | 중간 | 일반 메시 렌더링 | 1회성 메시 출력 |
| Graphics.DrawMeshInstanced | 고효율 | 동일 메시 반복 렌더 | GPU instancing 기반 |
| Graphics.DrawProcedural | 고급 | 정점/삼각형 수 직접 지정 | ComputeBuffer/셰이더 기반 |
| CommandBuffer | 중간~고급 | 렌더링 명령 예약 | 정확한 타이밍 지정 가능 |
| CustomPass (HDRP) | 고급 | 사용자 정의 렌더 패스 | HDRP 전용 |
| ScriptableRenderPass (URP) | 고급 | 사용자 정의 렌더 패스 | URP 전용 |
| Compute Shader | 고급 | GPU 연산 전용 | 렌더링 외에도 물리/시뮬 가능 |
GL (UnityEngine.GL)GL.Begin(), GL.Vertex(), GL.End())Graphics.DrawMeshGraphics.DrawMesh(mesh, matrix, material, layer)Graphics.DrawMeshInstancedGraphics.DrawMeshInstanced(mesh, submeshIndex, material, matrices)Graphics.DrawProceduralGraphics.DrawProceduralNow(...)CommandBufferCommandBuffer.DrawMesh, SetRenderTarget, ClearRenderTarget 등 가능CustomPass (HDRP 전용)CustomPassVolume 안에서 실행됨Execute() 메서드 안에서 CommandBuffer 사용 가능ScriptableRenderPass (URP 전용)ScriptableRendererFeature와 함께 사용AddRenderPasses()로 등록, Execute()에서 동작Compute Shader.compute 파일에서 작성Dispatch() 호출로 실행GraphicsBuffer, RWTexture, StructuredBuffer 등과 연동| 니가 하고 싶은 일 | 써야 하는 것 |
|---|---|
| 디버그용 선/사각형 그리고 싶다 | GL |
| 메시 하나 그리고 싶다 | Graphics.DrawMesh |
| 동일한 메시를 많이 반복해서 그린다 | Graphics.DrawMeshInstanced |
| 메시 없이 정점 수만으로 그린다 | Graphics.DrawProcedural |
| 그리기 순서, 타이밍을 정밀 제어 | CommandBuffer |
| HDRP에서 커스텀 효과 추가 | CustomPass |
| URP에서 커스텀 효과 추가 | ScriptableRenderPass |
| GPU로 직접 연산 시키고 싶다 | Compute Shader |

using UnityEngine;
[ExecuteInEditMode]
public class Clouds : MonoBehaviour
{
public Shader CloudShader;
float MinHeight = 0.0f;
public float MaxHeight = 5.0f;
public float FadeDist = 2;
public float Scale = 5;
public float Steps = 50;
public Texture ValueNoiseImage;
public Transform Sun;
Camera _Cam;
Material _Material;
public Material Material
{
get
{
if (_Material == null && CloudShader != null)
{
_Material = new Material(CloudShader);
}
if (_Material != null && CloudShader == null)
{
DestroyImmediate(_Material);
}
if (_Material != null && CloudShader != null && CloudShader != _Material.shader)
{
DestroyImmediate(_Material);
_Material = new Material(CloudShader);
}
return _Material;
}
}
void Start()
{
// 에디터에서 돌리다가 재컴파일되거나 스크립트가 붙은 상태로 Play 모드 전환될 때
// 기존 머티리얼이 중복으로 남아있는 걸 막기 위한 안전 장치
if (_Material)
DestroyImmediate(_Material);
}
Matrix4x4 GetFrustumCorners()
{
Matrix4x4 frustumCorners = Matrix4x4.identity;
Vector3[] fCorners = new Vector3[4];
_Cam.CalculateFrustumCorners(new Rect(0, 0, 1, 1), _Cam.farClipPlane, Camera.MonoOrStereoscopicEye.Mono, fCorners);
frustumCorners.SetRow(0, fCorners[1]);
frustumCorners.SetRow(1, fCorners[2]);
frustumCorners.SetRow(2, fCorners[3]);
frustumCorners.SetRow(3, fCorners[0]);
return frustumCorners;
}
[ImageEffectOpaque]
void OnRenderImage(RenderTexture source, RenderTexture destination)
{
if (_Material == null || ValueNoiseImage == null)
{
Graphics.Blit(source, destination);
return;
}
// 이런 초기화 코드들 Start()에 안 적는 이유 : 에디터 코드는 Start()를 계속 호출하는게 아니라서
if (_Cam == null)
_Cam = GetComponent<Camera>();
Material.SetTexture("_ValueNoise", ValueNoiseImage);
if (Sun != null)
Material.SetVector("_SunDir", -Sun.forward);
else
Material.SetVector("_SunDir", Vector3.up);
Material.SetFloat("_MinHeight", MinHeight);
Material.SetFloat("_MaxHeight", MaxHeight);
Material.SetFloat("_FadeDist", FadeDist);
Material.SetFloat("_Scale", Scale);
Material.SetFloat("_Steps", Steps);
Material.SetMatrix("_FrustumCornersWS", GetFrustumCorners());
Material.SetMatrix("_CameraInvViewMatrix", _Cam.cameraToWorldMatrix);
Material.SetVector("_CameraPosWS", _Cam.transform.position);
CustomGraphicsBlit(source, destination, Material, 0);
return;
}
// 구름을 스크린에 그린다
static void CustomGraphicsBlit(RenderTexture source, RenderTexture dest, Material fxMaterial, int passNr)
{
RenderTexture.active = dest;
fxMaterial.SetTexture("_MainTex", source);
GL.PushMatrix();
GL.LoadOrtho();
fxMaterial.SetPass(passNr);
GL.Begin(GL.QUADS);
GL.MultiTexCoord2(0, 0.0f, 0.0f);
GL.Vertex3(0.0f, 0.0f, 3.0f); // BL
GL.MultiTexCoord2(0, 1.0f, 0.0f);
GL.Vertex3(1.0f, 0.0f, 2.0f); // BR
GL.MultiTexCoord2(0, 1.0f, 1.0f);
GL.Vertex3(1.0f, 1.0f, 1.0f); // TR
GL.MultiTexCoord2(0, 0.0f, 1.0f);
GL.Vertex3(0.0f, 1.0f, 0.0f); // TL
GL.End();
GL.PopMatrix();
}
protected virtual void OnDisable()
{
if (_Material)
DestroyImmediate(_Material);
}
}
(_Cam은 Camera _Cam)
_Cam.CalculateFrustumCorners() : 카메라 frustumnew Rect(0,0,1,1) : 전체 뷰포트를 표현할 수 있음_Cam.farClipPlane : 카메라에서 가장 멀리 그려지는 거리에 위치한 평면Camera.MonoOrStereoscopicEye.Mono : 어느 눈의 시점인지 지정. Mono는 일반적인 단일 시점 카메라.SetRow() : 행렬의 행 할당OnRenderImage()
source)를 받아서 필터/이펙트 처리 후 destination에 출력하는 구조[ImageEffectOpaque]
Graphics.Blit()
Graphics.Blit(source, destination); : source 이미지를 destination으로 그냥 복사Graphics.Blit(source, destination, _Material); : _Material에 연결된 셰이더를 사용해서 source를 처리한 뒤 destination에 씀
화면에 맞추는 방법 : Camera에 Pixel Perfect Camera 컴포넌트 추가
Cinemachine 쓰고 있으면 Cinamachine Camera에도 Cinamachine Pixel Perfect 컴포넌트 추가
그래도 화면이 안 맞춰짐 : Sprite Pivot 때문에 0,0으로 놔도 offset이 생김

카메라를 그대로 두면 속도감이 전혀 느껴지지 않는다.

TriggerEnter2D는 rigidbody continous로 설정해도 의미없음
둘 중 하나라도 트리거 체크돼있으면 CollisionEnter2D 작동 안 함
OnCollisionEnter2D에선 linearVelocity 대신 relativeVelocity 써야 함.
linearVelocity는 충돌 때문에 0됨.
relativeVelocity 쓸 땐 rb가 아니라 collision에서 가져와야 함.