본문 바로가기

Unity

How to completely make 'Minecraft'(마인크래프트를 통째로 만드는 방법)을 따라해보며 유니티(Unity)를 연구하자 : 04-땅표면 아래를 흙블럭과 돌블럭로 채우기, 05-블럭을 스크립트(Script)로 제어

유튜브(Youtube) Vagabond Developer(방랑개발자)님의 How to completely make 'Minecraft'(마인크래프트를 통째로 만드는 방법)을 따라하면서 유니티(Unity)를 알아가고 있습니다.  그 정리입니다.

 

https://www.youtube.com/watch?v=xNozfY_Iah8

 

04-땅표면 아래를 흙블럭과 돌블럭로 채우기

01-기본설정 및 평평한 땅 만들기에서 GrassBlock의 프리팹(Prefab)을 작성한 것과 동일한 방식으로 DirtBlock, StoneBlock의 프리팹(Prefab)을 작성

이미지를 준비해 텍스처작성 / 큐브(Cube)를 추가하고 머티리얼(Material)입히기 / 프리팹(Prefab) 작성

구글(Google) 이미지검색에서 dirt,stone를 아이콘사이즈로 검색해서 Dirt, Stone으로 저장, 팝업메뉴의 (Import New Asset...)로 임포트(Import)하거나 드래그해서 Textures폴더에 넣음

 

WorldGenerator스크립트(Script) 코딩

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class WorldGenerator : MonoBehaviour
{
    public GameObject player;  //플레이어 설정용

    public int sizeX;
    public int sizeZ;

    public int groundHeight; //땅의 깊이

    public float terDetail;  //Mathf.PerlinNoise적용
    public float terHeight;  //Mathf.PerlinNoise적용
    int seed;                //Mathf.PerlinNoise적용

    public GameObject[] blocks;

    // Start is called before the first frame update
    void Start()
    {
        seed = Random.Range(100000,999999);
        GenerateTerrain();
    }

    // Update is called once per frame
    void Update()
    {
        
    }

    void GenerateTerrain()
    {
        for (int x = 0; x < sizeX; x++)
        {
            for (int z = 0; z < sizeZ; z++)
            {
                //maxY : 표면(GlassBlock)
                int maxY = (int)(Mathf.PerlinNoise((x / 2 + seed) / terDetail, (z / 2 + seed) / terDetail) * terHeight);
                maxY += groundHeight; // 표면(GlassBlock)을 땅의 깊이(groundHeight)만큼 올려줌
                
                GameObject grass = Instantiate(blocks[0], new Vector3(x, maxY, z), Quaternion.identity) as GameObject;
                grass.transform.SetParent(GameObject.FindGameObjectWithTag("Environment").transform,false);

                for (int y = 0; y < maxY; y++) // 표면(GlassBlock)아래의 땅 생성
                {
                   GameObject stone = Instantiate(blocks[1], new Vector3(x, y, z), Quaternion.identity) as GameObject;
                   stone.transform.SetParent(GameObject.FindGameObjectWithTag("Environment").transform, false);
                }
                
                if (x == (int)(sizeX / 2) && z == (int)(sizeZ / 2)) //표면(GlassBlock)의 정 중앙에 표시
                {
                    Instantiate(player, new Vector3(x, maxY + 3, z), Quaternion.identity);
                }

            }
        }
    }
}
public int groundHeight;
땅 깊이

int y = (int)(Mathf.PerlinNoise((x / 2 + seed) / terDetail, (z / 2 + seed) / terDetail) * terHeight);
기존의 처리를 변경(땅 깊이(groundHeight)만큼 표면(GrassBlock)을 올려줌)
int maxY = (int)(Mathf.PerlinNoise((x / 2 + seed) / terDetail, (z / 2 + seed) / terDetail) * terHeight);
maxY += groundHeight;

GameObject grass = Instantiate(blocks[0], new Vector3(x, maxY, z), Quaternion.identity) as GameObject;
grass.transform.SetParent(GameObject.FindGameObjectWithTag("Environment").transform,false);

for (int y = 0; y < maxY; y++)
{
   GameObject stone = Instantiate(blocks[1], new Vector3(x, y, z), Quaternion.identity) as GameObject;       
   stone.transform.SetParent(GameObject.FindGameObjectWithTag("Environment").transform, false);
}
표면(GrassBlock) 아래의 땅을 StoneBlock으로 채우기

if (x == (int)(sizeX / 2) && z == (int)(sizeZ / 2)) //표면(GlassBlock)의 정 중앙에 표시
{
   Instantiate(player, new Vector3(x, maxY + 3, z), Quaternion.identity);
}
플레이어의 위치도 표면(GlassBlock)보다 조금 높은 위치로 조정

Environment오브젝트(Object)의 Blocks에 StoneBlock프리팹(Prefab)설정

GroundHeight설정

Blocks의 사이즈를 2로 변경 
추가로 표시되는 Element 1에 StoneBlock프리팹(Prefab)을 설정(드래그 앤 드롭 또는 선택)

실행

표면(GrassBlock)과 땅밑이 깊이만큼 StoneBlock으로 잘 채워짐

 

