강의

멘토링

로드맵

C# 복습하기

C# 복습 인트로

객체 지향

유니티의 뷰

게임 뷰

  • 게임화면(플레이버튼)

 

유니티 설치 및 프로젝트

  • 탐색기에서 위치 확인 가능하다.

  • 컴포넌트 추가로도 스크립트 추가 가능하다

  • 컴포넌트 창에서 더블클릭해도 스크립트 수정 가능하다.

변수의 정리

변수

  • 선언: 메모리공간 할당

  • 해제: 자동

  • 우리가 고려할 것: 빨간박스

 

  • stack메모리에 변수 저장 공간 생긴다.

     

  • 정의: 자료형을 새로 정의

  • 선언: 메모리공간 확보.
    int num;

    ->num이라는 메모리 공간을 예약

  • 저장: num에 대입

  • 해제: 언어 차원에서 지원.

콘솔 출력

  void Start()
    {
        int num = 5;
        int value;
        value= num;
        //1.
        print(num);
        //2.
        Debug.Log(num);
        //3. 유니티에서 안 뜬다.
        Console.WriteLine(value);
    }

함수의 정리

구조체의 메모리

구조체

  • 정의도 한다.

public struct StructPos{//구조체 정의
public int x;//public: 외부에서 접근 가능.
public int y;
public int z;
public void Show(){
print(x+" "+y+" "+z);
}
}

 

  • 생성자: 초기화를 쉽게 하기 위해 사용

public StructPos(int x,int y,int z){
this.x=x;this.y=y;this.z=z;
}

 

  • 사용: 값이다-> 선언만 한다. (참조형변수x)

StructPos pos;//pos라는 구조체변수를 선언
pos.z=10;

print(pos.z);
pos.Show();

 

  • 해제: 쓰고 나면 자동으로 해제된다.

 

구조체 생성자: 초기화를 쉽게 하려고. 필수아니다.

 //생성자: 초기화를 쉽게하기 위함이다. 선언만 하고 써도 된다.
        StructPos pos=new StructPos(1,2,3);
        pos.Show();
        pos.x = 5;//public  없으면 빨간줄
        int x = pos.x;
        int y= pos.y;
        int z=pos.z;

        print(x + " " + y + " " + z);


        StructPos test;
        test.x = 1;
        print("걍 선언만 한 구조체 x: " + test.x);

클래스의 메모리

클래스

  • 정의도 한다.

  • 클래스: 힙에 생김.

  • 실제 데이터: 힙에 저장된다.

  • 참조변수: 참조하는 용도일 뿐이다.

  • 메모리해제: 가비지 컬렉터 역할.

     

 

메모리 구조

  • 참조 변수: 스택

  • 실 데이터(객체):

  • 메인이 끝나면 스택 데이터 사라짐: 참조변수 사라짐

  • 가비지컬렉터: 참조가 사라진 객체 해제

  • 참조변수를 함수 안에서 선언하고 쓴다면?

    다른 함수에서 못 쓴다.

  • 참조변수를 클래스 내에서 선언하면?

    클래스 내 다른 함수들에서 쓸 수 있다.

  • 업데이트: 프레임마다 함수 호출.

  • 참조를 잃어버린 객체->가비지 컬렉터가 해제한다.

클래스의 레퍼런스

클래스의 레퍼런스

  • 참조변수(인스턴스)는 스택, 객체는 힙

  • 참조변수1=참조변수2

    ->같은 걸 가리킨다-> 멤버 변수 값 바꾸면 똑같이 바뀐다.

ClassPos pos1=new ClassPos(2,3,4);
ClassPos pos2=pos1;
//똑같이 나온다. -> 같은 객체 가리킨다는 걸 알 수 있다. 
pos1.Show();
pos2.Show();

객체지향1(객체지향의 기본적 의미)

객체 지향

  • 객체를 중심으로 프로그래밍하는 사고방식

  • 변수: 속성

    함수: 기능 ==> 하나의 사물

  • 객체의 틀을 만들어 놓고(클래스는 틀이다) 이것을 인스턴스로 만들어 사용.

인스턴스

  • 붕어빵 틀->붕어빵

  • 클래스->인스턴스(객체)

사용하는 클래스 형태

  • 우리는 있는 클래스 잘 쓰는게 중요하다.

  • 주황 박스 매우 복잡->강의에서 다루지 않는다.

 

캡슐화

  • 객체의 외부에서는 기능만 사용, 내부 구조를 건드리지 않는다.

     

  • 가져와서 쓰자. (캡슐화)

    어떻게 쓰는지만 알면 된다.

GameObject클래스

  • 유니티에서 물체를 관리하기 위해 사용하는 클래스

 

  • 상속 받아 클래스 만듦(GameObject)

  • 관리 위해 스크립트에서 만든 클래스

  • 게임 오브젝트를 알고 사용해야 한다.

객체지향2 (객체지향의 관점)

  • 우린 갖다 쓰면 된다.

  • 사용하는 방법만 알면 된다.

  • 우리는 첫번째 박스 형태 안 쓰고 , 2,3번째 박스 형태 쓸거다.

게임개발과 유니티

게임오브젝트

게임 오브젝트

  • 우리는 스크립트 API를 본다.

  • 전부 알 필요 없다. 필요한 기능 일부분부터 살펴보자.

  • 게임오브젝트 클래스: 유니티 씬에 있는 항목들의 컨테이너 역할(폴더역할)

  • 각 요소 : 컴포넌트

컴포넌트

  • 컴포넌트 클래스: 기능

  • 폴더 속(게임오브젝트) 파일과 비슷하다(실제적 기능)

빈 오브젝트를 생성해보자

  • 메시필터, 메시렌더러 컴포넌트 추가

  • 객체 지향: 클래스 기능 먼저 이해

     

컴포넌트

컴포넌트

  • 기능을 의미한다.

  • 게임오브젝트는 컴포넌트를 가진다.

 

 

Transform컴포넌트

  • 위치 회전 확대.축소 나타낸다.

  • 모든 오브젝트에 필수적으로 하나씩 있는 컴포넌트

     

스크립트 컴포넌트

  • 스크립트를 통해 게임오브젝트를 동작시킨다.

  • 위치 0으로 초기화해준다.

  void Start()
    {
        transform.position = new Vector3(1, 0, 0);
    }
  • 위치 바뀐다.

게임오브젝트의 메모리

게임오브젝트의 메모리

  • 메뉴상에서도, 코드상에서도 추가할 수 있다.

  • 계층뷰 상에 빈 게임오브젝트가 생겨난다.

    void Start()
    {
        transform.position = new Vector3(1, 0, 0);
        GameObject obj1=new GameObject();

    }

new를 통해 생성

게임오브젝트의 메모리

  • 힙 가리킴

컴포넌트의 메모리

컴포넌트의 메모리

  • 게임오브젝트의 메모리
    : 게임이 플레이 되는 시점에서 계층뷰의 객체들이 메모리에 로딩

 

컴포넌트의 컴포넌트

public class test : MonoBehaviour
{
    void Start()
    {
        /*        transform.position = new Vector3(1, 0, 0);
                GameObject obj1=new GameObject();
        */
        gameObject.AddComponent<addScript>();
    }
}

모노비헤이버의 name 변수

  • Object클래스: name변수 있다

  • 내클래스: 상속받았다

  • ->name변수 쓸 수 있다.


        //Monobehaviour의 name변수: Object상속받아서 쓸 수 있다. 
        this.name = "잇힝";
        
        //상위클래스 속성 볼 수 있다. name  있다. 
        //base.name;

       /* Object a=new Object();
        a.name= "a";*/

MonoBehaviour

        //Monobehaviour의 name변수
        this.name = "잇힝";
  • 실행 시 바뀜

MonoBehaviour는 Object를 상속받는다.

  • Object클래스: name변수 있다

  • 내클래스: 상속받았다

  • ->name변수 쓸 수 있다.

  • Object클래스: name변수 있다

  • 내클래스: 상속받았다

  • ->name변수 쓸 수 있다.

  • Object클래스: name변수 있다

  • 내클래스: 상속받았다

  • ->name변수 쓸 수 있다.

  • Monobehaviour는 Object를 상속받는다.

  • Object: name변수 있다.

  • 내 클래스: 쟤네 상속받았다

  • ->name접근 가능하다.

  • name 변수는 Object 클래스에 있다.

  • 상속받았다-> 접근가능하다.

게임오브젝트의 클래스

GameObject의 함수 SetActive

 

  • SetActive: 게임오브젝트의 활성화/비활성화 책임지는 함수

  • GameObject는 Object를 상속 받는다.

모노비헤이버 안에 게임오브젝트 변수가 있다.

  • 모노비헤이버 안에 게임오브젝트 변수가 있다
    -> gameObject 사용할 수 있다.

  • gameObject의 기능인 setActive쓸 수 있다.

  • gameObject.SetActive(false);

  • 이래서 함수 쓸 수 있다

  • 참조하니까

 

