cien-unity-2020

CIEN - 2020 Unity3D 온라인 스터디

2020 CIEN 중급반 Unity3D 스터디


대상

프로그래밍을 처음하는 분들은 힘들 수도 있습니다

목표

스터디 방식

일주일에 한번(시험기간 제외) / 총 7번 수업

준비해야 할 것

“Visual Studio Community”와 “Unity 2018.. LTS”가 설치된 노트북

설치법: PPT PDF


일정 PDF PPT

1주차. 유니티 시작하기

1주차 스터디 자료: PPT PDF

숙제: 책상 만들기


2주차. 유니티 스크립트

2주차 스터디 자료: PPT PDF

숙제: Flappy Bird의 새의 점프 구현하기

Flappy Bird Jump 예시: CustomJump.cs

결과: https://iamgroooooot.github.io/cien-unity-2020/jump-using-position/

using UnityEngine;

public class CustomJump : MonoBehaviour
{
    public float jumpPower = 5f; // 점프 Force

    private const float GRAVITY = -9.81f; // 중력 가속도

    private Vector3 vel; // 현재 속도
    private Vector3 acc; // 현재 가속도

    private void Start()
    {
        // 속도 초기화
        vel = Vector3.zero;
        // 중력 설정
        acc = new Vector3(0, GRAVITY, 0);
    }

    void Update()
    {
        // 스페이스바 누르면 점프!
        if (Input.GetKeyDown(KeyCode.Space))
        {
            vel.y = jumpPower;
        }
        
        // 가속도 적용
        vel += acc * Time.deltaTime;

        // 땅으로 떨어지거나 하늘로 날아가지 않습니다
        if(transform.position.y < -0.5f)
        {
            if (vel.y < 0) vel = Vector3.zero;
        }
        else if(transform.position.y > 5)
        {
            if (vel.y > 0) vel = Vector3.zero;
        }

        // 속도만큼 이동시킨다
        transform.Translate(vel * Time.deltaTime);
    }
}

3주차. 물리엔진

3주차 스터디 자료: PPT PDF

숙제: 오늘 배운 Collider/Trigger/Collision 사용한 아무런 유니티 프로젝트 만들기

제출: 위의 유니티 프로젝트를 GitHub에 올린 후 링크 여기 톡방에 투척!

제출 방법
  1. GitHub에서 새로운 저장소(Repository)를 만든다.
    • 저장소의 이름은 자유롭게 설정
    • Description해도 되고 안해도 됨 (간략한 저장소 설명)
    • Public으로 설정
    • Initialize with README는 해도 되고 안해도 됨 (README는 프로젝트를 설명하는 파일임)
    • .gitignore를 unity로 설정
    • Add License해도 되고 안해도 됨 (내 저장소 저작권 설정하는 부분, MIT가 책임질 필요도 없고 무난함)
  2. 내 컴퓨터로 Clone한다
  3. 작업한 유니티 파일을 전부 Clone한 저장소로 옮긴다
  4. Stage에 모두 올린다
  5. 적절한 메시지와 함께 Commit하기
  6. Push한다!
  7. GitHub에 들오가서 잘올라갔는지 확인한다
  8. 링크 톡방에 투척한다

4주차. 프리팹과 코루틴

4주차 스터디 자료: PPT PDF

숙제. https://github.com/CIEN-Club/workshop-guestbook의 README.md를 읽고 저장소에 Push를 해서 방명록 남기기

강의 때 설명한 코드들

강의 때 설명용으로 보여드린 코드들입니다. 강의자료에는 없기 때문에 따로 올립니다. 복습용으로 사용하세용!

(1) 좌클릭할 때 Instantiate하기

using UnityEngine;

public class SpawnManager : MonoBehaviour 
{
    public GameObject missilePrefab;
    GameObject temp;
    int count = 0;

    void Update()
    {
        // "Fire1"은 좌클릭 말고도 다른 키(터치, 조이스특, ...)도 인식함.
        if (Input.GetButtonDown("Fire1"))
        {
            temp = Instantiate(missilePrefab);
            temp.name = "Missile" + count++;
            temp.transform.position = Vector3.zero;
        }
    }
}


(2) Find By


(3) LambdaFunc/Action

// 값을 반환하는 람다식 대리자 형식으로 변환해서 호출해보기
Func<int, int, int> addition = (x, y) => x + y;
Debug.Log(addition(1, 2));

Func<int, int, bool> isSame = (x, y) => x == y;
Debug.Log(isSame(1, 2));
// 값을 반환하지 않는 람다식 대리자 형식으로 변환해서 호출해보기
Action helloWorld = () => Debug.Log("Hello, World!");
helloWorld();

