
저번에 말했던 것 처럼 자연스럽게 Block 생성 애니메이션을 구현해보자.
원래 애니메이션 기능을 쓰려고 했는데 애니메이션 상태가 계속해서 유지되면서 transform의 색을 바꾸거나 할 때 문제가 생기는 경우를 막으려고 코드로 구현했다
/// Generate the map by loading Node Prefab based on MapData.
public void MapGenerate(Map map, Transform fieldObject) {
Debug.Log("Map Generation Start...");
// Initialize MapGenerator's prefab by retrieving Prefab from PrefabRepository
GetPrefabFromRepository();
if (NodePrefab == null || StartNodePrefab == null || EndNodePrefab == null) {
Debug.LogWarning("Node Prefab Error");
return;
}
float delayTime = 0.1f;
float StartNodeHeight = 100f;
float delayTimeMulti = 10f;
GameObject startNode = Instantiate(StartNodePrefab,
new Vector3(map.startNode.x, StartNodeHeight, map.startNode.z) ,
Quaternion.identity, fieldObject);
GameObject endNode = Instantiate(EndNodePrefab,
new Vector3(map.endNode.x, StartNodeHeight, map.endNode.z) ,
Quaternion.identity, fieldObject);
StartCoroutine(SmoothMoveToPosition(startNode.transform, map.startNode, delayTime * delayTimeMulti));
StartCoroutine(SmoothMoveToPosition(endNode.transform, map.endNode, delayTime * delayTimeMulti));
GameManager.Instance.endPoint = endNode.transform.Find(Names.endPoint).gameObject;
// Spawn Nodes to use Couroutine (interval Spawn)
StartCoroutine(SpawnNodesWithDelay(map, fieldObject, delayTime, StartNodeHeight, delayTimeMulti));
Debug.Log("Map Generation Finish!");
}
/// Coroutines for creating nodes at intervals
IEnumerator SpawnNodesWithDelay(Map map, Transform fieldObject, float delayTime, float StartHeight, float delayTimeMulti)
{
yield return new WaitForSeconds(delayTime * delayTimeMulti / 2);
foreach (Vector3 targetVec in map.nodesPosition)
{
Vector3 firstVec = new Vector3(targetVec.x, StartHeight, targetVec.z);
GameObject node = Instantiate(NodePrefab, firstVec, Quaternion.identity, fieldObject);
// Start the coroutine to move the node smoothly
StartCoroutine(SmoothMoveToPosition(node.transform, targetVec, delayTime * delayTimeMulti));
yield return new WaitForSeconds(delayTime);
}
}
/// Smoothly moves the node transform to the end position over the specified duration.
IEnumerator SmoothMoveToPosition(Transform node, Vector3 endPos, float duration) {
Vector3 startPos = node.position;
float elapsedTime = 0;
while (elapsedTime < duration) {
float t = elapsedTime / duration;
float smoothT = Mathf.SmoothStep(0, 1, t);
node.position = Vector3.Lerp(startPos, endPos, smoothT);
elapsedTime += Time.deltaTime;
yield return null;
}
node.position = endPos;
}
Lerp의 경우 파라미터로 넘기는 T의 값에 따라 그래프의 모양이 달라진다. 아래 영상을 참고해보자.



내가 원하는 속도가 점차 빨라지다가 속도가 느려지게 하기 위해서는 smoothStep을 파라미터로 사용하면 된다.
이 때 이동 속도는 v는 위치의 기울기이다. f'(t)라고 쓸 수 있다. 쉽게 말해 위 보이는 보라색 그래프의 기울기가 가파를수록 속도가 더 빠르게 이동하는 것이고 그래프가 완만할수록 느리게 이동한다고 볼 수 있다.
이 외에도 유튜브 영상을 참고하면 아래와 같은 그래프를 파라미터로 넘겨줄 수 있다(!!)





이 외에도 color의 lerp라던가 Zoom의 Lerp를 다루기도 하기 때문에 영상을 꼭 한 번씩은 보는 것을 추천한다! 파라미터 t에 따라 굉장히 색다른 이동을 구현할 수 있다.
위와 같이 작성하고 나면 다음 결과를 얻을 수 있다.

