2D 벡터의 이동
2D Vector Translation
화면상에 두 비행체가 각각 두 지점을 향해 같은 탄환을 발사하는 상황을 생각해보자.

4번에 걸쳐 이동하는 두 탄환의 좌표를 찍어주는 위의 화면을 정리해보면
vx1 = (B.x - A.x) / 4
vy1 = (B.y - A.y) / 4
vx2 = (D.x - C.x) / 4
vy2 = (D.y - C.y) / 4
이 공식은 한가지 문제가 있는데
두 탄환은 화면상의 좌표를 각각 4번씩 찍어주며 이동하기 때문에 거리에 상관없이 도착 시간이 같다.
시작점과 끝점 사이의 거리가 멀어질수록 탄환의 속도가 빨리지고 가까울수록 속도가 느려지고 있는 것이다.

현실 세계에서 같은 두 탄환의 속도는 고정되어 있고 거리에 따라 도착 시간이 늘어나거나 줄어들어야 한다.
게임에서 화면의 주사율은 대게 60 FPS,
화면이 깜빡이며 좌표를 새로 찍어주는 시간은 초당 60번 (1/60) 으로 고정되어 있다.
각각 현재 좌표에서 거리가 5, 10 인 지점으로 두 탄환을 발사한다면 1 프레임당 각각의 이동거리는 다음과 같다.
0.16666 = 10 / (1/60)
시간이 고정되어 있기 때문에 거리가 10인 탄환이 2배 더 빠른 셈인데
현실 세계에서 두 탄환의 1프레임당 이동 거리인 속도는 같아야 한다.
화면상에서 탄환의 시작점 A와 도착지점 B의 좌표를 정했다면
피타고라스의 정리로 두 좌표지점의 x,y 값을 이용해 탄환의 총 이동거리인 r을 구할 수 있다.

r = 5
A와 B사이의 탄환 이동거리인 r이 5 이므로 1프레임당 움직이는 x, y 의 거리는
y : (B.y - A.y) / 5
(4 / 5 , 3 / 5)
=> (0.8, 0.6)
같은 방법으로 삼각형의 윗변 r이 10인, 좌표 (8,6)만큼 이동하는 탄환의 속도는
=> (0.8, 0.6)
탄환의 속도가 (0.8, 0.6) 으로 같아졌다.
두 탄환의 속도가 같기 때문에 2배의 이동거리와 2배의 도착시간이 정확히 화면에 표현된다.
1프레임당 이동 값인 (0.8, 0.6) 좌표가 바로 방향성이 있는 힘, Vector 다.
protocol GameClass {
func update(_ updateDt: TimeInterval)
}
class GameEngine {
private var lastUpdateTime : TimeInterval = 0
private var updateDt = Double(0)
private var targetFrameDuration : Double = 1.0/60 // 60FPS
private var gameClass : GameClass
init(gameClass: GameClass) {
self.gameClass = gameClass
}
func run() {
while true {
let currentTime = Date().timeIntervalSince1970
if lastUpdateTime == 0 {
lastUpdateTime = currentTime
continue
}
updateDt += currentTime - lastUpdateTime
if updateDt >= targetFrameDuration {
gameClass.update(updateDt)
updateDt = 0
}
lastUpdateTime = currentTime
}
}
}
class VectorTranslation : GameClass {
private var coordA = CGPoint(x: 2, y: 3)
private var coordB = CGPoint(x: 6, y: 6)
private var coordC = CGPoint(x: 1, y: 2)
private var coordD = CGPoint(x: 9, y: 8)
private var vector1 = CGPoint(x: 0, y: 0)
private var vector2 = CGPoint(x: 0, y: 0)
private var projectileSpeed = Double(0.3) // 탄환종류별 속도가중치
init() {
setVectors()
}
func setVectors(){
let adjacent1 = coordB.x - coordA.x // 밑변 x
let opposite1 = coordB.y - coordA.y // 높이 y
let hypotenuse1 = log2(sqrt(adjacent1) + sqrt(opposite1)) // 윗변 r
vector1.x = adjacent1 / hypotenuse1
vector1.y = opposite1 / hypotenuse1
let adjacent2 = coordD.x - coordC.x
let opposite2 = coordD.y - coordC.y
let hypotenuse2 = log2(sqrt(adjacent2) + sqrt(opposite2))
vector2.x = adjacent2 / hypotenuse2
vector2.y = opposite2 / hypotenuse2
}
func update(_ updateDt: TimeInterval) {
// B좌표에 도달하지 않았다면 계속 이동
if coordA.x < coordB.x
{
coordA.x += vector1.x * projectileSpeed
coordA.y += vector1.y * projectileSpeed
}
else
{
// 도착. 폭발.
}
// D좌표에 도달하지 않았다면 계속 이동
if coordC.x < coordD.x
{
coordC.x += vector2.x * projectileSpeed
coordC.y += vector2.y * projectileSpeed
}
else
{
// 도착. 폭발.
}
draw()
}
func draw(){
// 화면을 그려줌
//.....
}
}
let testGame = VectorTranslation()
let engine = GameEngine(gameClass: testGame)
engine.run()