티스토리 뷰

SSU Open World

[SSU Open World] 1 주차

률무차 2021. 3. 9. 23:19

title: "1 주차"
category: SSU-Open-World
tags: [SSU, RPG, Soongsil-University, branch, directory, build, github-page]
date: "2021-03-08"


1 일차

Branch 구성

main develop feature
배포(Release) 버전 개발 버전 관리 디자인, 기능(?) 버전 관리
main branch에서 분기 develop branch에서 분기

Directory 구성

  • doc
    • 개발 기록(.md)
    • uploads
      • 이미지(.png, .jpg, .gif)
  • dev
    • unity project
  • des
    • blender
    • substance painter
  • release
    • build files

2 일차

Branch 생성

  • develop branch 생성
  • # git branch develop + git swtich develop git switch -c develop
  • feature branch 는 Design 이 들어갈 때 생성할 예정

Unity Project 생성 및 빌드 테스트

  • develop branch에서 Unity Project를 생성한다.
    • Project name: ssu-rpg
    • Template은 3D
  • Github page로 Client를 구동할 예정이므로 환경은 WebGL.
  • release 폴더에 Build.
  • Test.

Only. release directory in main branch

  • main branch에는 오로지 배포 파일만 존재하도록 git 명령어 구성

    • merge는 했지만 main branch에는 release directory만 존재
    # main branch로 checkout
    git switch main
    
    # merge develop
    # option: --no-commit(merge OK but, 커밋 기록 X), --no-ff(fast-forward 하지 않기)
    git merge develop --no-commit --no-ff
    
    # release directory를 제외한 Unstage로 전환
    # ssu-rpg directory를 Unstage로 전환
    git reset ssu-rpg
    
    # Untrack에 대해서 working directory file 지우기
    # option: -f(강제), -d(디렉토리 포함)
    git clean -fd
    
    # Modify에 대해서 되돌리기
    # .은 하위 디렉토리 모두 포함
    git restore .
  • main branch에는 release, doc directory만 존재하도록 함.

Github page로 build 파일 실행


3 일차: 2021-03-10

폴더 정리

  • ResourcesUnity의 Load API를 사용하기 위해서는 Resources 이름의 폴더 하위로 접근 가능하므로 Art, Prefab 등은 해당 폴더 하위로 보관한다.
  • Scripts
    • ControllersPlayer, Monster, NPC 등의 Controller
    • Managers각종 Managers: Input, Data, UI, Scene 등을 모아둘 예정.
    • UtilsEnum 값, Extension, Util(= 사용하면 유용할 함수) 등을 모아둘 예정.
  • Test각종 Test Script 등 모아둘 예정.
  • ...계속 추가될 예정.
    • ...계속 추가될 예정.

Manager 생성

  • Manager들은 모두 Singleton pattern 이용

Manager

  • Manager만 MonoBehaviour 상속 받음
  • InputManager 등 Input에 관한 Call-back 실행을 Manager의 Update()에서 실행
  • @Manager라는 이름의 빈 GameObject로 Scene이 이동해도 삭제 못하도록 DontDestroyOnLoad() 이용
  • 여러 Manager를 호출 가능하도록
  • Manager.Input: InputManager 호출
public class Manager : MonoBehaviour
{
    // Singleton
    static Manager _instance;
    static Manager Instance { get { if (_instance == null) Init(); return _instance; } }

    #region core
    InputManager _input = new InputManager();
    public static InputManager Input { get { return Instance._input; } }
    #endregion

    void Update()
    {
        // input의 Call-back을 실행시켜줌.
        _input.OnUpdate();
    }

    static void Init()
    {
        GameObject go = GameObject.Find("@Manager");
        if (go == null)
        {
            go = new GameObject() { name = "@Manager" };
            _instance = go.AddComponent<Manager>();
        }
        // Scene이 이동해도 삭제 [X]
        DontDestroyOnLoad(go);
    }
}

InputManager

  • 모든 Input을 관리할 예정.Mouse, KeyInput, Touch 등.
  • MonoBehaviour를 상속받지 않고 Manager에서 Update()에서 실행하도록 함.
public class InputManager
{
    public Action<Define.MouseEvent> MouseAction = null;
    float _pressedTime = 0f;  // 잠깐의 짧은 클릭인 지 시간 체크

    public void OnUpdate()
    {
        // Manager에서 Update 실행해줌.
        if (MouseAction != null)
            OnMouse();
    }

    public void Clear()
    {
        MouseAction = null;
    }

    #region Event
    void OnMouse()
    {
        if (Input.GetMouseButtonDown(0))
        {
            // LMB: 0
            MouseAction.Invoke(Define.MouseEvent.Down);

            // 시간 측정
            _pressedTime = Time.time;
        }
        else if (Input.GetMouseButton(0))
        {
            // LMB 누르고 있을 때
            MouseAction.Invoke(Define.MouseEvent.Press);
        }
        else if (Input.GetMouseButtonUp(0))
        {
            // LMB 떼었을 때
            if (Time.time - _pressedTime < Define.MousePressedTime)
                MouseAction.Invoke(Define.MouseEvent.Click);
            else
                MouseAction.Invoke(Define.MouseEvent.Up);
        }
    }
    #endregion
}