클래스의 포함관계

소유하는 참조

  • 프로그래밍 관점에서는 참조를 포함이라고 생각

  • 일반적으로 레퍼런스를 가지는 쪽이 소유함

역참조와 상호참조

역참조와 상호참조

  • 하부에서 상위로 접근하고자 할 때

GameObject와Monobehaviour

GameObject와 Monobehaviour

  • 클래스는 역할이 분담되어 있다.

  • 유니티 API->SetActive함수: GameObject의 메서드.

  • GameObject 클래스 안에 name과 SetActive()있다.

  • gameObject인스턴스를(변수를)통하여 게임오브젝트의 기능을 사용가능하게 함.

    void Start()
    {
        this.gameObject.SetActive(false);
    }

  • MonoBehaviour의 변수: gameObject

    void Start()
    {
        this.gameObject.SetActive(false);
        //스크립트를 AddComponent로 추가한다. 
        this.gameObject.AddComponent<AnotherScript>();
    }
  • SetActive(false)-> 추가한 스크립트 내용 실행되지 않는다. 비활성화 되어있기 때문.

  • 스크립트는 컴포넌트에 추가된게 맞음. 근데 실행은 안됨. //SetActive(false)

GameObject 의 name 변수

        //name 변수를 써보자
        this.gameObject.name = "큐브";

 

  • Monobehaviour에서도 name이 있었다

        //name 변수를 써보자
        //1.
        this.gameObject.name = "큐브";
        //2.
        this.name = "아아";

 

 

name 변수 정리

name변수 정리

  • 따라서 Object의 name변수를 사용할 수 있다.

  • 둘다 Object를 상속받는다 -> 두 클래스의 name변수 통해 이름 변경 가능하다.

다른 오브젝트의 접근

다른 오브젝트의 접근

  • 유니티상에서 연결 동작의 의미를 이해하자

  • 다른 물체에 코드로 접근하는 방법을 이해하자

 

        //다른 오브젝트의 접근
        //GameObject 변수 obj1를 통하여 게임오브젝트 기능을 사용가능함
        //obj1에 다른 오브젝트 대입: 연결가능
        obj1.name = "큐브1";
        obj1.SetActive(false);

API 문서 살펴보기

API문서 살펴보기

  • 필요한 기능만 찾아보며 사용하면 됨

  • 상속관계는 볼 필요가 있음

  • 따라서 name변수 사용 가능

inherited members: 상속된 변수


  • 따라서 this.name 사용 가능(Object를 상속받기 때문이다. )

  • 버전도 바꿀 수 있다

  • 변수를 클릭하면 그 내용도 또 자세히 볼 수 있다.

  • 예제도 있다.

  • 오브젝트 또한 클릭해볼 수 있다.

자기참조

자기 참조

  • 자기자신을 참조하도록 넘겼다.

 //자기참조
        MyGameObject m = new MyGameObject();
        m.name = "이름";
        print(m.name);
        print(m.myself.name);
  • 콘솔에 같은 이름 나온다.

자기 참조를 하는 이유

  • 다른 내용 숨기고 myself변수를 열어주면 MyGameObject 에 접근 가능해진다.

GameObject 내부의 gameObject 변수

GameObject 내부의 gameObject 변수

 

  • 오른쪽 예

    : GameObject 의 gameObject: 자기참조 변수이다.

  • 왜 있을까?

    프로그램 시 가독성이나 일관성을 위한 것

  • 왼쪽: 역 참조

  • 오른쪽: 자기 참조

    그냥 obj.name보다 obj.gameObject.name라고 하는게 가독성이 좋다

        //자기자신 이름 변경
        this.name = "큐브1";
        this.gameObject.name = "큐브브1";

        //다른 오브젝트 이름 변경
        obj1.name = "큐브333";
        obj1.gameObject.name = "큐브32323"; //자기참조

얘네가 가독성 훨씬 좋다.

 

  • 가독성을 위해서이다.

this 의 의미

this

  • 자기자신 객체 나타냄

 

  • 상속 받은 변수인지, 본인 멤버변수인지 구분하려고
    * 상위클래스: base


    보통 멤버 변수 표시하기 위해 m_obj 이런식으로 표시한다. ->그럼 this 필요하지 않으니.

     

기본문법과 유니티

기본문법과 유니티 인트로

Static변수와 Static 함수

static

  • 단 하나의 공간만을 가진다.

 

  • static: 전 객체가 공유한다.

  • static: 붕어빵 틀이다

  • 붕어빵 틀은 붕어빵의 정보를 모른다.

  • 따라서 멤버변수를 쓸 수 없다.

  • 멤버변수: 각 객체의 멤버변수 정보 , 따라서 붕어빵 틀은 붕어빵이 어떻게 생길지 모름

    public static int number;
    // Start is called before the first frame update
    void Start()
    {
        print(TestScript.number);//static멤버->바로 불러올 수 있다.     
        MyStaticFunc(10);
    }
    public static void MyStaticFunc(int a)
    {
        print(TestScript.number+a);//오류 없다. 
    }

static 메모리

Start 함수와 Update 함수

Start와 Update

Start

  • 시작할 때 한 번 실행

Update

  • 왜 쓰일까?

    물체가 움직이는 것처럼 보인다.

  • for문과의 비교

  • 계속 도느라 } 블록을 못 만남 - > 프로그램이 멈춘 것처럼 보인다.

  • 따라서 무한 반복 for가 있다면 프로그램은 실행되지 않는다.

물체가 깜박거리는 예제

물체가 깜박거리는 예제

Time.deltaTime

  • 1초 재는 로직

  • time.deltaTime더하자

  • 오브젝트와 결합

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

public class 깜박임 : MonoBehaviour
{
    bool flag = true;
    float time = 0;//시간재는 로직
    public GameObject obj1;

    /*    void Start()
        {
            //이 내용을 update로 옮겨보자.
            bool flag=true;
            for(int i = 0; i < 5; i++)
            {
                if (flag == true)
                {
                    print("true");
                    flag= false;
                }
                else
                {
                    print("false");
                    flag = true;
                }
            }
       
        }
    */
    void Update()
    {
        //shift+ tab: 정렬

        //time += 0.03f; //0.03초보다 빠른 프레임이면 더 빠르게 실행됨
        time += Time.deltaTime;//이러면 1초마다 출력된다.
        
        if (time > 1)
        {
            if (flag == true)
            {
                flag = false;
            }
            else
            {
                flag = true;
            }
            print(flag);
            obj1.SetActive(flag);
            time = 0;
        }

        //깜박임의 기본적인 로직.
        //bool flag = true;//이러면 논리적 오류가 생긴다. 지역변수이기 때문.
        /*if (flag == true)
        {
            flag = false;
        }
        else
        {
            flag = true;
        }
        print(flag);*/

    }
}

키보드 입력

키보드 입력

Enum(열거형)

  • 숫자로 되어있는 값을 상수로 바꾸어 사용하는 타입

  • 0,1,2,3,...

Input클래스 Getkey

 

  • 입력을 위해 제공된다

  • 키코드를 입력받는다.

 

  //키코드입력
        if(Input.GetKeyDown(KeyCode.Space))
        {
            print("스페이스다운");
        }
        if (Input.GetKey(KeyCode.Space))
        {
            print("스페이스입력중");
        }
        if (Input.GetKeyUp(KeyCode.Space))
        {
            print("스페이스업");
        }

KeyCode

  • 열거형이다

  • 각 키보드 값이 정의되어있다.

GetKey의 다른형태

  • 선호하지 않는 코드

GetButton 함수

  • 가상 버튼 : Input Manager에서 내가 설정한 키.

  • GetKeyDown으로 대체할 수도 있다. (string 오버로딩)

마우스 입력

마우스

getMouseButton

  //마우스
        if(Input.GetMouseButtonDown(0))
        {
            print("GetMouseButtonDown");
        }
        if (Input.GetMouseButton(0))
        {
            print("GetMouseButton");
        }
        if (Input.GetMouseButtonUp(0))
        {
            print("GetMouseButtonUp");
        }
  • API 문서에서 확인해볼 수 있다.

    //마우스이벤트. 물체 위에서만 작동한다(마우스가 물체 위에 있을 경우에만)
    private void OnMouseDown()
    {
        print("OnMousedown");
    }

    private void OnMouseUp() {
        print("OnMouseUp");
    }

    private void OnMouseDrag()
    {
        print("OnMouseDrag");
    }

 

  • //마우스이벤트. 물체 위에서만 작동한다(마우스가 물체 위에 있을 경우에만)

  • //마우스. 아무곳이나 클릭해도 됨. (GetMouseButtonDown, ...)

     

Generic

Generic

