* 실행 환경 변경 (강의 내용 아님)

임시적으로 내가 편한 환경으로 바꿨다.

Window 탭 - Layout - Tall 후 각 탭을 드래그하여 변경하였음

- 변경된 사항을 저장하고 싶으면 Layout - Save Layout을 클릭하여 저장하면 된다. 

레이아웃

 

 

Time.deltaTime

컴퓨터 사양에 따라 프레임이 달라지는데 Update() 메서드는 프레임에 종속적이다. 그래서 정규화 해줄 필요가 있다. 이 때 이용하는 방법 중 하나가 Time.DetaTime이다.

프레임

- Time.deltaTime은 유니티가 각 프레임을 실행하는데 얼마나 걸렸는지를 알려준다. => 이를 이용해서 게임을 프레임 레이트에서 독립시킬 수 있다.

- 값에 Time.deltaTime을 곱해주는 방식으로 많이 사용한다.

ex) 초당 10프레임 컴퓨터 => 1프레임 당 0.1초 vs 초당 100프레임 컴퓨터 => 1프레임 당 0.01초

10(프레임) * 0.1(초) => 1

100(프레임) * 0.01(초) => 1

 

실행

실행

 

Mover.cs

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

public class Mover : MonoBehaviour
{
    [SerializeField]
    float moveSpeed;
    void Start()
    {

    }

    void Update()
    {
        float xValue = Input.GetAxis("Horizontal") * Time.deltaTime * moveSpeed;
        float zValue = Input.GetAxis("Vertical") * Time.deltaTime * moveSpeed;
        transform.Translate(xValue, 0f, zValue);
    }
}

'유데미 강의 > C#과 Unity로 3D 게임 개발하기 : 장애물 코스' 카테고리의 다른 글

Collision  (0) 2022.07.28
Cinemachine  (0) 2022.07.28
C# 포맷팅, Input.GetAxis()메서드  (0) 2022.07.27
SerializedField  (0) 2022.07.27
변수  (0) 2022.07.27

C# 포맷팅

주석

// 내용

이런 식으로 사용하고 // 뒤의 내용은 코드에 영향을 주지 않는다. - 메모 같은 느낌 

주석

 

중괄호 위치와 코드 위치

가독성을 위해 중괄호와 코드 위치를 맞추는 것이 좋다. (반드시 따라야 하는 건 아님 - 작동이 됨)

ex) 한 메서드 내의 중괄호 위치와 코드의 옆 간격을 맞춤(들여쓰기)

중괄호 위치

 

메서드 실행

메서드가 실행되면 안의 코드도 같이 실행됨

메서드 실행

 

 

Input.GetAxis() 메서드

InputManager

Edit 탭 - Project Setting - InputManager

InputManager

Axes내에는 여러 값들이 있는데 Horizontal과 Vertical을 이용하면 키보드로부터 좌우, 위아래 값을 받아올 수 있다.

- Positive Button / Alt Positive Button - 이 키를 누르면 양수 값이 반환된다.

- Negative Button / Alt Negative Button - 이 키를 누르면 음수 값이 반환된다.

Axes

Input.GetAxis("")

"" 부분에 위 사진의 Name부분을 입력하면 GetAXis함수가 실행되어 float 값을 반환한다.

Input.GetAxis

이렇게 반환된 값은 변수에 저장하여 사용할 수 있음

변수 저장

 

실행

* 3D 환경에서 좌우는 x축과 관련있고 위아래는 y축 앞뒤는 z축과 관련있으므로 x축과 z축으로 조작을 건드렸다.

빨강 화살표 - x축

파랑 화살표 - z축

초록 화살표 - y축

xyz축
실행

 

Mover.cs

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

public class Mover : MonoBehaviour
{
    void Start()
    {

    }

    void Update()
    {
        float xValue = Input.GetAxis("Horizontal");
        float zValue = Input.GetAxis("Vertical");
        transform.Translate(xValue, 0f, zValue);
    }
}

'유데미 강의 > C#과 Unity로 3D 게임 개발하기 : 장애물 코스' 카테고리의 다른 글

Cinemachine  (0) 2022.07.28
Time.deltaTime  (0) 2022.07.27
SerializedField  (0) 2022.07.27
변수  (0) 2022.07.27
Start() 함수와 Update() 함수  (0) 2022.07.27

SerializedField

변수 값을 변경할 때 매번 스크립트로 돌아와서 수정해야하는 번거로움이 있다. 이 때 SerializedField로 변수를 선언하면 에디터에서 수정이 가능하다.

SerializedField
SerializedField2

* 여기서 인스펙터에서 값을 변경해도 스크립트에 적혀있는 기존 값은 변경되지 않는다.

* Play 모드에서 인스펙터 값을 수정하면 게임에 즉각 반영된다.

 

Material

생성

Material

오브젝트 색 변경, 재질 변경

*스크립트처럼 오브젝트의 컴포넌트에 추가해야함

Material2

 

Mover.cs

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

public class Mover : MonoBehaviour
{
    // 선언
    [SerializeField] float xValue = 0f;
    [SerializeField] float yValue = 0f;
    [SerializeField] float zValue = 0f;

    void Start()
    {

    }

    void Update()
    {
        // 프레임 당 값만큼 움직임
        // 변수 자동완성 됨 - 편리
       transform.Translate(xValue, yValue, zValue);
    }
}

변수

- 박스와 비슷한 개념

- 이름이 있음

- 데이터가 들어있음

- 특정 타입이 있음

ex)

타입 변수이름 = 값;

int hitPoints = 20;

float speed = 3.8f;

bool isAlive = true;

string myName = "Rick";

 

장점

1. 가독성 - 한 곳에 모아둬서 보기 편함

2. 조작이 쉬움 - 자동완성, 한 번 선언하면 여러 곳에서 사용 가능

 

 

실행

공중으로 발사

 

Mover.cs

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

public class Mover : MonoBehaviour
{
    // 선언
    float xValue = 0f;
    float yValue = 0.01f;
    float zValue = 0f;

    void Start()
    {

    }

    void Update()
    {
        // 프레임 당 값만큼 움직임
        // 변수 자동완성 됨 - 편리
        transform.Translate(xValue, yValue, zValue);
    }
}

Start(), Update()

Start()

게임 오브젝트와 스크립트가 활성화 되었을 때 딱 한 번만 실행됨

Update()

// 게임 오브젝트와 스크립트가 활성화 되었을 때 매 프레임마다 실행 됨(fps설정이 60이라면 매 초당 60번 실행됨)

 

 

스크립트 추가

게임 오브젝트에 스크립트를 추가해야지 코드를 사용할 수 있다.

스크립트 추가

 

컴파일 에러

메서드에 인자가 있다면 정해진 형식대로 넣어주어야 한다.

컴파일 에러
컴파일 에러2

double 형식을 float형식으로 바꿔줬다.

해결
해결2

 

Mover.cs

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

public class Mover : MonoBehaviour
{
    // 게임 오브젝트와 스크립트가 활성화 되었을 때 딱 한 번만 실행됨
    void Start()
    {
        // Mover스크립트를 플레이어에 추가했기 때문에 transform을 찾으면 지금 선택된 개체의 transform을 지칭함
        // transform.Translate(1, 0, 0);
    }

    // 게임 오브젝트와 스크립트가 활성화 되었을 때 매 프레임마다 실행 됨(fps설정이 60이라면 매 초당 60번 실행됨)
    void Update()
    {
        transform.Translate(0.1f, 0, 0);
    }
}

'유데미 강의 > C#과 Unity로 3D 게임 개발하기 : 장애물 코스' 카테고리의 다른 글

Time.deltaTime  (0) 2022.07.27
C# 포맷팅, Input.GetAxis()메서드  (0) 2022.07.27
SerializedField  (0) 2022.07.27
변수  (0) 2022.07.27
게임 디자인 - 장애물 코스  (0) 2022.07.27

C#과 Unity로 3D 게임 제작하기

 

게임 디자인

아주 간단하더라도 무엇을 만드는지 알 수 있도록 게임을 디자인 할 때는 적어두는 것이 좋다.

=> 게임 디자인을 수행하면 게임의 목적이 명확해짐(만드는 결정에 도움이 됨 ex : 플레이어 크기, 속도, 장애물 이동 반경)

 

플레이어 경험

플레이어가 이 게임을 플레이할 때 무엇을 느꼈으면 좋겠는지 생각하는 것이다.

이 프로젝트에선 날렵함과 민첩함

 

핵심 메커니즘

이 프로젝트에선 이동하고 장애물을 피하는 것

 

게임 루프

목표

이 프로젝트에선 A에서 B까지 가는 것

'유데미 강의 > C#과 Unity로 3D 게임 개발하기 : 장애물 코스' 카테고리의 다른 글

Time.deltaTime  (0) 2022.07.27
C# 포맷팅, Input.GetAxis()메서드  (0) 2022.07.27
SerializedField  (0) 2022.07.27
변수  (0) 2022.07.27
Start() 함수와 Update() 함수  (0) 2022.07.27