Action<string> helloName = name => Debug.Log("Hello, " + name);
helloName("Groot");
public int hp;
void Start()
{
    hp = 100;
    StartCoroutine(DeadOrAlive());
}

IEnumerator DeadOrAlive()
{
    Debug.Log("Waiting for player to be dead");
    yield return new WaitUntil(() => hp <= 0);
    Debug.Log("Player is dead");
}


(4) IEnumerator 열거자란? Current, MoveNext()

IEnumerator GiveMeNumber()
{
    yield return 1;
    yield return 2;
    yield return 3;
}

void Start()
{
    IEnumerator enumerator = GiveMeNumber();
    for(int i = 0; i<4; i++)
    {
        Debug.Log(i + "번쩨의 Current: " + enumerator.Current);
        Debug.Log(i + "번쩨의 MoveNext의 반환값: " + enumerator.MoveNext());
    }
}


(5) 일반적인 함수 vs 코루틴

using UnityEngine;

public class PrintTest : MonoBehaviour 
{
    void Print1()
    {
        Debug.Log("1-1");
        Debug.Log("1-2");
        Debug.Log("1-3");
    }

    void Print2()
    {
        Debug.Log("2-1");
        Debug.Log("2-2");
        Debug.Log("2-3");
    }
    
    private void Start()
    {
        Print1();
        Print2();
    }
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PrintTest : MonoBehaviour 
{
    IEnumerator Print3()
    {
        Debug.Log("1-1");
        yield return null;
        Debug.Log("1-2");
        yield return null;
        Debug.Log("1-3");
        yield return null;
    }

    IEnumerator Print4()
    {
        Debug.Log("2-1");
        yield return null;
        Debug.Log("2-2");
        yield return null;
        Debug.Log("2-3");
        yield return null;
    }

    private void OnEnable()
    {
        StartCoroutine(Print3());
        StartCoroutine(Print4());
    }
}


(6) 업데이트 타이머 vs 코루틴 타이머

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

public class TimerTest : MonoBehaviour 
{
    float time = 0;
    float limitTime = 3f;
    
    private void Update() 
    {
        time += Time.deltaTime;
        if(time >= limitTime)
        {
            Debug.Log(limitTime + " seconds passed.");
            time = 0;
        }

        // 업데이트에서 호출할 다른 함수들
    }
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class TimerTest : MonoBehaviour 
{
    private void OnEnable()
    {
        StartCoroutine(TimerEvent(3));
    }

    IEnumerator TimerEvent(float limitTime)
    {
        while (true)
        {
            yield return new WaitForSeconds(limitTime);
            Debug.Log(limitTime + " seconds passed.");
        }
    }
}

5주차. 적기 구현 및 적 매니저

🌜공지🌛

다음주(5/26) - 5주차 스터디 없습니다

제가 다음주부터 부담되는 과제+팀플+퀴즈가 하나씩 있어서 강의가 불가능합니다. 강의자료 없이 그냥 할까도 생각했는데 강의할 시간도 없을 것 같습니다ㅠㅠㅠ.

원래는 저번 주에 배운 코루틴과 Overriding같은 객체지향적인 코드에 익숙해지며 간단한 AI를 여러개 만들어보려고 했습니다. AI는 FSM(유한상태기계)으로 같이 설계해보려고 했으나 못할 것 같습니당..ㅠㅠ 암튼 간단한 AI를 제작할 때 FSM이 찰떡? 유용합니다. 나중에 간단하게 적 AI를 구현하려고 하는데 막힐 때가 있을 수 있습니다. 그때 FSM을 유튜브같은 것으로 공부하시면 도움될 겁니당. 일단 예시 코드를 아주 간단하게 올려드리겠습니다. 처음 보시면 이해하기 어려울 수도 있습니다.

죄송합니다.

바로 앞에서 멈추는 유도탄 만들기


6주차. 유니티의 UI

6주차 스터디 자료: PPT PDF

강의 때 설명한 코드들

강의 때 설명용으로 보여드린 코드들입니다.

using UnityEngine;
using UnityEngine.UI;

public class TextCtrl : MonoBehaviour
{
    private Text targetText;
    public int score = 99;
    // Start is called before the first frame update
    void Start()
    {
        targetText = GameObject.Find("MyTargetText").GetComponent<Text>();

        if (targetText != null)
            targetText.text = "점수: " + score;
        else
            Debug.Log("GameObject 체크 ㄱㄱ");
    }
}


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

public class OnClickFunctions : MonoBehaviour {
    int score = 0;
    UnityEngine.UI.Text textInstance = null;
    
    private void Start()
    {
        textInstance = GameObject.Find("MyTargetText").GetComponent<UnityEngine.UI.Text>();
    }