Generic: 함수 계속 추가하지 않아도 됨

  • 함수를 하나만 만들었는데 여러 데이터 타입으로 쓸 수 있다

주의할 것

  • 컴파일러에게 미리 일러주는 것

메시렌더러

메시렌더러

  • 물체의 표면을 표현하기 위한 컴포넌트

미티리얼

  • 물체의 표면 자체를 나타내는 클래스

GetComponent

GetComponent

  • 컴포넌트이기 때문

        //렌더러
        MeshRenderer rend=GetComponent<MeshRenderer>();
        rend.material.color = new Color(1, 0, 1, 1);

물체의 이동

Vector3 구조체

Vector3

  • 유니티에서 제공하는 구조체

  • 구조체->값 형태->생성 안 하고 선언만 해도 메모리에 생겨난다.

  • 구조체 생성: 초기화용. 필수 아니다.

Vector3 vec;
        vec.x= 1;
        vec.y= 2;
        vec.z= 3;
        print(vec);
  • 구조체->값->스택에 생긴다.

  • 가장 많이 쓰는 방법: 생성과 동시 초기화

  • 구조체->스택에 생긴다.

Vector3 vec=new Vector(1,2,3);
        Vector3 vec=new Vector3(10,20,30);
        Vector3 vec2=vec;
        print(vec);//10,20,30
        print(vec2);//10,20,30
        vec.x = 999;
        //구조체 : 값형식이라 값 복사-> vec.x바꿔도 vec2.x안 바뀐다. 
        print(vec2);//10,20,30

Vector3 어디에 사용될까?

 

        //위치에 사용된다
        this.transform.position = new Vector3(1,0,0);
        this.transform.localScale= new Vector3(2,2,2);

Transform 과 position 읽어오기

Transform과 position

  • position도 클래스이다->실제 객체가 있다.

  • 읽어오기 : 참조해서 가져온다.

  • position의 x 읽어오기

     //position 의 x 읽어오기
        Vector3 vec;
        vec=transform.position;
        print(vec);
        float f1;
        f1=transform.position.x;
        print(f1);
  • print: 소숫점 이하 없으면 그냥 정수처럼 출력
    2.0 (x)

프로퍼티

 

캡슐화

프로퍼티

private int hp;
        public int GetHp()
        {
            return hp;
        }
        public void SetHp(int value) {
            this.hp= value;
        }
  • 게터세터

프로퍼티(속성) 이용

 //프로퍼티(속성)
        private int _hp;
        public int hp {
            get { return _hp; }
            set { _hp = value; }
        }
//속성(프로퍼티)
        Player player = new Player();
        player.hp = 50;
        int hp= player.hp;
        print(hp);

Transform 과 position 변경하기

Transform과 position

    //Transform position예시
        Vector3 vec = new Vector3(1, 2, 3);
        MyTransform myTransform=new MyTransform(); 
        myTransform.position = vec;//x,y,z값에 직접 대입할 수 없다. 
        print(myTransform.position);

좌표계, 오른손좌표계와 왼손좌표계

좌표계

왼손좌표계

  • 유니티는 왼손 좌표계

게임루프1

게임루프

  • 게임을 만들 때 필요한 로직

  • 움직이는 것처럼 보이려면?

  • * f를 누르면 물체 줌인

기본적 이동(z축 이동)

   void Start()
    {
        transform.position = new Vector3(0, 0, 0); //position: 속성, getset에서 x,y,z,접근까지를 허용안함
                                                       //따라서 Vector 생성해준다. (x,y,z)
    }

    // Update is called once per frame
    void Update()
    {
        if (Input.GetKey(KeyCode.UpArrow))
        {
            this.transform.position += new Vector3(0, 0, 0.1f);//업데이트마다 참조하는 객체가 바뀐다->알아서 가비지컬렉터로 처리한다. 
        }
    }

게임루프2

게임 루프 2

  • 적이 있을 경우를 생각해보자

  • 따라서 객체지향이 사용된다.

  • 오브젝트 복제: ctrl+D

간단한 적 이동 스크립트

    void Update()
    {
        this.transform.position -= new Vector3(0, 0, 0.1f);    
    }

디버그시의 객체지향

물체의 이동과 속도

물체의 이동과 속도

  • 사용자의 프레임 률을 독립적으로 적용
    ->델타타임이 같은 값을 가지지 않는다는 뜻이다.

  • 계속 시간을 보상해가며 사용

속도와 거리

  • 보통 쓰는 것: 속도와 거리를 구하는 공식

속도와 거리 코드

  • Time.deltaTime*2; //시간 * 속도 2m/s

  • dist: 거리

  • 속도에 시간을 곱하면 이동거리가 나온다.

  • 현재 위치에 이동 거리를 더하면 위치가 이동 된다.

방향을 가지는 벡터

방향을 가지는 Vector3

 

 

  • 세 값을 필요로하는 모든 곳에 쓰인다.

  • 둘 다 같은 벡터

        float dist=Time.deltaTime* 2;//거리
        Vector3 pos=transform.position;
        pos.x = dist;
        transform.position = pos;   
        //실제 방향의 벡터를 통해서 이동거리를 구하는 예
        Vector3 v = new Vector3(1, 0, 0) * Time.deltaTime * 2;//방향*거리
        transform.position += v;

Vector3 의 여러가지 이동방법

Vector3의 여러가지 이동방법

 

 

물체의 전후좌우 이동(추가)

물체의 전후좌우 이동

  • 오른쪽+위->내용이 합해진다.

  • 속도가 달라진다.

  • 하늘색 화살표 벡터 필요하다(단위벡터)

물체의 관리와 충돌

물체의 관리 인트로

  • 사용하자.

  • 익숙해지자.

게임오브젝트의 Find

게임오브젝트의 Find

 GameObject other;
    // Start is called before the first frame update
    void Start()
    {
        //레퍼런스 변수를 선언하고 Start에서 Find함수 사용
        other = GameObject.Find("OtherCube");
    }
        //레퍼런스 변수를 선언하고 Start에서 Find함수 사용
        other = GameObject.Find("OtherCube");
        other.GetComponent<MeshRenderer>().material.color = Color.blue;

  • 큐브 색 변경

직접 연결하기

  • 드래그앤 드롭

 

매니저 사용 예제(색변경)

매니저 사용 예제(색변경)

  • 매니저클래스: 관리하는 클래스

  • 점차적으로 테스트해가며 코드를 보완해나가야한다.

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

public class Manager : MonoBehaviour
{
    public GameObject cube0;
    public GameObject cube1;
    bool flag=true;

    // Start is called before the first frame update
    void Start()
    {
        cube0 = GameObject.Find("큐큐브");
        cube1 = GameObject.Find("Cube");
        ChangeColor(true);
    }

    // Update is called once per frame
    void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            flag = !flag;
            ChangeColor(flag);
        }
    }

    void ChangeColor(bool Flag)
    {
       
            if (flag == true)
            {
                cube0.GetComponent<MeshRenderer>().material.color = Color.red;
                cube1.GetComponent<MeshRenderer>().material.color = Color.blue;
            }
            else
            {
                cube0.GetComponent<MeshRenderer>().material.color = Color.blue;
                cube1.GetComponent<MeshRenderer>().material.color = Color.red;
            }
        
    }


}

마우스 클릭시 물체 각각의 색변경

마우스 클릭시 물체 각각의 색변경

 

충돌체와 강체

충돌체와 강체

IsTrigger

강체

  • mass: 질량

  • drag: 저항

  • use gravity: 중력 사용할지

  • is Kinematic: 물리 연산 안 함.

    *kinetic: 물리 연산 실행

  • ->이 강의에서 세팅해야 할 사항

충돌체 강체 실습

충돌체 강체 실습

 

Destroy

Destroy

  • 받은 오브젝트를 파괴

  • 오른쪽 인자: 시간

 

Destroy(other.gameObject,1.0f);//other: Collider. 트리거 메서드에서 다루는  중

메모리 구조

슈팅게임 프로토타입 만들기 1 (캐릭터와 배경)

유닛, 픽셀퍼유닛

유닛

  • 세로 10 units

  • 유닛 맞추려면

  • 이런식으로 씬 비율, 해상도에서 맞추기

PixelPerUnit

이미지설정

  • 비트맵이기 때문에 랩모드: 고정

  • 필터모드: 포인트

  • 이 이미지는 100의 크기를 갖기 때문에 최대크기: 128

  • 압축x

    압축을 하게 되면 이미지가 뭉개지는 경우가 발생할 수 있음.

이미지 추가

  • 계층 뷰에 가져다 끌기

  • 픽셀 퍼 유닛이 100이기 때문에 이 이미지는 1 유닛이 되는 것이다.

  • 비행기: 48x48

  • 따라서 1/4 크기를 가지게 되는 것

  • 따라서 비행기의 픽셀 퍼 유닛을 48로 바꾸면 비행기가 1유닛을 차지하는 크기가 된다.

  • 그러나 여전히 비행기가 작다

    -> 픽셀퍼 유닛 20으로