Player 움직이기

  • BaseConroller를 작성해 움직이는 모든 WorldObject에 대해 기본 클래스를 작성
  • Monster, Player 등 모든 움직이는 WorldObject는 BaseController Class를 상속받는다.

BaseController.cs

  • 직접 Instance를 상속받을 수 없도록 abstract class
  • 상속 받는 Controller.cs 는 OnStart(), OnUpdate()를 갖도록BaseController에서 MonoBehaviour를 상속 받기 때문에 모든 Controller는 Start(), Update()를 작성할 필요 [X]
    Start()는 Unity의 Single-Thread의 실행 순서를 알 수 없으니 이를 이용.
public abstract class BaseController : MonoBehaviour
{
    [SerializeField] protected Define.State _state;
    [SerializeField] public Define.WorldObject WorldObjectType { get; protected set; }

    void Start()
    {
        OnStart();
    }

    void Update()
    {
        OnUpdate();
    }

    protected abstract void OnStart();
    protected abstract void OnUpdate();
}

PlayerController.cs

public class PlayerController : BaseController
{
    Vector3 _destPos;
    float _walkSpeed = 5f;
    float _angularSpeed = 10f;

    // Property를 이용해 State를 조작하는 순간 Animation 상태도 바뀐다.
    public Define.State State
    {
        get { return _state; }
        set
        {
            _state = value;
            Animator anim = GetComponent<Animator>();
            switch (_state)
            {
                case Define.State.Idle:
                    // anim.CrossFade("Idle", 0.1f);
                    break;
                case Define.State.Walking:
                    // anim.CrossFade("Walk", 0.1f);
                    break;
                case Define.State.Running:
                    // anim.CrossFade("Run", 0.1f);
                    break;
            }
        }
    }

    protected override void OnStart()
    {
        // Settings
        _state = Define.State.Idle;
        WorldObjectType = Define.WorldObject.Player;

        // Listener
        Manager.Input.MouseAction -= OnMouseEvent;  // Pooling으로 인해 두 번 등록 방지
        Manager.Input.MouseAction += OnMouseEvent;
    }

    protected override void OnUpdate()
    {
        // Update()
        switch (_state)
        {
            case Define.State.Die:
                UpdateDie();
                break;
            case Define.State.Idle:
                UpdateIdle();
                break;
            case Define.State.Walking:
                UpdateWalk();
                break;
            case Define.State.Running:
                UpdateRun();
                break;
        }
    }

    #region UpdateState
    void UpdateDie() { }
    void UpdateIdle() { }
    void UpdateWalk()
    {
        Vector3 dir = _destPos - transform.position;
        if (dir.magnitude < 0.1f)
        {
            // 오차 범위 안쪽이면 도착
            State = Define.State.Idle;
        }
        else
        {
            Debug.Log("이동중");
            // 범위를 Clamp() 로 최대, 최소를 집어줌.
            float moveDist = Mathf.Clamp(_walkSpeed * Time.deltaTime, 0, dir.magnitude);

            // Position 이동
            transform.position += dir.normalized * moveDist;

            // Slerp 이용한 부드러운 방향 전환
            transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.LookRotation(dir), _angularSpeed * Time.deltaTime);
        }
    }
    void UpdateRun() { }
    #endregion

    #region OnEvent
    void OnMouseEvent(Define.MouseEvent mouseEvent)
    {
        if (_state == Define.State.Die)
            return;

        // Ray
        Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
        RaycastHit hitInfo;
        bool isHit = Physics.Raycast(ray, out hitInfo, 100f, LayerMask.GetMask("Ground"));

        // Debug
        Debug.DrawRay(Camera.main.transform.position, Input.mousePosition - Camera.main.transform.position * 100f, Color.green, 1f);

        switch (mouseEvent)
        {
            case Define.MouseEvent.Down:
                break;
            case Define.MouseEvent.Press:
                break;
            case Define.MouseEvent.Up:
                break;
            case Define.MouseEvent.Click:
                if (isHit)
                {
                    Debug.Log($"이동! {hitInfo.point}");
                    _destPos = hitInfo.point;
                    State = Define.State.Walking;
                }
                break;
        }
    }
    #endregion
}

Define.cs

  • 여러 enum, const 상수를 정해줌.C##define 상수가 불가능
public class Define
{
    #region MouserEvent
    public const float MousePressedTime = 0.5f;
    public enum MouseEvent
    {
        Down,
        Press,
        Up,
        Click,
    }
    #endregion

    #region State
    public enum State
    {
        Die,
        Idle,
        Walking,
        Running,
    }
    #endregion

    #region WorldObjectType
    public enum WorldObject
    {
        Unknown,
        Player,
        Monster,
    }
    #endregion
}