    public void OnClickButtonTest()
    {
        Debug.Log("버튼을 눌렀습니다.");
    }

    public void OnClickCat()
    {
        Debug.Log("고양이을 눌렀습니다.");
        textInstance.text = "고양이 누른 횟수: " + ++score;
    }

    public void MoveToSampleScene()
    {
        SceneManager.LoadScene("Scenes/SampleScene");
    }
}


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

public class CoolTime : MonoBehaviour
{
    private Image coolTimeImage;
    // Start is called before the first frame update
    void Start()
    {
        coolTimeImage = GameObject.Find("CoolTime").GetComponent<Image>();
        StartCoroutine(ShowCoolTime(coolTimeImage, 4f));
    }

    IEnumerator ShowCoolTime(Image img, float coolTime)
    {
        float delta = 1f / coolTime;
        img.fillAmount = 1f;
        while (img.fillAmount >= 0)
        {
            yield return new WaitForSeconds(1);
            img.fillAmount -= delta;
        }
    }
}


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

public class OnCubeClick : MonoBehaviour {
    // Collider 필요!
    private void OnMouseDown()
    {
        Destroy(gameObject);
    }
}

7주차. 게임 데이터와 빌드하기

7주차 스터디 자료: PPT PDF

강의 때 설명한 코드들

강의 때 설명용으로 보여드린 코드들입니다.

public enum VariableType
{
    eUserName = 0,
    eUserScore,
    // …
}


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

public class GameVariableManager : MonoBehaviour
{
    public static GameVariableManager instance = null;        // for singleton
    private void Awake()
    {
        instance = this;
    }

    /// <summary>
    /// 타입 이넘값으로 값을 저장해준다.
    /// string value 전용
    /// </summary>
    public void SaveVariable(VariableType type, string value)
    {
        // 이넘값 변환
        string key = type.ToString();
        // 변환한 값을 그대로 넣어주자.
        PlayerPrefs.SetString(key, value);
    }

    /// <summary>
    /// 타입 이넘값으로 값을 저장해준다.
    /// Int value 전용
    /// </summary>
    public void SaveVariable(VariableType type, int value)
    {
        // 이넘값 변환
        string key = type.ToString();
        // 변환한 값을 그대로 넣어주자.
        PlayerPrefs.SetInt(key, value);
    }

    /// <summary>
    /// 타입 이넘값으로 값을 저장해준다.
    /// Float value 전용
    /// </summary>
    public void SaveVariable(VariableType type, float value)
    {
        // 이넘값 변환
        string key = type.ToString();
        // 변환한 값을 그대로 넣어주자.
        PlayerPrefs.SetFloat(key, value);
    }

    /// <summary>
    /// 타입 이넘값을 바탕으로 저장된 값을 불러온다.
    /// </summary>
    public string LoadStringVariable(VariableType type)
    {
        // 이넘값 변환
        string key = type.ToString();
        // 불러오기
        string returnValue = PlayerPrefs.GetString(key);
        Debug.Log("LoadValue[" + key + "] = " + returnValue.ToString());
        return returnValue;
    }

    /// <summary>
    /// 타입 이넘값을 바탕으로 저장된 값을 불러온다.
    /// </summary>
    public int LoadIntVariable(VariableType type)
    {
        // 이넘값 변환
        string key = type.ToString();
        // 불러오기
        int returnValue = PlayerPrefs.GetInt(key);
        Debug.Log("LoadValue[" + key + "] = " + returnValue.ToString());
        return returnValue;
    }

    /// <summary>
    /// 타입 이넘값을 바탕으로 저장된 값을 불러온다.
    /// </summary>
    public float LoadFloatVariable(VariableType type)
    {
        // 이넘값 변환
        string key = type.ToString();
        // 불러오기
        float returnValue = PlayerPrefs.GetFloat(key);
        Debug.Log("LoadValue[" + key + "] = " + returnValue.ToString());
        return returnValue;
    }
}


public class Charactor
{
    int age;
    string name;
    string materialPath;
    Material material;

    // Instance를 string으로 변환
    public override string ToString()
    {
        // Format은 Json, CSV, ..., 또는 내가 정의
        return name+ ", " + age + ", " + materialPath;
    }

    // 저장한 string 데이터로 Instance 다시 불러오기
    public Charactor(string classInfo)
    {
        // ,를 기준으로 split
        this.name = "name부분";
        this.age = System.Convert.ToInt32("age부분");
        this.materialPath = "materialPath부분";
        // Resources 폴더 안의 materialPath에 있는 Material을 불러온다
        material = Resources.Load<Material>(materialPath);
    }
}

특강. Git을 사용한 파일 관리

Source Tree 설치법: PPT PDF

Git 특강 자료: PPT PDF

Source Tree로 버전 관리하기