유니티 3D 게임 소스 - yuniti 3D geim soseu

[유니티 기초 예제 - BE1] 기초만 꾹꾹 눌러담은 3D 게임 만들기 : 골드메탈

님의 영상을 보고 쓰여진 게시글입니다. 

이번게시글에선

플레이어 만들기

아이템 만들기

플레이어와 아이템의 상호작용 코딩 

까지 3개 진행하도록 하겠습니다

여태까지 배운걸 기반으로 유니티 3D게임을 만들어 봅시다.

Step 1 기획

골드메탈님이 직접 쓰신 기획입니다. 유니티 기초 Roll a ball을 참고해서 발전시키셨다고 하네요 

게임을 만들때는 기획먼저 

유니티 3D 게임 소스 - yuniti 3D geim soseu

기본설정하기

새 프로젝트는 

Roll_A_Ball_골드메탈 로 지었습니다

유니티 3D 게임 소스 - yuniti 3D geim soseu

본격적으로 시작하기 전에 필요한 Assets들을 폴더로 만들어서 정리해줍니다

(폰트를 사용할 때는 저작권과 상업적 용도로 사용 가능 여부를 꼭 확인하도록 하세요)

hellchosun.net/52

저작권 걱정없는 상업용 무료 폰트 80가지

← 페이지 1  |  고딕/명조  |  페이지 2 → 먼저, TTF와 OTF 파일 차이 TTF : 애플과 마이크로소프트가 함께 만든 폰트 파일 형식. 주로 문서작업에 유용. 비교적 속도가 빠르지만 곡선처리가 미흡

hellchosun.net

유니티 3D 게임 소스 - yuniti 3D geim soseu

제가 이것들이 진짜 저작권 걱정없는 폰트인지 확인은 안해봤는데

혹시라도 상업용도의 게임을 만드실 거라면 꼭 한번 잘 알아보시고 사용하시길 바랍니다.

유니티 3D 게임 소스 - yuniti 3D geim soseu
Step2 코딩

기획 단계 구성을 봅시다 Player / Item / 지형 / 결승전 이 있네요 

모두 각자의 스크립트/ 텍스쳐 /피지컬 재질 / 재질 / 사운드 등등을 가질 것 입니다.

먼저 플레이어부터 코딩해봅시다 

1. 플레이어(공)

플레이어(공) 만들기

구 하나를 만들어 플레이어라고 이름을 붙여줍니다

유니티 3D 게임 소스 - yuniti 3D geim soseu

플레이어(구)를 조작할 수 있도록 스크립트를 작성해봅시다

PlayerBall이라는 스크립트를 생성해 주세요(분류해둔 폴더 안에 만드시면 되겠죠)

유니티 3D 게임 소스 - yuniti 3D geim soseu

물리 효과를 넣어주어야 하므로  Rigidbody를 사용해야겠네요

Rigidbody는 물리현상을 표현할 수 있는 컴포넌트라고 했습니다.

Inspector에 AddComponent로 Rigidbody를 추가해주시고요

유니티 3D 게임 소스 - yuniti 3D geim soseu

스크립트에 변수 생성과 초기화를 해주어야 스크립트에서 rigidbody를 이용한 물리현상을 만들어 낼 수 있습니다.

    Rigidbody rigid; // 변수 생성
    
    rigid = GetComponent<Rigidbody>(); //초기화

스크립트(Script)

유니티 라이프 사이클 게시글을 보시면 각 함수의 용도가 나와있을 것 입니다.

물체의 움직임을 표현하려면

1. 공의 이동

유니티 3D 게임 소스 - yuniti 3D geim soseu

Rigidbody초기화는 이 부분에 해주어야 겠네요

유니티 3D 게임 소스 - yuniti 3D geim soseu

움직임은 물리 연산이므로 위의 함수가 사용되어야 합니다.

Awake() , FixedUpdate() 함수를 사용하여 점프를 제외한 움직임을 구현해주었습니다.

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

public class PlayerBall : MonoBehaviour
{

    Rigidbody rigid; 
    
    void Awake(){
            rigid = GetComponent<Rigidbody>();
    }

    // Update is called once per frame
    void FixedUpdate() //공의 움직임 물리현상이므로 FixedUpdate사용 
    {
        float h = Input.GetAxisRaw("Horizontal"); //GetAxisRaw : 0, 1 ,-1로 떨어짐 
        float v = Input.GetAxisRaw("Vertical");

        rigid.AddForce(new Vector3(h,0,v), ForceMode.Impulse);
    }
}