트랜스폼의 scale?

  • 이것은 보통 애니메이션이나, 실제로 크기가 커지는 물체를 표현할 때 사용한다.

  • 픽셀 퍼 유닛을 써야 얼마만큼의 크기가 하나인지 가늠할 수 있다.

박스 콜라이더 추가

  • 2D로

  • 콜라이더 조절:
    edit collider

픽셀 퍼 유닛

  • 이미지마다 적용되는 내용이다

카메라사이즈1

카메라 사이즈

카메라

  • 유저가 게임세계를 바라보는 장치

  • 카메라 사이즈도 조정할 수 있다
    어떤 크기냐에 따라 전체화면 크기가 결정된다.

  • projection바꿀 수 있다: 2D, 3D따라.
    원근: 3D

     

씬 저장

  • file->save as->씬이름

  • 카메라 뷰 보는 방식

  • 가로크기는 해상도에 맞게 자동으로 조절된다.

카메라사이즈2

카메라 사이즈2

  • 1080x1920
    모바일에서 가장 많이 사용되는 9:16 해상도 중 하나

  • 해상도를 먼저 정하고 게임을 제작하는 게 좋다.

  • 여기에서의 PixelPerUnit은 해상도를 위한 값

    이 값은 프로젝트마다 하나로 고정

  • 이미지의 PixelPerUnit은 상관하지 않고 이 공식을 사용해도 관계x

  • 이것을 정해야 하는 이유: 해상도 따라 가로 사이즈가 자동 조절되기 때문.

  • 9:16에 100을 곱한 값으로도 해상도를 사용할 수 있다

    유니티가 알아서 확대 축소를 해주기 때문

타일맵 경우

  • 타일맵의 사이즈를 고정하고(PixelPerUnit), 저렇게 하는 것도 좋다. 타일이 x축으로 40개, y축으로 22.5개 있다고 생각할 수 있다.

PixelPerUnit

이미지의 PixelPerUnit

카메라의 PixelPerUnit

  • 하나의 값으로 게임 전체에서 고정해서 써야 한다.

  • 게임 전체를 고려한 값

1080의 세로사이즈를 사용할 경우

  • 1080/100->10.8(일반적으로 유니티에서 PixelPerUnit을 100으로 하기 때문에 여기선 그렇게 한다. )

  • 10.8/2->5.4

우리는 16:9를 쓸 것

  • PixelPerUnit을 48로 잡았을 경우

  • 1080/48/2->11.25


  • 900사이즈로 하겠다 함(9:16에 100 곱한거, 어차피 유니티가 크기조정 해주니까)

  • 900/100/2->4.5

  • 이 화면이 정수로 나눠떨어지기에 이 크기를 사용.

우리가 쓸 화면

스프라이트 및 캐릭터 이동

스프라이트

SpriteRenderer

 

이동

GetAxisRaw

  • 키보드 입력에서 축에 대한 값을 받아온다.

  • 넣을 수 있는 인자: horizontal, vertical

  • -1에서 1까지의 값을 출력

  • 중간값을 리턴하지 않고 -1,0,1 만 출력

이동 코드

화면 구조 바꾸기

콘솔 사용

계층 뷰 이름 바꾸기

  • F2

코드 뜯어보기

        float x = Input.GetAxisRaw("Horizontal");
        float y = Input.GetAxisRaw("Vertical");
  • API 찾아보자

    Input 검색

 

        float x = Input.GetAxisRaw("Horizontal");
        float y = Input.GetAxisRaw("Vertical");
        
        Vector3 dir=new Vector3(x,y,0).normalized;//방향을 나타내는 Vector
        transform.position = transform.position + dir * Time.deltaTime * speed;
        
  • speed가 public 이므로 inspector에서 수정 가능

  • speed 조절해가며 게임 창에서 움직여볼 수 있다.

  • 실행 중 인스펙터의 값을 수정하면 값이 원래대로 돌아감에 주의하자.

배출구 애니메이션 추가

배출구 애니메이션 추가

Sprite 수정

  • 그 다음 Sprite Editor 클릭

  • 적용 클릭

애니메이션

  • 이미지 다중 선택 후 계층 뷰에 끌어오기

 

  • 컨트롤러가 애니메이션 관리

플레이어에 추가.

  • 저 화염 애니메이션을 비행기의 배출구로 사용할 것이다.

  • 계층뷰의 화염을 Player로 끌어온다.

  • -> Player 가 thrusters를 소유.

  • 이렇게 함으로써 플레이어가 이동할 때 thrusters가 따라온다.

  • Player는 GameObject.

  • GameObject: 폴더와 같은 역할

  • 부모자식 관계를 만들어 플레이어에 추가.

  • 플레이어가 이동될때마다 배출구가 따라옴

  • 배출구: 애니메이션 갖고있다.

  • 애니메이션 관리: 애니메이터 컴포넌트

    세부적으로 보면
    애니메이터: 컨트롤러 관리

    컨트롤러: 애니메이션 관리

추가-프로젝트 학습방법(개발자가 되기 위한 연습)

프로젝트 학습 방법(개발자가 되기 위한 연습)

 

  • 기록하자.

  • 각 소제목을 직접 할 수 있게 안 보고 복습한다.

  • 시간을 기록한다.

  • 한 번 연습할 때 3~4 개씩.
    소제목만 가지고 다시 복습하는 것이기 때문에 시간이 짧게 걸린다.

View Port와 월드좌표계

View Port와 월드좌표계

  • API에서 살펴볼 수 있다.

        Vector3 worldPos=Camera.main.ViewportToWorldPoint(new Vector3(0, 0, 0));
        print(worldPos);

배경스크롤

배경 스크롤

  • 배경을 계층뷰로 옮기기

  • 레이어 문제
    : sorting layer

    ->이름 추가

  • v키를 클릭하고 옮기면 딱 맞게 이동 된다.

코드

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

public class BGScript : MonoBehaviour
{
    float speed = 2;
    SpriteRenderer spr;
    // Start is called before the first frame update
    void Start()
    {
        spr=GetComponent<SpriteRenderer>();
    }

    // Update is called once per frame
    void Update()
    {
        transform.position += Vector3.left * Time.deltaTime * speed;   
        Vector3 pos=transform.position;
        if (pos.x + spr.bounds.size.x / 2 < -8)
        {
            float size = spr.bounds.size.x * 2;//다시 스크롤 될 위치로 이동. (대기조)
            pos.x += size;// x에 대입
            transform.position = pos;//position에 대입함으로써 완전히 이동된 상태.
        }
    }
}

원경 -멀리있는배경- 스크롤

원경-멀리있는배경-스크롤

카메라의main (새로추가)

카메라의 main

  • Awake(): Start이전에 실행되는 함수

  • main=this; 코드->자기참조.
    외부에 static변수를 노출

 

  • Player Script

    void Start()
    {
        int value=ManagerScript.main.ManagerAdd(10, 20);
        print(value);
    }
  • Manager Script

    //자기참조
    public static ManagerScript main;
    private void Awake()
    {
        main = this;
    }
    public int ManagerAdd(int a,int b)
    {
        print("Manager Add");
        return a + b;
    }
  • 자기참조

    : main이라고도, instance라고도 많이 함

캐릭터 위치 제한걸기

캐릭터 위치 제한 걸기

  • 좌표: 센터 값

    따라서 비행기 절반 사이즈가 필요한 것

 

    Vector2 colSize;
    Vector2 chrSize;
    Vector3 min,max;
    void Start()
    {
        min = Camera.main.ViewportToWorldPoint(new Vector3(0, 0, 0));
        print(min);
        max = Camera.main.ViewportToWorldPoint(new Vector3(1, 1, 0));
        print(max);
        colSize=GetComponent<BoxCollider2D>().size;
        chrSize = new Vector2(colSize.x / 2, colSize.y / 2);
    
    }
        Vector3 dir=new Vector3(x,y,0).normalized;//방향을 나타내는 Vector
        transform.position = transform.position + dir * Time.deltaTime * speed;
        
        float newX=transform.position.x;
        float newY=transform.position.y;

        if (newX < min.x + chrSize.x)
        {
            newX= min.x + chrSize.x;
        }
        if (newX > max.x - chrSize.x)
        {
            newX = max.x - chrSize.x;
        }

        if (newY < min.y + chrSize.y)
        {
            newY = min.y + chrSize.y;
        }
        if (newY > max.y - chrSize.y)
        {
            newY = max.y - chrSize.y;
        }
        transform.position = new Vector3(newX, newY, transform.position.z);

캐릭터 제한 Clamp

