우당탕탕 개발일지
[Unity] 카메라 Drag 이동/줌 및 Scroll 줌 구현 본문
1. 드래그를 할 경우(손가락 or 마우스) 카메라가 이동한다. Y축은 고정이고, x축과 z축만 이동한다. (3D기준)
2. 마우스 휠로 스크롤을 하거나 (PC) 두 손가락으로 확대/축소를 할경우 카메라가 줌/아웃된다.
3. 드래그와 줌 모두 최소/최대값이 존재한다.
이 코드는 외부에서 Init을 수행하고 min,max값을 설정하지 않으면 작동하지 않는다는점을 주의하자
이 코드의 카메라이동은 x와 z 축만 이동이 가능하고, Y축은 고정이라는 점을 주의하자. 만약 Y축을 이동하고싶다면 CheckMoveInput() 부분을 약간 수정하면된다.
전체코드
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CameraManager : SingletonMono<CameraManager>
{
//movement
private bool moveOn = false;
private float moveSpeed = 10;
private Vector2 moveBoundaryMin = Vector2.zero, moveBoundaryMax = Vector2.zero;
private Vector3 moveStartPoint = Vector3.zero;
//zoom
private bool zoomOn = false;
private float zoomMin = 0, zoomMax = 0, zoomSpeed = 10;
private void Update()
{
bool isZoom = false;
if (zoomOn)
{
isZoom = CheckDragZoomInput();
if (!isZoom) isZoom = CheckScrollZoomInput();
}
if (!isZoom && moveOn) CheckMoveInput();
}
#region Init
public void Init()
{
zoomOn = moveOn = false;
transform.position = new Vector3(0, transform.position.y, 0);
}
public void SetMovement(bool isOn, Vector2 boundaryMin, Vector2 boundaryMax)
{
this.zoomOn = isOn;
moveBoundaryMin = boundaryMin;
moveBoundaryMax = boundaryMax;
}
public void SetZoom(bool isOn, float minSize = 0, float maxSize = 0)
{
moveOn = isOn;
zoomMin = minSize;
zoomMax = maxSize;
}
#endregion
#region Zoom
bool CheckScrollZoomInput()
{
float scrollValue = Input.GetAxis("Mouse ScrollWheel");
if (scrollValue == 0) return false;
var cam = Camera.main;
float final = 0;
if (cam.orthographic)
{
final = cam.orthographicSize + scrollValue * zoomSpeed;
final = Mathf.Max(final, 0.1f);
if (final < zoomMin) final = zoomMin;
else if (final > zoomMax) final = zoomMax;
cam.orthographicSize = final;
}
else
{
final = cam.fieldOfView + scrollValue * zoomSpeed;
final = Mathf.Clamp(final, 0.1f, 179.9f);
if (final < zoomMin) final = zoomMin;
else if (final > zoomMax) final = zoomMax;
cam.fieldOfView = final;
}
return true;
}
bool CheckDragZoomInput()
{
//drag zoom
if (Input.touchCount == 2)
{
Touch touchZero = Input.GetTouch(0);
Touch touchOne = Input.GetTouch(1);
Vector2 touchZeroPrevPos = touchZero.position - touchZero.deltaPosition;
Vector2 touchOnePrevPos = touchOne.position - touchOne.deltaPosition;
float prevTouchDeltaMag = (touchZeroPrevPos - touchOnePrevPos).magnitude;
float touchDeltaMag = (touchZero.position - touchOne.position).magnitude;
float deltaMagnitudeDiff = prevTouchDeltaMag - touchDeltaMag;
var cam = Camera.main;
float final = 0;
if (cam.orthographic)
{
final = cam.orthographicSize + deltaMagnitudeDiff * zoomSpeed;
final = Mathf.Max(final, 0.1f);
if (final < zoomMin) final = zoomMin;
else if (final > zoomMax) final = zoomMax;
cam.orthographicSize = final;
}
else
{
final = cam.fieldOfView + deltaMagnitudeDiff * zoomSpeed;
final = Mathf.Clamp(final, 0.1f, 179.9f);
if (final < zoomMin) final = zoomMin;
else if (final > zoomMax) final = zoomMax;
cam.fieldOfView = final;
}
return true;
}
return false;
}
#endregion
#region Move
void CheckMoveInput()
{
if (Input.GetMouseButtonDown(0)) moveStartPoint = Input.mousePosition;
if (Input.GetMouseButton(0))
{
var crrPos = Input.mousePosition;
var pos = Camera.main.ScreenToViewportPoint(moveStartPoint - crrPos);
pos.z = pos.y;
pos.y = 0f;
Vector3 finalPos = new Vector3(0, transform.position.y, 0);
var move = pos.normalized * (Time.deltaTime * moveSpeed);
transform.Translate(move);
finalPos.x = transform.position.x;
if (finalPos.x < moveBoundaryMin.x) finalPos.x = moveBoundaryMin.x;
else if (finalPos.x > moveBoundaryMax.x) finalPos.x = moveBoundaryMax.x;
finalPos.z = transform.position.z;
if (finalPos.z < moveBoundaryMin.y) finalPos.z = moveBoundaryMin.y;
else if (finalPos.z > moveBoundaryMax.y) finalPos.z = moveBoundaryMax.y;
transform.position = finalPos;
}
}
#endregion
}
코드분석
1. Manager 초기화 및 최소/최대값 세팅
public void Init()
{
zoomOn = moveOn = false;
transform.position = new Vector3(0, transform.position.y, 0);
}
public void SetMovement(bool isOn, Vector2 boundaryMin, Vector2 boundaryMax)
{
this.zoomOn = isOn;
moveBoundaryMin = boundaryMin;
moveBoundaryMax = boundaryMax;
}
public void SetZoom(bool isOn, float minSize = 0, float maxSize = 0)
{
moveOn = isOn;
zoomMin = minSize;
zoomMax = maxSize;
}
SetMovement : 이동기능의 경우 최소좌표와 최대좌표가 필요하다
SetZoom: 확대/축소 기능의 경우 최소카메라 크기, 최대 카메라크기가 필요하다.
GameManager에서 현재의 게임상황에 맞게끔 CameraManager를 초기화 및 초기설정을 해준다.
2. Update문
private void Update()
{
bool isZoom = false;
if (zoomOn)
{
isZoom = CheckDragZoomInput();
if (!isZoom) isZoom = CheckScrollZoomInput();
}
if (!isZoom && moveOn) CheckMoveInput();
}
줌/드래그 기능이 모두 한 곳에서 이루어지는데, 동작이 겹치지 않도록 하나의 작업이 성공적으로 끝날 경우 다른 작업은 시행되지 않도록 구현하였다.
zoomOn / moveOn 은 기능자체의 on/off 변수이다.
3. CheckDragZoomInput()
if (Input.touchCount == 2)
{
Touch touchZero = Input.GetTouch(0);
Touch touchOne = Input.GetTouch(1);
Vector2 touchZeroPrevPos = touchZero.position - touchZero.deltaPosition;
Vector2 touchOnePrevPos = touchOne.position - touchOne.deltaPosition;
float prevTouchDeltaMag = (touchZeroPrevPos - touchOnePrevPos).magnitude;
float touchDeltaMag = (touchZero.position - touchOne.position).magnitude;
float deltaMagnitudeDiff = prevTouchDeltaMag - touchDeltaMag;
var cam = Camera.main;
float final = 0;
if (cam.orthographic)
{
final = cam.orthographicSize + deltaMagnitudeDiff * zoomSpeed;
final = Mathf.Max(final, 0.1f);
if (final < zoomMin) final = zoomMin;
else if (final > zoomMax) final = zoomMax;
cam.orthographicSize = final;
}
else
{
final = cam.fieldOfView + deltaMagnitudeDiff * zoomSpeed;
final = Mathf.Clamp(final, 0.1f, 179.9f);
if (final < zoomMin) final = zoomMin;
else if (final > zoomMax) final = zoomMax;
cam.fieldOfView = final;
}
return true;
}
return false;
인풋을 조사하여 두손가락으로 zoom in /out 을 수행하는지 검사, 그러한 인풋이 들어왔다면 작업을 수행하고 true를 반환한다.
드래그는 손가락 두개이므로, input값이 총 2개이다. 초기값과 현재의 값(변화한 값) 을 계산하여 총 확대/축소를 얼마정도 했는지 수치로 변환한다(final)
초기에 세팅한 zoomMax 와 zoomMin을 활용하여 최대/최소값을 넘지 않도록 세팅해준다.
4. CheckScrollZoomInput()
bool CheckScrollZoomInput()
{
float scrollValue = Input.GetAxis("Mouse ScrollWheel");
if (scrollValue == 0) return false;
var cam = Camera.main;
float final = 0;
if (cam.orthographic)
{
final = cam.orthographicSize + scrollValue * zoomSpeed;
final = Mathf.Max(final, 0.1f);
if (final < zoomMin) final = zoomMin;
else if (final > zoomMax) final = zoomMax;
cam.orthographicSize = final;
}
else
{
final = cam.fieldOfView + scrollValue * zoomSpeed;
final = Mathf.Clamp(final, 0.1f, 179.9f);
if (final < zoomMin) final = zoomMin;
else if (final > zoomMax) final = zoomMax;
cam.fieldOfView = final;
}
return true;
}
얘는 간단한데, 스크롤 수치를 읽어서 확대/축소의 값에 사용한다. 나머지 부분은 (3)번과 동일하다.
5. CheckMoveInput()
void CheckMoveInput()
{
if (Input.GetMouseButtonDown(0)) moveStartPoint = Input.mousePosition;
if (Input.GetMouseButton(0))
{
var crrPos = Input.mousePosition;
var pos = Camera.main.ScreenToViewportPoint(moveStartPoint - crrPos);
pos.z = pos.y;
pos.y = 0f;
Vector3 finalPos = new Vector3(0, transform.position.y, 0);
var move = pos.normalized * (Time.deltaTime * moveSpeed);
transform.Translate(move);
finalPos.x = transform.position.x;
if (finalPos.x < moveBoundaryMin.x) finalPos.x = moveBoundaryMin.x;
else if (finalPos.x > moveBoundaryMax.x) finalPos.x = moveBoundaryMax.x;
finalPos.z = transform.position.z;
if (finalPos.z < moveBoundaryMin.y) finalPos.z = moveBoundaryMin.y;
else if (finalPos.z > moveBoundaryMax.y) finalPos.z = moveBoundaryMax.y;
transform.position = finalPos;
}
}
UI화면의 (x,y) 좌표의 이동값을 읽어서 카메라의 (x,0,z) 값으로 변환하여 이동하는 기능이다. 카메라는 3d 이기때문에 y값을 고정해야 의도한 대로 작동한다. 움직이기 전의 transform.position.y 값을 저장해놨다가 마지막에 y값에 넣어준다.
moveBoundaryMin, moveBoundaryMax 값 또한 UI좌표 기준으로 (x,y) 의 Vector2 형식을 띄고있으므로, 사실상 moveBoundary.y 는 카메라의 z의 바운더리를 나타낸다.
'Unity' 카테고리의 다른 글
[Unity] 스크린샷 및 갤러리에 저장하기 (0) | 2024.05.08 |
---|---|
[Unity] CSV 파일 파싱 및 자동 인스턴스화 구현 (advanced) (2) | 2024.03.06 |
[Unity] IOS BuildErr : GoogleService-Info.plist file missing REVERSED_CLIENT_ID (0) | 2024.02.26 |
[Unity] Build Err com.android.build.gradle.tasks.ProcessLibraryManifest$ProcessLibWorkAction (0) | 2024.02.26 |
[Unity] Build Err : unity exception: required API level 33. (0) | 2024.02.20 |