결과

 

A* 알고리즘 구현을 해보기 위해 준비하다가 , 마우스 클릭으로 쉽게 장애물을 만들고 장애물 경로를 피해 찾아갔으면 좋겠다 싶어서 마우스 클릭으로 장애물을 만드는 기능을 구현했다.

 

RayCast를 활용하여 구현 하였고 해당 기능이 있으면 Play를 하여 내가 원하는 경로를 직접 만들어 줄 수 있는것이 장점이다.

 

 

코드는 생각나는데로 하드 코딩하여 썩 좋은 코드는 아니다.

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

public class GridMapGenerator : MonoBehaviour
{
    [SerializeField] private GameObject obstacle;
    [SerializeField] private GameObject selectAreaPrefab;

    public LayerMask Selectlayer;
    private GameObject selectArea;

    float delayClick = 0.1f;
    float curClickTimer;

    private void Awake()
    {
        selectArea = Instantiate(selectAreaPrefab);
    }

    private void Update()
    {
        curClickTimer += Time.deltaTime;

        Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);


        if (Physics.Raycast(ray,out RaycastHit hit,1000.0f))
        {
            Debug.DrawLine(ray.origin, hit.point, Color.red);
            
            selectArea.transform.position = new Vector3(Mathf.Round(hit.point.x), 0.1f, Mathf.Round(hit.point.z));

            if (Input.GetMouseButton(0) && curClickTimer > delayClick && LayerMatchCheck(hit.transform.gameObject.layer, Selectlayer))
            {
                curClickTimer = 0;

                GameObject obj = Instantiate(obstacle);
                Vector3 pos = new Vector3(selectArea.transform.position.x, 0, selectArea.transform.position.z);
                obj.transform.position = pos;
            }
        }

       
    }

    private bool LayerMatchCheck(int target , LayerMask layer)
    {
        if(layer == (layer.value | 1 << target ))
        {
            return true;
        }

        return false;
    }
}

https://01149.tistory.com/178

 

Unity :: 셰이더 그래프

셰이더 그래프란?HLSL(High Level Shader Language) 코드 작성 없이 노드 기반으로 셰이더를 만들수 있는 기능입니다. 셰이더 그래프 사용 방법?1. Project 부분에서 해당 경로를 따라가서 셰이더 그래프를

01149.tistory.com


 

1. 포트폴리오 제작 중 

출시 목적으로 스토리 + 카드형 턴제 전투 게임을 개발 중이며 , 현재 대화시스템을 구글 스프레드 시트와 연동하여

테스트 중이다.

해당 작업이 완료되는데로 TIL에 따로 작성할 목적

 

2.내일배움캠프에서 진행되는 바로인턴10기 지원

Unity 클라이언트 프로그래머의 취업문이 좁다보니 , 한달동안 인턴 생활을 하는 제도가 있어

지원서를 작성하여 신청하였다. 지원서에 붙으면 서류 및 과제를 이틀동안 작성하여 제출하여야한다.

작성되어 크니브 스튜디오에 인턴이 되었으면 좋겠다. 

UI가 아닌 오브젝트를 클릭해서 이동하는 것을 구현 하고 싶었다.

 

현재 드래그 이동이 아닌 클릭 후 카드를 움직이는 방식으로 구현하였다.

 

public class TestInputController : MonoBehaviour
{
    [SerializeField] private LayerMask cardLayerMask;
    private GameObject cardObject = null;
    private bool isCardSelect = false;

    private void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            //화면 좌표계에 있기에 월드 좌표로 변환
            Vector2 worldPos = Camera.main.ScreenToWorldPoint(Mouse.current.position.ReadValue());
            RaycastHit2D hit = Physics2D.Raycast(worldPos, Vector2.zero, 0f, cardLayerMask);

            if (!isCardSelect && hit.collider != null)
            {
                Debug.Log(hit.transform.name);
                cardObject = hit.collider.gameObject;
                isCardSelect = true;
            }
            else if (isCardSelect)
            {
                cardObject = null;
                isCardSelect = false;
            }
        }

        if (cardObject != null)
        {
            Vector3 pos = Camera.main.ScreenToWorldPoint(Mouse.current.position.ReadValue());
            pos.z = 0;
            cardObject.transform.position = pos;
        }
    }
}

현재 Update() 문으로 구현을 해서 최적화에 좋지 않을것이다. 

1.마우스 클릭 Class를 생성후, event를 등록시켜서 필요할때만 호출 시키는 형식으로 리팩토링이 필요하다. 

2.InputSystem을 활용하여 event로 연동 시킨다.

 

이 두가지 방법 중 하나를 택해서 진행하게 될것같다. 

 

Card Class 안에 IPointerHandler를 상속시켜 쉽게 진행 할 수도 있었겠지만, 

