본문 바로가기

Unity

How to completely make 'Minecraft'(마인크래프트를 통째로 만드는 방법)을 따라해보며 유니티(Unity)를 연구하자 : 02-울퉁불퉁한 지형 만들기, 03-Standard Assets의FPSController로 1인칭시점 플레이어추가

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

 

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

번외-게임오브젝트(Game Object)정리

 

우선 지난 정리의 상태로 실행하면 Hierarchy에 GrassBlock(Clone)이 무수히 생성되어 보기 좋지 않으니 GrassBlock(Clone)을 Environment아래로 속하도록 스크립트(Script)를 조정실시

 

WorldGenerator.cs

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

public class WorldGenerator : MonoBehaviour
{
    public int sizeX; //땅의 좌우크기
    public int sizeZ; //땅의 상하크기

    public GameObject[] blocks; //블록 저장

    // Start is called before the first frame update
    void Start()
    {
        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++)
            {
                //Instantiate(blocks[0], new Vector3(x,0,z), Quaternion.identity); //기존처리
                GameObject grass = Instantiate(blocks[0], new Vector3(x, 0, z), Quaternion.identity) as GameObject;
                grass.transform.SetParent(GameObject.FindGameObjectWithTag("Environment").transform);
            }
        }
    }
}
GameObject grass = Instantiate(blocks[0], new Vector3(x, y, z), Quaternion.identity) as GameObject;
GrassBlock(Clone)를 GameObject grass작성

grass.transform.SetParent(GameObject.FindGameObjectWithTag("Environment").transform);
grass의 부모를 Environment로 설정

Transform.SetParent를 찾아봅니다.

public void SetParent (Transform parent);
public void SetParent (Transform parent, bool worldPositionStays);

Transform의 부모를 설정합니다.
worldPositionStays : 기본값은 true, 자식의 position정보를 부모의 position을 기반으로 하는 localPosition으로 함
                           false: 자식의 position정보를 부모를 기본으로 기존의 worldPosition만큼 멀어진 좌표로 함

worldPositionStays은 정확히 이해를 하지못했으나 테스트결과를 정리

둘다 작동상 차이점은 확인 못했으나 true, false에 따라 position값이 달라짐을 확인

  worldPositionStays(true) worldPositionStays(false)
Environment position.x=10 position.x=10
GrassBlock(Clone) position.x=-10 position.x=0

 

변경하고 실행하면 루트에서 표시되던 GrassBlock(Clone)이 Environment의 아래로 속함

02-울퉁불퉁한 땅 만들기

땅을 만드는 GrassBlock(Clone)의 position의 x,z값만 지정했기에 평평한 땅이 만들어짐

position의 y값도 지정해 울퉁불퉁하게 만듦

WorldGenerator스크립트(Script) 코딩

y의 값을 랜덤하게 넣어줘봄

    void GenerateTerrain()
    {
        for (int x = 0; x < sizeX; x++)
        {
            for (int z = 0; z < sizeZ; z++)
            {

                int y = Random.Range(0, 10);  //0~10사이의 랜덤값으로 y를 설정

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

            }
        }
    }

울퉁불퉁한 지형이 아닌 서로 떨어진 우주공간에 떠 있는 형태의 모양이됨

 

부드럽게 이어줘야 함

여기에서 유용한 것이 Mathf.PerlinNoise 함수

입력 값(x,y)의 변화에 따라 서서히 변화하는 난수를 생성
얻을 수 있는 값은 0~1
x와 y의 값이 같으면 항상 같은 값을 반환

 

Mathf.PerlinNoise 함수이용

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

public class WorldGenerator : MonoBehaviour
{
    public int sizeX;
    public int sizeZ;

    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++)
            {
                int y = (int)(Mathf.PerlinNoise((x / 2 + seed) / terDetail, (z / 2 + seed) / terDetail) * terHeight);
                GameObject grass = Instantiate(blocks[0], new Vector3(x, y, z), Quaternion.identity) as GameObject;
                grass.transform.SetParent(GameObject.FindGameObjectWithTag("Environment").transform,false);

            }
        }
    }
}
public float terDetail;
public float terHeight;
int seed;