new Vector3(h,0,v) // 0은 y좌표로의 벡터인데 이는 점프 기능으로 구현해야하므로 제외입니다.

이제 스크립트를 저장하고 오브젝트에 스크립트를 적용한뒤 실제로 제대로 구현이 되었나 확인해주어야 합니다.

게임을 만들 때는 기능을 하나 만들 때 마다 확인해서 제대로 돌아가는지 확인해주는게 좋습니다.

유니티 3D 게임 소스 - yuniti 3D geim soseu

드래그로 오브젝트에 스크립트를 적용시킨 후

바닥(Quad)을 만들어 적당히 조절해 준 뒤 실행시켜보았습니다.

유니티 3D 게임 소스 - yuniti 3D geim soseu

공이 너무 빠르다 생각하면 

공의 무게를 늘리거나(Rigidbody - Mass -> 점프등등 모든 공의 움직임에 영향을 주겠네요) 파워를 줄여주시면됩니다.

Mass : 5를 사용했습니다.

2. 공의 점프

점프는 Update에 구현해줍니다.

점프도 물리 연산아닌가? 라고 저는 생각하는데.. 잘 모르겠네요

유니티 3D 게임 소스 - yuniti 3D geim soseu

아래와 같이 구현하면 스페이스 바를 누르면 점프가 가능해지는데

jumpPower변수를 두어 조작하는게 숫자를 기입하는 거보다 후에 훨씬 편합니다.

    void Update(){
        if(Input.GetButtonDown("Jump")){
            rigid.AddForce(new Vector3(0,jumpPower,0),ForceMode.Impulse);
        }
    }

여기서 JumpPower변수를 public으로 선언하면 스크립트 상이 아닌 유니티상에서도 변경이 가능하다고 합니다!

유니티 3D 게임 소스 - yuniti 3D geim soseu

public으로 선언해주면, (public으로 선언해주면 = 30 이렇게 초기화 안하고 변수명만 써주셔도됩니다.)

유니티 3D 게임 소스 - yuniti 3D geim soseu

이렇게 유니티 오브젝트의 스크립트 부분에서 변수를 조작할 수 있어서 점프 파워를 어느정도로 해야 좋을지 테스트 할때 편리합니다

3. 점프 횟수 제한하기

위와 같이 하면 점프를 무한대로 할 수 있게됩니다.

이제 점프를 1단점프만 가능하도록 bool변수를 써서 조작해봅시다.

bool변수를 통해 현재 점프상태이면 true, 점프상태가 아니면(바닥에 붙어있음) false를 넣어줍시다

bool변수를 만들고 / 변수를 false상태로 초기화하고 / 점프를 할때마다 true로 바꾸었다가 / 바닥에 다시 닿으면 false로 바꾸기

4단계를 진행하면됩니다.

bool isJump; //1. 변수를 만들고


isJump=false; // 2. 변수를 초기화하고 (점프가 안된 상태라고 가정) -> 초기화 awake함수에 넣어주어야함



//3. 점프를 할 때마다 true로 변경 
if(Input.GetButtonDown("Jump") && !isJump){ //isJump가 false일 때만가능(1회만 가능)
            isJump=true; // 점프를 하면 true로 변경 
            rigid.AddForce(new Vector3(0,jumpPower,0),ForceMode.Impulse);
        }
        
        
//4. 바닥에 닿으면 false로 변경 ( 물리 충돌 이벤트를 사용 ). 
void OnCollisionEnter(Collision collision) { //공이 바닥과 닿으면 jump상태 해제 
        
        if(collision.gameObject.name == "Floor") isJump=false;
    }

이 단계를 진행하는데 풀버전으로 다시한번 올려드립니다.

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

public class PlayerBall : MonoBehaviour
{
    public float jumpPower;
     bool isJump;

    Rigidbody rigid; 
    
    void Awake(){
            rigid = GetComponent<Rigidbody>();
            isJump=false; // 점프가 안된 상태라고 가정 
    }

    // Update is called once per frame


    void Update(){
        if(Input.GetButtonDown("Jump") && !isJump){ //isJump가 false일 때만(1회만 가능)
            isJump=true;
            rigid.AddForce(new Vector3(0,jumpPower,0),ForceMode.Impulse);
        }
    }

    void FixedUpdate() //공의 움직임 물리현상이므로 FixedUpdate사용 
    {
        float h = Input.GetAxisRaw("Horizontal"); //GetAxisRaw : 0, 1 ,-1로 떨어짐 
        float v = Input.GetAxisRaw("Vertical");

        rigid.AddForce(new Vector3(h,0,v), ForceMode.Impulse);
    }

