Unity 3D 21.04.16.수업내용
Post Processing(후처리)
-게임화면이 최종 출력되기 전까지 카메라의 이미지를 버퍼에 삽입하는 추가 처리
작은 노력으로 영상미를 구현할 수 있다.
ex) 카메라앱 필터
포스트 프로세싱 연산은 렌더링 파이프라인의 주요과정의 마지막 부분에 적용된다.
유니티는 포스트 프로세싱을 쉽게 사용할 수 있는 포스트 프로세싱 스택패키지를 제공한다.

Main Camera
Rendering Path : 렌더링이 처리되는 순서와 방법을 결정하는 옵션
Forward Rendering : 각각의 오브젝트를 그릴때마다 해당 오브젝트에 영향을 주는 모든 라이팅을 함께 계산하는 전통적인 방식이다. 메모리 사용량이 적고 저사양에서도 비교적 잘 돌아간다. 연산속도가 느리고 광원이 움직이거나 수가 많아질수록 연산량이 급증한다.
디퍼드 : 라이팅 연산을 미뤄서 실행하는 방식이다. 첫번째 패스에서 오브젝트의 메시를 그리되 라이팅 연산을 하거나 색을 채우지 않는다. 대신 오브젝트의 여러 정보를 종류별로 버퍼에 저장한다. 두번째 패스에서 정보를 활용해 라이팅을 계산하고 최종 색상을 결정한다.
Anti-Aliasing