캐릭터 위치 고정 Clamp

  • 위 함수를 이 클래스에서 구현

 

소팅레이어

소팅 레이어

Tag

  • Find : 오브젝트 이름으로 찾는다

  • 그러나 태그로도 오브젝트를 찾을 수 있다.

  • GameObject의 static 함수

  • Player태그는 유니티에서 기본적으로 제공

  • 인스펙터에서 태그 붙일 수 있다.

  • Camera의 main도 태그로 찾았던 것임.

Order in Layer

더 높은 숫자일수록 더 앞에서 보인다.

슈팅게임 프로토타입 만들기 2 (발사체와 회전)

프리펩및Instantiate

프리팹

  • 계층 뷰에 옮기는 것: 메모리를 만드는 것과 비슷

  • 프리팹은 반대의 동작

  • 메모리를 파일화하는 것이다.

  • 프로젝트뷰: 클래스 영역

  • 계층뷰: 인스턴스 영역

  • 즉 클래스를 만드는 것과 같다.

  • 압축:없음

총알

프리팹 만들기

  • 프리팹 편집

  • 왼쪽: 프리팹

  • 오른쪽: 프리팹에서 만들어진 메모리

  • 따라서 하이어러키에서 삭제함.(필요x)

  • 인스펙터 락 걸기

Instantiate

발사체

발사체

  • 보면서 조정할 수 있다는 장점

    public Transform ShotPointTr;

 

키입력에 따라 발사

키입력에 따라 발사

키입력에 따라 Instantiate

  • 범위 벗어나면 삭제해야 한다.

오일러각도와 쿼터니언

오일러 각도와 쿼터니언

  • 세 축이 겹칠 때 구분 안 가는 거 해결: 쿼터니언

  • 나눠놓은 이유:

    우리가 쿼터니언을 사용하지 않고도 동작할 수 있게 유니티에서 지원하는 것

  • 우리는 오일러 각도를 통해 쿼터니언 다룬다
    따라서 여러 오류가 날 수 있다.

  • 여기서의 speed: 회전 속도

대입 대신 다른 방법을 써야 한다.

소행성 추가

소행성 추가&회전

 

소행성 회전하면서 이동 - 추가

소행성 회전하면서 이동

  • Translate함수 사용 시 문제가 발생한다.

왜 그렇게 될까?

  • 로컬 좌표: 오브젝트 중심

  • 글로벌 좌표: 전체 좌표

  • Translate: 로컬 좌표 기준으로 동작한다.

  • Space.World하면 달라진다.

발사체와 소행성 충돌

발사체와 소행성 충돌

소행성 hp 추가

소행성 hp 추가

발사체 이펙트1 프리펩

발사체 이펙트

 

 

  • 애니메이터 실행

  • window->animation->animation

  • 블록 더블클릭하면 인스펙터 바뀐다

  • 루프 할지 안할지 선택

  • 휠 누르면 시간 조정

  • 점 세개->프레임 가능

  • 다이아몬드 모양: 프레임 자체를 클릭

  • 상단 하늘색: 프레임의 시간을 클릭

  • 점세개->show sample rate

  • samples 12-> 12가 1초라는 뜻

  • Sprite누르면 이미지 나온다.

발사체 이펙트2 사용

발사체 이펙트-사용

  • 화면구성 레이아웃 저장할 수 있다.

            Instantiate(shotEffect, transform.position, Quaternion.identity);//Quaternion.identity: 기본값
  • OntriggerEnter메서드

  • 애니메이션 samples 조절로 애니메이션 속도를 조절할 수 있다.

 

    public float time=1.0f;
    void Start()
    {
        Destroy(gameObject,time);
    }

슈팅게임 프로토타입 만들기 3 (적과 발사체)

코인 생성

코인생성

  • 스프라이트 추가

  • 애니메이션 추가

     

  • 물리레이어 추가

  • 물리 컴포넌트 추가

     

  • CoinScript추가

    public float speed = 1.5f;
    void Update()
    {
        transform.Translate(Vector3.left * speed * Time.deltaTime);
    }
    private void OnBecameInvisible()
    {
        Destroy(gameObject);
    }

코인 프리펩 사용

코인 프리펩 사용

  • coin 변수 추가

파괴 에니메이션 추가

파괴 애니메이션 추가

 

  • 1초 후 파괴

파괴 애니메이션 사용

파괴 애니메이션 사용

메니저 생성

게임 매니저

 

  • 오브젝트 생성 관리 목적

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

public class GameManager : MonoBehaviour
{
    public GameObject astroid;
    public float time = 0;
    public float maxTime = 2;

    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        time += Time.deltaTime;
        if (time > maxTime)
        {
            time = 0;
            Instantiate(astroid,new Vector3(10,Random.Range(-4.0f,4.0f),0),Quaternion.identity);//랜덤좌표 필요
        }
    }
}

적의 추가

적 추가

적의 생성

적 생성

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

public class GameManager : MonoBehaviour
{
    public GameObject astroid;
    public float time = 0;
    public float maxTime = 2;
    public List<GameObject> enemies;
    
    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        time += Time.deltaTime;
        if (time > maxTime)
        {
            int check=Random.Range(0, 2);//0,1
            if (check == 0)
            {
                Instantiate(astroid, new Vector3(7, Random.Range(-4.0f, 4.0f), 0), Quaternion.identity);//랜덤좌표 필요
            }
            else
            {
                int type=Random.Range(0, 3);//0,1,2
                Instantiate(enemies[type], new Vector3(7, Random.Range(-4.0f, 4.0f), 0), Quaternion.identity);//랜덤좌표 필요
            }
            time = 0;
        }
    }
}

적의충돌

적 충돌

  • 발사체와 적의 충돌 추가

 

        if (collision.gameObject.tag == "Enemy")
        {
            EnemyScript enemyScript =                collision.gameObject.GetComponent<EnemyScript>();
            enemyScript.hp -= 3;
            Instantiate(shotEffect, transform.position, Quaternion.identity);//Quaternion.identity: 기본값

            if (enemyScript.hp <= 0)
            {
                Instantiate(explosion, transform.position, Quaternion.identity);
                Vector3 randomPos = new Vector3(Random.Range(-0.1f, 0.1f),
                    Random.Range(-0.1f, 0.1f), 0);
                Instantiate(coin, transform.position + randomPos, Quaternion.identity);
                Destroy(collision.gameObject);
            }

        }

//ShotScript

코인의 값 적용

코인의 값 적용

코인의 플레이어 충돌

코인과 플레이어 충돌

적 발사체 구현

적 발사체 구현

적 발사체발사

적 발사체 발사

 

플레이어 파괴

플레이어 파괴

  • 닿으면 플레이어, 닿은 애 파괴되게

  • 폭파 애니메이션도 PlayerScript에 추가

게임의 UI

캔버스

  • 화면 해상도 따라 UI 변경되도록 하기 위함

  • EventSystem: 자동으로 생김. 입력 이벤트 처리

 

캔버스 렌더모드

캔퍼스 렌더모드

오버레이모드

 

  • 캔버스 Rect Transform 위치 변경 불가

  • 각 UI들은 가능

  • 즉 UI 수정되면 캔버스가 확대 축소 자동으로 조절됨: 유니티가 지원

카메라 모드

  • UI를 게임 뒤로도, 앞으로도 이동할 수 있는 모드

  • 퍼즐게임에서 많이 사용

  • 캔버스 위치: 카메라의 위치와 캔버스의 위치가 동일

  • 오버레이모드와 거의 동일

  • 다른점:
    캔버스의 크기가 게임화면과 같음

    UI위치를 게임 화면 뒤로 보낼 수 있음

  • 카메라모드로 변경하려면 카메라를 연결해주는 과정 필요하다

  • 메인 카메라와 캔버스 위치 동일

  • 버튼과 캐릭터 레이아웃 조절하는 법

    z축 조절

월드 스페이스 모드

  • 캔버스 조절해줘야 한다.

  • 카메라모드와 다른점: 캔버스 자동으로 확대축소 되지 않는다.

  • 월드 스페이스 좌표가 실제 UI 와 같은 평면에 존재

Rect Transform

Rect Transform

 

  • 피벗: UI의 중심점

  • 앵커: 저 텍스트 UI 의 기준점

  • Transform위치를 수정하는 게 아니라 코너를 수정한다.

  • 앵커: 트랜스폼 십자모양

  • 텍스트 UI 가 앵커를 바라보고 있다는 뜻

앵커에 따라 UI가 어떻게 바뀔까?

 

  • 앵커 드래그해서 옮길 수 있다.

캔버스 스케일러

캔버스 스케일러

스크린 좌표계

스크린 좌표계

  • 스크린 좌표: 화면 따라, 디바이스 따라 바뀌는 것

     

  • 화면 왼쪽 하단

 

  • 마우스 포지션: 스크린 포지션을 가지고 있다.