WorldGenerator스크립트(Script) 코딩

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class WorldGenerator : MonoBehaviour
{
    public GameObject player;  //플레이어 설정용

    public int sizeX;
    public int sizeZ;

    public int groundHeight; //땅의 깊이

    public float terDetail;  //Mathf.PerlinNoise적용
    public float terHeight;  //Mathf.PerlinNoise적용
    int seed;                //Mathf.PerlinNoise적용

    public GameObject[] blocks;

    // Start is called before the first frame update
    void Start()
    {
        seed = Random.Range(100000,999999);
        GenerateTerrain();
    }

    // Update is called once per frame
    void Update()
    {
        
    }

    void GenerateTerrain()
    {
        for (int x = 0; x < sizeX; x++)
        {
            for (int z = 0; z < sizeZ; z++)
            {
                //maxY : 표면(GlassBlock)
                int maxY = (int)(Mathf.PerlinNoise((x / 2 + seed) / terDetail, (z / 2 + seed) / terDetail) * terHeight);
                maxY += groundHeight; // 표면(GlassBlock)을 땅의 깊이(groundHeight)만큼 올려줌
                
                GameObject grass = Instantiate(blocks[0], new Vector3(x, maxY, z), Quaternion.identity) as GameObject;
                grass.transform.SetParent(GameObject.FindGameObjectWithTag("Environment").transform,false);

                for (int y = 0; y < maxY; y++) // 표면(GlassBlock)아래의 땅 생성
                {
                   int dirtLayers = Random.Range(1, 5); //흙(DirtBlock)의 깊이를 랜덤하게
                   if (y >= maxY - dirtLayers)
                   {
                      //흙(DirtBlock)
                      GameObject dirt = Instantiate(blocks[2], new Vector3(x, y, z), Quaternion.identity) as GameObject;
                      dirt.transform.SetParent(GameObject.FindGameObjectWithTag("Environment").transform, false);
                   }
                   else
                   {
                      //돌(StoneBlock)
                      GameObject stone = Instantiate(blocks[1], new Vector3(x, y, z), Quaternion.identity) as GameObject;
                      stone.transform.SetParent(GameObject.FindGameObjectWithTag("Environment").transform, false);
                   }
                }
                
                if (x == (int)(sizeX / 2) && z == (int)(sizeZ / 2)) //표면(GlassBlock)의 정 중앙에 표시
                {
                    Instantiate(player, new Vector3(x, maxY + 3, z), Quaternion.identity);
                }

            }
        }
    }
}
int dirtLayers = Random.Range(1, 5);
흙(DirtBlock)의 깊이를 랜덤하게

GrassBlock
------------- maxY
DirtBlock
-------------  ( maxY - dirtLayers )
StoneBlock
------------- 0

if (y >= maxY - dirtLayers)
{
   GameObject dirt = Instantiate(blocks[2], new Vector3(x, y, z), Quaternion.identity) as GameObject;       
   dirt.transform.SetParent(GameObject.FindGameObjectWithTag("Environment").transform, false);
}
else
{
   GameObject stone = Instantiate(blocks[1], new Vector3(x, y, z), Quaternion.identity) as GameObject;     
   stone.transform.SetParent(GameObject.FindGameObjectWithTag("Environment").transform, false);
}

 

Environment오브젝트(Object)의 Blocks에 DirtBlock프리팹(Prefab)설정

Blocks의 사이즈를 3로 변경 

추가로 표시되는 Element 2에 DirtBlock프리팹(Prefab)을 설정(드래그 앤 드롭 또는 선택)

실행

표면(GrassBlock)과 땅밑이 깊이만큼 랜덤의 DirtBlock과 StoneBlock으로 잘 채워짐

 

05-블럭을 스크립트(Script)로 제어

BlockScript스크립트(Script)을 추가하고 코딩

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class BlockScript : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        
    }

    //마우스눌렀을때
    private void OnMouseDown()
    {
        Destroy(this.gameObject);
    }
}
private void OnMouseDown()
{
   Destroy(this.gameObject);
}

OnMouseDown : 마우스 왼쪽 클릭만 작동

public static void Destroy (Object obj, float t= 0.0F) :  오브젝트(obj)를 t만큼의 딜레이후 삭제

this.gameObject: 자기 자신 (스크립트(Script)를 블럭에 설정할 예정이므로 클릭된 블럭의 의미)

참고) 마우스 왼쪽, 오른쪽, 중간버튼 클릭 체크

    private void OnMouseOver()
    {
        if(Input.GetMouseButtonDown(0))
        {
            Debug.Log("Left");
        }
        if (Input.GetMouseButtonDown(1))
        {
            Debug.Log("RIGHT");
        }
        if (Input.GetMouseButtonDown(2))
        {
            Debug.Log("CENTER");
        }
    }

 

블럭프리팹(Prefab)에 BlockScript스크립트(Script)을 설정

프리팹(Prefab)폴더안의 각 블럭프리팹(Prefab)을 선택>>Inspector >> Add Component 후 BlockScript검색해서 추가
혹은 스크립트(Script)폴더의 BlockScript를 블럭프리팹(Prefab)으로 드래그 앤 드롭

실행

마우스를 클릭하면 화면의 정중앙의 블록이 잘 삭제됨

FPSController조정

발밑의 땅을 파도(삭제) FPSController의 크기가 커서 빠지지 않으므로 크기를 조정

FPSController선택>>Inspector >>Transform>>Scale 조정
x,y,z 1 -> 0.8

 

다음엔 정중앙을 직관적으로 표시하기위해 십자선(Crosshair)넣기를 진행

반응형