2d 게임:화재에서 움직이는 대상을 예측하여 교차로의 발사체 및 단위
-
20-09-2019 - |
문제
좋아요 이 모든 걸리는 곳에는 좋은 간단한 2D 세계...:)
한 두 가지가 아닌 정적 객체에 위치 Apos,그리고 선형으로 이동체에서 B Bpos 와 bVelocity,와 탄약은 라운드와 속도 Avelocity...
는 방법을 찾겠 각도는 있을 촬영을 칠 B,고려 B's 선속도 및 속도의 탄?
지금 목적의 현재 위치에 있는 객체의 것을 의미하는 시간으로 내 발사체가의 단위에 이동하는 안전 위치:)
해결책
먼저 AB가 세로가되도록 축을 회전시킵니다 (회전을 통해)
이제 B의 속도 벡터를 X 및 Y 구성 요소 (BX 및 BY)로 분할하십시오. 이것을 사용하여 촬영 해야하는 벡터의 X 및 Y 구성 요소를 계산할 수 있습니다.
B --> Bx
|
|
V
By
Vy
^
|
|
A ---> Vx
당신은 필요합니다 Vx = Bx
그리고 Sqrt(Vx*Vx + Vy*Vy) = Velocity of Ammo
.
이것은 새로운 시스템에서 필요한 벡터를 제공해야합니다. 오래된 시스템으로 다시 변환하면 (다른 방향으로 회전을함으로써) 완료됩니다.
다른 팁
내가 쓴 것을 목표로에 대한 서브 루틴 xtank 입니다.내가 노력하는 배치했던 방법니다.
면책 조항: 내가 만들 수 있습니다 하나 더 많은 어리석은 실수를 어디서나 여기에서;나는 단지를 재구성하려는 추론을 가진 나 녹스는 수학 능력입니다.그러나 나는 절단을 쫓아 첫째로,이 프로그래밍 Q&A 대 수학 수업:-)
그것을 어떻게
귀결하는 해결 방정식의 형태로 되어 있습니다.
a * sqr(x) + b * x + c == 0
참고는여 sqr
I mean square,반대로 사각형 뿌리입니다.다음과 같은 값을 사용:
a := sqr(target.velocityX) + sqr(target.velocityY) - sqr(projectile_speed)
b := 2 * (target.velocityX * (target.startX - cannon.X)
+ target.velocityY * (target.startY - cannon.Y))
c := sqr(target.startX - cannon.X) + sqr(target.startY - cannon.Y)
지금 우리가 볼 수 있는 판별하여 결정하는 경우 우리가 가능한 솔루션입니다.
disc := sqr(b) - 4 * a * c
는 경우 판별 0 보다 작은 잊을 타격의 대상을-당신의 발사체하지 못할 수 있는 시간입니다.그렇지 않으면을 봐 두 후보는 솔루션:
t1 := (-b + sqrt(disc)) / (2 * a)
t2 := (-b - sqrt(disc)) / (2 * a)
참고하는 경우 disc == 0
다음 t1
고 t2
같습니다.
이 없는 경우 다른 고려 사항 등으로 개입 장애물,단순히 선택한 작은 긍정적인 값입니다.(정 t 값을 필요로 발 뒤에서 사용하는 시간!)
대체 선택 t
값으로 다시 대상의 위치를 방정식을 얻을 수의 좌표를 선도하는 점을 목표로해야한다:
aim.X := t * target.velocityX + target.startX
aim.Y := t * target.velocityY + target.startY
유도
T 시간에서,발사체해야(유클리드)거리 대포에서 같은 경과 시간을 곱하여 발사체의 속도입니다.이 제공한 방정식,원형 파라미터에 표시됩니다.
sqr(projectile.X - cannon.X) + sqr(projectile.Y - cannon.Y)
== sqr(t * projectile_speed)
마찬가지로,시간,대상이 따라 이동 벡터에 의해 시간을 곱한 값으로 속도:
target.X == t * target.velocityX + target.startX
target.Y == t * target.velocityY + target.startY
발사체를 칠 수 있는 대상을 때의 거리 대포에서 일치하는 발사체의 거리입니다.
sqr(projectile.X - cannon.X) + sqr(projectile.Y - cannon.Y)
== sqr(target.X - cannon.X) + sqr(target.Y - cannon.Y)
Wonderful!대체하는 표현에 대한 대상입니다.X 습니다.Y 제공
sqr(projectile.X - cannon.X) + sqr(projectile.Y - cannon.Y)
== sqr((t * target.velocityX + target.startX) - cannon.X)
+ sqr((t * target.velocityY + target.startY) - cannon.Y)
대체 방정식의 다른 측면이 제공:
sqr(t * projectile_speed)
== sqr((t * target.velocityX + target.startX) - cannon.X)
+ sqr((t * target.velocityY + target.startY) - cannon.Y)
...빼 sqr(t * projectile_speed)
양쪽에서 내리고 그 주위:
sqr((t * target.velocityX) + (target.startX - cannon.X))
+ sqr((t * target.velocityY) + (target.startY - cannon.Y))
- sqr(t * projectile_speed)
== 0
...이제를 해결합한 결과를 제곱 하위 식...
sqr(target.velocityX) * sqr(t)
+ 2 * t * target.velocityX * (target.startX - cannon.X)
+ sqr(target.startX - cannon.X)
+ sqr(target.velocityY) * sqr(t)
+ 2 * t * target.velocityY * (target.startY - cannon.Y)
+ sqr(target.startY - cannon.Y)
- sqr(projectile_speed) * sqr(t)
== 0
...고 그룹 유사한다.
sqr(target.velocityX) * sqr(t)
+ sqr(target.velocityY) * sqr(t)
- sqr(projectile_speed) * sqr(t)
+ 2 * t * target.velocityX * (target.startX - cannon.X)
+ 2 * t * target.velocityY * (target.startY - cannon.Y)
+ sqr(target.startX - cannon.X)
+ sqr(target.startY - cannon.Y)
== 0
...그런 다음 그들을 결합하...
(sqr(target.velocityX) + sqr(target.velocityY) - sqr(projectile_speed)) * sqr(t)
+ 2 * (target.velocityX * (target.startX - cannon.X)
+ target.velocityY * (target.startY - cannon.Y)) * t
+ sqr(target.startX - cannon.X) + sqr(target.startY - cannon.Y)
== 0
...을 주는 표준 방정식에서 t.을 찾는 긍정적인 실제 제이 방정식을 제공합(제로,하나 또는 두 개의)가 충돌 위치를 수행할 수 있습 차 formula:
a * sqr(x) + b * x + c == 0
x == (-b ± sqrt(sqr(b) - 4 * a * c)) / (2 * a)
Jeffrey Hantin의 훌륭한 답변에서 +1. 나는 Google을 Google으로하여 내가 관심이있는 경우 (2D 공간의 단순한 일정한 속도 발사체)에 대해 너무 복잡한 솔루션을 찾았습니다. 그는 자체 포함 된 JavaScript 솔루션을 생산하는 데 필요한 것입니다.
내가 추가 할 한 가지 요점은 판별자가 부정적이라는 몇 가지 특별한 경우가 있다는 것입니다.
- "A == 0": 대상과 발사체가 같은 속도로 이동하는 경우 발생합니다. (솔루션은 2 차가 아닌 선형입니다)
- "a == 0 및 b == 0": 대상과 발사체가 모두 고정 된 경우. (C == 0, 즉 SRC & DST가 동일하지 않는 한 해결책이 없습니다.)
암호:
/**
* Return the firing solution for a projectile starting at 'src' with
* velocity 'v', to hit a target, 'dst'.
*
* @param Object src position of shooter
* @param Object dst position & velocity of target
* @param Number v speed of projectile
* @return Object Coordinate at which to fire (and where intercept occurs)
*
* E.g.
* >>> intercept({x:2, y:4}, {x:5, y:7, vx: 2, vy:1}, 5)
* = {x: 8, y: 8.5}
*/
function intercept(src, dst, v) {
var tx = dst.x - src.x,
ty = dst.y - src.y,
tvx = dst.vx,
tvy = dst.vy;
// Get quadratic equation components
var a = tvx*tvx + tvy*tvy - v*v;
var b = 2 * (tvx * tx + tvy * ty);
var c = tx*tx + ty*ty;
// Solve quadratic
var ts = quad(a, b, c); // See quad(), below
// Find smallest positive solution
var sol = null;
if (ts) {
var t0 = ts[0], t1 = ts[1];
var t = Math.min(t0, t1);
if (t < 0) t = Math.max(t0, t1);
if (t > 0) {
sol = {
x: dst.x + dst.vx*t,
y: dst.y + dst.vy*t
};
}
}
return sol;
}
/**
* Return solutions for quadratic
*/
function quad(a,b,c) {
var sol = null;
if (Math.abs(a) < 1e-6) {
if (Math.abs(b) < 1e-6) {
sol = Math.abs(c) < 1e-6 ? [0,0] : null;
} else {
sol = [-c/b, -c/b];
}
} else {
var disc = b*b - 4*a*c;
if (disc >= 0) {
disc = Math.sqrt(disc);
a = 2*a;
sol = [(-b-disc)/a, (-b+disc)/a];
}
}
return sol;
}
Jeffrey Hantin 은이 문제에 대한 좋은 해결책을 가지고 있지만 그의 파생은 지나치게 복잡합니다. 다음은 맨 아래의 결과 코드로이를 도출하는 더 깨끗한 방법입니다.
나는 XY를 사용하여 벡터 도트 생성물을 나타냅니다. 벡터 수량이 제곱되면 그 자체로 점을 찍고 있음을 의미합니다.
origpos = initial position of shooter
origvel = initial velocity of shooter
targpos = initial position of target
targvel = initial velocity of target
projvel = velocity of the projectile relative to the origin (cause ur shooting from there)
speed = the magnitude of projvel
t = time
우리는 t
시간은 일부 방정식으로 설명 할 수 있습니다.
curprojpos(t) = origpos + t*origvel + t*projvel
curtargpos(t) = targpos + t*targvel
우리는 이것들이 어느 시점 (교차점)에서 서로 동등하기를 원하므로 서로 동등하게 설정하고 자유 변수를 해결해 봅시다. projvel
.
origpos + t*origvel + t*projvel = targpos + t*targvel
turns into ->
projvel = (targpos - origpos)/t + targvel - origvel
원래의 개념과 목표 위치/속도를 잊어 봅시다. 대신, 한 가지의 움직임은 다른 것과 관련이 있기 때문에 상대적인 용어로 일합시다. 이 경우, 우리가 지금 가지고있는 것은입니다 relpos = targetpos - originpos
그리고 relvel = targetvel - originvel
projvel = relpos/t + relvel
우리는 무엇을 모른다 projvel
그러나 우리는 우리가 원하는 것을 알고 있습니다 projvel.projvel
동일합니다 speed^2
, 그래서 우리는 양쪽을 제곱하고 우리는 얻을 수 있습니다
projvel^2 = (relpos/t + relvel)^2
expands into ->
speed^2 = relvel.relvel + 2*relpos.relvel/t + relpos.relpos/t^2
이제 유일한 자유 변수는 시간임을 알 수 있습니다. t
, 그런 다음 우리는 사용할 것입니다 t
해결하기 위해 projvel
. 우리는 해결할 것입니다 t
2 차 공식으로. 먼저 그것을 분리하십시오 a
, b
그리고 c
, 그런 다음 뿌리를 해결하십시오.
그러나 해결하기 전에 우리는 최고의 솔루션을 원한다는 것을 기억하십시오. t
가장 작지만 우리는 t
부정적이지 않습니다 (과거에는 무언가를 때릴 수 없습니다)
a = relvel.relvel - speed^2
b = 2*relpos.relvel
c = relpos.relpos
h = -b/(2*a)
k2 = h*h - c/a
if k2 < 0, then there are no roots and there is no solution
if k2 = 0, then there is one root at h
if 0 < h then t = h
else, no solution
if k2 > 0, then there are two roots at h - k and h + k, we also know r0 is less than r1.
k = sqrt(k2)
r0 = h - k
r1 = h + k
we have the roots, we must now solve for the smallest positive one
if 0<r0 then t = r0
elseif 0<r1 then t = r1
else, no solution
이제 우리가 있다면 t
값, 우리는 플러그를 꽂을 수 있습니다 t
원래 방정식으로 돌아가서 해결하십시오 projvel
projvel = relpos/t + relvel
이제 발사체를 촬영하기 위해 발사체의 결과적인 글로벌 위치와 속도는 다음과 같습니다.
globalpos = origpos
globalvel = origvel + projvel
그리고 당신은 끝났습니다!
LUA에서 솔루션을 구현하는데, 여기서 vec*vec은 벡터 도트 제품을 나타냅니다.
local function lineartrajectory(origpos,origvel,speed,targpos,targvel)
local relpos=targpos-origpos
local relvel=targvel-origvel
local a=relvel*relvel-speed*speed
local b=2*relpos*relvel
local c=relpos*relpos
if a*a<1e-32 then--code translation for a==0
if b*b<1e-32 then
return false,"no solution"
else
local h=-c/b
if 0<h then
return origpos,relpos/h+targvel,h
else
return false,"no solution"
end
end
else
local h=-b/(2*a)
local k2=h*h-c/a
if k2<-1e-16 then
return false,"no solution"
elseif k2<1e-16 then--code translation for k2==0
if 0<h then
return origpos,relpos/h+targvel,h
else
return false,"no solution"
end
else
local k=k2^0.5
if k<h then
return origpos,relpos/(h-k)+targvel,h-k
elseif -k<h then
return origpos,relpos/(h+k)+targvel,h+k
else
return false,"no solution"
end
end
end
end
다음은 C ++의 극지 좌표 기반 조준 코드입니다.
직사각형 좌표와 함께 사용하려면 먼저 대상 상대 좌표를 각도/거리로 변환하고 x/y 속도를 각도/속도로 변환해야합니다.
"속도"입력은 발사체의 속도입니다. 속도와 목표 속도의 단위는 속도의 비율 만 계산에 사용되므로 관련이 없습니다. 출력은 발사체가 발사되어야하는 각도와 충돌 지점까지의 거리입니다.
알고리즘은 소스 코드에서 제공됩니다 http://www.turtlewar.org/ .
// C++
static const double pi = 3.14159265358979323846;
inline double Sin(double a) { return sin(a*(pi/180)); }
inline double Asin(double y) { return asin(y)*(180/pi); }
bool/*ok*/ Rendezvous(double speed,double targetAngle,double targetRange,
double targetDirection,double targetSpeed,double* courseAngle,
double* courseRange)
{
// Use trig to calculate coordinate of future collision with target.
// c
//
// B A
//
// a C b
//
// Known:
// C = distance to target
// b = direction of target travel, relative to it's coordinate
// A/B = ratio of speed and target speed
//
// Use rule of sines to find unknowns.
// sin(a)/A = sin(b)/B = sin(c)/C
//
// a = asin((A/B)*sin(b))
// c = 180-a-b
// B = C*(sin(b)/sin(c))
bool ok = 0;
double b = 180-(targetDirection-targetAngle);
double A_div_B = targetSpeed/speed;
double C = targetRange;
double sin_b = Sin(b);
double sin_a = A_div_B*sin_b;
// If sin of a is greater than one it means a triangle cannot be
// constructed with the given angles that have sides with the given
// ratio.
if(fabs(sin_a) <= 1)
{
double a = Asin(sin_a);
double c = 180-a-b;
double sin_c = Sin(c);
double B;
if(fabs(sin_c) > .0001)
{
B = C*(sin_b/sin_c);
}
else
{
// Sin of small angles approach zero causing overflow in
// calculation. For nearly flat triangles just treat as
// flat.
B = C/(A_div_B+1);
}
// double A = C*(sin_a/sin_c);
ok = 1;
*courseAngle = targetAngle+a;
*courseRange = B;
}
return ok;
}
다음은 재귀 알고리즘을 사용하여 예측 타겟팅 문제에 대한 솔루션을 고안하고 구현 한 예입니다. http://www.newarteest.com/flash/targeting.html
한 단계로 계산하는 것이 더 효율적으로 보이기 때문에 제시된 다른 솔루션 중 일부를 시험해보아야하지만, 제가 생각해 낸 솔루션은 목표 위치를 추정하고 그 결과가 알고리즘으로 다시 피드를 추정하는 것이 었습니다. 더 정확한 추정치가 여러 번 반복됩니다.
첫 번째 추정치의 경우, 대상의 현재 위치에서 "발사"를 한 다음 삼각법을 사용하여 샷이 해고 된 위치에 도달 할 때 대상이 어디에 있는지 확인합니다. 그런 다음 다음 반복에서 나는 그 새로운 위치에서 "발사"하고 이번에는 대상이 어디에 있을지 결정합니다. 약 4 회 반복 후 나는 정확도의 픽셀 내에 들어갑니다.
방금 2D 공간을 조준하기 위해이 버전을 해킹했는데 아직 철저히 테스트하지는 않았지만 작동하는 것 같습니다. 그 뒤에있는 아이디어는 이것입니다.
총구에서 대상으로 가리키는 벡터에 수직 인 벡터를 만듭니다. 충돌이 발생하기 위해서는 대상의 속도 와이 벡터 (축)의 발사체가 동일해야합니다! 상당히 간단한 코사인 물건을 사용 하여이 코드에 도착했습니다.
private Vector3 CalculateProjectileDirection(Vector3 a_MuzzlePosition, float a_ProjectileSpeed, Vector3 a_TargetPosition, Vector3 a_TargetVelocity)
{
// make sure it's all in the horizontal plane:
a_TargetPosition.y = 0.0f;
a_MuzzlePosition.y = 0.0f;
a_TargetVelocity.y = 0.0f;
// create a normalized vector that is perpendicular to the vector pointing from the muzzle to the target's current position (a localized x-axis):
Vector3 perpendicularVector = Vector3.Cross(a_TargetPosition - a_MuzzlePosition, -Vector3.up).normalized;
// project the target's velocity vector onto that localized x-axis:
Vector3 projectedTargetVelocity = Vector3.Project(a_TargetVelocity, perpendicularVector);
// calculate the angle that the projectile velocity should make with the localized x-axis using the consine:
float angle = Mathf.Acos(projectedTargetVelocity.magnitude / a_ProjectileSpeed) / Mathf.PI * 180;
if (Vector3.Angle(perpendicularVector, a_TargetVelocity) > 90.0f)
{
angle = 180.0f - angle;
}
// rotate the x-axis so that is points in the desired velocity direction of the projectile:
Vector3 returnValue = Quaternion.AngleAxis(angle, -Vector3.up) * perpendicularVector;
// give the projectile the correct speed:
returnValue *= a_ProjectileSpeed;
return returnValue;
}
나는이 문제를 수학적으로 해결하는 여러 가지 방법을 보았지만, 이것은 내 수업이 고등학교에서해야 할 프로젝트와 관련된 구성 요소였으며,이 프로그래밍 수업의 모든 사람이 미적분학을 가진 배경을 가지고 있지는 않았거나 그 문제에 대한 벡터가 없었습니다. 그래서 나는 더 많은 프로그래밍 접근법 으로이 문제를 해결할 수있는 방법을 만들었습니다. 교차점의 지점은 정확합니다. 그러나 수학 계산보다 늦게 1 프레임에 도달 할 수 있습니다.
고려하다:
S = shooterPos, E = enemyPos, T = targetPos, Sr = shooter range, D = enemyDir
V = distance from E to T, P = projectile speed, Es = enemy speed
이 문제의 표준 구현에서 [s, e, p, es, d]는 모두 givens이며, 당신은 t 또는 촬영할 각도를 찾아서 적절한 타이밍에서 t를 때릴 수 있습니다.
문제를 해결하는이 방법의 주요 측면은 슈터의 범위를 주어진 시간에 촬영할 수있는 모든 가능한 포인트를 포괄하는 원으로 간주하는 것입니다. 이 원의 반경은 다음과 같습니다.
Sr = P*time
시간이 루프 반복으로 계산되는 경우.
따라서 우리는 벡터를 만듭니다.
V = D*Es*time
이제 실제로 문제를 해결하기 위해 대상 (T)에서 사수 (S)까지의 거리가 사수 (SR)의 범위보다 적은 지점을 찾고자합니다. 다음은이 방정식의 유사 코드 구현입니다.
iteration = 0;
while(TargetPoint.hasNotPassedShooter)
{
TargetPoint = EnemyPos + (EnemyMovementVector)
if(distanceFrom(TargetPoint,ShooterPos) < (ShooterRange))
return TargetPoint;
iteration++
}
여기에서 공개 도메인 Unity C# 기능을 만들었습니다.
http://ringofblades.com/blades/code/predictiveaim.cs
3D이지만 벡터3을 벡터 2로 교체하고 중력이있는 경우 중력을 위해 선택 축을 사용하여 2D에 대해 쉽게 수정할 수 있습니다.
이론이 당신에게 관심을 갖는 경우, 나는 여기서 수학의 파생을 안내합니다.
http://www.gamasutra.com/blogs/kainshin/20090515/83954/predictive_aim_mathematics_for_ai_targeting.php
기본적으로, 발사체 모션을 사용하는 한, 교차로 개념은 실제로 필요하지 않습니다. 특정 각도로 치기 만하면 촬영시 인스턴스화하여 소스에서 대상의 정확한 거리를 얻을 수 있습니다. 거리가 있으면 대상을 치기 위해 촬영 해야하는 적절한 속도를 계산할 수 있습니다.
다음 링크는 개념을 명확하게하고 도움이되는 것으로 간주되며 도움이 될 수 있습니다.항상 움직이는 대상에 부딪히는 발사체 움직임
나는 여기에서 솔루션 중 하나를 잡았지만, 그들 중 누구도 사수의 움직임을 고려하지 않습니다. 사수가 움직이면, 당신은 그것을 고려할 수 있습니다 (사수의 속도가 발사 될 때 총알의 속도에 추가되어야 함). 실제로 당신이해야 할 일은 대상의 속도에서 사수의 속도를 빼기 만하면됩니다. 따라서 위의 Broofa의 코드를 사용하는 경우 (권장 할 것입니다) 라인을 변경하십시오.
tvx = dst.vx;
tvy = dst.vy;
에게
tvx = dst.vx - shooter.vx;
tvy = dst.vy - shooter.vy;
그리고 당신은 모두 설정되어야합니다.