게임내 코인의 텍스트 UI 추가

게임 내 코인의 텍스트 UI 추가

  • 앵커: 가운데 위

    어떤 해상도여도 가운데 위임이 변하지 않음

 

 

  • 폰트 적절한 크기로 조정

게임내부UI의 구성

게임 내부 UI의 구성

  • 앵커: UI위치하는 곳에 두면 좋다.

  • 에셋스토어: 창->에셋스토어

  • 네이티브 크기로 변경된다.

패널과 Pause Menu 추가

패널과 Pause menu 추가

  • 안의 내용만 확대축소 되고 바깥의 내용은 확대축소 되지 않는다.

 



  • 소속됐기 때문에 앵커가 표지판 위에 붙는다.

Pause Menu - Resume 추가

Pause Menu - Resume 추가

 

  • 심플: 기본 이미지 사이즈

  • 슬라이스: 내가 적용한 라인 패치 적용됨

     

메인화면 구성

메인화면 구성

  • 이 두 개 직접 생성하고

  • 애니메이터 안에 애니메이션 넣는다.

  • TitleController를 TitleText에 넣는다.

  • UI 이기 때문에 앵커드 포지션 써야한다.

  • 이 버튼 누르고 물체 움직이면 애니메이션 기록되어 적용된다.

 

메인화면 버튼 추가

메인화면 버튼 추가

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;//필요

public class MenuManager : MonoBehaviour
{
    public void GoGameScene()
    {
        SceneManager.LoadScene("GameScene");
    }
    public void Quit()
    {
        Application.Quit();
    }
}
  • 씬 추가

프로토타입의 오류 수정

프로토타입의 오류 수정

  • UI 위치 달라지는 문제
    ->앵커를 UI위치에 박는게 좋다.

  //ViewportToWorldPoint 써야 위치를 정확히 알 수 있다. 
        min = Camera.main.ViewportToWorldPoint(new Vector3(0, 0, 0));
        //min = new Vector3(-8, -4.5f, 0);
        print(min);
        max = Camera.main.ViewportToWorldPoint(new Vector3(1, 1, 0));
        //max = new Vector3(8, 4.5f, 0);
        maxRight = Camera.main.ViewportToWorldPoint(new Vector3(1, 1, 0)).x;
//maxRight: float

 

슈팅게임 만들기 1 (프로토타입을 변경)

레터박스 - 정사각형에 가까운 화면

레터박스-정사각형에 가까운 화면

뷰포트를 수정하여 게임 화면의 크기를 수정한다.

내 화면보다 작은 화면의 크기를 원하는 화면 비율로 수정함.

값 비교 이유: 연두 화면이 더 작은지 보려고

두 화면 비율을 나눈 값을 scaleWidth 변수에 저장한다.

1보다 작다면, 그 화면이 내가 원하는 화면보다 작다는 것이다.

카메라 스크립트

  • Awake()함수 쓰는 이유: 카메라 함수가 게임에서 제일 먼저 실행되어야 하기 때문.

  • 1. 우선 현재 화면 비율을 저장한다.

첫 줄: 현재 화면 비율 저장

두 번째 줄: 현재 화면 비율을 16:9 비율로 나눈 값을 저장-> 만약 이 값이 1보다 작다면, 16:9보다 작은 화면이 현재의 화면 비율이라는 뜻이다.

 

 

  • rect: 카메라 컴포넌트

Viewport Rect

  • 0~1까지의 값

  • 0: 시작점이 0

  • 0.15: 15%에서 시작

  • H: 70프로로 축소한다는 뜻

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

public class CameraScript : MonoBehaviour
{
    private void Awake()
    {
        Camera camera=GetComponent<Camera>();
        Rect rect = camera.rect;
        float ratio=(float)Screen.width / Screen.height;//화면비율
        float scaleWidth = ratio / (float)(16 / 9);//1이하인가?
        print(scaleWidth);
        if (scaleWidth < 1)
        {
            rect.height = scaleWidth;
            rect.y = (1 - scaleWidth) / 2;
        }
        camera.rect = rect;
    }
}

결론

상하를 잘라서 화면 비율을 맞춘다.

방법 : 카메라 rect 조정

레터박스 수정 - 가로로 긴화면

레터박스 수정-가로로 긴 화면

  • 이번엔 scaleHeight이 필요(전 강의: scaleWidth)

  • 해상도 수정: 카메라가 보여주는 영역을 바꾼다는 뜻.

 

코드

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

public class CameraScript : MonoBehaviour
{
    private void Awake()
    {
        Camera camera=GetComponent<Camera>();
        Rect rect = camera.rect;
        float ratio=(float)Screen.width / Screen.height;//화면비율
        float scaleWidth = ratio / ((float)16 / 9);//1이하인가?
        float scaleHeight = 1 / scaleWidth;
        print(scaleHeight);//0.84. 화면을 바라보는 영역을 0.84로 축소하여 0.16을 보지 않겠다는 뜻. 0.16이 양쪽에 들어가야하니 /2하면 0.08씩 안 봄.
        if (scaleWidth < 1)
        {
            rect.height = scaleWidth;
            rect.y = (1 - scaleWidth) / 2;
        }
        else//가로를 자르는 경우
        {
            rect.width = scaleHeight;
            rect.x=(1-scaleHeight) / 2;
        }
        camera.rect = rect;
    }
}

 

WorldSpace UI 수정

WorldSpace UI 수정

  • 캔버스의 오버레이모드를 월드스페이스로 조정한다.

  • 이벤트카메라에 메인 카메라를 넣는다

  • 소팅레이어: add sorting layer
    -> UI추가,

  • 캔버스 리셋

  • rect transform의 width를 1600, height를 900으로

  • scale을 0.01로(x,y,z)

UI를 화면크기에 맞게

Title 애니메이션

  • canvas(* menu scene)

  • title-> scale 0.8, 0.8로->이래도 애니메이션이 화면을 벗어남(근데 난 화면 안 벗어남. 방법만 알자)

  • 애니메이션을 변경해보자->anim폴더->titleanim

  • 애니메이션 다이아몬드 위치를 새로 만들자

  • add property->anchored position

버튼

*transform 점 세개->copy component-> 위치복사할 곳에서 paste component values

 

캔버스수정(게임씬)

  • 렌더러: 월드스페이스

  • 메인카메라 대입

  • 소팅레이어: UI

  • rect transform 초기화 후 1600, 900으로 변경, scale 0.01로 고정

DataScript 의 싱글톤

DataScript의 싱글톤

씬을 이동하며 싱글톤을 사용할 때

씬 B로드->오브젝트가 또 생겨남.

  • 이렇게 싱글톤이 동작된다.

씬 B에서 씬 A로

마찬가지로.

  • 아까와같이 새로운 오브젝트가 생성되어도 그 오브젝트 파괴됨.

  • 씬이 바뀌어도 잘 동작

이제 만들어보자

  • 1. 데이터매니저 생성

  • 2. DataScript 컴포넌트 생성

싱글톤에서 코인을 저장하고 로드

싱클톤에서 코인을 저장하고 로드

  • 게임매니저의 coin : coinInGame(이름변경)

  • 게임데이터 스크립트에서 전체코인 수정
    이 값을 저장할 필요가 있다.

  • PlayerPrefs라는 기능이 있다.

 public float GetCoin()
    {
        this.coin=PlayerPrefs.GetFloat("TotalCoin", 0);
        return this.coin;
    }
    public void AddCoin(float coin)
    {
        this.coin += coin;
        PlayerPrefs.SetFloat("TotalCoin", this.coin);
    }
        //잠시 후 수정(전체코인 변경예정)
        //coinText.text=coinInGame.ToString();
        coinText.text=DataScript.instance.GetCoin().ToString();
//DataScript
            DataScript.instance.AddCoin(coinScript.coinSize);
            GameManager.instance.coinText.text = DataScript.instance.GetCoin().ToString();   
//PlayerScript

캐릭터 고정데이터 로딩

캐릭터 고정데이터 로딩

Resources 클래스

  • 데이터 로드 가능

  • as: 형변환 연산자

엑셀 데이터 추가

저장된 파일을 유니티에서 불러오기

 

탭과 라인으로 구분

 

  • 엑셀데이터 작성&저장->유니코드 텍스트로.

  • 유니티에서 폴더생성: Resources

  • 여기에 엑셀 파일 추가

이 내용을 로드

  • String에는 Split함수가 있다. (엔터로 파일을 나눠 배열로 저장)

  • 데이터 매니저 스크립트 수정

  • 첫줄과 빈줄도 출력됨(엔터)

=>0번과 4번 줄 제거해야한다.

 

  • 이중루프를 통해 값을 출력

캐릭터 구조체 생성 및 로드

캐릭터 구조체 생성 및 로드

  • *shipData쓰려면 using문으로 namespace추가해야 함

  • int.Parse=>string을 int로