    void OnCollisionEnter(Collision collision) { //공이 바닥과 닿으면 jump상태 해제 
        
        if(collision.gameObject.name == "Floor") isJump=false;
    }
}

이렇게 하면 공은 1단점프만 가능하게 됩니다.

재질(Material)

플레이어 꾸미기 

3Material 폴더에 새로운 Material을 생성해주었습니다. (PlayerBallStyle)

Texture폴더에 새로운 텍스쳐를 가져온 후 

유니티 3D 게임 소스 - yuniti 3D geim soseu
저렴하게 그려봤는데 안 예쁘게 적용되는데 할수없죠 뭐

생성한 Material Albedo 옆 네모박스에 텍스쳐를 드래그해서 적용시켜준 후

PlayerBallStyle Material을 공오브젝트에 드래그해 적용시켜 주었습니다.

유니티 3D 게임 소스 - yuniti 3D geim soseu
왜 이렇게 어둡게 느껴지는지 모르겠네요 좀 불좀켜고싶어요

이로서 플레이어 기본 설정은 끝났습니다

이동과 점프가 가능해졌고 공도 적절하게 꾸며줬습니다.  

이제 아이템을 만들어 보도록 합시다.

아이템(Item)

골드메탈님은 캡슐모양으로 하셨는데 전 그럼 큐브로 해보도록..하겠습니다.

일단 재질먼저 (겉모습) 만들어보도록 합시다

일단 그전에 크기나 전체적 형태를 먼저 조절해보도록 하죠

아이템이니까 공을 옆에 두고 공과의 크기나 등등을 조절하면서 해보면 좋을 것 같습니다

비교하려면 화면조작이 잘되어야 할 탠데 이김에 연습해 보세요 화면조작은 유니티 기초 게시글에 있습니다.

Q / W / 마우스 오른쪽 정도면 하실 수 있을 겁니다

유니티 3D 게임 소스 - yuniti 3D geim soseu

크기는 이정도면 괜찮을 것 같네요

E를 이용해서 뭔가 카트라이더 아이템스럽게 회전을 주어봤어요

유니티 3D 게임 소스 - yuniti 3D geim soseu

재질은 플레이어에서 했던 것과 같은 방법으로 적용시켜 주면 됩니다.

유니티 3D 게임 소스 - yuniti 3D geim soseu

여기서도 씹덕 티를 내야 합니다

십덕티를 열심히 낸 디자인이 완성되었습니다 

유니티 3D 게임 소스 - yuniti 3D geim soseu

스크립트

아이템을 회전시켜 봅시다

스크립트 폴더에 아이템에게 넣어줄 스크립트를 만들었습니다

ItemCan으로 

유니티 3D 게임 소스 - yuniti 3D geim soseu

일단 프레임당 실행되어야 하니 Update함수에 넣어주었고요

rotateSpeed변수를 퍼블릭으로 해서 보고있습니다

저는 x축으로 회전시키고싶어서 Vector3.right를 했습니다

    public float rotateSpeed;

    // Update is called once per frame
    void Update()
    {
        transform.Rotate(Vector3.right * rotateSpeed *Time.deltaTime);
        
    }

실행시켜 보시면 

유니티 3D 게임 소스 - yuniti 3D geim soseu

저는 오른쪽으로 돌아가길 기대했는데 실제로는 노란 화살표 방향으로 돌아갑니다

그 이유는 제가 아이템 오브젝트를 회전시켜서 오브젝트의 x축이 노란화살표 쪽으로 돌아갔기 때문일 겁니다

위에있는 Local을 눌러보시면 Global로 변경해서 오브젝트의 기울기에 구애받지 않고 

실세계의 좌표로 보여줍니다

유니티 3D 게임 소스 - yuniti 3D geim soseu

그럼 글로벌로 돌아가도록 코드를 수정해봅시다

여기서 보니 제트축이었네요 .. down으로도 변경해주었습니다.

transform.Rotate(Vector3.down * rotateSpeed *Time.deltaTime, Space.World);

Space.World로 변경해주시면 Global좌표계를 사용하여 적용됩니다.

Space.Self가 오브젝트에 해당하는 좌표계이고 이게 디폴트가 되겠네요 

이제 제가 원하는 방향으로 돌아가게 되었습니다

이제 여러분들이 돌려보시면서 속도도 조절해주고 뭐 회전이나 크기도 적절히 다시 조절해주시면 됩니다

