본문 바로가기
개발/android

객체 속도가 빠를수록 충돌검사가 부정확해진다면

by 매몰 2019. 4. 2.

충돌검사는 기본적으로 두 객체의 좌표가 겹쳐있느냐를 보는것이다.

하지만 간혹 검사가 제대로 안될때가 있다.

 

객체의 좌표 이동거리가 자신의 크기보다 더 클때 그렇다.

다음과 같이 말이다.

 

그림1. 좌표 이동거리가 자신보다 작을때

 

그림2. 좌표 이동거리가 자신보다 클때

 

게임은 프레임별로 동작하기 때문에 그림1에서는 Frame2와 Frame3에서 두 객체는 겹치지만 그림2에서는 Frame2와 Frame3에서 서로를 지나쳐 간다. 만약 프레임이 중간에 하나가 더 있었다면 충돌값은 참이었겠지만 거짓이 되었다. 이렇듯 이동거리가 자신보다 크면 프레임 사이에 공간이 생겨 원하는 결과를 얻지 못한다.

 

원인은 프레임이 현실세계에서처럼 한없이 짧을수는 없어 빠르게 움직이는 객체는 좌표의 이동값을 크게 잡아서 표현해야 하기 때문이다.

 

그래서 겹침과 함께 이동경로의 교점도 확인해야 한다.

교점은 두 객체의 경로를 연립방정식으로 바꾸면 쉽게 구할수 있다.

아래와 같다.

 

여기서는 역행렬을 이용해 연립방정식을 구했다.

 

private fun isCollision(start1: PointF, end1: PointF, start2: PointF, end2: PointF): Boolean {
    val line1 = line(start1, end1) //첫번째 객체의 경로(직선의방정식)
    val line2 = line(start2, end2) //두번째 객체의 경로
    if (line1 != null && line2 != null) {
        Log.e("MyLog", "line a1: " + line1[0] + ", b1: " + line1[1] + ", c1: " + line1[2])
        Log.e("MyLog", "line a2: " + line2[0] + ", b2: " + line2[1] + ", c2: " + line2[2])

        //두 경로(직선)의 교점을 구한다.
        val point = intersection(line1, line2)
        if (point != null) {
            Log.e("MyLog", "intersection x: " + point.x + ", y: " + point.y)

            //교점이 해당 프레임의 객체 경로 사이(시작점과 끝점 사이)에 있는지 확인
            return contains(point, start1, end1) && contains(point, start2, end2)
        }
    }

    return false
}

private fun intersection(line1: FloatArray, line2: FloatArray): PointF? {
    //역행렬을 이용해 연립방정식을 푼다.
    val det = (line1[0] * line2[1]) - (line1[1] * line2[0]) //행렬식
    if (det == 0f)
        return null //가역이 아니면

    val d = 1 / det //행렬식에서 구한 계수

    //역행렬 공식
    return PointF(d * ((line2[1] * line1[2]) + (-line1[1] * line2[2])), d * ((-line2[0] * line1[2]) + (line1[0] * line2[2])))
}

private fun line(point1: PointF, point2: PointF): FloatArray? {
    //두점의 직선의 방정식(일반형)을 구한다.
    val d = point2.x - point1.x
    if (d == 0f)
        return null

    val a = (point2.y - point1.y) / d   //기울기

    //ax + by + c = 0
    return floatArrayOf(a, -1f, (a * point1.x) - point1.y)
}

private fun contains(point: PointF, point1: PointF, point2: PointF): Boolean {
    //교점(point)이 두점(point1,point2) 사이에 있는지 확인
    return (point1.x - point.x) * (point2.x - point.x) < 0 && (point1.y - point.y) * (point2.y - point.y) < 0
}

 

 

실행해보자

 

//교점이 두점 사이에 있는 경우
val result1 = isCollision(PointF(1f, 3f), PointF(6f, 9f), PointF(2f, 7f), PointF(9f, 1f))
Log.e("MyLog", "isCollision1: $result1")

//교점이 두점 사이에 없는 경우
val result2 = isCollision(PointF(1f, 3f), PointF(6f, 9f), PointF(4f, 5f), PointF(9f, 1f))
Log.e("MyLog", "isCollision2: $result2")

 

E/MyLog: line a1: 1.2, b1: -1.0, c1: -1.8
E/MyLog: line a2: -0.85714287, b2: -1.0, c2: -8.714286
E/MyLog: intersection x: 3.361111, y: 5.8333335
E/MyLog: isCollision1: true
E/MyLog: line a1: 1.2, b1: -1.0, c1: -1.8
E/MyLog: line a2: -0.8, b2: -1.0, c2: -8.2
E/MyLog: intersection x: 3.1999998, y: 5.64
E/MyLog: isCollision2: false

 

교점이 두점 사이에 있는 경우

 

교점이 두점 사이에 없는 경우

 

 

 

 

 

도움이 되셨다면~ 정성으로 빚은 저희 앱!  많은 이용 바래요:)

 

https://meorimal.com/index.html?tab=spaceship

 

우주선 - 방치형 인공지능 투자 체험기

미리 맛보는 인공지능 투자!

(주)머리말 meorimal.com

 

https://meorimal.com/subway.html

 

지하철어디있니

더이상 고민하지 마세요. 뛸지 말지 딱 보면 알죠.

(주)머리말 meorimal.com

 

사업자 정보 표시
주식회사 머리말 | 고영진 | 서울특별시 송파구 중대로 135 서관 10층 (가락동, 아이티벤처타워) | 사업자 등록번호 : 524-88-00727 | TEL : 010-9990-3674 | Mail : gyjmeba@hanmail.net | 통신판매신고번호 : 2017-서울강남-03941호 | 사이버몰의 이용약관 바로가기