저장로드하는 캐릭터 데이터

저장로드하는 캐릭터 데이터(변경되는 데이터)

  • 참고(PlayerPrefs)

            int chr_level = PlayerPrefs.GetInt("Chr_Level"+i.ToString(), 1);// 만약 키 값에 해당하는 변수가 있다면 그 변수를 가져오겠지만 존재하지 않는다면 Get자료형의 두번째 파라미터 값을 초기값으로 설정하여 가져오게 된다.

캐릭터의 계산하는 데이터

캐릭터의 계산하는 데이터

슈팅게임만들기 2 (케릭터로딩 및 메인메뉴 스크롤)

메뉴 UI 변경

패널에 스크롤 뷰

 

  • 여기 컨텐트에 아이템을 넣어주면 스크롤이 동작

  • 컨텐트의 높이: 스크롤 뷰와 같아야 한다.

메뉴 UI 변경

메뉴의 캐릭터 패널 추가

메뉴의 캐릭터 패널 추가

  • 캐릭터 선택하는 형태로 UI 변경

메뉴아이템 스크립트 추가

메뉴아이템 스크립트 추가

 

캐릭터 실제 데이터를 메뉴아이템에 적용

캐릭터 실제 데이터를 메뉴아이템에 적용

 

 

캐릭터 이미지 변경

캐릭터 이미지 변경

 

Resources 폴더 생성부터 다시하기(네이버카페)

Lerp 선형보간 사용

Lerp 선형보간 사용

 

선형보간

  • ab사이의 c를 구함.

세 번째 인수 0.5: 50퍼센트라는 뜻

업데이트문에서 선형보간 함수를 써보자 .

3번째 인자 0.2f인 경우

  • 0.5f 처럼 급격한 것은 마찬가지.

물체 이동에 사용해보자.

  • x값을 변경하는 코드

  • a.x 업데이트

  • 그리고 position 에 벡터값 a를 넣는다.

이 움직임은 플레이어에 사용할 수 없다.

따라서 일반적으로 UI에 사용한다.

  • 너무 빨라서 캐릭터에 사용할 수 없다.

  • position을 받아서 다시 대입

스크롤뷰 스냅기능1

스크롤뷰 스냅기능1

  • 스크롤뷰 스냅 기능을 추가하자.

스크롤 뷰 하부 내용을 살펴보자.

  • 이 오브젝트 쓰는 이유
    : 현재 스크롤뷰의 중심점을 알기 위해서.

스냅 적용 원리

  • 빨간 숫자: 월드 포지션

  • 드래그 했을 때

  • 센터와 아이템간의 거리가 가장 가까운 항목을 정한다.

  • 이걸 스냅에 사용해본다.

스크롤뷰 스냅기능2

스크롤뷰 스냅기능 2

 

  • 목표:
    스크롤뷰 스냅 기능을 추가

스냅 적용 원리

코드

코드2(distance계산)

코드3

  • minDistance의 인덱스 값을 구한다.

  • 월드포지션 좌표임=버튼들이 월드포지션 값을 갖고 있음.

스크롤뷰 스냅기능3

스크롤뷰 스냅기능3

  • 목표: 스크롤뷰의 스냅을 추가 테스트

 

RectTransform

코드1

  • 앵커드포지션 사용

코드2

  • 드래그 체크

  • UI니까

스크롤뷰 스냅기능 4

스크롤뷰 스냅기능4

  • 스크롤뷰에 스냅기능 추가

원리

코드

게임메뉴에 스냅기능추가

게임메뉴에 스냅기능 추가

  • 스크롤뷰의 스냅기능을 메인메뉴로 이동

  • 배열을 리스트로 수정

캐릭터 선택 기능

캐릭터 선택 기능

  • 목표:

    캐릭터를 선택하여 저장하고 로드하는 기능 추가

캐릭터 선택 오류 수정

캐릭터 선택 오류 수정

  • 목표:
    선택기능의 코드를 정리하고 오류를 수정

슈팅게임만들기 3

슈팅게임3 인트로

슈팅게임 3

 

코인 테스트 버튼 추가

코인 테스트 버튼 추가

  • 목표:

    -추가 개발 항목의 선택방법

    -테스트 버튼 추가

추가 개발 항목

  • 간단한 것부터

테스트 버튼 추가

코드 1

  • 코인 값이 0일 때 안 보이게

  • 코드 추가할 때:
    쉬운 것부터 우선 완료하는 형태로 작업을 진행

Unlock UI 초기로딩값 적용

Unlock UI 초기로딩값 적용

  • 목표: 데이터에 따라 Unlock UI 적용

unlock Coin의 UI표시

Unlock Coin의 UI표시

  • 목표: Unlock Coin의 UI 표시

unlock Get Set 추가와 오류수정

Unlock Get Set 추가와 오류수정

unlock Get Set의 방어코드 추가

unlock Get Set의 방어코드 추가

  • 방어코드: 오류가 발생하지 않도록 코드 작성

 

get set 사용 이유

Key의 string을 틀리지 않게 하기 위해서

  • CanUnlock: 코인으로 언락 할 수 있는지 판단

  • Unlock: 언락 실행 함수

  • 인자 id: 비행기의 아이디

unlock 가능여부체크를 하는 함수추가

Unlock 가능여부를 체크하는 함수 추가

함수와 AddListener

함수와 AddListener

저장

  • 클릭할 때 GoGameScene함수가 실행된다는 뜻

  • onClick: deligate, 함수를 저장하는 변수

 

이 이벤트를 드래그 없이 코드를 통해 구현해보자.

AddListener

Delegate(대리자)

Delegate( 대리자 )

정의, 선언, 해제를 알아보자

정의

  • 클래스: 정의 ,선언

  • 딜리게이트: 마찬가지

선언

저장

사용

해제

  • void OnFuncType():
    델리게이트로 정의하는 것. 타입이 됨

  • onValueChange:
    함수를 저장하는 변수

  • +=:
    onValueChange에 추가

  • onValueChange():
    추가한 두 함수가 실행됨.

딜리게이트 쓰는 이유

  • 서로 다른 클래스에서 함수를 주고받기 위해서.

    //정의
    public delegate void OnFuncType();//void OnFuncType(): 타입
    //선언. 선언할 떄 타입은 OnFuncType
    //void OnFuncType()형태의 함수를 저장할 수 있게 되는 것임. 
    OnFuncType onValueChange;
    void Print1()
    {
        print("Pritn1");
    }
    void Print2()
    {
        print("Pritn2");
    }
    void Start()
    {
        onValueChange += Print1;
        onValueChange += Print2;
        onValueChange();
        print("-= 실행");
        onValueChange -= Print1;
        onValueChange();

    }

메모리 구조

  • -= 안 쓴 이유: 스크립트 사라질 때 같이 사라지니까.

  • 선언 : 함수를 저장할 수 있는 변수

Delegate 와 event로 가상 UI 테스트

두 클래스 간 딜리게이트 사용

Delegate와 event로 가상 UI 테스트

Event

  • event 는 하나의 키워드

 

  • event : 사용하지 않아도 동작

  • event 가 없을 경우 사용자에서도 사용 가능
    사용자가 onClick()을 실행할 필요가 없다.
    그렇기 때문에 이벤트가 있는 것.

  • UIScript
    : 관리자==UI 만 관리

  • ItemScript

    : 사용자

사용자

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

public class ItemScript : MonoBehaviour
{   //아이템스크립트: 사용자
    //관리자에 등록할 함수 구현, 연결 역할 
    public UIScript uiscript;// 이 레퍼런스는 유니티에서 연결

    // Start is called before the first frame update
    void Start()
    {
        uiscript.onClick += ClickAction;//딜리게이트에 함수 등록
    }
    void ClickAction()
    {
        print("ClickAction");
    }
    // Update is called once per frame
    void Update()
    {
        
    }
}

 

관리자

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

public class UIScript : MonoBehaviour
{
    public delegate void OnClick();//딜리게이트 정의
    public OnClick onClick;//딜리게이트 선언
    //딜리게이트에 연결할 함수: 사용자에서 구현한다. //ItemScript
    
    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            onClick();//UI스크립트: 실행 담당//관리자. 
        }
        //UIManager에서 Key를 입력받음->아이템으로전달->클릭액션실행
        
    }
}

delegate 변수를 private로 숨기기

delegate 변수를 private로 숨기기

  • 사용자가 대기를 하는 입장
    ->따라서 Listener라는 이름 사용
    ->인자: 나 이거 기다릴래.

  • onClick() 실행: UI 관리자

  • 사용자: 대기하고 있다가 구현해놨던 함수 실행.

  • private event OnClick onClick
    ->private이기에 onClick변수를 외부에서 접근 불가.

  • public void SetListener(OnClick listener){
    onClick+=listerner;
    }
    ->public이기에 외부에서 사용 가능하도록 열어줌
    ->onClick변수는 내부에서만 사용하도록.
    다른 클래스에서는 onClick 사용 못 함.

