[Unity]/[Unity 2D]

[Unity 2D] 아이소메트릭의 맵에서 카메라 범위 제한하기 (Constraining Camera Ranges in Isometric Maps)

dogfootman 2023. 7. 23. 16:55

개요

아이소매트릭형태의 맵에서 카메라 범위를 제한

 

 

구현방법

선1선2교점이 있을경우 카메라의 이동을 제한 

 

선1= 카메라의 현재 위치이동할려고 하는 위치를 연결한 선

선2= 맵의 경계선 (이미지의 TopRight를 연결한 선)

교점= 선1선2이 만나는 지점

 

 

0. 이동 방법은 기존에 작성한 스크립트 활용

https://dogfootman.com/2

 

[Unity 2D] 드래그 카메라 이동 (drag camera move)

1. 필요한 변수 선언 // 상수 : 이동 관련 private const float DirectionForceReduceRate = 0.935f; // 감속비율 private const float DirectionForceMin = 0.001f; // 설정치 이하일 경우 움직임을 멈춤 // 변수 : 이동 관련 private

error37.tistory.com

 

 

1. 맵 정보를 담을 클래스 작성

    public class RhombusBounds
    {
        public float MinX { get; }
        public float MinY { get; }
        public float MaxX { get; }
        public float MaxY { get; }

        public RhombusBounds(float minX, float minY, float maxX, float maxY)
        {
            MinX = minX;
            MinY = minY;
            MaxX = maxX;
            MaxY = maxY;
        }
    }

교점이 발생했을때의 제한 할 범위를 반환 할때 사용하기 위한 클래스

 

public class MapSize
    {
        public Vector3 Center { get; }
        
        public float X { get; }
        public float Y { get; }
        
        public float MinX { get; }
        public float MaxX { get; }
        public float MinY { get; }
        public float MaxY { get; }

        public MapSize(Vector3 center, float sizeX, float sizeY)
        {
            // center
            Center = center;
            
            // size
            X = sizeX;
            Y = sizeY;
            
            // half
            var xHalf = X / 2;
            var yHalf = Y / 2;

            // x
            MinX = center.x - xHalf;
            MaxX = center.x + xHalf;
            
            // y
            MinY = center.y - yHalf;
            MaxY = center.y + yHalf;
        }
    }
    
    public class MapSizeIsometric : MapSize
    {
        public Vector3 Bottom { get; }
        public Vector3 Top { get; }
        public Vector3 Left { get; }
        public Vector3 Right { get; }

        public MapSizeIsometric(Vector3 center, float sizeX, float sizeY, float cellSizeX, float cellSizeY) : base(center, sizeX, sizeY)
        {
            // isometric point
            Bottom = new Vector3(center.x, MinY * cellSizeY, center.z);
            Top = new Vector3(center.x, MaxY * cellSizeY, center.z);
            Left = new Vector3(MinX * cellSizeX, center.y, center.z);
            Right = new Vector3(MaxX * cellSizeX, center.y, center.z);
        }
    }

맵에 대한 정보를 저장하기 위한 클래스

 

 

2. 변수 선언

        // 변수 : 맵 크기
        private MapSize _mapSize;

 

 

3. Start 함수

        private void Start()
        {
            ...

            // 맵정보
            _mapSize = new MapSizeIsometric(transform.position, 20f, 20f, 1f, 0.5f);
            
            ...
        }

맵 정보

x=20f

y=20f

cellSizeX=1f

cellSizeY=0.5f

 

 

