Ориентация на компьютер симулятора космического корабля с концентрическими квадратами индикатора - PullRequest
11 голосов
/ 21 марта 2012

Я работаю над трехмерной космической торговой игрой с некоторыми людьми, и одна из задач, которую мне поручили сделать, - создать «туннель» для компьютерного руководства, через который проходит корабль, с туннелем, сделанным из квадратов.что пользователь летит к месту назначения, увеличиваясь по мере приближения к месту назначения.

Необходимо только визуализировать квадраты для точек перед кораблем, поскольку это все, что видно пользователю.,По пути к пункту назначения компьютер корабля должен установить квадраты на HUD, которые представляют фиксированные точки в пространстве между вами и пунктом назначения, которые находятся на небольшом расстоянии и увеличиваются по мере приближения точек к кораблю.

guidance squares example

Я попытался реализовать это и не могу понять это, в основном используя логарифмы (Math.log10(x) и тому подобное).Я попытался определить положение корабля в «логарифмическом пространстве», чтобы выяснить, с какого индекса начинать рисовать квадраты, но тот факт, что у меня есть только расстояние до пункта назначения для работы, сбивает с толку вопрос, особенно когда выучтите, что число квадратов должно изменяться динамически, чтобы убедиться, что они остаются фиксированными в нужных местах в пространстве (т. е. квадраты располагаются с интервалами в 200 или около того перед логарифмическим преобразованием).

Что касаетсяу меня была рабочая реализация с кораблем между началом 0.0d и концом 1.0d, хотя реализация не была такой хорошей.Во всяком случае, проблема по сути сводится к 1-й природе.Буду признателен за любые советы по этой проблеме, включая возможные обходные пути для достижения того же эффекта или решения.

Frontier: Elite 2

(Также есть видео на Youtube, показывающее этот эффект: http://www.youtube.com/watch?v=79F9Nj7GgfM&t=3m5s)

Приветствия,Крис

Редактировать: перефразировать весь вопрос.

Редактировать: новый код испытательного стенда:

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 Ответ

10 голосов
/ 26 марта 2012

Предполагая, что квадраты должны быть одинаковой высоты (когда вы их достигнете), вы можете рассчитать коэффициент масштабирования на основе расстояния до места назначения (d) и необходимой высоты квадратов при достижении их (h).

Из этих двух фрагментов информации вы можете рассчитать обратный тангенс (atan) угла (alpha) между линией, соединяющей корабль с пунктом назначения (горизонтальная линия на вашем изображении) и линией, соединяющей верхнюю часть. квадратов с местом назначения (угловая линия на вашем изображении).

РЕДАКТИРОВАТЬ: исправлено
формула Используя угол, вы можете рассчитать высоту квадрата (h') на любом заданном расстоянии от пункта назначения: вы знаете расстояние до пункта назначения (d') и угол (alpha); Высота квадрата на расстоянии d' равна h'=r'*sin(alpha) - sin(alpha)=cos(alpha)*tan(alpha) и r'=d'/cos(alpha) (расстояние между пунктом назначения и вершиной квадрата - «радиус»). Или проще: h'=d'*tan(alpha).

Примечание: применение алгоритма для квадратов различной высоты (когда вы их достигаете) является относительно простым: при расчете угла просто возьмите (фантомный) квадрат фиксированной высоты и масштабируйте квадраты относительно этого.

Если высота квадрата на расстоянии d' рассчитана для вас вашей графической библиотекой, тем лучше, вам нужно только вычислить расстояния для размещения квадратов.

На каком расстоянии размещать квадраты от пункта назначения?

1) Если вы хотите, чтобы отображалось различное количество квадратов (перед кораблем), но потенциально бесконечное число квадратов для рассмотрения (на основе d), вы можете выбрать расстояние от ближайшего квадрата до места назначения. (d1) и вычислите расстояния других квадратов по формуле s^k*k*d1, где s (коэффициент масштабирования) - это число> 1 для k-го квадрата (считая от пункта назначения). Вы можете остановить алгоритм, если результат больше d.

Обратите внимание, что если d достаточно велико, квадраты, расположенные ближе всего к расстоянию, заблокируют пункт назначения (их много, а их высота мала из-за низкого угла). В этом случае вы можете ввести минимальное расстояние (возможно, основанное на d), ниже которого вы не отображаете квадраты - вам придется поэкспериментировать с точными значениями, чтобы увидеть, что выглядит правильным / приемлемым.

2) Если вы хотите, чтобы фиксированное количество квадратов (sn) отображалось всегда, независимо от d, вы можете рассчитать расстояния квадратов от пункта назначения по формуле d*s^k, где s - это число <1, <code>k является индексом квадрата (считая с корабля). Мнение о небольших квадратах, вероятно, здесь не применимо, если sn не является высоким

Чтобы исправить обновленный код, измените соответствующую часть на:

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); 
} 

Вам также следует уменьшить коэффициент масштабирования до величины ~ 1,5.

РЕДАКТИРОВАТЬ: Если вы замените формулу s^k*k*d1 на s^(k-1)*k*d1, тогда первый квадрат будет точно на расстоянии d1.

РЕДАКТИРОВАТЬ: формула расчета фиксированной площади

РЕДАКТИРОВАТЬ: обновленный код

...