2012-03-21 7 views
11

Pracuję nad grą w przestrzeni 3D z niektórymi ludźmi, a jedną z rzeczy, do których zostałem przydzielony, jest stworzenie komputerowego "tunelu" prowadzącego statek przejeżdża, z tunelem wykonanym z kwadratów, które użytkownik leci do celu, zwiększając jego liczbę, gdy użytkownik zbliża się do celu.Kierowanie komputerowym symulatorem statku kosmicznego z koncentrycznymi polami wskaźnika

Konieczne jest jedynie renderowanie kwadratów dla punktów znajdujących się przed statkiem, ponieważ to wszystko jest widoczne dla użytkownika. W drodze do miejsca docelowego komputer pokładowy ma postawić kwadraty na HUD, które reprezentują stałe punkty w przestrzeni między tobą a celem, które są małe w oddali i stają się większe, gdy punkty zbliżają się do jednostki.

guidance squares example

miałem iść na realizację tego i nie wydaje się zrozumieć to, głównie za pomocą logarytmów (Math.log10(x) i takie). Próbowałem uzyskać pozycję statku w "przestrzeni logarytmicznej", aby dowiedzieć się, od jakiego indeksu zacząć rysowanie kwadratów, ale wtedy fakt, że mam tylko odległość do miejsca docelowego, aby pracować, myląc sprawę, zwłaszcza gdy należy wziąć pod uwagę, że liczba kwadratów musi się zmieniać dynamicznie, aby upewnić się, że są one stałe w odpowiednich miejscach w przestrzeni (tj. kwadraty są rozmieszczone w odstępach co najmniej 200, zanim zostaną przekształcone logarytmicznie).

W związku z tym miałem działającą implementację ze statkiem między początkiem 0.0d a końcem 1.0d, chociaż wdrożenie nie było takie przyjemne. W każdym razie problem zasadniczo sprowadza się do natury 1d. W tym przypadku docenimy każdą radę, w tym możliwe obejścia w celu osiągnięcia tego samego efektu lub rozwiązania.

Frontier: Elite 2

