반응형

 

 

 

 

 

GitHub - realnity09/SpriteMeshGen

Contribute to realnity09/SpriteMeshGen development by creating an account on GitHub.

github.com

 

스프라이트의 Custom outline이나 Custom physics shape 정보를 바탕으로 Mesh를 생성하는 커스텀 에디터다.

렌더링 되는 에셋들은 2D지만 3D물리를 사용해야해서 만들게 되었다.

※ Mesh collider에 사용할 메쉬 데이터이기 때문에 uv 설정은 따로 하지 않았다.

 

 

 

에디터 경로

패키지를 임포트한 뒤, CustomMeshMesh generator를 클릭하면 Mesh generator 창이 열리는것을 확인할 수 있다.

 

 

에디터 설명

1. Target save path : Generate한 메쉬가 저장될 디렉토리를 설정한다.

2. Target sprite : 메쉬로 Generate할 스프라이트를 선택한다. (스프라이트를 선택해야 하단 설정이 활성화 된다.)

3. Target vertices

   - Physics shape : Custom physics shape 정점을 바탕으로 메쉬를 Generate한다.

   - Out line : Custom outline 정점을 바탕으로 메쉬를 Generate한다.

4. Thickness : 메쉬의 두께(Z축)의 수치이다.

5. Generate : 메쉬를 생성한다.

※ 스프라이트의 정점 정보를 수정했을 때, 기존 메쉬를 지웠다가 재생성 할 필요 없이 Generate 해주면 메쉬를 변경한 정점 정보 대상으로 업데이트 해준다.

※ 메쉬를 업데이트 했을 시, Scene에 적용되어있는 메쉬가 업데이트 전의 메쉬로 보일텐데 이건 해당 Scene에서 갱신이 안되서 그렇지 업데이트가 되지 않은것이 아니다. (Play 모드를 해주거나 Scene을 다시 열면 업데이트 된 메쉬로 확인이 가능하다.)

 

 

 

스프라이트 에디터

- 스프라이트 에디터에 대해서 잘 모른다면 다음 메뉴얼 참고

 

스프라이트 에디터 - Unity 매뉴얼

스프라이트 텍스처에 단 하나의 그래픽 요소만 있는 경우가 있으나 관련된 여러 그래픽스를 하나의 이미지로 합치는 게 더욱 편리할 때가 많습니다. 예를 들어 바퀴가 차체와는 독립적으로 움

docs.unity3d.com

 

- Physics shape로 메쉬를 생성하려 할 시, 스프라이트 에디터에서 좌측 상단 드롭다운 메뉴를 클릭한 뒤, Custom physics shape을 선택한다.

- Outline으로 메쉬를 생성하려 할 시, 스프라이트 에디터에서 좌측 상단 드롭다운 메뉴를 클릭한 뒤, Custom Oueline을 선택한다.

※ 정점을 배치한 뒤 반드시 우측 상단의 Apply를 눌러서 저장해야 정상적으로 메쉬가 Generate된다.

 

 

 

주의사항

- 스프라이트 에디터에서 Custom physics shape로 정점 생성 후, 메쉬를 Generate하면 메쉬가 비정상적인 모양으로 생성될 때가 있다. (Custom physics shape은 triangle 정보가 없기 때문에...)

※ Outline은 정점 위치 상관 없이 정상적으로 생성된다.

- 렌더링쪽 지식이 조금이라도 있으면 제대로 나오게 정점 위치를 수정할 수 있겠지만, 만약 지식이 없는 사람은 정상적으로 모양이 나올때까지 정점들 위치를 시계방향으로 한칸씩 돌려서 Generate하면 된다.

 

 

 

 

 

 

반응형
반응형

 

 

 

 

보통 카메라 흔드는 기능에 대해 구글링을 해보면 Random.insideUnitCircle이나 Sphere로 카메라를 흔드는 코드를 많이 볼 수 있었다. 그런데 이런식으로 구성을하면 끊기면서 흔들리는(?) 느낌이 강하게 들어서 좀 더 찾아보니 관련된 에셋이 몇개 있었는데 받아서 뜯어보니 Mathf.PerlinNoise라는 노이즈 함수를 이용해서 카메라를 흔드는것을 봤다.

 

에셋에서는 위치 및 회전값까지 변경해서 건드리고있었는데, 회전값까지 변경하는 코드는 카메라 흔들림이 너무 정신없어서 전부 빼고 x, y 좌표만 변경하는 방법으로 코드를 바꿨다.

 

※ Perlin noise (펄린노이즈)

- 단계적 텍스처를 만들기 위해 개발된 노이즈 함수, 지형(마인크래프트 등)을 생성할 때 이용한다고한다.

- 위키 링크 (영어)

- 유니티 링크 (영어)

 

 

 

 

코드

using System.Collections;
using UnityEngine;