SetListener에서 대입연산자 차이점

관리자

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

public class UIScript : MonoBehaviour
{
    public delegate void OnClick();//딜리게이트 정의
    private event OnClick onClick;//딜리게이트 선언. private: 외부 접근 불가. 직접 이 변수 써서 함수  추가 못 함.
    //딜리게이트에 연결할 함수: 사용자에서 구현한다. //ItemScript
    
    public void SetListener(OnClick onClick)//매개변수 OnClick타입: 리턴 void  이고 매개변수 () 인 함수만 넣겠다. 
    {
        this.onClick += onClick;
    }

    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            onClick();//UI스크립트: 실행 담당//관리자. 
        }
        //UIManager에서 Key를 입력받음->아이템으로전달->클릭액션실행
        
    }
}

 

사용자

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

public class ItemScript : MonoBehaviour
{   //아이템스크립트: 사용자
    //관리자에 등록할 함수 구현, 연결 역할 
    public UIScript uiscript;// 이 레퍼런스는 유니티에서 연결

    // Start is called before the first frame update
    void Start()
    {
        uiscript.SetListener(ClickAction);//딜리게이트에 함수 등록. ClickAction대기
        uiscript.SetListener(ClickAction);//딜리게이트에 함수 등록. ClickAction대기
    }
    void ClickAction()
    {
        print("ClickAction");
    }
    // Update is called once per frame
    void Update()
    {
        
    }
}

delegate 를 키입력이 두 개있는 형태로 확장

delegate를 키 입력이 두 개있는 형태로 확장

 

  • 두 개의 이벤트//딜리게이트 변수: 이벤트

사용자

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

public class ItemScript : MonoBehaviour
{   //아이템스크립트: 사용자
    //관리자에 등록할 함수 구현, 연결 역할 
    public UIScript uiscript;// 이 레퍼런스는 유니티에서 연결

    // Start is called before the first frame update
    void Start()
    {
        uiscript.SetYesListener(YesAction);//딜리게이트에 함수 등록. ClickAction대기
        uiscript.SetNoListener(NoAction);//딜리게이트에 함수 등록. ClickAction대기
    }
    void YesAction()
    {
        print("YesAction");
    }
    void NoAction()
    {
        print("NoAction");
    }
    // Update is called once per frame
    void Update()
    {
        
    }
}

관리자

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

public class UIScript : MonoBehaviour
{
    public delegate void OnClick();//딜리게이트 정의
    private event OnClick onYesClick;//딜리게이트 선언. private: 외부 접근 불가. 직접 이 변수 써서 함수  추가 못 함.
    private event OnClick onNoClick;//딜리게이트 선언. private: 외부 접근 불가. 직접 이 변수 써서 함수  추가 못 함.

    //딜리게이트에 연결할 함수: 사용자에서 구현한다. //ItemScript

    public void SetYesListener(OnClick onClick)//매개변수 OnClick타입: 리턴 void  이고 매개변수 () 인 함수만 넣겠다. 
    {
        this.onYesClick += onClick;
    }
    public void SetNoListener(OnClick onClick)//매개변수 OnClick타입: 리턴 void  이고 매개변수 () 인 함수만 넣겠다. 
    {
        this.onNoClick += onClick;
    }

    // Update is called once per frame
    void Update()
    {
        if (Input.GetKeyDown(KeyCode.A))
        {
            onYesClick();//UI스크립트: 실행 담당//관리자. 
        }
        else if (Input.GetKeyDown(KeyCode.B))
        {
            onNoClick();//UI스크립트: 실행 담당//관리자. 
        }
        //UIManager에서 Key를 입력받음->아이템으로전달->클릭액션실행

    }
}

버튼을 추가하여 팝업구현 1

  • 아이템: 주

  • 팝업: 부

버튼을 추가하여 팝업 구현1

  • 아이템: 주

  • 팝업: 부

  • 아이템이 팝업 패널을 소유하고 있는 것과 마찬가지

관리자

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

public class PopupScript : MonoBehaviour
{
    public Button yesButton;
    public Button noButton;
    public delegate void OnClick();//딜리게이트 정의
    private event OnClick onYesClick;//딜리게이트 선언. private: 외부 접근 불가. 직접 이 변수 써서 함수  추가 못 함.
    private event OnClick onNoClick;//딜리게이트 선언. private: 외부 접근 불가. 직접 이 변수 써서 함수  추가 못 함.

    //딜리게이트에 연결할 함수: 사용자에서 구현한다. //ItemScript

    public void SetYesListener(OnClick onClick)//매개변수 OnClick타입: 리턴 void  이고 매개변수 () 인 함수만 넣겠다. 
    {
        this.onYesClick += onClick;
    }
    public void SetNoListener(OnClick onClick)//매개변수 OnClick타입: 리턴 void  이고 매개변수 () 인 함수만 넣겠다. 
    {
        this.onNoClick += onClick;
    }
    void Start()
    {
        //yes버튼과 no버튼의 액션 추가(인스펙터 창에서x 코드에서 클릭이벤트 연결
        yesButton.onClick.AddListener(PopupYesAction);
        noButton.onClick.AddListener(PopupNoAction);
    }
    void PopupYesAction()
    {
        //딜리게이트 실행하도록. 
        onYesClick();
    }
    void PopupNoAction()
    {
        onNoClick();
    }
}

사용자

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

public class ItemScript : MonoBehaviour
{   //아이템스크립트: 사용자
    //관리자에 등록할 함수 구현, 연결 역할 
    public PopupScript popupScript;// 이 레퍼런스는 유니티에서 연결

    // Start is called before the first frame update
    void Start()
    {
        popupScript.SetYesListener(ItemYesAction);//딜리게이트에 함수 등록. ClickAction대기
        popupScript.SetNoListener(ItemNoAction);//딜리게이트에 함수 등록. ClickAction대기
    }
    void ItemYesAction()
    {
        print("ItemYesAction");
    }
    void ItemNoAction()
    {
        print("ItemNoAction");
    }
    // Update is called once per frame
    void Update()
    {
        
    }
}

버튼을 추가하여 팝업구현 2

버튼을 추가하여 팝업구현2

  • onYesClick, onNoClick: 딜리게이트 변수

  • PopupYesAction, PopupNoAction: 버튼 클릭 이벤트

  • 버튼 클릭 시 딜리게이트 변수 호출해 최종적으로 ItemYesAction, ItemNoAction 함수 실행.

  • PopupCreate를 통해 팝업 생성. 팝업을 프리팹으로 만든다.

  • 아이템: 주, 팝업: 부//아이템이 각각의 팝업을 소유

게임상에서 팝업 UI 구현 1

게임 상에서 팝업 UI 구현

UI 생성

게임상에서 팝업 UI 구현 2

게임 상에서 팝업 UI 구현2

게임상에서 팝업 UI 구현 3(단일버튼)

게임 상에서 팝업UI 구현3

UI 생성(코인 부족 알림)

  • no버튼 가려 놓음(있던 것 복붙하는거라 없애면 문제생길 수 있다. )

슈팅게임만들기 4

슈팅게임 만들기 4 인트로

슈팅 게임 만들기 4

업그레이드 UI 추가

업그레이드 UI 추가

  • 작업이 많을 경우 순서 정하는 것이 좋다.

업그레이드 코드 적용 1

업그레이드 코인 적용1

업그레이드 코드 적용 2

업그레이드 코드 적용 2

업그레이드 코드 UI 적용

업그레이드 코인 적용3

캐릭터 게임씬에 적용

게임씬에 캐릭터 적용

데미지 전달

캐릭터 따라 이미지 변경

게임오버 패널 작성 및 기능추가

게임오버 패널 작성 및 기능 추가

게임오버 패널에 정보 표시

게임 오버 패널에 정보 표시

  • coinInGame변수: 한 게임에서 얻은 코인

게임오버시 CoverPanel 적용

게임오버시 CoverPanel 적용

CoverPanel

일정 시간 이후 실행

게임성공시의 ClearPanel UI 추가

게임 성공시의 ClearPanel UI추가

  • 밑줄: 현재 씬 연결

게임성공시의 Clear 조건 체크

게임성공시의 Clear조건 체크

  • 테스트 할 때 주의

Game Clear시 오류수정

Game Clear시 오류 수정

OnBecameInvisible 오류수정1

OnBecameInvisible 오류 수정1

확인하기 어려운 오류 실행

  • 프리팹을 원하는 위치에 놓음

  • 정지버튼 누르고 플레이->한 프레임씩 실행(맨 오른쪽)

OnBecameInvisible 오류수정2

OnBecameInvisible 오류수정2