저번에 말했던 반투명 블럭 로직을 수정해보자. 원래 문제는 반투명 블럭을 노드별로 오브젝트로 넣어주고 활성화 비활성화 해주다보니 다른 종류의 블럭을 골랐을때 해당 블럭으로 반투명 블럭을 바꿔줘야하는 문제가 발생했다. 그대로 구현한다면 노드안에 모든 종류의 반투명 블럭을 넣고 고른 블럭에 맞게 활성화 비활성화 해주면 되는데 그러면 블럭의 종류가 많아질수록 + 노드가 많아질수록 쓸데 없는 오브젝트의 수가 너무 많아져서 간단하게 구현하도록 바꿨다.
원리는 간단하다 TransparentBlock에 반투명 블럭의 종류를 하나씩 넣어준 다음에 마우스 이벤트에 따라 해당 오브젝트의 위치만 옮기면서 활성화 해준다. 그러면 노드별로 반투명 블럭을 유지할 필요가 없다.
public class MapGenerator : MonoBehaviour
{
public PrefabRepository prefabRepository;
[Space(10)]
[Header("Node Prefabs")]
public GameObject NodePrefab;
public GameObject StartNodePrefab;
public GameObject EndNodePrefab;
public void GetPrefabFromRepository() {
prefabRepository.InitializeDictionary();
NodePrefab = prefabRepository.GetPrefab(Names.Node);
StartNodePrefab = prefabRepository.GetPrefab(Names.startNode);
EndNodePrefab = prefabRepository.GetPrefab(Names.endNode);
}
/// Generate the map by loading Node Prefab based on MapData.
public void MapGenerate(Map map, Transform fieldObject) {
Debug.Log("Map Generation Start...");
// Initialize MapGenerator's prefab by retrieving Prefab from PrefabRepository
GetPrefabFromRepository();
if (NodePrefab == null || StartNodePrefab == null || EndNodePrefab == null) {
Debug.LogWarning("Node Prefab Error");
return;
}
float delayTime = 0.1f;
float StartNodeHeight = 100f;
float delayTimeMulti = 10f;
GameObject startNode = Instantiate(StartNodePrefab,
new Vector3(map.startNode.x, StartNodeHeight, map.startNode.z) ,
Quaternion.identity, fieldObject);
GameObject endNode = Instantiate(EndNodePrefab,
new Vector3(map.endNode.x, StartNodeHeight, map.endNode.z) ,
Quaternion.identity, fieldObject);
StartCoroutine(SmoothMoveToPosition(startNode.transform, map.startNode, delayTime * delayTimeMulti));
StartCoroutine(SmoothMoveToPosition(endNode.transform, map.endNode, delayTime * delayTimeMulti));
GameManager.Instance.endPoint = endNode.transform.Find(Names.endPoint).gameObject;
// Spawn Nodes to use Couroutine (interval Spawn)
StartCoroutine(SpawnNodesWithDelay(map, fieldObject, delayTime, StartNodeHeight, delayTimeMulti));
Debug.Log("Map Generation Finish!");
}
/// Coroutines for creating nodes at intervals
IEnumerator SpawnNodesWithDelay(Map map, Transform fieldObject, float delayTime, float StartHeight, float delayTimeMulti)
{
yield return new WaitForSeconds(delayTime * delayTimeMulti / 2);
foreach (Vector3 targetVec in map.nodesPosition)
{
Vector3 firstVec = new Vector3(targetVec.x, StartHeight, targetVec.z);
GameObject node = Instantiate(NodePrefab, firstVec, Quaternion.identity, fieldObject);
// Start the coroutine to move the node smoothly
StartCoroutine(SmoothMoveToPosition(node.transform, targetVec, delayTime * delayTimeMulti));
yield return new WaitForSeconds(delayTime);
}
}
/// Smoothly moves the node transform to the end position over the specified duration.
IEnumerator SmoothMoveToPosition(Transform node, Vector3 endPos, float duration) {
Vector3 startPos = node.position;
float elapsedTime = 0;
while (elapsedTime < duration) {
float t = elapsedTime / duration;
float smoothT = Mathf.SmoothStep(0, 1, t);
node.position = Vector3.Lerp(startPos, endPos, smoothT);
elapsedTime += Time.deltaTime;
yield return null;
}
node.position = endPos;
}
}
public class BuildMenu : MonoBehaviour
{
BuildManager buildManager;
private void Start()
{
buildManager = BuildManager.Instance;
}
public void SelectStandardBlock()
{
Debug.Log("Standard Block Selected");
TransparentBlockManager.Instance.SetSelectedBlockType(0); // set selected block Type to 0 ( = standard block )
// TODO - selected Block Type = !!hard cording!! ( Block Ropository, Inventory Manager )
buildManager.SetBlockToBuild(buildManager.standardBlockPrefab); // set selected block to Standard block
}
public void SelectOtherBlock()
{
Debug.Log("Other Block Selected");
TransparentBlockManager.Instance.SetSelectedBlockType(1);
// TODO - selected Block Type = !!hard cording!! ( Block Ropository, Inventory Manager )
buildManager.SetBlockToBuild(buildManager.otherBlockPrefab); // set selceted block to Other block;
}
}

잘 작동한다! 이러면 이제 모든 노드가 반투명 블럭을 가지고 있을 필요가 없어 게임 오브젝트 수도 많이 줄었고 추후에 블럭의 종류가 많이 추가되도 노드를 수정할 필요없이 TransparentBlock 매니저에게 반투명 블럭을 넘겨주기만 하면 된다.
Block Repository와 Block Inventory를 구현할 예정이다. Block Repository는 블럭 오브젝트들과 블럭 타입을 가지고 있도록 해서 Block Type을 위에는 임의로 하드 코딩해놨는데 그게 아니라 Repository에 블럭 타입을 가져다가 쓸 예정이다. Block Inventory는 Map에 따라 건설할 수 있는 블럭의 종류와 수량을 설정해줘서 해당 Inventory에 따라 아래 BuildMenu Ui를 수정해줄 예정이다.