public class CamShake : MonoBehaviour
{
    [SerializeField]
    private float m_roughness;      //거칠기 정도
    [SerializeField]
    private float m_magnitude;      //움직임 범위

    private void Update()
    {
        if(Input.GetKeyDown(KeyCode.Space))
        {
            StartCoroutine(Shake(1f));
        }
    }

    IEnumerator Shake(float duration)
    {
        float halfDuration = duration / 2;
        float elapsed = 0f;
        float tick = Random.Range(-10f, 10f);

        while (elapsed < duration)
        {
            elapsed += Time.deltaTime / halfDuration;

            tick += Time.deltaTime * m_roughness;
            transform.position = new Vector3(
                Mathf.PerlinNoise(tick, 0) - .5f,
                Mathf.PerlinNoise(0, tick) - .5f,
                0f) * m_magnitude * Mathf.PingPong(elapsed, halfDuration);

            yield return null;
        }
    }
}

※ roughness : 거칠기 정도, 카메라가 움직이는 동안 카메라의 떨림을 제어하는 수치이다. 값이 높아질수록 카메라가 움직이는 시간동안 많이 떨린다.

※ magnitude : 카메라 움직임 범위, 카메라가 움직이는 범위 수치이다.

※ tick : 펄린노이즈 함수에 들어갈 값이다. 초반에 랜덤으로 값을 설정하는 이유는 노이즈 함수에 들어가는 값을 다르게하기 위해서이다. (항상 같은 움직임으로 흔들게 하고싶으면 틱값을 랜덤이 아닌 고정값으로 초기화하면 된다.)

 

 

 

 

결과

 

 

 

 

 

 

 

반응형
반응형

 

 

 

 

NavMeshAgent에 Angular Speed를 아무리 높게 줘도 회전이 늦게 일어난다. (해당 프로퍼티가 초당 회전각도로 알고있는데 아무리 수치를 높여도 회전이 느림)

위와 같이 회전이 몬스터 같은 유닛의 경우 어색할 가능성이 다분하다.

그래서 NavMeshAgent.updateRotation의 값을 false로 설정해준 뒤, 직접 각을 계산해서 넣어줘야한다.

 

 

 

공통

private NavMeshAgent m_agent;

void Start()
{
    m_agent = GetComponent<NavMeshAgent>();
    m_agent.updateRotation = false;
}

 

 

 

 

1. NavMeshAgent의 DesireVelocity값을 이용해 회전값 조정

- NavMeshAgent.DesireVelocity - 회피를 고려한 목표 속도

- Qaternion.LookRotation을 이용해서 회전값을 적용해주면 된다.

void Update()
{
    transform.rotation = Quaternion.LookRotation(m_agent.desireVelocity);
}

 

 

- 결과

그런데 한가지 문제가 있다면 커브길 주변에서 유닛이 떨리는 현상이 발견된다. (실제로 적용해보면 심각할정도로 떨린다.)

 

 

 

2. 직접 각을 계산해서 적용하기

- NavMeshAgent.steeringTarget값을 이용해 회전 값을 계산해서 적용하기

void Update()
{
    Vector2 forward = new Vector2(transform.position.z, transform.position.x);
    Vector2 steeringTarget = new Vector2(m_agent.steeringTarget.z, m_agent.steeringTarget.x);
    
    //방향을 구한 뒤, 역함수로 각을 구한다.
    Vector2 dir = steeringTarget - forward;
    float angle = Mathf.Atan2(dir.y, dir.x) * Mathf.Rad2Deg;
    
    //방향 적용
    transform.eulerAngles = Vector3.up * angle;
}

※ 3차원 공간에서는 전진 방향이 z축이니 Vector2의 x값을 transform.position.z값으로 설정한다.

※ steeringTarget : 경로상의 다음 목적지.

 

- 결과

1번과 다르게 떨리는 현상 없이 바로 회전하는것을 볼 수 있다.

 

 

 

 

 

반응형
반응형

 

 

 

 

Navigation Bake (Baked Agent)

  • Agent Radius : Agent의 반지름, Agent와 벽까지의 거리이다. 해당 반지름의 길이만큼 Agent가 벽에 붙어서 움직일 수 있다.
  • Agent Height : Agent의 높이, Agent의 높이를 지정해서 천장이 있는 지형의 통과 여부를 결정한다.
  • Max Slope : 이동 가능한 경사도
  • Step Height : 이동 가능한 단차의 한계 값, 설정한 수치만큼의 단차를 이동 할 수 있다.

※ Agent Height에 따른 네비메쉬 차이

 

 

NavMeshAgent Component

- A* PathFinding 알고리즘을 사용하여 길을 탐색한다.

- Bake된 네비메쉬의 정보를 바탕으로 목적지까지의 최단거리 이동을 계산하며, Agent간의 충돌을 회피할 수 도 있다.

 

AgentType : Navigation의 Agents 탭에서 지정한 정보