seed = Random.Range(100000,999999);
seed값을 100000과 999999의 사이츼 수로 랜덤하게 설정

int y = (int)(Mathf.PerlinNoise((x / 2 + seed) / terDetail, (z / 2 + seed) / terDetail) * terHeight);
랜덤값이지만 이어지는 y값을 출력

TerDetail=20, TerHeight=20정도의 설정으로 생성된 지형

 

강좌에서 사용한 알고리즘이 아닌 스스로 여러가지 변경해봄

TerDetail=20, TerHeight=20
int y = (int)(Mathf.PerlinNoise( (x + seed) / terDetail, (z+seed) / terDetail) * terHeight));

 

TerDetail=20, TerHeight=40
int y = (int)(Mathf.PerlinNoise((x / 2 + seed) / terDetail, (z / 2 + seed) / terDetail) * terHeight);

TerDetail=40, TerHeight=20
int y = (int)(Mathf.PerlinNoise((x / 2 + seed) / terDetail, (z / 2 + seed) / terDetail) * terHeight);

강좌에서 사용된 알고리즘이 좋은 알고리즘이겠지만 자유롭게 변경해도 문제없을 것 같음

 

03-Standard Assets의FPSController로 1인칭시점 플레이어추가

Asset Store에 연결해서 Standard Assets 다운로드(Download) 및 임포트(Import)

유니티(Unity)의 메뉴에서 Window >> Asset Store 선택 ( 혹은 Ctrl+9) 해서 Asset Store에 연결
유니티(Unity) 어카운트로 로그인 필요

Asset Store에서 'Standard'키워드로 검색
'Standard Assets'를 다운로드(Download) , 임포트(Import)

필요한 요소 선택
위의 그림은 이미 임포트(Import)된 상태에서 다시 임포트(Import)를 했을 경우임
반대(체크->비체크, 비체크->체크)로 해서 임포트(Import), 혹은 전체 임포트(Import)해도 무방

임포트(Import) 한 것들

Standard Assets
--Characters
----FirstPersonCharacter
------Prefabs
--------FPSController  <-이것을 이용함
--CrossPlatformInput
--Editor
--Environment
--PhysicsMeterials
--Utility

플레이어(Player)를 추가하기 위해 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 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++)
            {
                int y = (int)(Mathf.PerlinNoise((x / 2 + seed) / terDetail, (z / 2 + seed) / terDetail) * terHeight);
                GameObject grass = Instantiate(blocks[0], new Vector3(x, y, z), Quaternion.identity) as GameObject;
                grass.transform.SetParent(GameObject.FindGameObjectWithTag("Environment").transform,false);

                if (x == (int)(sizeX / 2) && z == (int)(sizeZ / 2)) //땅의 정 중앙에 표시
                {
                    Instantiate(player, new Vector3(x, y + 3, z), Quaternion.identity);
                }

            }
        }
    }
}
public GameObject player;
유니티(Unity)에서 임포트(Import)한 FPSController를 설정할 예정

if (x == (int)(sizeX / 2) && z == (int)(sizeZ / 2))
{
  Instantiate(player, new Vector3(x, y + 3, z), Quaternion.identity);
}
플레이어를 땅의 정 중앙에 땅보다 조금 높은 위치에 캐릭터를 위치시킴

Environment의 Player에 FPSController 설정

FPSController를 Environment의 Player에 드래그 앤 드롭
(혹은 Player 입력폼 옆의 동그래미를 클릭해 FPSController를 선택)

실행

1인칭 시점으로 출력됨
WASD키로 이동,  스페이스로 점프, 마우스로 좌우 시점 변경가능

그러나 마우스로 상하 시점변경이 불가능

이유는 FPSController에 포함된 카메라가 아닌 기본의 Main Camera로 표시되기 때문

Hierarchy >> Main Camera 의 팝업메뉴 >> Delete
Main Camera 삭제

재실행

마우스로 상하좌우 모든 시점변경가능

 

현재는 울툴불퉁한 땅의 표면만 GrassBlock(Clone)으로 표현상 상태로 다음엔 표면의 아래를 흙과 돌의 블럭으로 채우기진행

반응형