(Również, istnieje wideo Youtube pokazujący ten efekt: http://www.youtube.com/watch?v=79F9Nj7GgfM&t=3m5s)

Wiwaty,
Chris

Edit: rephrased całą pytanie.

Edit: nowy kod testowa:

package st; 

import java.awt.BorderLayout; 
import java.awt.Canvas; 
import java.awt.Color; 
import java.awt.Dimension; 
import java.awt.Font; 
import java.awt.Graphics; 
import java.awt.Graphics2D; 
import java.awt.GraphicsDevice; 
import java.awt.GraphicsEnvironment; 
import java.awt.event.ActionEvent; 
import java.awt.event.ActionListener; 
import java.awt.image.BufferStrategy; 
import java.text.DecimalFormat; 
import javax.swing.JFrame; 
import javax.swing.JPanel; 
import javax.swing.SwingUtilities; 
import javax.swing.Timer; 

public class StUI2 extends JFrame { 
    public static final double DEG_TO_RAD = Math.PI/180.0d; 
    public static final DecimalFormat decimalFormat = new DecimalFormat("0.0000"); 

    public static final Font MONO = new Font("Monospaced", Font.PLAIN, 10); 

    public class StPanel extends Canvas { 
     protected final Object imgLock = new Object(); 
     protected int lastWidth = 1, lastHeight = 1; 
     protected boolean first = true; 
     protected Color bgColour = Color.DARK_GRAY, gridColour = Color.GRAY; 

     double shipWrap = 700; 
     double shipFrame = 100; 
     double shipPos = 0; 
     long lastUpdateTimeMS = -1; 
     long currUpdateTimeMS = -1; 

     public StPanel() {  
      setFocusable(true); 
      setMinimumSize(new Dimension(1, 1)); 
      setAlwaysOnTop(true); 
     } 

     public void internalPaint(Graphics2D g) { 
      synchronized (imgLock) { 
       if (lastUpdateTimeMS < 0) { 
        lastUpdateTimeMS = System.currentTimeMillis(); 
       } 
       currUpdateTimeMS = System.currentTimeMillis(); 
       long diffMS = currUpdateTimeMS - lastUpdateTimeMS; 

       g.setFont(MONO); 

       shipPos += (60d * ((double)diffMS/1000)); 
       if (shipPos > shipWrap) { 
        shipPos = 0d; 
       } 

       double shipPosPerc = shipPos/shipWrap; 
       double distToDest = shipWrap - shipPos; 
       double compression = 1000d/distToDest; 

       g.setColor(bgColour); 
       Dimension d = getSize(); 
       g.fillRect(0, 0, (int)d.getWidth(), (int)d.getHeight()); 

       //int amnt2 = (int)unlog10((1000d/distToDest)); 

       g.setColor(Color.WHITE); 
       g.drawString("shipPos: " + decimalFormat.format(shipPos),  10, 10); 
       g.drawString("distToDest: " + decimalFormat.format(distToDest), 10, 20); 

       g.drawString("shipWrap: " + decimalFormat.format(shipWrap), 150, 10); 

       int offset = 40; 

       g.setFont(MONO); 

       double scalingFactor = 10d; 

       double dist = 0; 
       int curri = 0; 
       int i = 0; 
       do { 
        curri = i; 
        g.setColor(Color.GREEN); 

        dist = distToDest - getSquareDistance(distToDest, scalingFactor, i); 
        double sqh = getSquareHeight(dist, 100d * DEG_TO_RAD); 
        g.drawLine(30 + (int)dist, (offset + 50) - (int)(sqh/2d), 30 + (int)dist, (offset + 50) + (int)(sqh/2d)); 
        g.setColor(Color.LIGHT_GRAY); 
        g.drawString("i: " + i + ", dist: " + decimalFormat.format(dist), 10, 120 + (i * 10)); 
        i++; 
       } while (dist < distToDest); 

       g.drawLine(10, 122, 200, 122); 
       g.drawString("last/i: " + curri + ", dist: " + decimalFormat.format(dist), 10, 122 + (i * 10)); 

       g.setColor(Color.MAGENTA); 
       g.fillOval(30 + (int)shipPos, offset + 50, 4, 4); 

       lastUpdateTimeMS = currUpdateTimeMS; 
      } 
     } 

     public double getSquareDistance(double initialDist, double scalingFactor, int num) { 
      return Math.pow(scalingFactor, num) * num * initialDist; 
     } 

     public double getSquareHeight(double distance, double angle) { 
      return distance/Math.tan(angle); 
     } 

     /* (non-Javadoc) 
     * @see java.awt.Canvas#paint(java.awt.Graphics) 
     */ 
     @Override 
     public void paint(Graphics g) { 
      internalPaint((Graphics2D)g); 
     } 

     public void redraw() { 
      synchronized (imgLock) { 
       Dimension d = getSize(); 
       if (d.width == 0) d.width = 1; 
       if (d.height == 0) d.height = 1; 

       if (first || d.getWidth() != lastWidth || d.getHeight() != lastHeight) { 
        first = false; 

        // remake buf 
        GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); 
        //create an object that represents the device that outputs to screen (video card). 
        GraphicsDevice gd = ge.getDefaultScreenDevice(); 
        gd.getDefaultConfiguration(); 

        createBufferStrategy(2); 

        lastWidth = (int)d.getWidth(); 
        lastHeight = (int)d.getHeight(); 
       } 

       BufferStrategy strategy = getBufferStrategy(); 
       Graphics2D g = (Graphics2D)strategy.getDrawGraphics(); 
       internalPaint(g); 
       g.dispose(); 
       if (!strategy.contentsLost()) strategy.show(); 
      } 
     } 
    } 

    protected final StPanel canvas; 

    protected Timer viewTimer = new Timer(1000/60, new ActionListener() {  
     @Override 
     public void actionPerformed(ActionEvent e) { 
      canvas.redraw(); 
     } 
    }); 
    { 
     viewTimer.setRepeats(true); 
     viewTimer.setCoalesce(true); 
    } 

    /** 
    * Create the applet. 
    */ 
    public StUI2() { 
     JPanel panel = new JPanel(new BorderLayout()); 
     setContentPane(panel); 
     panel.add(canvas = new StPanel(), BorderLayout.CENTER); 
     setVisible(true); 
     setDefaultCloseOperation(EXIT_ON_CLOSE); 
     setSize(800, 300); 
     setTitle("Targetting indicator test #2"); 
     viewTimer.start(); 
    } 

    public static double unlog10(double x) { 
     return Math.pow(10d, x); 
    } 

    public static void main(String[] args) { 
     SwingUtilities.invokeLater(new Runnable() { 
      @Override 
      public void run() { 
       StUI2 ui = new StUI2(); 
      } 
     }); 
    } 
} 
+1

1) Nie mieszać Swing ('JApplet') & AWT (' Canvas') elementy. 2) Aplet jest nieco trudniejszy do przetestowania przez innych. Rozważ użycie aplikacji hybrydowej/apletu. 3) Czy masz pytanie? Co to jest? –

+1

OK. Uruchomiłem teraz kod. Oprócz pytania nadal nie jestem do końca jasny, co powinno się dziać. Czy czerwone i żółte kropki powinny być wyrównane? Również magenta porusza się od lewej do prawej. Czy żółty wzorzec podobno jest tym, co pilot widzi przedni oknem (będzie rzutowany na prawą ścianę z naszej perspektywy)? –

+0

Tak, byłoby idealnie, gdyby czerwone i żółte punkty były wyrównane. Muszę stworzyć mały podsekcję czerwonych kropek, które * powinny * być żółtymi kropkami. Powinny one pozostawać w ustalonych punktach w przestrzeni na ścieżce celu związanej z odległością od celu. –

