Page 1 of 1
Ennakkoon ampuminen
Posted: Tue Oct 02, 2012 4:55 pm
by Pate5
Hei,
minulla on eräässä projektissani ongelmana ennakon ottaminen ampuessa ja päätin tuoda ongelman foorumin matikkaguruille.
Minulla on kohteen 3-ulotteinen paikkavektori ja 3-ulotteinen nopeusvektori. Haluan saada selville, mihin suuntaan ammus täytyy ampua origosta, kun sen nopeus tiedetään.
A on kohteen paikkavektori
B on kohteen nopeusvektori
t on kohtaamiseen kuluva aika
C on ammuksen nopeusvektori, |C| tiedetään
t ja C ovat tuntemattomia.
Yritin ratkaista yhtälöä mutta en onnistunut kun en keksinyt, miten pystyn hyödyntämään |C|:tä. :(
Re: Ennakkoon ampuminen
Posted: Tue Oct 02, 2012 5:32 pm
by legend
Minun on ei pitäisi kysyä mitään, sillä en osaa kuitenkaaa vastata, mutta ihan uteliaisuuttani kysyn...
Tarkoitatko, että ampujalla A on kohde B. B:n sijainti, suunta ja nopeus tiedetään.
Pitäisi laskea kuinka paljon pitäisi ampua eteenpäin, jotta ammus osuisi B:tä.
Re: Ennakkoon ampuminen
Posted: Tue Oct 02, 2012 5:38 pm
by Jonez
legend, luulen että tässä tarkoitetaan että ampuja on origossa O, eli kohteen sijainti on
A, eli
O+A, ja kohteen tuleva sijainti seuraavassa updatessa on
A+B.
En ole vektoreita pitkään aikaan laskenut, mutta... Jos törmäyskohta on piste D, niin eikö
D =
A + r
B ja
D = t
C, tai jotain vastaavaa...
Tuostahan saisi yhtälöryhmällä t:n ja r:n, eikö...?
Edit. ainii eiku
C:tä ei tiedetty... No jos teet siitä yksikkövektorin tai jotain... En tiiä
Re: Ennakkoon ampuminen
Posted: Tue Oct 02, 2012 6:16 pm
by axu
Jonez wrote:legend, luulen että tässä tarkoitetaan että ampuja on origossa O, eli kohteen sijainti on
A, eli
O+A, ja kohteen tuleva sijainti seuraavassa updatessa on
A+B.
En ole vektoreita pitkään aikaan laskenut, mutta... Jos törmäyskohta on piste D, niin eikö
D =
A + r
B ja
D = t
C, tai jotain vastaavaa...
Tuostahan saisi yhtälöryhmällä t:n ja r:n, eikö...?
Tuota... mistäs tuo r pöllähti tuonne?
B:llähän on tietenkin sama kerroin kuin
C:llä, eli aika ampumisesta osumiseen (kuten Pate5:n kuvassa olikin). Tästä seuraa, että
A + t
B = t
C. Vielä en keksinyt, miten
C:n pituuden tietäminen auttaa, mutta selvästi se on ratkaisevassa roolissa.
Re: Ennakkoon ampuminen
Posted: Tue Oct 02, 2012 11:55 pm
by koodaaja
Ongelma on varsin mielenkiintoinen, mutta lähestymistapa ei ehkä se kaikista helpoin. Kuten kaavastasi näkee, t|C| muodostaa origokeskisen pallon, jonka voi lausua myös seuraavasti: sqrt(x²+y²+z²) = r <=> x²+y²+z² = r² <=> (ax+bx*t)²+(ay+by*t)²+(az+bz*t)² = t²|C|² <=> ax²+ax*bx*t+bx²t²+ay²+ay*by*t+by²t²+az²+az*bz*t+bz²t² - t²|C|² = 0 <=> t²(bx²+by²+bz²-|C|²)+t(ax*bx+ay*by+az*bz)+ax²+ay²+az², joka on toisen asteen yhtälö muotoa at²+bt+c=0, jossa a,b ja c ovat kaikki vakiollisia, joten sen menee suoraan t = (-b(+-)sqrt(b²-4ac))/2a, mihin kannattaa ohjelmoidessa käyttää jopa välimuuttujia. Neliöjuuren sisäinen diskriminantti (kiitos cce:lle terminologiamuistutuksesta!) b²-4ac kertoo törmäyksistä, sen ollessa negatiivinen ei ammus ehdi enää mitenkään osumaan, sen ollessa 0 se ehtii juuri yhteen pisteeseen ja diskriminantin positiivisilla arvoilla mahdollisia törmäyskohtia on kaksi. Kannattanee myös tarkistaa, että osuminen tapahtuu varmasti tulevaisuudessa, muuten kaava voi antaa t:lle negatiivisenkin arvon. Parhaan tuloksen takaamiseksi kannattanee myös kahden mahdollisuuden tapauksessa valita aiempi vielä tulevaisuudessa oleva jotta osuma tapahtuu mahdollisimman pian. Ajan laskemisen jälkeen tunnetaankin etäisyys (a+b*t) ja aika (t), joten ammuksen nopeusvektori saadaan yksinkertaisesti näistä jakolaskulla (s = vt <=> v = s/t). Esimerkki seuraa.
Code: Select all
SCREEN 640, 480
Type pari
Field tx#
Field ty#
Field tz#
Field txv#
Field tyv#
Field tzv#
Field px#
Field py#
Field pz#
Field pxv#
Field pyv#
Field pzv#
Field t0#
Field ct#
EndType
start = Timer()
Repeat
ft# = (Timer()-start)*.001
If Timer()>t
p.pari = New(pari)
p\tx = Rnd(-80,-40)
p\ty = Rnd(-10, 10)
p\tz = Rnd(-10, 10)
p\txv = Rnd(5, 10)
p\tyv = Rnd(-2, 2)
p\tzv = Rnd(-2, 2)
p\px = Rnd(-5, 5)
p\py = Rnd(-16,-15)
p\pz = Rnd(-15,-13)
ax# = p\tx-p\px
ay# = p\ty-p\py
az# = p\tz-p\pz
bx# = p\txv
by# = p\tyv
bz# = p\tzv
s# = Rnd(8,20) // projectile speed
a# = bx*bx+by*by+bz*bz-s*s
b# = ax*bx+ay*by+az*bz
c# = ax*ax+ay*ay+az*az
d# = b*b-4*a*c
If d<.0 Then
Delete p // no hits, don't shoot
Else
If d>.0 Then
ct1# = (-b-Sqrt(d))/(2*a)
ct2# = (-b+Sqrt(d))/(2*a)
If ct1>.0 Then
p\ct = ct1
If ct2>.0 And ct2<ct1 Then p\ct = ct2
Else
p\ct = ct2
EndIf
ElseIf d = .0 Then
p\ct = -b/(2*a)
EndIf
p\pxv = (p\tx+p\txv*p\ct-p\px)/p\ct
p\pyv = (p\ty+p\tyv*p\ct-p\py)/p\ct
p\pzv = (p\tz+p\tzv*p\ct-p\pz)/p\ct
p\t0 = ft
SetWindow Str(p\ct)
If p\ct < .0 Then Delete p // no point in shooting
EndIf
t = Timer()+1000
EndIf
k = 0
For p.pari = Each pari
pt# = ft-p\t0
s1# = 50.0/(20.0+p\tz+p\tzv*pt)
s2# = 50.0/(20.0+p\pz+p\pzv*pt)
Circle 320+s1*(p\tx+p\txv*pt-3), 240-s1*(p\ty+p\tyv*pt-3), 6*s1
Circle 320+s2*(p\px+p\pxv*pt-3), 240-s2*(p\py+p\pyv*pt-3), 6*s2
If pt>p\ct Then Delete p
k = k + 1
Next p
DrawScreen
Forever
Re: Ennakkoon ampuminen
Posted: Wed Oct 03, 2012 12:04 am
by naputtelija
koodaaja wrote:Ongelma on varsin mielenkiintoinen, mutta lähestymistapa ei ehkä se kaikista helpoin. Kuten kaavastasi näkee, t|C| muodostaa origokeskisen ympyrän, jonka voi lausua myös seuraavasti: sqrt(x²+y²+z²) = r <=> x²+y²+z² = r² <=> (ax+bx*t)²+(ay+by*t)²+(az+bz*t)² = t²|C|² <=> ax²+ax*bx*t+bx²t²+ay²+ay*by*t+by²t²+az²+az*bz*t+bz²t² - t²|C|² = 0 <=> t²(bx²+by²+bz²-|C|²)+t(ax*bx+ay*by+az*bz)+ax²+ay²+az², joka on toisen asteen yhtälö muotoa at²+bt+c=0, jossa a,b ja c ovat kaikki vakiollisia, joten sen menee suoraan t = (-b(+-)sqrt(b²-4ac))/2a, mihin kannattaa ohjelmoidessa käyttää jopa välimuuttujia. Neliöjuuren sisäinen diskriminantti (kiitos cce:lle terminologiamuistutuksesta!) b²-4ac kertoo törmäyksistä, sen ollessa negatiivinen ei ammus ehdi enää mitenkään osumaan, sen ollessa 0 se ehtii juuri yhteen pisteeseen ja diskriminantin positiivisilla arvoilla mahdollisia törmäyskohtia on kaksi. Kannattanee myös tarkistaa, että osuminen tapahtuu varmasti tulevaisuudessa, muuten kaava voi antaa t:lle negatiivisenkin arvon. Parhaan tuloksen takaamiseksi kannattanee myös kahden mahdollisuuden tapauksessa valita aiempi vielä tulevaisuudessa oleva jotta osuma tapahtuu mahdollisimman pian. Esimerkki seuraa.
Code: Select all
SCREEN 640, 480
Type pari
Field tx#
Field ty#
Field tz#
Field txv#
Field tyv#
Field tzv#
Field px#
Field py#
Field pz#
Field pxv#
Field pyv#
Field pzv#
Field t0#
Field ct#
EndType
start = Timer()
Repeat
ft# = (Timer()-start)*.001
If Timer()>t
p.pari = New(pari)
p\tx = Rnd(-80,-40)
p\ty = Rnd(-10, 10)
p\tz = Rnd(-10, 10)
p\txv = Rnd(5, 10)
p\tyv = Rnd(-2, 2)
p\tzv = Rnd(-2, 2)
p\px = Rnd(-5, 5)
p\py = Rnd(-16,-15)
p\pz = Rnd(-15,-13)
ax# = p\tx-p\px
ay# = p\ty-p\py
az# = p\tz-p\pz
bx# = p\txv
by# = p\tyv
bz# = p\tzv
s# = Rnd(8,20) // projectile speed
a# = bx*bx+by*by+bz*bz-s*s
b# = ax*bx+ay*by+az*bz
c# = ax*ax+ay*ay+az*az
d# = b*b-4*a*c
If d<.0 Then
Delete p // no hits, don't shoot
Else
If d>.0 Then
ct1# = (-b-Sqrt(d))/(2*a)
ct2# = (-b+Sqrt(d))/(2*a)
If ct1>.0 Then
p\ct = ct1
If ct2>.0 And ct2<ct1 Then p\ct = ct2
Else
p\ct = ct2
EndIf
ElseIf d = .0 Then
p\ct = -b/(2*a)
EndIf
p\pxv = (p\tx+p\txv*p\ct-p\px)/p\ct
p\pyv = (p\ty+p\tyv*p\ct-p\py)/p\ct
p\pzv = (p\tz+p\tzv*p\ct-p\pz)/p\ct
p\t0 = ft
SetWindow Str(p\ct)
If p\ct < .0 Then Delete p // no point in shooting
EndIf
t = Timer()+1000
EndIf
k = 0
For p.pari = Each pari
pt# = ft-p\t0
s1# = 50.0/(20.0+p\tz+p\tzv*pt)
s2# = 50.0/(20.0+p\pz+p\pzv*pt)
Circle 320+s1*(p\tx+p\txv*pt-3), 240-s1*(p\ty+p\tyv*pt-3), 6*s1
Circle 320+s2*(p\px+p\pxv*pt-3), 240-s2*(p\py+p\pyv*pt-3), 6*s2
If pt>p\ct Then Delete p
k = k + 1
Next p
DrawScreen
Forever
Epic solve, dear koodaaja. Epic solve indeed.
Re: Ennakkoon ampuminen
Posted: Wed Oct 03, 2012 2:34 pm
by valscion
koodaaja wrote:Ongelma on varsin mielenkiintoinen, mutta lähestymistapa ei ehkä se kaikista helpoin. Kuten kaavastasi näkee, t|C| muodostaa origokeskisen pallon, jonka voi lausua myös seuraavasti: sqrt(x²+y²+z²) = r <=> x²+y²+z² = r² <=> (ax+bx*t)²+(ay+by*t)²+(az+bz*t)² = t²|C|² <=> ax²+ax*bx*t+bx²t²+ay²+ay*by*t+by²t²+az²+az*bz*t+bz²t² - t²|C|² = 0 <=>
t²(bx²+by²+bz²-|C|²)+t(ax*bx+ay*by+az*bz)+ax²+ay²+az², joka on toisen asteen yhtälö muotoa at²+bt+c=0, jossa a,b ja c ovat kaikki vakiollisia, joten sen menee suoraan t = (-b(+-)sqrt(b²-4ac))/2a, mihin kannattaa ohjelmoidessa käyttää jopa välimuuttujia. Neliöjuuren sisäinen diskriminantti (kiitos cce:lle terminologiamuistutuksesta!) b²-4ac kertoo törmäyksistä, sen ollessa negatiivinen ei ammus ehdi enää mitenkään osumaan, sen ollessa 0 se ehtii juuri yhteen pisteeseen ja diskriminantin positiivisilla arvoilla mahdollisia törmäyskohtia on kaksi. Kannattanee myös tarkistaa, että osuminen tapahtuu varmasti tulevaisuudessa, muuten kaava voi antaa t:lle negatiivisenkin arvon. Parhaan tuloksen takaamiseksi kannattanee myös kahden mahdollisuuden tapauksessa valita aiempi vielä tulevaisuudessa oleva jotta osuma tapahtuu mahdollisimman pian. Ajan laskemisen jälkeen tunnetaankin etäisyys (a+b*t) ja aika (t), joten ammuksen nopeusvektori saadaan yksinkertaisesti näistä jakolaskulla (s = vt <=> v = s/t). Esimerkki seuraa.
Mietinkin että ongelmalle olisi varmasti jokin helposti ymmärrettävä ratkaisu ja olin oikeassa
olen iloinen kun kerrankin ymmärsin kaiken selityksestä alusta loppuun asti. Lihavoin ja suurensin muuten koodaaja yhtä kohtaa laskuistasi, jossa olit vielä kerran unohtanut merkitä lausekkeen olevan yhtäsuuri nollan kanssa.
Re: Ennakkoon ampuminen
Posted: Wed Oct 03, 2012 3:56 pm
by Pate5
Kiitos loistavasta vastauksesta koodaaja!
Re: Ennakkoon ampuminen
Posted: Wed Oct 03, 2012 7:09 pm
by koodaaja
Haa, tiesihän sen että joku virhe sinne jää! Hatunnosto VesQlle siitä, että ylipäänsä luit kaavan johtamisen noin tarkasti. Huomionarvoista on myös, että kun pallon säde pidetään vakiona, ei tapahdu muuta kuin että -|C|² hyppää t²-termistä vakiotermiin, ja saadaan pallon ja säteen törmäyksen kaava, jonka tajusin lopullisen kaavan saatuani myös johtaneeni muutamaan kertaan raytrace-juttuja viritellessäni, joten ongelma oli ainakin alitajuisesti tuttu.
Mutta kuten sanottua, ongelma on usein helpoin ratkaista muuttamalla sitä!