좋아, 나는 모든 것이 작동하고 있고, 영원히 걸렸으므로 여기에 자세한 솔루션을 게시 할 것입니다.
참고 : 모든 코드 샘플은 JavaScript로되어 있습니다.
따라서 문제를 기본 부분으로 나누겠습니다.
0..1
베 지어 곡선 의 길이와 점 사이를 계산해야합니다.
이제 T
배를 한 속도에서 다른 속도로 가속 하기 위해 스케일링을 조정해야합니다
베 지어 권리 얻기
베 지어 곡선을 그리는 코드를 찾는 것은 쉽지만, 여러 가지 접근 방식이 있지만 그중 하나는 DeCasteljau Algorithm 이지만 입방 베 지어 곡선에 대한 방정식 을 사용할 수도 있습니다 .
// Part of a class, a, b, c, d are the four control points of the curve
x: function (t) {
return ((1 - t) * (1 - t) * (1 - t)) * this.a.x
+ 3 * ((1 - t) * (1 - t)) * t * this.b.x
+ 3 * (1 - t) * (t * t) * this.c.x
+ (t * t * t) * this.d.x;
},
y: function (t) {
return ((1 - t) * (1 - t) * (1 - t)) * this.a.y
+ 3 * ((1 - t) * (1 - t)) * t * this.b.y
+ 3 * (1 - t) * (t * t) * this.c.y
+ (t * t * t) * this.d.y;
}
이와 함께, 하나는 지금 호출하여 베 지어 곡선을 그릴 수 x
와 y
함께 t
범위하는 0 to 1
, 살펴 보겠습니다 :
어 .. 그건 사실 균등 한 분포가 아니에요?
베 지어 곡선의 특성으로 인해 점의 점이 0...1
서로 다르 arc lenghts
므로 시작과 끝 근처의 선분이 곡선의 중간에있는 선분보다 길다.
곡선 AKA 호 길이 매개 변수화에서 T를 균등하게 매핑
그래서 뭐 할까? 그럼 간단한 측면에서 우리는 우리를 매핑하는 기능이 필요 T
에 t
우리 있도록 곡선의 T 0.25
결과 t
그에서의 25%
곡선의 길이.
우리는 어떻게합니까? 글쎄, 우리는 구글 ...하지만 그 용어는 googleable 이 아니며 , 어느 시점 에서이 PDF 를 보게 될 것 입니다. 어느 쪽이 좋은지 잘 알지만 학교에서 배운 모든 수학 자료를 이미 잊어 버린 경우 (또는 수학 기호가 마음에 들지 않는 경우) 그것은 쓸모가 없습니다.
지금 무엇? 잘 가서 구글에 대해 더 읽어보십시오 (6 시간 읽기). 마침내 주제에 관한 훌륭한 기사 (멋진 그림 포함! ^ _ ^ ") :
http://www.planetclegg.com/projects/WarpingTextToSplines.html
실제 코드 수행
오래 전에 오래 전에 수학 지식을 잃어 버렸지 만 (그리고 훌륭한 기사 링크 를 건너 뛰어도) PDF를 다운로드하는 것을 막을 수없는 경우 , 이제 다음과 같이 생각할 수 있습니다. 수백 줄의 코드와 수많은 CPU "
아닙니다. 우리는 모든 프로그래머가하는 일을하기 때문에 수학에 관해서
는 간단히 속입니다.
게으른 방식으로 아크 길이 매개 변수화
우리는 게임에서 끝없는 정밀도가 필요하지 않습니까? 따라서 Nasa에서 일하고 화성을 사람들에게 보낼 계획이 아니라면 0.000001 pixel
완벽한 솔루션이 필요하지 않습니다 .
그래서 우리는 어떻게지도 할 T
에 t
? 간단하고 3 단계로만 구성됩니다.
를 N
사용하여 곡선의 점을 계산 하고 해당 위치 t
의 arc-length
(일명 곡선의 길이)를 배열에 저장
매핑하려면 T
에 t
처음 곱셈, T
얻을 수있는 곡선의 총 길이가 u
다음보다 작의 가장 큰 값의 인덱스에 대한 길이의 배열을 검색u
우리가 정확하게 맞았다면, 그 인덱스의 배열 값을로 나눈 값을 반환하고 N
, 찾은 점과 다음 점 사이의 비트를 보간하지 않으면 물건을 다시 한 번 나누고 N
반환하십시오.
그게 다야! 이제 전체 코드를 살펴 보겠습니다.
function Bezier(a, b, c, d) {
this.a = a;
this.b = b;
this.c = c;
this.d = d;
this.len = 100;
this.arcLengths = new Array(this.len + 1);
this.arcLengths[0] = 0;
var ox = this.x(0), oy = this.y(0), clen = 0;
for(var i = 1; i <= this.len; i += 1) {
var x = this.x(i * 0.05), y = this.y(i * 0.05);
var dx = ox - x, dy = oy - y;
clen += Math.sqrt(dx * dx + dy * dy);
this.arcLengths[i] = clen;
ox = x, oy = y;
}
this.length = clen;
}
이것은 우리의 새로운 곡선을 초기화하고를 계산하며 arg-lenghts
, 또한 마지막 길이를 total length
곡선 의 길이로 저장합니다 . 여기서 중요한 요소 this.len
는 우리의 N
입니다. 위의 그림에서 크기의 곡선 100 points
이 충분하기 때문에 매핑이 높을수록 더 정확할 것입니다. 길이가 길면 25
이미 1 픽셀 만 사용하여 작업을 수행 할 것입니다. 예,하지만 당신은 아니 그렇게 고른 분포가 발생합니다 덜 정확한 매핑해야합니다 T
매핑 할 때를 t
.
Bezier.prototype = {
map: function(u) {
var targetLength = u * this.arcLengths[this.len];
var low = 0, high = this.len, index = 0;
while (low < high) {
index = low + (((high - low) / 2) | 0);
if (this.arcLengths[index] < targetLength) {
low = index + 1;
} else {
high = index;
}
}
if (this.arcLengths[index] > targetLength) {
index--;
}
var lengthBefore = this.arcLengths[index];
if (lengthBefore === targetLength) {
return index / this.len;
} else {
return (index + (targetLength - lengthBefore) / (this.arcLengths[index + 1] - lengthBefore)) / this.len;
}
},
mx: function (u) {
return this.x(this.map(u));
},
my: function (u) {
return this.y(this.map(u));
},
실제 매핑 코드는 먼저 binary search
저장된 길이에 대해 간단한 것보다 작은 최대 길이를 찾은 targetLength
다음 보간 또는 보간 및 반환을 수행합니다.
x: function (t) {
return ((1 - t) * (1 - t) * (1 - t)) * this.a.x
+ 3 * ((1 - t) * (1 - t)) * t * this.b.x
+ 3 * (1 - t) * (t * t) * this.c.x
+ (t * t * t) * this.d.x;
},
y: function (t) {
return ((1 - t) * (1 - t) * (1 - t)) * this.a.y
+ 3 * ((1 - t) * (1 - t)) * t * this.b.y
+ 3 * (1 - t) * (t * t) * this.c.y
+ (t * t * t) * this.d.y;
}
};
다시 t
커브에서 계산 합니다.
결과 시간
지금 사용하여 mx
그리고 my
당신이 균등하게 분배받을 T
곡선에 :)
그렇게 힘들지 않습니까? 다시 한 번, 단순한 (완벽한 해결책은 아니지만) 게임으로 충분하다는 것이 밝혀졌습니다.
완전한 코드를보고 싶다면 Gist를 사용할 수 있습니다 :
https://gist.github.com/670236
마지막으로 배를 가속
이제 남은 것은 곡선 T
을 찾기 위해 사용 하는 위치를 매핑하여 경로를 따라 선박을 가속하는 t
것입니다.
먼저 우리는 두 가지가 필요 운동 방정식 , 즉 ut + 1/2at²
및(v - u) / t
실제 코드에서는 다음과 같습니다.
startSpeed = getStartingSpeedInPixels() // Note: pixels
endSpeed = getFinalSpeedInPixels() // Note: pixels
acceleration = (endSpeed - startSpeed) // since we scale to 0...1 we can leave out the division by 1 here
position = 0.5 * acceleration * t * t + startSpeed * t;
그런 다음 다음 0...1
을 수행 하여 축소합니다 .
maxPosition = 0.5 * acceleration + startSpeed;
newT = 1 / maxPosition * position;
그리고 거기서 당신은 배들이 길을 따라 부드럽게 움직이고 있습니다.
작동하지 않는 경우 ...
이 글을 읽을 때 모든 것이 잘 작동하고 멋지지만 처음에는 가속 부분에 문제가 있었는데 gamedev 대화방의 누군가에게 문제를 설명 할 때 내 생각에 최종 오류가 있음을 발견했습니다.
원래 질문의 그림에 대해 이미 잊어 버린 s
경우, s
속도가 도 단위로 밝혀 지지만 배는 경로를 따라 픽셀 로 이동 하고 그 사실을 잊어 버렸습니다. 그래서이 경우에해야 할 일은 픽셀 단위의 변위를 픽셀 단위의 변위로 변환하는 것이 었습니다.
function rotationToMovement(planetSize, rotationSpeed) {
var r = shipAngle * Math.PI / 180;
var rr = (shipAngle + rotationSpeed) * Math.PI / 180;
var orbit = planetSize + shipOrbit;
var dx = Math.cos(r) * orbit - Math.cos(rr) * orbit;
var dy = Math.sin(r) * orbit - Math.sin(rr) * orbit;
return Math.sqrt(dx * dx + dy * dy);
};
그리고 그게 다야! 읽어 주셔서 감사합니다 ;)