게임 오브젝트, 트랜스폼, 컴포넌트, 카메라

게임 오브젝트

GameObjectExample.cs

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

public class GameObjectExample : MonoBehaviour
{
    // Start is called before the first frame update
    
    [SerializeField]
    private GameObject goPrefab;
    void Start()
    {
        
        // 1. 오브젝트 생성
        // PrimitiveType.미리 정의된 오브젝트들
        // 0,0,0에 생성됨
        // GameObject go = GameObject.CreatePrimitive(PrimitiveType.Cube);
        // PrimitiveType.Capsule;
        // PrimitiveType.Cylinder;
        // PrimitiveType.Plane;
        // PrimitiveType.Quad;
        // PrimitiveType.Sphere;


        // 2. 오브젝트 파괴
        // Destroy(go);
        // Destroy(go, 3f);
        // 커스터마이징 사용할 때 아니면 사용 X
        // DestroyImmediate(go);

        // 3. 오브젝트의 활성화 여부 확인
        // 본인이 체크되어있냐 (부모 활성화에 상관 없이 체크면 true 체크해제면 false)
        // 자식이 켜있고 부모가 꺼져있을 때
        // ex) preload 미리 불러오기 외부로부터 동적으로 생성 - SetActive 켜져있음
        // 이후 부모를 끄는 상태가 있음
        // Debug.Log(go.activeSelf);
        // 하이어라키상에서 활성화 되어있는지 여부를 알려줌 (부모 비활성화시 비활성화시 체크 false 체크해제 false)
        // Debug.Log(go.activeInHierarchy);


        //4. Tag 확인
        // GameObject의 꼬리표(표식)
        // 한 오브젝트 당 하나만 가능
        // Array GameObject[] 가 있을 때
        // foreach를 돌려서 go.tag == "Player"; go.CompareTag("tag"); 등 물리 엔진을 다룰 때 사용

        // 프로젝트를 이관할 때 Export Pakage와 폴더 전체를 전송하는 경우 중 각 폴더만 Export하면
        // Project Setting(태그, 등등) 이 포함이 안 된다.
        // export 패키지는 중간중간 필요한 리소스를 받을 때 사용한다.

        // Jenkins는 파일 / 셋팅, 깃허브 연동 => 프로젝트를 서버에서 빌드 
        // 소스코드를 GIT, SVN, Mercurial, Collaborate 버전관리툴을 사용해서 올린다.
        // 젠킨스로는 바뀐 부분만 올려짐 세팅 같은 건 바꿀 수 없게 세팅을 해놓음

        // boolean 반환
        // Debug.Log(gameObject.CompareTag("GOExample"));


        // Find는 하이러키 창의 위에서부터 찾음
        //5. Tag로 검색 - 해당 태그를 가지고있는 오브젝트 저장
        // GameObject goWithTag1 = GameObject.FindWithTag("Player");
        // GameObject goWithTag2 = GameObject.FindWithTag("Finish");
        // Debug.Log(goWithTag1 != null);
        // Debug.Log(goWithTag2 != null);


        // Transform.Find("");
        // 자식들만 가져올 수 있는데 Active false도 가져올 수 있음

        // FindWithTag("");
        // GameObject.Find("");
        // SetActive가 꺼진 애들은 가져오지 못함

        // GetComponent(Active or not Active).gameObject();
        // 선택해서 가져올 수 있다.

        //6. 이름으로 검색

        GameObject goWithFind1 = GameObject.Find("FindWithName");
        // GameObject goWithFind2 = GameObject.Find("FindWithName2");
        // Debug.Log(goWithFind1 != null);
        // Debug.Log(goWithFind2 != null);

        // 사용자가 정의한 게임오브젝트 생성
        GameObject goTest = Instantiate(goPrefab);
        goTest.name = "InstanceSphere";
        goTest.transform.parent = goWithFind1.transform;
        goTest.transform.localPosition = Vector3.zero;
        goTest.transform.localRotation = Quaternion.identity;
        goTest.transform.localScale = Vector3.one;
    }

    void Update()
    {
        
    }
}

 

트랜스폼

TransformExample.cs

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

public class TransformExample : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        // Transform 반환
        Transform tr1 = transform.Find("FindInChild");
        Transform tr2 = transform.Find("FindInChild2");


        // GameObject go1 = new GameObject();
        // Transform과 GameObject는 상호참조관계
        // Transform trG = go1.transform;
        // GameObject goT = tr1.gameObject;

        Debug.Log(transform.GetChild(0).name);
        // tr1의 오브젝트를 맨 밑으로 보내겠다.
        tr1.SetAsLastSibling();
        Debug.Log(transform.GetChild(0).name);
        // tr1의 오브젝트를 맨 위로 보내겠다.
        tr1.SetAsFirstSibling();
        Debug.Log(transform.GetChild(0).name);
        // tr1을 index(1)번으로 보내겠다.
        // 언제쓰냐  GetComponents 다 가져올 때 특정 순서를 가져옴 Getcomponent는 첫 번째 것 가져옴 
        // Sprite (0,0,0) (0,0,0) 두 개가 있을 때 하이어러키 상에서 맨 아래에 있는게 제일 앞에 보임 (z축은 의미가 없음)
        // => 인덱스 0번부터 그리기(렌더링하기) 때문에
        tr1.SetSiblingIndex(1);

        // tr1의 부모 이름 가져오기
        Debug.Log(tr1.parent.name);
        
        // 최상위 것 가져옴
        // tr1.root
    }

    void Update()
    {
        // 부모 자식 관계에서 자식 스크립트에서 transform.position은 월드 좌표 기준 position이다.
        // 컴파일 에러
        // transform.position은 Vector3이다. Vector는 struct(값에 의한 복사)이므로 못 씀
        // transform.position.x += 0.01f;

        // 부모 자식 관계에서 자식 스크립트에서 transform.rotation은 월드 좌표 기준 rotation이다.
        // Vector3 vecP = transform.localPosition;
        // vecP.x += 0.01f;
        // transform.localPosition = vecP;
        
        // Vector3 vecR = transform.localRotation.eulerAngles; -> Vector3
        // vecR.x += 0.01f;
        // transform.localRotation = Quaternion.Euler(vecR);

        // lossyScale은 root 스케일(월드) localScale은 로컬 스케일(로컬)
        // Vector3 vecS = transform.lossyScale;
        // vecS.x += 0.01f;
        // 에러
        // transform.lossyScale = vecS;

        // Euler 3축 Quternion 4축

        // Update는 초당 30번 실행된다. 30fps
        // 1Unit = 1M
    }
}

 

컴포넌트

ComponentExample.cs

using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;

public class ComponentExample : MonoBehaviour // 컴포넌트 상속
{
    void Start()
    {
        // MonoBehaviour를 상속받는 클래스들은 new로 생성하면 안 된다.
        // GameObject를 생성하고(이미 있을 시 그냥) AddComponent로 컴포넌트를 붙여야한다.
        // 변수로 컴포넌트를 선언하면 asrc.~ 등 바로 접근이 가능하다.
        AudioSource asrc = transform.AddComponent<AudioSource>(); // Camera, GameObject (MonoBehaviour)를 상속하는 것이라면 가능
        // 이미 있으면 Getcomponent로 이 클래스에 붙어있는 컴포넌트를 가져옴
        AudioSource asrc2 = transform.GetComponent<AudioSource>();

        transform.GetComponent<Component>();

        // 아래 방식으로 Active가 꺼진 애들을 가져올 수 있다.
        GameObject go = new GameObject();
        
        GameObject.Find("");
        
        go.transform.Find("");
        go.GetComponentInChildren<MeshRenderer>(true);
        MeshRenderer[] meshArray = go.transform.GetComponentsInChildren<MeshRenderer>(true);
        foreach (var mesh in meshArray)
        {
            mesh.enabled = false;
        }
        Component[] coms = transform.GetComponentsInChildren<Component>(true);
        AudioSource[] asrcArray = transform.GetComponentsInChildren<AudioSource>(true);
    }

    void Update()
    {
        
    }
}

 

카메라

