충돌검사는 기본적으로 두 객체의 좌표가 겹쳐있느냐를 보는것이다.
하지만 간혹 검사가 제대로 안될때가 있다.
객체의 좌표 이동거리가 자신의 크기보다 더 클때 그렇다.
다음과 같이 말이다.
게임은 프레임별로 동작하기 때문에 그림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
https://meorimal.com/subway.html
'개발 > android' 카테고리의 다른 글
drawText의 문자가 밑으로 삐져나오지 않게 하기 (0) | 2019.06.24 |
---|---|
FragmentPaper에서 ListView가 매번 초기화되지 않도록 하기 (0) | 2019.05.23 |
Dialog를 닫았는데 background로 나갔다 들어오니 다시 열린다??? (0) | 2019.03.13 |
EditText에 자동으로 단위 콤마 넣기 (0) | 2019.01.17 |
sqlite의 time이 자정을 인식 못할 경우, 간단한 꼼수 해결법 (0) | 2018.09.27 |