저는 rotateSpeed 30으로 두었습니다

플레이어(공)과 아이템의 상호작용

이제 본격적으로 게임 목표를 코딩해봅시다

우리의 공이 움직여 아이템을 먹고 점수가 올라가는 것을 구현해봅시다

각각 오브젝트가 어떤 일을 해야할지 따로따로 생각해 주어야 합니다. 

오브젝트와 그에 해당하는 스크립트가 나누어져 있으니까요

공 : 움직이기 / 아이템과 닿기(움직이기) / 점수올리기 (움직이기는 이미 했으니까 제외)

아이템 : "플레이어"와 닿으면 사라지기 

이렇게인데 누구의 스크립트에 작성해도 상관은 없지만 아이템은 먹어지면 비활성화 되므로 다른 추가적인 코딩(소리넣기 등) 이 불가하여 아이템이 사라지는 등 모든 효과를 공의 스크립트에 넣도록 합시다 

<공PlayerBall.cs : 점수 보유>

일단 공오브젝트에게 점수라는 변수를 줍시다

public int itemCount;

아이템 스크립트와 상호작용이 필요하기 때문에 public으로 주셔야 아이템 스크립트에서도 접근이 가능합니다

<아이템과 닿으면 아이템 사라지기 / 공의 점수 올려주기 >

먼저 물리 충돌이 발생하니 아이템의 Is Trigger를 켜주어야합니다. (공 켜주시면 공 바닥뚫고 내려가요)

유니티 3D 게임 소스 - yuniti 3D geim soseu

이제 트리거에 해당하는 함수를 이용해 코딩을 해보면 

    void OnTriggerEnter(Collider other){

        if (other.name == "Item"){ //충돌 오브젝트가 아이템일때
            itemCount++; // 점수 올리기 
            audio.Play(); // 사운드 재생 
            other.gameObject.SetActive(false); //아이템 비활성화 

        }
    }

사운드 재생은 아래서 설명 

이제 실행해보시면 아이템을 먹으면 아이템이 사라지는 모습 + 아이템 카운트가 올라가는 모습 을 볼 수 있습니다. 

유니티 3D 게임 소스 - yuniti 3D geim soseu

움직여서 아이템을 먹어줬습니다.(아이템 사라짐)

유니티 3D 게임 소스 - yuniti 3D geim soseu

아이템 카운트가 늘어난 모습 (처음엔 0이었음)

유니티 3D 게임 소스 - yuniti 3D geim soseu
public으로 했기 때문에 보여지네요

이제 다 되었으니 아이템을 먹을 때 소리가 나도록 해봅시다.

<소리(효과음) 넣기>

공(플레이어)에다가

Audio Source컴포넌트를 추가해봅시다.

이제 오디오를 넣으면 AudioClip이라고 부릅니다 ( 마치 Material에서 넣어줬던 그림이 Texture이듯 오디오 소스에서는 오디오 클립이라고합니다)

유니티 3D 게임 소스 - yuniti 3D geim soseu

더보기

AudioClip 구하기  - AssetStore

Filter를 통해 Audio / Free를 선택하고 맘에드는 것 다운받으시면되는데

이게 가장 기본적인가봐요

유니티 3D 게임 소스 - yuniti 3D geim soseu

다운 받으셨으면 import 하시고->sound폴더로 (저는 골드메탈님이 28번 사용하셔서 그냥 28번만 import했어요)

지금 소리를 듣기 어려워서 

28번 이제 드래그해주시면 적용이 됩니다

볼륨도 조절해주세요 (0.3)

Play On Awake도 꺼주세요( 시작할때 한번 소리가 나는 건데 효과음엔 필요없는 )

유니티 3D 게임 소스 - yuniti 3D geim soseu

뭔가 생겼네요 

유니티 3D 게임 소스 - yuniti 3D geim soseu

이제 아이템을 먹으면 소리가 나도록 코딩합시다

AudioSource컴포넌트를 사용했으니 해당 컴포넌트 변수를 만들어주고 초기화(Awake함수에)는 기본이겠죠

//변수만들기
AudioSoucre audio;


//초기화
audio = GetComponent<AudioSource>();


//실행
audio.Play(); 

이렇게 되는데 이제 전체코드를 한번 올려서

PlayerBall.cs

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

public class PlayerBall : MonoBehaviour
{
    public int itemCount;
    public float jumpPower;
    bool isJump;

    AudioSource audio;

    Rigidbody rigid; 
    