// Camera
    // Clear Flags는 배경을 뭘로 할거냐 SkyBox, Solid Color,
    // Depth Only 카메라의 뒷배경은 투명 - UI, Sprite, Video, WebPage ex) 로그인창 => 3rd party Library, 내부카메라 WebPage
    // 이 때 기존 카메라에 UI 띄우는 경우도 있는데 최적화 Draw Call 문제와 UI.Object가 카메라에 같이 비치는 문제가 있다.
    // => 웬만하면 UI 카메라와 게임 카메라를 분리를 한다.
    // Don't Clear는 (Blit Clear가 클리어를 하는데 전체 프레임을 한 번 지워준다) => 이 클리어를 안 하겠다는 거임

    // Culling Mask - Layer 개념 비슷
    // 1번 카메라는 오브젝트만 비추고 2번 카메라는 캐릭터만 비추고 싶을 때 Post Processing(후처리 과정) ex) 사진 필터
    // 캐릭터에만 효과를 주고싶을 때 Layer와 Culling Mask를 사용

    // Projection
    // - Perspective : 원근 적용
    // FOV(FiledOfView) : Vertical/Horizontal 60도만큼 화면을 찍어서 가져옴
    // - Orthographic : 원근 적용 X (카메라 각도가 직각)
    // Size : Unity Unit 1(1m) 중점 기준으로 a만큼 사이즈가 잡힘(높이) 이에 대해 가로는 해상도에 따라 달라짐
    // 16:9일 때 세로가 5라면 세로는 5/9*16이 됨

    // - Clipping Planes
    // Near : 제일 가까운 부분 (~보다 가까워지면 안 보임)
    // Far : 가시거리 (~가 넘어가면 안 보임)

    // - ViewportRect
    // X, Y 는 좌측 아래를 기준으로 0~1의 위치
    // W, H 는 화면의 0~1만큼의 크기

    // TargetTexture
    // Render Textrue를 만들고 타겟 텍스처에 넣고 Material 알베도 부분에 입혀서 오브젝트에 입히면
    // CCTV같이 입혀진다.

    // Occlusion Culling
    // Occlusion 계산은 CPU를 사용 Occlusion 가리고 안 가리고는 GPU를 사용
    // 겹쳐보이는 부분이 있을 때 안 그려줌
    // 인스펙터 위의 static 옆에 Occluder (가리는 애), Occludee (가려지는 애) 잘 선택해서 체크
    // GPU와 CPU에 사용량에 따라 사용
    // Occulsion -> Yellow box 형성 기준
    // 1. Occlusion Area
    // 2. Dynamic Area
    // 파란색 박스가 unit을 나누는 기준

 

 

씬, 벡터 , Input, 물리

SceneExample.cs

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

public class SceneExample : MonoBehaviour
{
    // 씬을 관리하는 경우
    // 1. 실제로 게임상/기능상 화면전환/기능전환
    // 2. 리소스 관리 차원 -> 던전분리 ex) RPG Dungeon이동
    // 3. OpenWorld RPG - Object Pooling, LOD 관리
    
    // 1번 씬에서 2번씬으로 이동시 1번 씬에 있던 오브젝트는 모두 삭제됨 (Additive를 안 했다고 가정)

    void Start()
    {
        DontDestroyOnLoad(this.gameObject); // 씬을 로드를 했을 때 파라미터 부분을 Destroy 안 하겠다.

        // File - Build Setting - Scenes In Build에 씬을 드래그앤드롭으로 추가해야함
        // 인덱스를 통해서 불러오는 방식
        // SceneManager.LoadScene(1);
        // 씬의 이름을 통해서 불러오는 방식
        // 비슷할 때 풀 경로로 써주면 됨
        // SceneManager.LoadScene("2");

        // SceneManager.LoadScene("2", LoadSceneMode.Single);

        // Scene 1이 살아있는 상태로 불림
        // 뒤에 생긴 카메라가 앞에 씬 카메라 덮어씌워짐(UI 설명할 때 처럼 한 번 더 그림)
        // 웬만하면 Addtive 씬에는 카메라를 안 넣음 (일부러 겹치고 카메라를 사용해야될 때만 사용)
        SceneManager.LoadScene("Scenes/2", LoadSceneMode.Additive);
    }
}

 

벡터

Day5.cs

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

public class Day5 : MonoBehaviour
{
    // Vector -> struct  방향 / 크기
    // △x △y 방향

    Vector3 vec = new Vector3(0,0,0);
    Vector3 vec2 = new Vector3(10,10,10);
    float currentVel;

    // 배열 선언 : 한정자 자료형[] 변수이름 = new 자료형[사이즈];
    private Vector3[] vecArray = new Vector3[3];
    // 리스트 : 동적 배열 List<자료형/클래스/구조체> 변수명 = new List<앞과 같음>();
    private List<Vector3> vecList = new List<Vector3>();
    
    // 딕셔너리 : Dictionary<키의 자료형/인스턴스형, 값의 자료형/인스턴스형> 변수명 = new Dictionary<앞과 동일>();
    private Dictionary<string, Vector3> vecDictionary = new Dictionary<string, Vector3>();

    private float prev;
    private float threshhold = 2;
    // Start is called before the first frame update
    void Start()
    {
        // 벡터의 크기
        Debug.Log(vec2.magnitude);
        // 벡터 정규화 (0~1)사이
        // normailized되지만 vector2의 값이 바뀌지는 않음
        Debug.Log(vec2.normalized);
        // 값이 바뀌는 건 함수
        // vec2.Normalize()
        // vec2.Scale()

        //Vector3.one; (1, 1, 1)
        //Vector3.zero; (0, 0, 0)
        //Vector3.left; (-1, 0, 0)
        //Vector3.right; (1, 0, 0)
        //Vector3.up; (0, 1, 0)
        //Vector3.down; (0, -1, 0)
        //Vector3.forward; (0, 0, 1)
        //Vector3.back; (0, 0, -1)

        // 두 벡터 사이의 거리를 잼
        // ex) 충돌 처리
        Debug.Log(Vector3.Distance(vec, vec2));

        Transform obj = transform;
        Transform obj2 = transform;
        Vector3.Distance(obj.position, obj2.position);
        // 간단한 충돌처리 각 오브젝트에 가상의 원을 씌움
        // 각 원의 센터를 그림
        // 반지름 r1, r2일 때
        // 원이 겹치는 부분 계산 중심의 거리 < r1 + r2 이면 충돌

        vecList.Add(vec2);
        vecList.Add(vec);
        vecList.Add(vec);
        vecList.Add(vec);

        // List의 한도값 - 갯수 한정 (따로 안 줬을 때 기본 값은 메모리가 계속 두 배로 늘어남)
        // vecList.Capacity = 10;

        // ~을 포함하는지 확인하고 bool 값을 반환
        // vecList.Contains(vec);

        // vecList의 1번 인덱스에 vec값을 넣음 (인덱스 1번부터 우측으로 밀려남)
        // vecList.Insert(1, vec);

        // vec을 앞에서부터 찾아서 없앰
        // vecList.Remove(vec);

        // (LINQ)
        // 나중에 배움

        // 자바의 Map 개념이랑 비슷
        // 사전 : 단어, 뜻 Dictionary : 키(key), 값(value)
        // vecDictionary.Add("Test", vec);
        // Vector3 o = vecDictionary["Test"];
        // vecDictionary.Add("vec2", vec2);
        // 참조는 없으면 에러 뜰 수 있음
        // vecDictionary["Test"].Normalize();
        // 대입은 권장하지 않음 => 원래 값이 있거나 키가 null 값일 수 있기 때문
        // vecDictionary["vec"] = vec;
        
        // 갖고있는 키를 다 반환
        // vecDictionary.Keys

        Debug.Log(vecList[0]);
        Debug.Log(vecDictionary["vec"]);
    }

    // Update is called once per frame
    void Update()
    {
        var currentVel = Vector3.one;
        // Lerp(Linear Interpolation) 선형 보간
        // time 값에 따라 vec과 vec2 사이의 거리를 반환(0~1)
        Debug.Log(Vector3.Lerp(vec, vec2, 0.5f));

        // clamp는 값을 잘라놓은 것(그냥lerp) unClamp 잘라놓지 않은 것
        // 값에 비례해서 자름(-1이면 거리의 -1배만큼 vec에서 거리만큼 전으로 계산해서 반환)
        // Vector3.LerpUnclamped(vec, vec2, -1f);

        // 2D게임을 만들 땐 Vector3.Lerp 대신 Mathf.Lerp를 써도 쓸만함
        // (0~1) 비례해서 0.5만큼의 거리만큼 계산
        // Mathf.Lerp(vec.x, vec2.x, 0.5f);

        // 50f라는 실제 값을 넣었을 때 비율을 반환
        // vec.x가 0이고 vec2.x가 100일 때 50을 넣었다치면 그 비율인 0.5가 나옴
        // Mathf.InverseLerp(vec.x, vec2.x, 50f);

        // ex) vec과 vec2 사이에 2초만에 도착하고 싶을 때
        // transform.position = Vector3.Lerp(vec, vec2, Time.time * 0.5f);
        // (Mathf.Sin(Time.time) + 1) / 2) -> 0부터 1 => vec, vec2 사이를 왔다갔다함
        // transform.position = Vector3.Lerp(vec, vec2, (Mathf.Sin(Time.time) + 1) / 2);

        // 위의 Sin 함수를 쓴 것과 같은 효과
        // Sigmoid를 사용 활성함수 0과 1으로 가까워질수록 변화값이 작고
        // 0.5로 가까워질수록 변화값이 크다.
        // transform.position = Vector3.SmoothDamp(vec, vec2, ref currentVel, Time.deltaTime);
        // Debug.Log(Vector3.SmoothDamp(vec,vec2, ref currentVel, Time.deltaTime));
	}
}

 

Input

