자작 게임도 잘 부탁드려요~

adsense



유니티 2D 슈팅게임 개발일지 - 8일차 (패럴랙스 스크롤뷰 레벨 맵)

스크롤러블 레벨 맵


동적 생성되는 레벨 맵


스크롤뷰와 연동하는 배경화면의 패럴랙스 스크롤


위 내용을 정리할 예정이었으나

처음엔 이렇게 잘 나오는데

페이징을 위해서 생성된 맵을 삭제하고 다시 구성하면 왼쪽으로 치우지는 문제점이 생기고 있습니다.


대충 해결이 되었으므로, 이어지는 내용에서 상세하게 기록해 보겠습니다.


동적생성되는 레벨 맵

프리팹을 동적생성하는거니까 자체는 복잡할게 없고,
레벨 맵으로 쓰려면 상하로 간격을 남겨야 하니
맵의 상하 폭보다 아이콘 두 개 분 정도를 더해서 스크롤뷰의 콘텐츠 영역을 재설정합니다.

  1. int idx = 0;
  2. int ea = (world + 1) * 20;
  3. int[] xpos = { -1, 1 };
  4. contentRect.sizeDelta = new Vector2(contentRect.sizeDelta.x, ((ea + 2) * 160 + 80));
  5. Vector3 basicPos = new Vector3(scvRect.sizeDelta.x * 0.5f, -((ea + 2) * 160 - 320 + 40), 0);
  6.  
  7. for (int i = 0; i < ea; i++)
  8. {
  9. var stage = Instantiate(mapButton, contentRect.transform);
  10. stage.SetActive(true);
  11. stage.transform.localPosition = basicPos;
  12. mapIcons.Enqueue(stage);
  13. int lpos = Random.Range(0, 2);
  14. basicPos += new Vector3(xpos[lpos] * 200, 160, 0);
  15. if (basicPos.x < scvRect.sizeDelta.x * 0.5f - 400)
  16. basicPos.x += 400;
  17. if (basicPos.x > scvRect.sizeDelta.x * 0.5f + 400)
  18. basicPos.x -= 400;
  19.  
  20. if (preObj != null)
  21. {
  22. preObj.GetComponent<MapIcon>().SetIcon(stage, i, scrollRect, canvas);
  23. }
  24.  
  25. preObj = stage;
  26. idx++;
  27. }
  28.  
맵 모양은 사실 그렇게까지 중요하지 않다고 생각해서 y방향으로는 위로 한 칸(160픽셀) 씩 올라가고 x방향으로 +-1씩(200픽셀) 해 주고 화면 좌우 밖으로는 벗어나지 않게 해 주는 정도로 구현하고 있습니다.

나중에 이 +-1을 랜덤이 아니라 고정으로 바꿀 예정.

맵 아이콘을 생성할 때는 Queue에 담아 맵 페이징을 할 때 쉽게 지울 수 있도록 합니다.

아이콘에는 아이콘끼리 연결해주는 화살표 라인 스프라이트가 하나 추가되어 있고,
preObj의 SetIcon호출 시, 이전 아이콘이 현재 아이콘 방향으로 화살표를 기울여주도록 합니다.

  1. var deg = FuncManager.GetDegree(arrow.transform.position, nxtIcon.transform.position);
  2. arrow.transform.rotation = Quaternion.Euler(new Vector3(0, 0, 180 - deg));
GetDegree는 두 점 사이의 각도를 degree로 구하는 자작함수. (atan2 사용)

마지막에 오는 아이콘은 화살표를 감춥니다.


기존에 제대로 안 나온 이유는, 각 스크립트별 Start 함수가 불리는 시점 차이 때문에
맵 생성이 먼저 되고, 그 후에 스크롤뷰의 크기가 캔버스에 맞춰 리스케일링되었기 때문.

즉, 처음에 제대로 나온게 로직 오류였던 겁니다.

맵 생성을 코루틴으로 바꾸고, 초기 맵 생성 시점을 살짝 늦게 시작하도록 해서 일단 커버.
  1. IEnumerator test()
  2. {
  3. yield return new WaitForSeconds(0.25f);
  4. MapRenew();
  5. }