    void Awake(){
            rigid = GetComponent<Rigidbody>();
            audio = GetComponent<AudioSource>();
            isJump=false; // 점프가 안된 상태라고 가정 
    }

    // Update is called once per frame


    void Update(){
        if(Input.GetButtonDown("Jump") && !isJump){ //isJump가 false일 때만(1회만 가능)
            isJump=true;
            rigid.AddForce(new Vector3(0,jumpPower,0),ForceMode.Impulse);
        }
    }

    void FixedUpdate() //공의 움직임 물리현상이므로 FixedUpdate사용 
    {
        float h = Input.GetAxisRaw("Horizontal"); //GetAxisRaw : 0, 1 ,-1로 떨어짐 
        float v = Input.GetAxisRaw("Vertical");

        rigid.AddForce(new Vector3(h,0,v), ForceMode.Impulse);
    }

    void OnCollisionEnter(Collision collision) { //공이 바닥과 닿으면 jump상태 해제 
        
        if(collision.gameObject.name == "Floor") isJump=false;
    }

    void OnTriggerEnter(Collider other){

        if (other.name == "Item"){ //충돌 오브젝트가 아이템일때
            itemCount++; // 점수 올리기 
            audio.Play(); // 사운드 재생 
            other.gameObject.SetActive(false); //아이템 비활성화 

        }
    }
}

이제 먹으면 소리도 납니다 

<여러개의 아이템 사용하기>

여기서 문제점은 

other.name == "Item"으로 할 경우 다른 아이템이 이름이 변경되면 적용이 안된다는 점입니다

이때, Tag를 사용합니다.

아이템 인스펙터에서 

유니티 3D 게임 소스 - yuniti 3D geim soseu

AddTag를 누르고 Item이라는 태그를 만든 뒤 적용시켜줍시다

유니티 3D 게임 소스 - yuniti 3D geim soseu
유니티 3D 게임 소스 - yuniti 3D geim soseu

이렇게 한 뒤 코드를

other.tag == "Item" 으로 변경해주면됩니다. 

앞으로 플레이어나 아이템 모든 오브젝트는 태그로 받도록 합시다 

플레이어에도 플레이어 태그를 주었습니다.

유니티 3D 게임 소스 - yuniti 3D geim soseu

이제 아이템 여러개 설치가 가능해졌습니다.

마지막으로 전체적으로 올리고 다음 게시글에 이어서 쓰도록 하겠습니다

공의 스크립트 (PlayerBall.cs)

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

public class PlayerBall : MonoBehaviour
{
    public int itemCount;
    public float jumpPower;
    bool isJump;

    AudioSource audio;

    Rigidbody rigid; 
    
    void Awake(){
            rigid = GetComponent<Rigidbody>();
            audio = GetComponent<AudioSource>();
            isJump=false; // 점프가 안된 상태라고 가정 
    }

    // Update is called once per frame


    void Update(){
        if(Input.GetButtonDown("Jump") && !isJump){ //isJump가 false일 때만(1회만 가능)
            isJump=true;
            rigid.AddForce(new Vector3(0,jumpPower,0),ForceMode.Impulse);
        }
    }

    void FixedUpdate() //공의 움직임 물리현상이므로 FixedUpdate사용 
    {
        float h = Input.GetAxisRaw("Horizontal"); //GetAxisRaw : 0, 1 ,-1로 떨어짐 
        float v = Input.GetAxisRaw("Vertical");

        rigid.AddForce(new Vector3(h,0,v), ForceMode.Impulse);
    }

    void OnCollisionEnter(Collision collision) { //공이 바닥과 닿으면 jump상태 해제 
        
        if(collision.gameObject.name == "Floor") isJump=false;
    }

    void OnTriggerEnter(Collider other){

        if (other.tag == "Item"){ //충돌 오브젝트가 아이템일때
            itemCount++; // 점수 올리기 
            audio.Play(); // 사운드 재생 
            other.gameObject.SetActive(false); //아이템 비활성화 

        }
    }
}

아이템의 스크립트 (ItemCan.cs)

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

public class ItemCan : MonoBehaviour
{


    public float rotateSpeed;


    // Update is called once per frame
    void Update()
    {
        transform.Rotate(Vector3.down * rotateSpeed *Time.deltaTime, Space.World);
        
    }
    
}

<참고자료>

[유니티 기초 예제 - BE1] 기초만 꾹꾹 눌러담은 3D 게임 만들기 : 골드메탈

https://www.youtube.com/watch?v=pTc1dakebow&t=44s

유니티 3D 게임 소스 - yuniti 3D geim soseu