// Input
private float prev;
private float threshhold = 2;

		//Update문 안에서
        
        // 현재 마우스 위치
        // vec2 = Input.mousePosition;
        // 0:좌클릭, 1:우클릭, 2:휠버튼
        // Input.GetMouseButton(0)

        // 엔터는 KeyCode.Return을 씀
        // KeyPadEnter는 우측 숫자패드의 엔터이다.
        // GetKey, GetMouseButton 등 받으면
        // 키보드 이벤트 3가지
        // 눌렀을 때, 누르고 있을 때, 누른걸 땠을 때
        // Input.GetKeyDown(KeyCode.L); // 눌렀을 때
        // Input.GetKey(KeyCode.L); // 누르고 있을 때
        // Input.GetKeyUp(KeyCode.L); // 누른걸 땠을 때
        // Update문에서 GetKey는 프레임당 한번 씩 받음(캐릭터 이동 등)
        // 한 번만 받아야되면 GetKeyDown이나 GetKeyUp 사용
        // 위와 같음
        // Input.GetMouseButtonDown(0);
        // Input.GetMouseButton(0);
        // Input.GetMouseButtonUp(0);


        // esc버튼인데 모바일로 가면 backButton이다.
        // Input.GetKey(KeyCode.Escape);
        //Input.GetKey(KeyCode.A);
        //Debug.Log(Input.GetKey(KeyCode.A));
        //Input.GetMouseButton(0);
        //0 for left button, 1 for right button, 2 for the middle button
        //Input.GetKeyDown(KeyCode.A);
        //Input.GetKeyUp(KeyCode.A);

        // 기기에 있는 가속도계 => 핸드폰 기울이는 로직을 사용할 때 씀
        // Input.acceleration
        // 기기 방향
        // Input.compass
        // 기기 회전 값을 잡을 때
        // Input.gyro
        // 기기 터치 관련
        // Input.touches

        // 모바일 멀티터치 index 0 1 2 3 4 ~~
        // 터치에 따라 리스트가 구현이 되어있음
        Touch t = Input.GetTouch(0);
        Touch t2 = Input.GetTouch(0);
        // 멀티 터치 갯수를 반환
        // Input.touchCount;
        // 위와 동치 touches는 멀티터치를 배열로 갖고있음 
        // Input.touches.Length;
        // 터치 이동한 만큼 델타 벡터를 계산
        // t.deltaPosition;


        // 줌인 줌아웃
        if (Input.touchCount == 2)
        {
            if (Mathf.Abs(Vector2.Distance(t.position, t2.position) - prev) > threshhold) return;
                
            if (prev > Vector2.Distance(t.position, t2.position))
            {
                //pinch in
            }
            if (prev < Vector2.Distance(t.position, t2.position))
            {
                //pinch in
            }

            prev = Vector2.Distance(t.position, t2.position);
        }

 

물리

FixedUpdateEx.cs

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

public class FixedUpdateEx : MonoBehaviour
{
    // 유니티 엔진, 렌더링 엔진, 물리 엔진

    // 옛날엔 3D 밖에 지원을 안 했음 => 원래는 3D 물리 엔진만 지원했음(PhysX)
    // static
    // 3D rigidbody
    // Kinematic : 물리법칙 받지 X but 충돌처리는 됨 ex) 맵
    // Dynamic : 물리법칙 O ex) 플레이어

    // 2d UGUI 나중에 2D물리엔진 지원 (Box2d)
    // 강체, static, dynamic, kinematic(3D의 Static과 동치)
    // 2D rigidbody
    // Kinematic - AddforceV에는 움직이지는 않는데 간접적인 힘에는 움직임
    // Static : 3D Kinematic과 동치 - 아예 움직이지 않음
    // Dynamic - 다 움직임

    // Rigidbody는 물리의 영향을 받아야 될 때 붙이면 됨
    // transform.position.x 조작은 Render 엔진 부분이라 static이어도 이동하긴 함
    // velocity.x 조작은 이동 안 함
    // Animation의 움직임이 기본적으로 transform이나 물리엔진 이동부분을 override함


// 두 객체의 충돌구현
    // 적어도 둘 중 하나는 rigidbody를 가져야함 
    // collider는 둘 다 가져야함

    // rigidbody info에서 속도나 이런 거 확인 가능
    // mass : 질량
    // drag : 위치에 대한 저항
    // Angular Drag : 각도에 대한 저항(회전 저항)
    // Use Gravity : 중력 체크
    // Is Kinematic : 물리 체크
    // Interpolate : 충돌 검출은 프레임별로 일어나는데 전의 프레임을 참고함
    // Extrapolate : 충돌 검출에 다음 프레임을 참고함

    // 객체의 Collider에 줄 수 있고
    // Project Setting에서 Physics 부분에 넣으면 모든 물체에 기본으로 적용됨
    // Physics Material
    // - Dynamic Friction : 움직이고 있을 때 마찰력
    // ex) 공이 굴러가고 있을 때 마찰력
    // - Static Friction : 멈춰 있을 때 마찰력
    // ex) 다른 공에 부딪혔을 때 멈춰있던 공의 마찰력
    // - Bounciness : 탄성 계수
    // - Friction Combine : 두 개체 사이의 마찰력
    // -- averge 평균을 내겠다 minimum 작은 쪽의 마찰력을 내겠다
    // -- Maximum 더 큰 값을 내겠다 multiply 곱해서 내겠다
    // Bounce Combine : 두 개체 사이의 탄성력
    // -- averge 평균을 내겠다 minimum 작은 쪽의 마찰력을 내겠다
    // -- Maximum 더 큰 값을 내겠다 multiply 곱해서 내겠다
    
    // Physics
    
    // Default Material
    // 기본 Physics Material
    
    // Bounce Threshold
    // 다른 물체에 부딪혔을 때 부딪치는 물체의 속력이 이 값보다 낮을 시 탄성 작용 없음
    
    // Sleeping Threshold
    // Sleep 모드일 때 일정 값보다 큰 힘이 작용했을 때 Awake 상태가 되는 기준 값


    // Default Contact Offset => 얼마를 붙어있는 걸로 기준을 잡을래?
    // contact는 모서리의 버텍스를 가져오기 때문에 각 모서리의 길이보다 짧으면 붙었다고 가정

    // Queries Hit Backfaces 
    // 면에 대해서 hit을 감지하는데 뒷면에서 올 때도 감지할거냐?

    // Enable Adaptive Force
    // 가중치 ex) 당구 벽에 공 겹쳤을 때 하중 

    // Cloth Gravity
    // 천(옷) 중력

    // Collision Detection
    // - Discrete : 고정
    // - Continuous : 더 잦은 충돌 검출 (부하up)
    // ex) 유저가 벽뚫기 하는 경우에 씀
    //     - 속도가 빠를 때(deltapos가 클 때)
    //     - Collider 빈 부분이 있거나
    //     - Collider 검출이 안 되거나
    // Contraints - 6개 다 키면 Kinematic과 동치
    // - Freeze Position 앞으로만 움직여야 되면 다른 좌표 값을 체크(고정됨)
    // - Freeze Rotation 달려가다가 옆에서 쳤을 때 주사위 굴러가듯이 넘어가면 Rotation을 막아버림
    // ex) 좌우 각도만 있으면 된다 Freeze Rotation의 X,Z를 체크함 

    // Capsule Collider vs Sphere Collider
    // Capsule Collider Height를 1로하면 Sphere와 같아짐
    // 실무에서는 Capsule Collider를 더 많이 씀 (범용성이 넓음)

    // Collider
    // Edit모드에서 크기 조절 가능

    // Trigger vs Collision
    // Trigger : 범위 침범 O
    // ex) Rpg 특정 장소에 도달(보이지 않는 공간을 만들고 플레이어 도달) => Trigger 사용
    // Rpg에선 인스턴싱 오브젝트가 많을 시(의도하지 않게 길막 생길 수 있음) 웬만하면 트리거
    // Collision : 범위 침범 X
    // ex) 당구 게임, 퍼즐 게임이나 어드벤처 게임에서 길을 막을 오브젝트

    // Joint : 연결을 시켜주거나 상호작용이 가능하게 만들어줌
    // Fixed Joint
    // 처음 생성된 거리가 유지가 됨
    // - Connected Body(rigidbody 참조해야함)
    // - Break Force 일정 값이 넘어가면 끊어짐 Infinity면 절대 안 부셔짐
    // - Break Toque 일정 회전 값이 끊어짐 Infinity면 절대 안 부셔짐
    // ex) cut the rope(공룡 사탕 먹는 게임) spring Joint 사용
    // Character Joint
    // 3D 모델 흔들리는 모습 다 구현 가능
    // Hinge Joint
    // 문 경첩 수직 Axis를 기준으로 회전 가능
    // axis 움직이는 축
    // 기준점이 되는 좌표
    // Spring Joint
    // 매달려서 연결 됨

    // Terrain