4. UpdateCameraPosition 함수 갱신

        private void UpdateCameraPosition()
        {
            ...
            
            // 기존 소스: start
            var targetPosition = new Vector3(targetX, targetY, currentPosition.z);
            cameraTransform.position = Vector3.Lerp(currentPosition, targetPosition, 0.5f);
            // 기존 소스: end
            
            // 여기 부터 추가
            // isometric limit
            if (typeof(MapSizeIsometric) == _mapSize.GetType())
            {
                var rhombusRounds = GetRhombusRounds(targetPosition.x, targetPosition.y);
                if (rhombusRounds != null)
                {
                    targetX = Mathf.Clamp(targetPosition.x, rhombusRounds.MinX, rhombusRounds.MaxX);
                    targetY = Mathf.Clamp(targetPosition.y, rhombusRounds.MinY, rhombusRounds.MaxY);
                    
                    var fixedPosition = new Vector3(targetX, targetY, transform.position.z);
                    cameraTransform.position = fixedPosition;
                    
                    // 위치 보정이 일어난 만큼 스타트 위치를 보정
                    var fixPosition = new Vector3(targetX, targetY, currentPosition.z) - fixedPosition; 
                    if (_userMoveInput)
                    {
                        fixPosition = new Vector2(Mathf.Abs(fixPosition.x) > Mathf.Epsilon ? fixPosition.x : 0f, Mathf.Abs(fixPosition.y) > Mathf.Epsilon ? fixPosition.y : 0f);
                        _startPosition -= fixPosition;
                    }
                    else
                    {
                        _directionForce -= fixPosition;
                    }
                }
            }
        }

rhombusRounds != null: 교점이 존재할 경우

반환받은 범위로 카메라의 위치를 제한

 

 

5. GetRhombusRounds 작성

        public RhombusBounds GetRhombusRounds(float targetX, float targetY)
        {
            var mapSize = (MapSizeIsometric)_mapSize;
            
            Vector2 localPosition = transform.position - mapSize.Center;

            var absPositionX = mapSize.Center.x + Mathf.Abs(localPosition.x);
            var absPositionY = mapSize.Center.y + Mathf.Abs(localPosition.y);

            var topRightIntersectionPosition = GetIntersectPosition(mapSize.Center, new Vector2(absPositionX, absPositionY), mapSize.Top, mapSize.Right);
            if (topRightIntersectionPosition == Vector2.zero)
            {
                return null;
            }

            return new RhombusBounds(-topRightIntersectionPosition.x, -topRightIntersectionPosition.y,
                topRightIntersectionPosition.x, topRightIntersectionPosition.y);
        }
            Vector2 localPosition = transform.position - mapSize.Center;

            var absPositionX = mapSize.Center.x + Mathf.Abs(localPosition.x);
            var absPositionY = mapSize.Center.y + Mathf.Abs(localPosition.y);

Top,Right를 이은 선과의 교점을 구하기 위해 절댓값으로 위치 변환

 

 

6. GetIntersectPosition 작성

        private static Vector2 GetIntersectPosition(Vector2 aP1, Vector2 aP2, Vector2 bP1, Vector2 bP2) 
        {
            var under = (bP2.y - bP1.y) * (aP2.x - aP1.x) - (bP2.x - bP1.x) * (aP2.y - aP1.y);
            if (Mathf.Abs(under) < Mathf.Epsilon)
            {
                return Vector2.zero;
            }
            
            var t1 = (bP2.x - bP1.x) * (aP1.y - bP1.y) - (bP2.y - bP1.y) * (aP1.x - bP1.x);
            var s1 = (aP2.x - aP1.x) * (aP1.y - bP1.y) - (aP2.y - aP1.y) * (aP1.x - bP1.x);
            if (Mathf.Abs(t1) < Mathf.Epsilon && Mathf.Abs(s1) < Mathf.Epsilon)
            {
                return Vector2.zero;
            }
            
            var t2 = t1/under;
            var s2 = s1/under;
            if (t2 < 0f || t2 > 1.0f || s2 < 0f || s2 > 1f) {
                return Vector2.zero;
            }
            
            var intersectionX = aP1.x + t2 * (aP2.x - aP1.x);
            var intersectionY = aP1.y + t2 * (aP2.y - aP1.y);
            return new Vector2(intersectionX, intersectionY);
        }

 

 

교점 알고리즘 참고: http://www.gisdeveloper.co.kr/?p=89