Build Test: 3 일차

  • 모바일도 Mouse Click과 같은 Event 받는 것을 확인
  • 모바일 Touch도 잘 된다는 뜻.
PC Mobile
3-day build test mobile-test

4 일차: 2021-03-11

모바일 미지원 알림 제거

  • Assets/EditorPostBuildAction.cs 작성
    • Bulid할 때, Call-back 으로 작용
using System.IO;
using UnityEditor;
using UnityEditor.Callbacks;

public class PostBuildAction
{
    [PostProcessBuild]
    public static void OnPostProcessBuild(BuildTarget target, string targetPath)
    {
        var path = Path.Combine(targetPath, "Build/UnityLoader.js");
        var text = File.ReadAllText(path);
        text = text.Replace("UnityLoader.SystemInfo.mobile", "false");
        File.WriteAllText(path, text);
    }
}

Scene Manage

  • SceneManagerEx.cs

    • Scene Manager는 이미 Unity Engine에 있으므로 Scene Manager Extended 작성
    • C# Reflection 이용한 Enum type name 추출
    public class SceneManagerEx
    {
        public BaseScene CurrentScene { get { return GameObject.FindObjectOfType<BaseScene>(); } }
        public void LoadScene(Define.Scene type)
        {
            // 다른 Scene으로 움직이면 모든 Manager에 대해 Clear()
            Manager.Clear();
    
            string sceneName = GetSceneName(type);
            SceneManager.LoadScene(sceneName);
        }
    
        public void Clear()
        {
            CurrentScene.Clear();
        }
    
        string GetSceneName(Define.Scene type)
        {
            // Reflaction
            return System.Enum.GetName(typeof(Define.Scene), type);
        }
    }
  • BaseScene.cs

    • 추상 클래스로 모든 Scene Script의 Base.
    public abstract class BaseScene : MonoBehaviour
    {
        public Define.Scene SceneType { get; protected set; } = Define.Scene.UnKnown;
    
        void Awake()
        {
            OnAwake();
        }
    
        protected abstract void OnAwake();
        // Scene Manager에서 Load할 때 Clear()
        public abstract void Clear();
    }

Camera Control

  • CameraController.cs

    • LateUpdate()Player가 움직인 다음 Update() 되도록.
      Unity 생명 주기 참고.
    • Player의 null 값 비교Destroy, SetActive false에 대해서 비교 가능하도록
    public class CameraController : MonoBehaviour
    {
        // Player와 떨어진 거리 차
        [SerializeField] Vector3 _delta = new Vector3(0, 5f, -10f);
        [SerializeField] GameObject _player = null;
    
        public GameObject Player { get { return _player; } set { _player = value; } }
    
        void LateUpdate()
        {
            // Unity Engine의 "null" 문자열 비교 이용
            // Set Active: false에 대해서도 비교
            if (!_player.IsValid())
                return;
    
            // Camera의 Position은 _delta 만큼 차이나도록 매번 LateUpdate()
            transform.position = _player.transform.position + _delta;
            // Player를 항상 바라보도록
            transform.LookAt(_player.transform);
        }
    }
  • 좀 더 수정 필요

    • PC 경우Wheel 로 확대 및 축소
    • Mobile 경우두 손 Touch로 확대 및 축소

5 일차: 2021-03-12

Camera Zoom, Rotate: not yet

  • 마우스 왼쪽 버튼으로 잡아 당기면 구 형태로 회전
void Rotate()
{
    // 구현 아직...
}
  • 마우스 휠로 Zoom in, Zoom out
void Zoom()
{
    if (Input.mouseScrollDelta.y > 0)
    {
        // 축소
        Debug.Log("축소");
        // 구현 아직...
    }
    else if (Input.mouseScrollDelta.y < 0)
    {
        // 확대
        Debug.Log("확대");
        // 구현 아직...
    }
}

6 일차: 2021-03-13

Mobile, PC 기기 구분

  • PC vs Mobile
    • PC에서는 Mouse를 이용
    • Mobile에서는 Touch를 이용
  • Click과 Touch의 인터페이스가 다르기 때문에 MobilePC구분은 필수!
  • Assets/Plugins/MobileOrPc.jslib
var MobileOrPc = {
  isMobile: function () {
    return UnityLoader.SystemInfo.mobile;
  },
};

mergeInto(LibraryManager.library, MobileOrPc);
  • Util.cs
    • DLL 사용Unity를 Loading할 때, Mobile 확인
    • Property로 접근
#if !UNITY_EDITOR && UNITY_WEBGL
    [DllImport("__Internal")] private static extern bool isMobile();
    public static bool IsMobile { get; } = isMobile();
#else
    public static bool IsMobile { get; } = false;
#endif

7 일차: 2021-03-14

Camera Zoom, Rotate 계획

PC Mobile
Zoom Mouse Scroll Wheel 이용 두 손가락 터치 간격으로 조절
Rotate Left Mouse Button을 길게 누른 것을 이용 한 손 터치 길게 누른 것을 이용

728x90
반응형
댓글
05-03 11:03
링크