    // Mesh Collider
    // 3D 객체를 다루는 파일에서 자주 나옴
    // Shaded WireFrame에서 볼 수 있음
    // 더 자세히 콜라이더 체크를 할 수 있음
    // Mesh collider 일반 상태에서는 trigger가 불가능하다
    // - Convex
    // Convex Hull (구멍채우기)
    // 체크해야 Trigger가 가능하다
    // 컨벡스헐 알고리즘 - 가장 바깥의 점을 찾아서 이어줌
    // 도형에서 안쪽으로 파여져 있는 부분이 컨벡스
    // 이렇게 하기 싫으면 버텍스를 다 검출하여
    // 컨벡스헐 생성이 안 되면 콜라이더를 생성하여 계산
    // ex) 스왐피는 동적생성
    // - 정점을 가져와서 MeshFilter에 CreateMesh를 실행하여
    // Mesh배열을 가져온 후 메시 콜라이더에 넣음
    // 메시를 구획으로 나누어서 동적 생성
    // 물줄기가 떨어짐 - 원형의 콜라이더에 파티클을 씌운 것임
    // 물 - 흘러야됨 / 뭉쳐져야됨
    // 지름보다 작은 콜라이더를 넣고 - 뭉쳐짐
    // Friction을 0으로 넣음 - 흘름
    // Bounceness를 0.6정도로 하면 더 자연스러움

    // Wheel Collider
    // 레이싱 게임을 목적으로 만든 콜라이더
    // 댐핑, 서스펜션 등등이 있음

    // Rigidbody2D
    // - Auto Mass 질량을 자동으로 계산해줌(size 기반)

    // Distance Joint
    // 거리를 조정할 수 있음

    // Effector 2D
    // Collider에 Used By Effector을 체크 해야함
    // Buoyancy Effector 2D

    // Particle System Collision
    // - Lifetime Loss
    // Collision과 만나면 빨리 사라지게 할 수 있다.
    // Collider를 타고 파티클이 진행할 수 있다.

    private ForceMode m;
    Rigidbody m_Rigidbody;
    public float m_Thrust = 20f;
    private int m_nCount = 0;
    void Start()
    {
        //Fetch the Rigidbody from the GameObject with this script attached
        m_Rigidbody = GetComponent<Rigidbody>();
        // 물리 끄고싶을 때는 키네마틱을 켜버림
        // m_Rigidbody.isKinematic = true;
        // 중력 가속도 * 시간 => 속도 / 속도 * 시간 => 거리 //  9.81 * 0.02 * 0.02
    }

    // Update는 Fps -> Cpu(프레임)에 종속적
    // Render쪽에 주로 쓰임 (시각적)
    // - 사람이 초당 볼 수 있는 한계에 맞춰져 있음
    // Frame과 Delta.Time은 반비례 관계

    // FixedUpdate는 Tick(Cpu 시간을 재는 단위 - 고정)에 종속적
    // => 프레임 저하에 상관 X(프레임에 종속 X) - 0.02초에 한 번 불림
    // 실무에선 0.1초에 한 번으로 불림

    // 물리 엔진은 예측 개념이 조금 있는데
    // 프레임이 끊겼을 때 순간 이동을 한다거나 버그가 생길 수 있음
    void FixedUpdate()
    {
        // 정속
        // m_Rigidbody.velocity = new Vector3(0, 5f, 0);

        // 방향을 가진 힘을 줌 (월드 포지션 기준)
        // 한 번 더 감싸는 큐브가 있다고 쳤을 때 부모 객체의 각도를 돌렸을 때
        // 예상치 못한 방향으로 힘이 가해질 수 있음
        // m_Rigidbody.AddForce();

        // 해당 트랜스폼이 바라보는 방향에 대한 벡터
        // transform.forward;

        // 특정 지점에 힘들 줌 (토크랑 같이 먹음)
        // m_Rigidbody.AddForceAtPosition();


        //F(힘) = ma
        //a(가속도) = F / m(질량)

        //a = deltaV / deltaT
        //a = (v2 - v1) / (t2 - t1) 가속도 
        //a - 9.8(중력) = v2 / t2 


        // m = 1, F = 20, t2 = 0.02 
        // v1 = 0 t1 = 0
        // a - 9.8 = v2 / t2
        // v2 = (a - 9.8) * t2
        // v2 = (F / m - 9.8) * t2
        // v2 = (F - 9.8) * t2
        // v2 = (20 - 9.8) * 0.02
        // v2 = 0.2 RigidbodyInfo에서 확인 가능

        // Ex)
        //  v2 = (a - 9.8) * t2
        //  v2 = (F/m - 9.8) * t2
        //  v2 = (F - 9.8) * 0.02


        if (Input.GetKeyDown(KeyCode.A))
        {
            m_nCount = 50;
            m = ForceMode.Force;
            // force * deltaTime / mass
            // F = 20 * 0.02 / 1
            // v2 = 0.204
        }

        if (Input.GetKeyDown(KeyCode.S))
        {
            m_nCount = 50;
            m = ForceMode.Acceleration;
            // force * deltaTime
            // F = 20 * 0.02
            //
        }

        if (Input.GetKeyDown(KeyCode.D))
        {
            m_nCount = 50;
            m = ForceMode.Impulse;
            // force / mass
            // F = 20 / 1
            // 
        }

        if (Input.GetKeyDown(KeyCode.F))
        {
            m_nCount = 50;
            m = ForceMode.VelocityChange;
            // force
            // F = 20
        }

        if (m_nCount > 0)
        {
            m_nCount--;
            m_Rigidbody.AddForce(transform.up * m_Thrust, m);
            Debug.Log(m_Rigidbody.velocity);
            Debug.Log(Time.fixedDeltaTime);
        }
        // 원래 개념은 카메라에서 눈에 보이지않는 Ray(선) 을 쐈을 때 처음으로 걸리는 객체 
        // 유니티에서의 개념은 쏘는 부분(object, 좌표)에서 쏴서(필터링) 특정 객체가 맞으면 true를 반환
        // 제일 처음 만난 콜라이더를 반환함
        // 다 반환받고 싶으면 CastAll을 붙임 => 반환값 Array
        RaycastHit hit;

        // 현재 포지션에서 forward 방향으로 레이저를 100fm 만큼 쐈을 때 맞으면 true
        // 거리 안 쓰면 무한 : float.PositiveInfinity
        // Layer는 특정 객체를 검출하게 할 수 있다.


        // out hit
        // RaycastHit은 구조체로 값 전달밖에 못 하는데 out으로 전달하면 참조로 받아
        // hit을 Raycast함수문 탈출 후에도 사용할 수 있다.

        if (Physics.Raycast(transform.position, Vector3.forward, out hit, 100f, LayerMask.NameToLayer("UI")))
        {
            hit.transform.GetComponent<GameObject>().SetActive(false);
        }

        // 캐스트 되는 범위를 박스 형태로 날림
        if (Physics.BoxCast(transform.position, Vector3.one, Vector3.forward, out hit))
        {

        }

        RaycastHit[] hits = Physics.BoxCastAll(transform.position, Vector3.one, Vector3.forward);
        // Physics.CapsuleCast()
        // Physics.SphereCast();
        // Physics.Linecast();
    }
    
    // Edit - Project Setting - Physics Layer Collision Matrix - 체크를 해제하면 충돌을 막을 수 있다.
    
    // OnTriggerEnter/Stay/Exit
    // Collider 영역에 진입했을 때
    private void OnTriggerEnter(Collider other)
    {
        
    }
    // Collider 영역에 있을 때
    private void OnTriggerStay(Collider other)
    {

    }
    // Collider 영역에서 나왔을 때
    private void OnTriggerExit(Collider other)
    {

    }

    // OnCollisionEnter/Stay/Exit
    // 부딪쳤을 때
    private void OnCollisionEnter(Collision collision)
    {
        // 사각형 사각형 
        // 충돌 포인트들을 가져올 수 있음
        // collision.contacts
        // 부딪친 충돌체의 컴포넌트들을 가져올 수 있음
        // collision.transform.GetComponent<>();
    }
    // 붙어있는 상태
    private void OnCollisionStay(Collision collision)
    {
        
    }
    // 부딪쳤다 떨어졌을 때
    private void OnCollisionExit(Collision collision)
    {
        
    }
}

 

 

실습 과제

물리를 이용한 플랫폼형 게임을 만들어봤다.

실습과제

 