MapRenew가 앞의 아이콘 생성에 해당합니다.



스크롤 뷰의 현재 위치와 위치 강제 변경

아이콘을 눌러 시작 준비창을 띄우면, 현재 스크롤뷰의 위치를 기억합니다.
  1. FuncManager.SetFloat(
  2. string.Format("MAP{0}POS", FuncManager.nowWorld),
  3. screct.verticalNormalizedPosition);
(FuncManager.SetFloat는 내부적으로 PlayerPrefs의 SetFloat를 부르는 자작 펑션입니다)

그리고, 다시 맵이 불리면 기억해 둔 위치로 스크롤뷰를 이동시킵니다.

  1. float adjustPos = FuncManager.GetFloat(
  2. string.Format("MAP{0}POS", FuncManager.nowWorld), 0f);
  3. scrollRect.verticalNormalizedPosition = adjustPos;

스크롤뷰와 연동하는 배경화면의 패럴랙스 스크롤


솔직히 맵 배경용으로 쓰는 그림만 몇 메가씩 하는데 이걸 넣어야 할지는 모르겠으나..
용량은 나중에 걱정하고 일단 넣어봅니다.

이 패럴랙스 스크롤은 게임 중 스크롤처럼 무한반복하는게 아니라 일정 범위 안에서만 상하로 움직이므로 한 장 씩만 붙여줍니다.
세 장의 이미지-화면을 채우는 배경, 투명이 있는 중근경, 투명이 있는 근경-으로 되어 있으며
각 이미지에는 동일한 스크립트를 써서 파라미터로 스크롤 속도를 바꿔줍니다.

그리고, 스크롤뷰가 OnValueChanged 이벤트를 호출하면,
각 배경면에 붙은 스크립트에 이 이벤트에서 받은 값(Vector2)을 전달합니다.

  1. public void OVC(Vector2 vec)
  2. {
  3. float adjust = vec.y;
  4.  
  5. if (adjust < 0)
  6. adjust = 0f;
  7. if (adjust > 1f)
  8. adjust = 1f;
  9.  
  10. transform.localPosition = new Vector3(
  11. 0, posY - adjust * power * (3000 * scale - Screen.height),
  12. transform.localPosition.z);
  13. }
수치가 좀 복잡하게 이거저거 들어갔는데, 화면 비율에 따라 크기랑 보정치가 달라져서 그렇습니다.

3000은 이미지의 세로 크기.
scale이 화면 크기에 맞춰 배경 그림 크기를 바꿔준 값.
3000*scale-Screen.height로 그림이 상하로 이동할 범위 값이 정해집니다.

종횡비가 다른 화면에서의 출력 예


받은 스크롤뷰의 위치값은 normalized된 값이라 0~1 사이의 값이 들어옵니다.

y좌표의 경우 0이 스크롤뷰의 최하단, 1이 최상단이며
(탄성 스크롤이라 0 미만이나 1초과도 가능한데 강제 보정)
이걸 상하 이동 범위 값에 곱해서 배경 그림의 위치를 정합니다.

왼쪽이 adjust가 0일 때, 오른쪽이 adjust가 1일 때.

power값은 움직임 범위의 보정값, 즉 이동 속도입니다.
1보다 크면 위 그림의 상하 폭보다 넓게 움직이고
작으면 좁게 움직여서
앞의 행성이나 소행성 등 장식물의 이동 속도가 바뀌어 패럴랙스 효과를 냅니다.





덧글

댓글 입력 영역


Books

Geek라이프

기초부터 시작하는 모형 전자공작
박성윤 저

사이토 나오키의 일러스트 첨삭 레슨 Before & After
사이토 나오키 저/박수현 역

MSX&재믹스 퍼펙트 카탈로그
마에다 히로유키 저/조기현 역

핵심강좌! Cocos2d-x
이재환 저
예스24 | 애드온2
일본서적 전문사이트 NEPIC