Odpowiedz

10

Zakładając chcesz kwadraty być równa wysokości (jeśli do nich dotrzeć), można obliczyć współczynnik skalowania w zależności od odległości do miejsca przeznaczenia (d) oraz wymaganą wysokość kwadratów po ich osiągnięciu (h).

Z tych dwóch kawałków informacji można obliczyć arcus tangens (atan) kąta (alpha) pomiędzy linią łączącą statku do miejsca przeznaczenia (poziomej linii w obrazie) i linią łączącą górną część kwadratów z miejscem docelowym (zakrzywiona linia na twoim obrazie).

EDIT: poprawione
formuła Korzystanie z kąt można obliczyć wysokość kwadratu (h') w dowolnej odległości od miejsca przeznaczenia: wiesz, odległość do celu (d') i kąt (alpha); Wysokość kwadratu w odległości d' wynosi h'=r'*sin(alpha) - sin(alpha)=cos(alpha)*tan(alpha) i r'=d'/cos(alpha) (odległość między punktem docelowym a wierzchołkiem kwadratu - "promień"). Lub łatwiej: h'=d'*tan(alpha).

Uwaga: zastosowanie algorytmu do zmiany wysokości (gdy je osiągniesz) jest stosunkowo proste: podczas obliczania kąta wystarczy przyjąć kwadrat (widmowy) o stałej wysokości i skalować kwadraty w stosunku do tego.

Jeśli wysokość kwadratu na odległość d' jest obliczana dla Ciebie przez twoją bibliotekę graficzną, tym lepiej, musisz tylko obliczyć odległości, aby umieścić kwadraty.

Jakie odległości, aby umieścić kwadraty od miejsca docelowego?

1) Jeśli chcesz mieć zmienną liczbę wyświetlanych kwadratów (przed statkiem), ale potencjalnie nieskończoną liczbę kwadratów do rozważenia (na podstawie d), możesz wybrać odległość najbliższego kwadratu do miejsca docelowego (d1) i obliczyć odległości innych kwadratów według wzoru s^k*k*d1, gdzie s (współczynnik skalowania) jest liczbą> 1 dla kwadratu k (licząc od miejsca docelowego). Algorytm można zatrzymać, gdy wynik jest większy niż d.

Należy zauważyć, że jeśli d jest wystarczająco duży, kwadraty znajdujące się najbliżej odległości zablokują cel podróży (jest ich wiele, a ich wysokość jest mała ze względu na niski kąt). W tym przypadku możesz wprowadzić minimalną odległość (prawdopodobnie opartą na d), poniżej której nie wyświetlasz kwadratów - będziesz musiał poeksperymentować z dokładnymi wartościami, aby zobaczyć, co wygląda dobrze/do zaakceptowania.

2) Jeśli chcesz ustaloną ilość kwadratów (sn) przedstawiające zawsze, niezależnie od d, można obliczyć odległości kwadratów od przeznaczenia wzorem d*s^k, gdzie s jest liczbą jest indeks kwadratu (liczony od statku). Rozważania dotyczące małych kwadratów prawdopodobnie nie mają tu zastosowania, chyba że są one wysokie.

Aby rozwiązać zaktualizowany kod, zmieniać obowiązujących, odnośnych części do:

double dist = 0; 
double d1 = 10; 
int curri = 0; 
int i = 1; 
int maxSquareHeight = 40; 
double angle = Math.atan(maxSquareHeight/distToDest); 
while (true) 
{ 
    curri = i; 
    g.setColor(Color.GREEN); 

    dist = getSquareDistance(d1, scalingFactor, i); 
    if (dist > distToDest) { 
    break; 
    } 
    double sqh = getSquareHeight(dist, angle); 
    g.drawLine(30 + (int)(shipWrap - dist), offset+50-(int)(sqh/2d), 30 + (int)(shipWrap - dist), offset+50+(int)(sqh/2d)); 
    g.setColor(Color.LIGHT_GRAY); 
    i++; 
} 

public double getSquareHeight(double distance, double angle) { 
    return distance * Math.tan(angle); 
} 

Należy również zmniejszyć scalingFactor do wielkości ~ 1,5.

EDYCJA: Jeśli zastąpisz formułę s^k*k*d1 przez s^(k-1)*k*d1, to pierwszy kwadrat będzie dokładnie na odległość d1.

EDIT: stała wysokość kwadratowy obliczania formuła

EDIT: zaktualizowany kod

+0

Pozdrawiam - wypróbuję to i skontaktuję się z Tobą :) –

+0

Zaktualizowane formuły w trzecim akapicie – Attila

+0

Odnośnie kolejności operacji na 's^k * k * d1', czy jest to '(s^(k * k)) * d1'? –

Powiązane problemy