한 주 후기

 이번 주에는 게임 오브젝트부터 각 컴포넌트가 무슨 일을 하는지, 또 유니티에서 물리 법칙이 어떤 방식으로 이루어지는지를 배웠다. 현실의 물리 법칙(ex : F = ma)이 내부 물리 법칙 계산과 같은 것에 놀랐고 이 값들을 조정하여 나만의 게임을 만들 수도 있다는 것을 알았다.

 실습 과제를 진행하면서 어려운 점이 많았다. 내 프로젝트는 2D로 진행했는데 3D를 주로 다뤄보다가 2D 컴포넌트를 쓰려니 까먹고 바꿔써야 하는 걸 안 바꿔 쓴 적이 많았다. 프로젝트 중 첫 난관은 Vector.Velocity의 x축으로 속도를 조절하려니 중력 값이 override돼서 플레이어가 생각대로 움직이지 않는 것이었다. 이는 그냥 내가 수동으로 중력 값을 넣어줘서 해결하였다. 두 번째 난관은 Raycast부분에서 어쩔 때는 hit이 되고 어쩔 때는 hit이 안 되는 것 때문에 애를 먹었다. DrawRay로 선을 그려가면서 해보았지만 안 되는 것에 시간을 많이 낭비한 것 같다. 여기서 첫 번째 문제는 3D환경에서 사용하던 것처럼 Physics를 사용한 것이었고 두 번째 문제는 바닥의 끝으로 갔을 때 Ray로 검출하다보니 캐릭터 transform의 위치 때문에 검출이 안 될 때가 있던 것이었다. 이후에 첫 번째 문제는 Physics2D를 사용해서 해결하였고 두 번째 문제는 Boxcast로 바닥을 체크해 해결하였다.

 이번 주차에 토이 프로젝트를 진행하면서 느낀 점은 기능 기능을 따로 구현하는 것이 아니라 작은 게임을 처음부터 구현한다는 점이 좋았다. 처음부터 스스로 구현하다보니 어떻게 구현하면 좋을지에 대해서 먼저 생각하는 시간도 있고 그 생각대로 프로그래밍을 하는 것과 그 구현방식에 따른 다른 구현 방법도 달라지는 것이 재밌었다. 그리고 내가 부족한 점이 어느 부분에 있다는 것을 알 수 있어서 좀 더 성장할 수 있었다고 생각이 들었다.

 이전의 개발 교육 기간에 들었던 이론들을 기반으로 눈에 보이는 결과물이 있는 실습 시간으로 들어오니까 학습 성취도도 늘고 재밌게 교육에 참여할 수 있었던 것 같다. 학습이 재밌고 내가 성장하는 걸 보면서 동기부여도 되는 것 자체도 재밌는 것 같다. 이 마음가짐으로 끝까지 교육을 수행했으면 좋겠다. 다음주도 파이팅!

 

 

유데미코리아 바로가기

본 포스팅은 유데미-웅진씽크빅 취업 부트캠프 유니티 1기 과정 후기로 작성되었습니다.

고박사의 유니티 기초 Part 01

강좌 내용

이 강좌에서는

유니티 엔진이 제공하는 것들

- 그래픽스, 렌더링, 물리, 다중 플랫폼, 오디오, 인공지능, 협업시스템, 광고 등

 

엔진 내 각 인터페이스나  게임오브젝트, 좌표체계

- 인터페이스, 프로젝트, 씬, 게임오브젝트, 프로젝트 등 유니티 용어와 좌표체계

- 3D 게임 오브젝트 - Cube, Sphere, Capsule, Cylinder, Plane, Quad

- 2D 게임 오브젝트 - Sprite, Sprite Mask, Sprite Shape, Sprite Shape Spline

- 왼손 좌표계(x, y, z)

 

오브젝트

Effect - Particle System, Trail, Line

Audio - AudioSource

Video - Video Player

UI - TextMeshPro

 

Camera Light

Camera와 Light의 속성들

 

C# 스크립트

변수, 조건문, 연산자 등

유니티 LifeCycle(Start, Update, Ondestroy 등)

 

으로 구성되어있다.

 

배운 내용

이 강좌에서 나는 Sprite Shape로 각도에 따라 Sprite의 모양을 다르게 지정할 수 있고 Trail 컴포넌트를 이용하여 총알이나 무기의 잔상을 남길 수 있다는 것을 배웠고 Camera의 Clear Flags에서 Depth only나 Don't Clear 모드일 때 오브젝트 이동 시 잔상이 남는다는 것을 배웠다.

 

이 강좌 추천 대상

이 강좌는 쉽게 유니티의 전반적인 개념과 기초적인 C# 스크립팅 개념들, 간단한 스프라이트 렌더링 예제를 포함하여 유니티를 처음 접하는 사람도 쉽게 배울 수 있을 것 같아 입문자에게 추천하고싶다.

 

2D 스프라이트 렌더링

 

3D 스프라이트 렌더링

 

후기

 이 강의에서는 유니티의 전반적인 개념들을 다루고 있다보니 예제가 좀 부족하다고 생각이 들었다. 나는 예제를 풀어가면서 이해하고 학습하는 것을 좋아하는 편인데 개념만 들으려다보니 조금 지루하기도 했다. 강의 후반부의 C# 스크립팅이나 LifeCycle같은 부분은 스타터스 강의를 들을 때 이미 배웠던 부분이라 복습한다는 생각으로 들었던 것 같다. 하지만 입문자의 입장에서 이 강의는 매우 친절하고 개념적으로 잘 알려주는 것 같아서 좋을 거라고 생각한다. 특히 무료강의라 듣기에 부담도 덜한 것 같다.

 이 다음 강의로 유니티 2D 기초 강의와 유니티 3D 기초 강의가 있던데 그 강의에서 여기서 배운 개념들을 활용하는 식으로 되지 않을까 싶어 다음 강의들이 기대가 된다.

 

유데미코리아 바로가기

본 포스팅은 유데미-웅진씽크빅 취업 부트캠프 유니티 1기 과정 후기로 작성되었습니다.

유니티 맛보기

주 강사님의 코드 교육을 들어가기 전에 유니티를 다뤄보는 시간을 가졌다.

유니티의 화면 구성부터 간단한 컴포넌트를 다뤄보았다.

 

Scene : 플레이 화면의 객체를 조작할 수 있는 세계

Hierarchy : 씬의 객체를 배치하는 곳(주인공들)

Inspector : 객체의 상태를 나타내는 탭

Project : 프로젝트의 파일들(실제 폴더) 

유니티 화면 구성

 

주 강사님 수업(자료형부터 객체지향까지)

 

유니티의 생명 주기

Awake()

<- OnEnable

Start() - 한번만 불림

Update() - 프레임당 한번씩 불림(사양에 따라 달라질 수 있음)

<- TriggerEnter

<- CollisionEnter

<- OnDisable

(대략적으로 이렇게 된다고 한다.)

유니티 생명주기 Docs

 

 

자료형

 

변수 : 메모리에 할당(적재)

ex) 숫자, 문자, 문자열, bool, 텍스처, 게임오브젝트(class)

 

변수이름 xx ~만큼(size)

사이즈 명시 = 자료형

 

ex) (int) (hp) = (1);

(자료형) (변수명) = (값);

자료형 : 어떤 식으로 적재 할거다(크기 포함)

1바이트 = 8비트

1비트는 (0과1) 2개 표현

1바이트는 2의 8승 256개 표현 가능

signed(음수까지 표현)

u~ unsigned(0부터 양수 표현)

 

 

리터럴 – 따로 선언 안 하고 그냥 숫자 사용 ex) a = 15; (여기서 a가 상수 15가 리터럴)

 

 

게임프로그래밍에서..

변수 -> 명사

함수 -> 동사

ex) FPS 게임

총을 쏴서 적군을 맞춘다.

명사 – 총, 적군

동사 – 쏴서 맞춘다.

 

 

숫자 – 정수형, 실수형

 

int와 long

ex) 귀찮아서 long으로 쓰면 하나 당 8바이트가 1000개 있으면 FPS가 30이라고 쳤으면 8000*30 = 240000 너무 커짐 ** 주의

 

nint

nuint

32bit os 64bit os

runtime(운영체제에 따라 달라짐) word

 

게임 만들 때 자료형보다 큰 숫자로 넘어간다면 overflow로 다시 최소값부터 시작

underflow는 자료형보다 작은 숫자가 될 때 => 웬만하면 int

 

cf) 네트워크를 들어갈 때 opcode일 때 byte 사용할 때가 있음

 

 

값 참조 vs 주소 참조

값을 참조할 때는 값만 복사해서 대상과 독립적으로 사용하는 경우고

주소를 참조할 때는 같은 대상의 주소를 담아서 직접 사용하는 경우다.

 

 

부동소수점 / 고정소수점

부동 – float(부유하다, 떠다니다) => 지수를 안 정함

- 부호비트 +-

- 32비트 1비트에서 최상위 비트 - 부호비트

- 가수에서 쓸 만큼만 쓰고 나머지를 소숫점을 표현하는 데 씀 => 경계가 떠다님

오차가 생길 수 있음

 

 

고정 – fixed => 지수를 정하고 감

가수x밑수(지수 – 제곱표현)

 

특징)

가수가 커지면 지수가 작아짐(정확도 내려감)

지수가 커지면 가수로 표현할 부분이 줄어듬(정확도 올라감)

용량이 큼(메모리 잡아먹음)

 

ex) 10진수 4.123

가수 4123

밑수 10

지수 –3

4123x10(-3)

 

 

float(4바이트) vs double(8바이트)

지수를 표현하는 부분이 float과 double이 다름

비트를 사용하는 부분이 다르기 때문

 

 

소수를 비교할 때 a == b 등 등치 비교는 잘 안 함

=> 오차 범위를 지정하거나 문자열화 시켜서 비교(얼추 맞을 수 있음)

 

 