BaseOffset : NavMeshAgent는 Bake된 네비메쉬의 표면에 붙어있기 떄문에 실제 맵 오브젝트로부터 공중으로 살짝 떨어지게 되어있다. 해당 값으로 높이조절이 가능하다.

 

Steering

  • Speed : 최대 이동 속도
  • Angular Speed : 회전 속도(Degree/sec)
  • Acceleration : 최대 가속도
  • Stopping Distance : 목표점에 가까워졌을 때, 설정한 거리에 들어오면 감속
  • Auto Braking : 목적지에 도착하기 직전에 감속을 시작할 것인지 결정

 

Obstacle Avoidance

- 다른 Agent와 Obstacle을 어떻게 회피할 것인지 결정하는 프로퍼티

  • Radius : 지형과 관계없이 다른 Agent나 Obstacle의 충돌하는 영역의 두께가 조절된다.
  • Height : Agent끼리의 높이 충돌을 조절한다.
  • Quality : 회피 품질(None은 회피 무시)
  • Priority : Agent간의 회피 우선 순위, 낮은 수가 높은 우선 순위이며 자신보다 낮은 Agent는 회피 대상에서 제외된다.

 

Path Finding

  • Auto Traverse Off Mesh Link : 분리된 메쉬 간에 자동으로 링크를 생성하는 옵션. Off Mesh Link 컴포넌트를 사용할 시 해당 기능을 비활성화해야한다.
  • Auto Repath : 네비메쉬에 변동이 생기거나 길이 막혔을 때, 재탐색한다.
  • Area Mask : 네비메쉬의 영역별로 이동을 제한할 수 있는 기능

 

 

 

 

 

반응형
반응형

 

 

 

 

유니티 엔진의 Input 클래스에는 touch라는 구조체가 존재한다. 이 touch라는 구조체의 정보를 바탕으로 모바일 환경에서의 제스처기능을 적용해 카메라를 이동하거나 줌 인/아웃 기능을 구현할 수 있다.

 

 

카메라 이동

using UnityEngine

enum GESTURE
{
    MOVE = 1,
    ZOOM,
}

private void MoveCam()
{
    if (Input.touchCount == (int)GESTURE.MOVE)
    {
        Touch touch = Input.touches[0];
        Camera.main.transform.position = new Vector3(
            Camera.main.transform.position.x - touch.deltaPosition.x,
            Camera.main.transform.position.y - touch.deltaPosition.y,
            Camera.main.transform.position.z);
    }
}
  • Touch 구조체에는 deltaPosition이 존재하는데 이전 프레임과 현재 프레임 사이의 움직인 벡터 값을 받을 수 있다.
  • 이 벡터 값을 이용해서 카메라의 위치를 변경해주면 된다.
  • deltaPosition을 바로 적용해주면 카메라가 엄청 빠르게 움직이니 해당 값에 speed(float) 값을 곱해서 적용해준다.
  • 간단하게 보여주기 위해 작성된 코드로, Camera.main의 카메라 클래스는 캐싱해서 사용하는게 최적화에 도움이 된다.

 

 

 

카메라 줌 인/아웃

private void ZoomCam()
{
    if (Input.touchCount == (int)TOUCH.ZOOM)
    {
        Touch touch_1 = Input.touches[0];
        Touch touch_2 = Input.touches[1];

        //이전 프레임의 터치 좌표를 구한다.
        Vector2 t1PrevPos = touch_1.position - touch_1.deltaPosition;
        Vector2 t2PrevPos = touch_2.position - touch_2.deltaPosition;

        //이전 프레임과 현재 프레임 움직임 크기를 구함.
        float prevDeltaMag = (t1PrevPos - t2PrevPos).magnitude;
        float deltaMag = (touch_1.position - touch_2.position).magnitude;

        //두 크기값의 차를 구해 줌 인/아웃의 크기값을 구한다.
        float deltaMagDiff = prevDeltaMag - deltaMag;

        Camera.main.orthographicSize += deltaMagDiff;
        Camera.main.orthographicSize = Mathf.Clamp(Camera.main.orthographicSize, 7, 30);
    }
}
  • 줌 기능은 카메라 이동 코드와는 다르게 조금 복잡한데, 결론적으로 이전 프레임과 현제 프레임의 두 터치 사이의 벡터 크기(Maginitude) 값을 이용해서 deltaMagnitude 값을 구한다.
  • 이렇게 구해진 deltaMagnitude 값을 Camera의 orthographicSize에 더해주면 줌 인/아웃 기능 구현이 가능하다.
  • orthographicSize가 너무 작아져서 음수로 내려가면 화면이 뒤집어져 보이므로 Mathf.Clamp로 적절하게 최소, 최대값을 고정해준다.
  • deltaMagnitude 값을 바로 적용하면 줌이 빠르게 적용되니 해당 값에 speed(float) 값을 곱해서 사용해준다.

 

 

 

결과

 

 

 

 

 

 

반응형

+ Recent posts