Card에는 카드가 게임에서 사용되는 효과,연출이 담겨 있어야 되고 

Card를 조작하는 것은 InputController에서 담당을 해야 SRP(단일 책임 원칙)을 지킬 수 있을것 같아

이렇게 구현 하였다. 

 

 if (Input.GetMouseButtonDown(0))
        {
            //화면 좌표계에 있기에 월드 좌표로 변환
            Vector2 worldPos = Camera.main.ScreenToWorldPoint(Mouse.current.position.ReadValue());
            RaycastHit2D hit = Physics2D.Raycast(worldPos, Vector2.zero, 0f, cardLayerMask);

이부분은 InputManager 와 InputSystem 둘다 사용 중이기에 하나를 택해서 바꿔서 진행해야 된다. 

 

InputSystem의 현재 마우스 좌표를 반환하는 메서드, 반환값은 Vector2
Layer를 Card로 선언하여 RayCastHit를 이용하여 체크후 이동시킨다.

 

https://github.com/lostwaltz/PossibleDefense

 

GitHub - lostwaltz/PossibleDefense

Contribute to lostwaltz/PossibleDefense development by creating an account on GitHub.

github.com

 

해당 프로젝트에서 발생한 트러블 슈팅 중의 하나인

<타워 이동시 기존에 있던 타일 데이터가 초기화 되지 않는 이슈> 가 있었는데 , 발표 자료에만 작성이 되어있어

블로그에 기록 하려고 한다. 

 

발표에서 사용했던 자료

문제원인

1.타워가 원하는 타일로 이동 후 기존 타일의 데이터를 따로 가지고 있지 않아


타일 데이터가 타워가 없는데도 있다고 판정을 함
2.해당 문제로 인하여 타워 소환 및 타워 판매시에 데이터가 꼬여버림
 

문제원인

1.BaseSlimeTower 에 프로퍼티로 설치 가능한 타워의 Index를 저장하고
필요할때마다 불러서 값을 수정해주기로 변경

다음 프로젝트시 적용

1. Tile 코드를 작성하면서 Inventory와 유사한 방식이였던것을 알아서


다음 프로젝트 진행 시 Tile처럼 데이터를 보관할 일이 생기면 event를
활용해야 된다고 생각함

 


타워의 데이터 대신에 Index(int) 자료값을 사용하여 , 메모리를 최적화 하였습니다. 

최종프로젝트가 종료되고 포트폴리오 정리를 하고 있는 도중에 내가 작업한 부분의 트러블 슈팅이 TIL에 기록되있지 않아 

지금이라도 기록을 하려고 한다. 

 

 

구현을 목적으로 작업하는 도중 매니저 클래스들이 많아지며 점점 호출 시점이 꼬이기 시작

해당 문제를 해결하기 위해 GameSceneTrigger 라는 클래스를 만들어 매니저들의 호출시점을 정리해주었습니다.

최종 프로젝트를 진행하면서 느낀 것이 하나 있다.

DB를 관리하면서 너무 불편했던 경험이 있다.

GitHub를 이용해서 데이터 관리를 하니 데이터가 변경 되었을때 해당 작업자가 상위 브런치에 Merge를 해야만

변경된 데이터를 확인할수가 있어서 너무 불편했었다. 

 

해당 프로젝트에서는 메인 기획자라고 할 사람이 없었지만 , 내가 회사에 취직했을때는 기획자가 데이터 값을 변동만하면 바로 적용 하게 해야할 필요를 느꼇다.

 

구글 스프레드 시트를 이용하여 온라인으로 외부 데이터를 읽어오는 방식이다.

현재 방법은 구글 스프레드 시트 API를 이용하지 않는 방법으로 간단한 프로젝트를 구성할때 좋은 방식이다. 

 

 

 

해당 구글 스프레드 시트에 데이터를 기입하자.

 

구글 스프레드시트에 데이터를 기입하고 [공유] 버튼을 눌러 링크를 활성화 하자.

 

공유에 있는 링크를 가져오자

 

이렇게 가져온 링크를 밑에 스크립트 코드에 붙여 넣는다. 

using System.Collections;
using UnityEngine;
using UnityEngine.Networking;

//복사해온 URL : URL주소

//1.복사해 온 주소에서 끝에 있는 "edit?usp=sharing" 삭제한다.
//2.삭제 한 주소에 다음을 추가한다 "export?format=tsv&range=A2:E";
//2-1. 설명 : 모드와 포맷 그리고 시트의 범위를 뜻함, =A2:E 를 엑셀 연산식에 적용시 범위를 알수 있음 

//사용할 URL :  URL주소 + export?format=tsv&range=A2:E


public class GoogleSheetDBLoader : MonoBehaviour 
{
    private string sheetData; // URL에서 불러온 데이터를 저장하는 변수
    private readonly string googleSheetURL = "사용할 URL";

    public string SheetData { get => sheetData; set => sheetData = value; }

    IEnumerator Start()
    {
        //UnityWebRequest 인스턴스 리소스 해제를 위한 using
        using(UnityWebRequest www = UnityWebRequest.Get(googleSheetURL))
        {
            yield return www.SendWebRequest();

            if(www.isDone)
            {
                sheetData = www.downloadHandler.text;
            }
        }

        DisplayText();
    }

    public void DisplayText()
    {
        //Split 함수로 데이터 처리하기
        string[] rows = sheetData.Split('\n'); //행 데이터 (카드의 데이터 모음)

        string str = "";
        for (int j = 0; j < rows.Length; j++)
        {
            string[] columns = rows[j].Split('\t'); // 열 데이터 (카드의 데이터 중 하나)

            //첫번째 행의 카드 데이터를 출력한다.
            for (int i = 0; i < columns.Length; i++)
            {
                str += columns[i] + " ";
            }

            str += "\n";
        }

        Debug.Log(str);
    }
}

 

이렇게하면 해당 스크립트에 범위에 표시된 데이터를 다 문자열로 파싱해온다.

 

범위는 추가되는 URL의 ( A2:E ) 이 부분을 수정해서 사용하면된다.

물론 이 코드는 확장성에서 많이 떨어지는 부분이 있기에 나중에 프로젝트가 커지면 API를 사용하는게 더 효율적일것이다. 

URL에 추가할때 해당 빨간상자 친 부분을 수정해주면된다.

 

 

불러온 구글 스프레드 시트 데이터가 출력된다.

 

해당 기능을 이용하여 나중에 게임에 DB를 구성하여 사용 할수 있을것이다. 

프로젝트가 마무리 시점이 되어 , 최적화 작업에 들어갔다.

 

* Dynamic Batching(동적 배칭)

동적배칭Mesh파티클 시스템과 같은 동적으로 생성된 지오메트리에 대해 작은 메쉬(vertex 900개 이하)같은 Material를 사용하는 조건하에 하나의 드로우콜로 처리 하는 것입니다. 해당 프로젝트에서 해당 기술을 사용하여 Batch수를 약 40% 감소 시켰습니다.

player setting - build 에서 동적배칭을 설정해줘야한다.

 

* Static Batching(정적 배칭)

정적배칭같은 Material를 사용하는 움직이지 않은 오브젝트를 하나의 Mesh로 결합하여 최적화 하는 기법입니다. 메모리 사용량이 증가 할 수 있지만 , CPU에서 오브젝트를 배치한 후(하나의 Mesh로 결합) GPU에서 한번만 처리하기에 드로우 콜이 감소하여 최적화를 할 수 있습니다.

 

정적 배칭은 생각밖으로 최적화 효과를 못봤지만 , 동적 배칭에 대해서는 많은 최적화가 진행 되었다 . 

using DG.Tweening

Sequence seq = DOTween.Sequence();
seq.SetUpdate(true);
seq.SetUpdate(false);

 

Sequence의 SetUpdate() 메서드를 사용하면 TimeScale 이 0이여도 DoTween 시퀀스에 저장된 애니메이션들은 동작하게 된다.

 

예제)

  private void MakeSquence()
  {
      Time.timeScale = 0;
      Sequence seq = DOTween.Sequence();
      seq.SetUpdate(true);
      seq.Append(gachaImage.transform.DOMoveY(500, 0));
      seq.Append(gachaText.DOFade(0, 0));
      seq.Append(gachaText.DOFade(1, 1));
      seq.Append(gachaText.DOFade(0, 0.5f));
      seq.Append(gachaImage.DOColor(Color.white, 1));
      seq.Append(gachaImage.transform.DOMoveY(-500, 1));
      seq.Append(gachaImage.DOColor(Color.black, 1));
      seq.OnComplete(() =>
      {
          Time.timeScale = 1;
          this.gameObject.SetActive(false);
      });
  }

모든 애니메이션이 동작한 뒤 Time.timeScale을 원복하는 메서드이다.

 

+p.s) DoTween 에셋 기능이 아닌 , Animator의 UpdateMode를 사용하는것도 방법이다.

 

animator.updateMode = AnimatorUpdateMode.UnscaledTime;

 

  • Normal: Time.timeScale에 따라 애니메이션이 재생됩니다.
  • UnscaledTime: Time.timeScale 값에 관계없이 애니메이션이 계속 재생됩니다.
  • AnimatePhysics: 물리 시뮬레이션과 함께 애니메이션이 업데이트됩니다.

3가지를 선언하여 사용하면된다. 

 

+ Recent posts