소수점 어떻게 표현?

- IEEE 754 표준

 

 

암시적 자료형 var

var 쓰는 이유

- 변수를 선언하는데 숏코딩을 위해 사용

 

특징

- 지역변수로만 사용 가능

- 숫자 타입은 var 지양(형변환 문제 때문에)

- 가독성 문제

  

ex)

int a = 3; // var a = 3;

float b = 1.23f; // var b = 1.23f;

char c = ‘h’; // var c = ‘h’;

 

 

문자형 / 문자열

char / string

‘’ / “”

string은 char의 배열

string은 내장 클래스도 있음

 

 

bool형 : 참과 거짓을 위한 자료형

c#에서 true와 false 아니면 값 안 받음

 ex) bool isTrue = true;

 

 

배열

– 같은 자료형 + 이름 + 메모리 상에 순차 저장

ex) int[5] arrayHp = new int[5];

0번부터 시작 int라면 4바이트 크기만큼 담기고 그 다음으로

 

c# .NET Framework Array라는 클래스 사용 – 내장함수 포함

List

Collection

IEnumerable – 순회

 

c#

int[ ][ ] 가변길이 배열

int[ , ] 다차원 배열

 

 

연산자

 

산술연산자 

+, -, *, /, %

 

 

복합대입 연산자

+=, -=, *=, /=, %= 등등

 

 

증감연산자

++, --

전위 연산자

ex) ++i, --i

후위 연산자

ex) i++,i--

 

 

비교 연산자

==, >=, >, <=, <

 

 

논리연산자

&&, &, ||, |, !, ^

ex) Instanciate(“Enemy”) == null

 

중요! 많이 나오는 실수

&&를 쓰면 앞에서 false일시 뒤의 나머지 부분을 스킵하는데

&를 쓰면 조건문에서 앞에가 false여도 조건 부분은 실행됨

||를 쓰면 조건문에서 앞에가 true일시 나머지 부분을 스킵하는데

|를 쓰면 조건문에서 앞에가 true여도 조건 부분을 다 실행함

 

ex) bool a = true;

a &= false;

a = false && a;

a |= false

 

연산자 우선순위(Microsoft Docs)

 

 

형변환(캐스트 : 형태에 맞춰서 본을 뜸)

 

암시적 형변환

- 담을 형태 변수 = 담길 변수;

- int를 float에 담는 것은 가능

- float -> double 괜찮음

 

명시적 형변환

- 담을 형태 변수 = (담을 형태와 같은 바꿀 형태)담길 변수;

- float을 int에 담는 것은 불가능(상실되는 부분이 많으면 명시적)

- double(8바이트) -> float(4바이트)은 X

- 명시적으로 = 뒤 부분에 (double)을 붙여줘야 됨

 

ex)

float a = 5f;

a = 5; // 암시적 형변환

float b = (int)a; // 명시적 형변환

 

 

예약어

사용자 변수를 선언할 때 예약어는 쓰면 안 됨

C#예약어(Microsoft Docs)

 

 

코드 표기법

 

파스칼 케이스 : 단어들이 연속적으로 이어질 때 각 단어의 첫글자를 대문자로 사용

ex) PlayerName

카멜 케이스 : 단어들이 연속적으로 이어질 때 첫 단어를 제외하고 각 단어의 첫글자를 대문자로 사용

ex) playerName

- 회사마다 가이드가 다름(미리 숙지를 하면 좋음)

 

 

코딩 컨벤션(표준)

- 변수이름 설정하는 게 중요

 

 

조건문

if (조건)

{

    // 실행문

}

 

 

반복문

for문

for (var i = 0; i < 5; i++)

{
Debug.Log(i);

}

 

 

Coroutine

yield return

 

 

7.4 (월) 수업 내용 요약

변수 => 데이터형을 메모리에 올리는 것

배열 – int[] student = new int[5];

배열만으로 다루기 어려움

=> 클래스를 만들자

클래스 : 어떤 문제를 해결하기 위한 데이터를 만들기 위해 추상화(쓰고자 하는 내용)

ex) 챔피언의 hp mp status를 필요한 것만 빼놓은 것

캐릭터 A(name)가 공격(Attack)을 하면 메소드 {데미지(명사) 10이 박힌다(동사)} .

이런 개념이 합쳐지면 클래스가 됨

 
 

 

메서드

형식

public void Move () {}

접근한정자 반환형(리턴형) 메서드이름 매개변수 {뭐할지}

 

ex)

public int Add(int a, int b)

{

        return a + b;

}

int a = 1;

int b = 3;

int result = a + b;

debug.Log(result);

int newResult = Add(1, 3);

debug.Log(newResult);

 

- 반환형 void – 반환 값이 없음 (return 안 써줘도 됨)

- 반환형 나머지(int, string , , ,) 맞는 형이 return에 나와야 됨

- 매개변수 – 받을 땐 형식 명시 줄 땐 그냥 형식에 맞춰서 보내면 됨

- 함수를 래핑도 가능 Debug.Log(string str);를 시간을 찍히게 MyLog(string str);로 만들 수 있음

 

 

클래스

Champion champ = new Champion();

클래스 변수명 = new한정자 클래스 생성자 ;

 

ex)

class Champion

{

        int hp;

        int mp;

        int Add(int a, int b)

        {

                return a + b;

        }

}

 

ex) int[] arr = new int[]4;

- new는 클래스 new 한정자이다. 자료형과는 다르게 참조형 자료형이다.

- int, string 등등은 값형 자료형

 

 

 

오버로딩

함수 이름은 같은데 매개변수의 타입이 다름

- 오버로딩으로 구현하면 같은 함수를 작성했을 때(Add를 썼을 때 위아래 방향키를 눌러서 다른 함수를 선택할 수 있음 => 개발 편의성)

 

ex)

public float Add(float a, float b)

{

return a + b;

}

// 매개변수가 같고 반환 값이 다른 경우는 에러 뜸

 

 

한정자

ex)

class System

{

        int hp;

        int lv;

        public int GetHp()

        {

               return hp;

        }

        private void SetLvUp()

        {

               lv++;

        }

}

 

class Program

{

        static void Main(string[] args)

        {

                System system = new System();

                int hp_ = system.GetHp();

                // system.SetLvUp(); 에러 – private라서

        }

}

 

유니티에서 inspector에서 접근하고 싶을 때 코드 상으로는 private여야 한다면 무지성 public말고

[SerializedField]

private int hp = 0; 이런 식으로 선언해야 한다.

 

 

생성자

 

ex)

Champion champ = new Champion();

champ.hp = 500;

Champion champ = new Champion();

champ.hp = 600;

Champion champ = new Champion();

champ.hp = 700; 일 때 항상 넣기 힘드니까

 

class Champion

{

        int _hp;

        int _mp;

 

        // 외부에서 접근하니까 public
        public Champion(int hp, int mp)

        {

               _hp = hp;

               _mp = mp;

        }

}

 

 

소멸자

ex)

~Champion()

{

        Debug.Log(“Free”);

        _hp = 0;

        _mp = 0;

}

 

 

프로퍼티(property)

ex)

public int HP

{

        // champ.HP => get을 호출

        // 무조건 return을 써줘야됨

        get

        {

               return _hp;

        }

        // champ.HP = -100; => set을 호출

        // 무조건 value라는 값을 써줘야됨

        set

        {

                if (value < 0)

                {

                        value = 0;

                }

                _hp = value;

        }

}

 

// 다른 클래스에서

int A = champ.HP; => get

champ.HP = -100; => set

 

// HP 값 자체에는 접근 가능, 받아올 수 있음

public int HP { get; set; }

// 가져오는 건 가능한데 넣는 건 작업을 막을 수 있음

public int MP { get; private set; }

 

 

값 자료형, 참조 자료형, 클래스 자료형

 

값 자료형

ex)

int a = 20;

TestValue(a);

Debug.Log(a); // 출력 값 20

 

void TestValue(int a)

{

        a = 10;

}

 

 

참조 자료형

ex)

int[] a = new int[1]{ 20 };

TestValue(a);

Debug.Log(a[0]); // 10 출력

 

void TestValue(int[] a)

{
        a[0] = 10;

}

 

 

클래스 자료형

ex)

class TestValueClass

{

        public int value = 0;

}

 

void TestValue(TestValueClass t)

{

        t.value = 10;

}

 

// 다른 클래스에서

TestValueClass tvClass1 = new TestValueClass();

tvClass1.value = 20;

TestValue(tvClass1);

Debug.Log(tvClass1.value);

 

string a = “Test”;

Debug.Log(a);

TestValue(a);

Debug.Log(a);

 

TestValue(string a)

{

        a = “Test2”;

}

 

 

상속 

부모로부터 자식이 속성이나 특성을 물려받음

 

// 챔피언 아칼리 아리 아무무

// 속력 2 3 4

// Move(); SkillQ,W,E,R 챔피언마다 조건이 다름

public class Champion

{

        public int _lv = 1;

        public int _hp = 650;