============================================================================
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
public class Logo : MonoBehaviour
{
public UnityAction onComplete;
public void Init()
{
StartCoroutine(WaitForDisplayLogo());
}
private IEnumerator WaitForDisplayLogo()
{
yield return new WaitForSeconds(3f);
this.onComplete();
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Title : MonoBehaviour
{
public System.Action onTouch;
// Update is called once per frame
void Update()
{
if (Input.GetMouseButtonDown(0))
{
this.onTouch();
}
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Newtonsoft.Json;
using System.Linq;
public class DataManager
{
private static DataManager instance;
public Dictionary<int, CharacterData> dicCharacterData;
private DataManager()
{
}
public static DataManager GetInstance()
{
if(DataManager.instance == null)
{
DataManager.instance = new DataManager();
}
return DataManager.instance;
}
public void LoadDatas()
{
var ta = Resources.Load<TextAsset>("Data/character");
var json = ta.text;
var arr = JsonConvert.DeserializeObject<CharacterData[]>(json);
this.dicCharacterData = arr.ToDictionary(x => x.id);
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CharacterData
{
public int id;
public string name;
public float damage;
public string res_name;
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class App : MonoBehaviour
{
public enum eSceneType
{
App, Logo, Title, InGame
}
// Start is called before the first frame update
void Start()
{
DontDestroyOnLoad(GameObject.Find("App"));
this.ChangeScene(eSceneType.Logo);
}
public void ChangeScene(eSceneType sceneType)
{
switch (sceneType)
{
case eSceneType.Logo:
{
AsyncOperation ao = SceneManager.LoadSceneAsync("Logo");
ao.completed += (obj) =>
{
var logo = GameObject.FindObjectOfType<Logo>();
logo.Init();
logo.onComplete = () =>
{
this.ChangeScene(eSceneType.Title);
};
};
}
break;
case eSceneType.Title:
{
AsyncOperation ao = SceneManager.LoadSceneAsync("Title");
ao.completed += (obj) =>
{
var title = GameObject.FindObjectOfType<Title>();
title.onTouch = () =>
{
DataManager.GetInstance().LoadDatas();
this.ChangeScene(eSceneType.InGame);
};
};
}
break;
case eSceneType.InGame:
{
var ao = SceneManager.LoadSceneAsync("InGame");
ao.completed += (obj) =>
{
InGame inGame = GameObject.FindObjectOfType<InGame>();
inGame.Init(100);
};
}
break;
}
}
// Update is called once per frame
void Update()
{
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class InGame : MonoBehaviour
{
public void Init(int characterId)
{
var shell = new GameObject();
shell.name = "Hero";
var model = CreateModel(characterId);
model.name = "Model";
model.transform.SetParent(shell.transform);
var hero = shell.AddComponent<Hero>();
var info = new CharacterInfo(characterId);
hero.Init(info, model);
}
public GameObject CreateModel(int id)
{
var data = DataManager.GetInstance().dicCharacterData[id];
var path = string.Format("Prefabs/{0}", data.res_name);
var prefab = Resources.Load<GameObject>(path);
var go = Instantiate<GameObject>(prefab);
return go;
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Hero : MonoBehaviour
{
public CharacterInfo characterInfo;
public GameObject model;
private Animation anime;
public void Init(CharacterInfo info, GameObject model)
{
this.characterInfo = info;
this.model = model;
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CharacterInfo : MonoBehaviour
{
public int Id { get; private set; }
public CharacterInfo(int id)
{
this.Id = id;
}
}
-데이터 저장 및 불러오기
ㄴC#때와 마찬가지로 Json을 이용하여 직렬/역직렬화를 한다. C#과 같이 AssetStore에서 Json기능을 해주는 에셋을 다운 받고 임포트를 한다. 그러면 Json기능을 사용할 수있다.


ㄴ그렇다면 아래와같이 에셋폴더 하위에 파일이 생긴다.

이제 준비는 끝났다.
============================================================================
-역직렬화
ㄴ역직렬화는 C#에서 했지만 존재하는 제이슨형식의 데이터를 불러오는 행동이다.
ㄴ위 코드에서 DataManager가 역직렬화는 수행하고있다.
public void LoadDatas()
{
var ta = Resources.Load<TextAsset>("Data/character");
var json = ta.text;
var arr = JsonConvert.DeserializeObject<CharacterData[]>(json);
this.dicCharacterData = arr.ToDictionary(x => x.id);
}
ㄴ이 문단에서 하고있는데, Resources라는 에셋폴더 하위에 존재하는 폴더에서 TextAsset을 Load(Load<TextAsset)하겠다는 것이다. 위치는 Resource폴더의 Data하위에 character("Data/character")파일을 가리키고있다.
ㄴC#과 불러오는 형식이 살짝 다르지만 그래도 비슷하다. 받아온 TextAssset형식의 ta를 .text를 사용하여 string 형식으로 바꿔준다.
ㄴ그리고 string화 된 json을 배열로 만든다.
var arr = JsonConvert.DeserializeObject<CahracterData[]>(json);
ㄴ여기서 JsonConvert.DeserializeObject<T>();를 사용하기위해 상단에 using Newtonsoft.Json;을 입력해주어야만 역직렬화 기능을 불러올 수 잇다. 이 코드로 데이터를 배열로 변환할 수 있는데, 이를 아이디에 맞는 데이터를 불러올 수 있도록 딕셔너리화 시킨다.
ㄴ불러오는 ID는 정수값이므로 int가 키값이며, 불러오는 정보는 CharacterData이므로 형식은 Dictionary<int,CharacterData>이다.
============================================================================
-비동기적 씬전환
ㄴ게임을 시작하면 로고 - 타이틀씬 - 인게임정도로 게임이 진행되는데, 로고는 화면만 뜨는 형식으로 하고 타이틀은 터치(또는 아무키)를 입력받으면 데이터들을 불러오게되며 작업이 끝나면 인게임으로 넘어가게된다. 인게임에서는 불러온 데이터를 연결하여 캐릭터를 생성한다.
-빈 게임오브젝트에 불러온 데이터 씌우고 기능 부여하기
-위 코드에서는 App에서 비동기적 전환을 하는데, Logo에서는 Coroutine을 사용하여 3초동안 로고를 띄운 뒤, Title로 바로 넘어가게 했으며 Title에서는 클릭을 입력받으면 데이터매니저를 통해 데이터를 불러오도록 했다.