        public int _mp = 0;

 

        public void Move()

        {

                Debug.Log(“Move”);

        }

 

        public virtual void SkillQ()

        {

                Debug.Log(“SkillQ”);

        }

 

        public virtual void SkillW()

        {

               Debug.Log(“SkillW”);

        }

 

        public virtual void SkillE()

        {

                Debug.Log(“SkillE”);

        }

 

        public virtual void SkillR()

        {

                Debug.Log(“SkillR”);

        }

}

// 다른 클래스

public void new Move()

{
        Debug.Log(“Move”);

}

 

 

가상 함수

virtual – 부모 객체의 함수를 자식들을 통해서 변화를 줄 수 있음

 

 

한정자

base 한정자 - 부모

new 한정자 – 덮어씀

 

 

접근 한정자

public – 외부 접근 가능

protected – 자식(파생 클래스)에서 접근 가능

private -  같은 클래스 내에서 접근 가능

 

 

 

외의 개념들

 

모듈화 : 기능적인 기본 사양을 공유하는 그룹핑을 지은 unit

 

상속 : 이미 부모에서 구현된 거에서 갖다 쓰는 것

 

라이브러리 : using을 통하여 갖다 씀

 

[Serializable] - 클래스 위에 쓰여서 인스펙터에 정보가 뜨게 함

 

 

이번 주 과제들

 

배열 안의 회색 값 찾기
롤 챔피언 과제(Json의 텍스트 파일의 정보를 가져오기)
롤 챔피언 과제(로그 띄우기)

 

 

한 주 후기

 드디어 개발교육 첫 주차가 끝났다. 이번 주 수업은 C#과 관련된 수업으로 진행하였고 부족했던 부분을 다시 공부할 수 있는 시간이었다. 강사님이 수업이 끝나고 유니티를 같이 다뤄보면서 진행할 수 있는 과제를 내주셨는데 배열부분에서 코드를 이해하는데 조금 걸렸지만 끝까지 해낼 수 있었다.

 개발 교육을 들으면서 질문이 여럿 생겼는데 enum의 선언 부분이나 접근 한정자 부분에서 이해하는데 꽤나 걸렸던 것 같다. 클래스 안에서 선언 되었는지와 클래스 밖에서 선언 되었는지에 따라 enum변수에 접근할 수 있는지가 달라지는 것을 배웠다. enum 선언에서 클래스 밖에서 선언될 때(namespace레벨)는 private와 protected는 쓰이지 않고(class도 internal과 public만 쓰임) 기본적으로 아무것도 안 쓰면 internal이라 같은 어셈블리 안이면 어디든 사용 가능하고 클래스 안에서 선언될 때는 선언하려는 변수의 한정자보다 범위가 같거나 넓은 한정자로 enum을 선언해야한다는 것을 배웠다.

 원래는 이런 것들이 왜 이렇게 되는지에 대해서 의문을 가지지 않고 그냥 되는 길을 찾아서 생각없이 진행했다면 이번처럼 질문을 통해서 이해를 하는 방향으로 가야지 기억에 더 잘 남는다는 것을 알게 되었다. 특히나 이번엔 친절하게 설명해주시는 강사님이 계셔서 조금이라도 이해가 가지 않으면 물어보고 개념을 확실히 잡고 가야겠다고 생각했다.

 아직 내가 직접 뭔가를 만들기엔 조금 부족한 것 같다. 그리고 정답지를 안 보고도 끝까지 만들어보는 습관을 들어야 겠다고 생각했다. 이러한 연습을 통해 스스로 생각한 것을 구현할 수 있을 것이라고 생각한다.

 이번 주 마지막 과제에서 살을 붙이는 작업이 이후의 과제들이라는데 내가 한 과제들이 이어진다는게 재밌을 것 같다. 다음 주도 열심히 해야겠다. 파이팅!!

 

유데미코리아 바로가기

본 포스팅은 유데미-웅진씽크빅 취업 부트캠프 유니티 1기 과정 후기로 작성되었습니다.

기획 : 시스템 기획부터 펑션트리까지

 

* 가상현실 교육콘텐츠 인터페이스의 어포던스 유형

 

인터페이스 – input, output

인지적 : 사용자에게 일어날 일을 인지할 수 있게 하는 디자인

기능적 : 사용자에 의해 통제/제어에 따른 반응을 행동을 유인하는 디자인

물리적 : 신체적 행동할 때 사용자를 돕는 디자인 ex) 당기시오

감각적 : 사용자가 직감적으로 느낄 수 있는 디자인 ex) 예뻐지기 위한 노력

(학습적) : 현실 적용, 학습자가 얼마나 수용 했는지 - 자신감 갖고 해(틀려도 괜찮아)

* 황효현 교수님의 콘텐츠 기획의 기초 내용 중

 

시스템 기획 vs 콘텐츠 기획

시스템 기획 - 게임 내적 구성요소(재미)

ex) 시나리오, 설정, 레벨(맵), 캐릭터

 

게임 콘텐츠 - 기초 구조 및 동작원리

ex) 캐릭터가 지능형, 체력형, 아이템형으로 나뉨

 

 

플로우 차트 그려보기

플로우 차트 : 서비스 개발 과정에서 이해관계자가 서로 이해하기 위해 서비스와 기능 위주로 작성하는 구조도

 

스토리라인

** 우연히 인간의 땅에 내려온 신이 숲을 산책하던 중, 잘 가꿔진 숲을 보고 이 숲을 관리한 짐승들에게 선물을 주기로 했습니다.

신은 숲에 사는 모든 동물에게 이 소식을 전하고, 해가 지기 전에 가장 먼저 신이 있는 곳을 찾아오는 동물들에게 포상을 하기로 했습니다.

숲에 숨어 있는 신을 찾아주세요.

** 황효현 교수님 강의 중 ppt 내용

 

시스템 기획

 

플랫폼형 게임으로 동물 캐릭터를 선택하여 제한시간 내에 장애물을 피하고 100m 골라인을 넘으면 신을 찾는 게임의 플로우 차트이다.

 

펑션트리

펑션트리 : 시스템 기능 간의 종속성을 보여주는 다이어그램

펑션트리

 

위는 이 주간 만든 역사교육 메타버스의 펑션트리의 내용을 캡쳐한 사진이다.

 

피그마

피그마1

 

피그마2

위 두 사진은 역사교육 메타버스의 펑션트리의 내용을 기반으로 간단한 애니메이션으로 나타낸 것이다.

 

한 주 후기

 

 이번 주는 기획의 실습적인 부분 위주로 교육을 들었던 것 같다. 지난 주는 시장 조사와 경쟁자 조사 그리고 사용자 분석을 통하여 우리가 제공할 서비스의 틀을 만들었다면 이번 주는 그것들을 구체화하여 세세하게 글로 작성(펑션트리)하고 프로토타입으로 보여주는 작업(피그마로 제작)까지 했다.

 우리조는 지난 주부터 마지막 결과물까지 오면서 기획 방향을 총 4번정도 바꿨던 것 같다. 처음 아이디어는 재난 상황에 대피하는 안전교육이었다가 한국사 교육으로 바뀌고 또 한국사 교육 내에서도 스토리 모드로 표현하여 시뮬레이션 형식으로 하다가 퍼즐 게임을 메인으로 역사 교육을 하는 것으로 바꾸고 또 스토리 모드와 퍼즐을 합쳤다가 마지막엔 각 챕터별로 퀘스트를 진행하고 그 퀘스트 안에 미니게임을 만들어서 각 챕터 진행 후에는 역사적 사실을 알려주는 방식으로 바뀌었다. 글만 봐도 어지럽고 무슨 말인지 이해가 어려운 만큼 다사다난 했던 것 같다. 조원 간의 의견 대립이 있었는데 그것을 조율하고 또 다시 아이디어를 내는 과정이 꽤나 재밌었다. 결과물이 어느정도 만들어지는 게 보여서 끝까지 마무리 할 수 있었다. 이번에 의견 대립이 있을 때 그냥 말로 표현하는 식으로 내 주장을 펼쳤는데 다음 번엔 각 입장의 예시를 찾아서 비교하여 우리 과제에서 어떤 식으로 표현하는 게 좋은 지 상의하는 식으로 해봐야겠다.

 이번 주로 기획 수업이 마무리되는데 나중에 같이 일하게 될 기획자의 입장을 조금이나마 이해할 수 있게 되지 않을까 하는 생각이 들었다. 기획을 교육해주신 담당 교수님과 보조 강사님이 보여주신 열정에 다들 으쌰으쌰 한 것 같다.

 다음 주에는  개발 교육을 들어간다. 설렘 반 두려움 반인 마음이지만 다음 주에는 꼭 인간 스펀지가 되어서 강사님의 지식들을 흡수하고 체화해야겠다. 다음 주도 파이팅!

 

 

 

 

유데미코리아 바로가기

본 포스팅은 유데미-웅진씽크빅 취업 부트캠프 유니티 1기 과정 후기로 작성되었습니다.

+ Recent posts