Следование по прямой линии (через Путь?)

Я работаю над игрой, в которой будут использоваться снаряды. Итак, я создал класс Projectile, и новый экземпляр создается, когда пользователь касается экрана:

@Override
public boolean onTouch(View v, MotionEvent e){
    float touch_x = e.getX();
    float touch_y = e.getY();
    new Projectile(touch_x, touch_y);
}

И класс снаряда:

public class Projectile{

    float target_x;
    float target_y;
    Path line;

    public Projectile(float x, float y){
        target_x = x;
        target_y = y;

        line = new Path();
        line.moveTo(MyGame.mPlayerXPos, MyGame.mPlayerYPos);
        line.lineTo(target_x, target_y);
    }
}

Таким образом, получается путь с двумя точками, позицией игрока и координатами касания. Мой вопрос: как вы можете получить доступ к точкам на этой линии? Например, если бы я хотел получить координаты x,y снаряда в половине точки линии или точки, в которой снаряд будет через 100 тиков (двигаясь со скоростью X пикселей/такт)?

Мне также нужно, чтобы снаряд продолжал двигаться после достижения конечной точки. Нужно ли мне использовать line.addPath(line), чтобы продолжать расширять путь?

ИЗМЕНИТЬ

Мне удалось заставить Снаряды двигаться по прямой линии, но они летели в странных направлениях. Мне пришлось подправить код:

private void moveProjectiles(){
        ListIterator<Projectile> it = Registry.proj.listIterator();
        while ( it.hasNext() ){
            Projectile p = it.next();
            p.TimeAlive++;

            double dist = p.TimeAlive * p.Speed;
            float dx = (float) (Math.cos(p.Angle) * dist);
            float dy = (float) (Math.sin(p.Angle) * dist);

            p.xPos += dx;
            p.yPos += -dy;
        }
    }

Проблема должна быть в Angle. Я использую этот метод, который отлично работает:

private double getDegreesFromTouchEvent(float x, float y){
        double delta_x = x - mCanvasWidth/2;
        double delta_y = mCanvasHeight/2 - y;
        double radians = Math.atan2(delta_y, delta_x);

        return Math.toDegrees(radians);
    }

Однако он возвращает 0-180 для касаний над центром экрана и от 0 до -180 для касаний ниже. Это проблема?


person Snailer    schedule 04.11.2010    source источник
comment
Бьюсь об заклад, вы работаете в левосторонней системе координат -- (0,0) в верхнем левом углу экрана. Большинство триггерных функций предполагают правостороннюю систему координат -- (0,0) в левом нижнем углу экрана. Если вы используете мое решение, не имеет значения, какую систему координат вы выберете.   -  person Jon Snyder    schedule 05.11.2010


Ответы (3)


Лучше всего смоделировать это с помощью параметрических уравнений. Нет необходимости использовать триггерные функции.

class Path {
  private final float x1,y1,x2,y2,distance;

  public Path( float x1, float y1, float x2, float y2) {
    this.x1 = x1;
    this.y1 = y1;
    this.x2 = x2;
    this.y2 = y2;
    this.distance = Math.sqrt( (x2-x1)*(x2-x1)+(y2-y1)*(y2-y1));
  }

  public Point position( float t) {
     return new Point( (1-t)*x1 + t*x2,
                       (1-t)*y1 + t*y2);
  }

  public Point position( float ticks, float speed) {
    float t = ticks * speed / distance;
    return position( t); 
  }

}

Path p = new Path(...);
// get halfway point
p.position( 0.5);
// get position after 100 ticks at 1.5 pixels per tick
p.position( 100, 1.5);
person Jon Snyder    schedule 04.11.2010
comment
Я мог бы попробовать это, если я не могу заставить работать другой метод... это кажется немного более интуитивным. Но сработает ли это, если я хочу получить точку за пределами (x2, y2)? - person Snailer; 05.11.2010
comment
Да, это сработает. Например, p.position(2.0) вернет удвоенное расстояние до точки; p.position(-1.0) вернет точку, идущую в другом направлении. - person Jon Snyder; 05.11.2010
comment
Это сработало прекрасно с первого раза, когда я попробовал это! Спасибо! Это также пригодится в будущем, если я захочу, чтобы что-то произошло, когда оно достигнет position(1) или что-то в этом роде. - person Snailer; 05.11.2010

Из геометрии, если это прямая линия, вы можете вычислить любую точку на ней, используя полярные координаты.

Если найти угол прямой:

ang = arctan((target_y - player_y) / (target_x - player_x))

Тогда любую точку на линии можно найти с помощью триггера:

x = cos(ang) * dist_along_line
y = sin(ang) * dist_along_line

Если вам нужна средняя точка, вы просто берете dist_along_line равным половине длины линии:

dist_along_line = line_length / 2 = (sqrt((target_y - player_y)^2 + (target_x - player_x)^2)) / 2

Если вы хотите считать точку после 100 тиков, двигаясь со скоростью X пикселей/тик:

dist_along_line = 100 * X

Надеюсь, кто-то может прокомментировать способ сделать это более непосредственно, используя библиотеки Android.

person Zack Bloom    schedule 04.11.2010
comment
O_O Я могу понять это отсюда .. Я просто не знаю триг, лол :P Кажется довольно прямолинейным, большое спасибо! Я проверю это, прежде чем отмечу ответ. - person Snailer; 05.11.2010
comment
Вы определенно хотите изучить свой триггер, если хотите делать игры, основанные на графике. Вы также можете сделать это вычисление с помощью теоремы Пифагора. Просто относитесь к линии как к гипотенузе прямоугольного треугольника, а к двум другим сторонам — как к координатам X и Y. - person JOTN; 05.11.2010
comment
+1 к изучению Трига! Чем больше вы увлекаетесь 3D, тем хуже становится! - person CaseyB; 05.11.2010
comment
Вы должны сделать особый случай для вертикальных линий, иначе вы получите ошибку деления на ноль при нахождении угла линии. - person Jon Snyder; 05.11.2010

Во-первых, класс Path нужно использовать для рисования, а не для расчета местоположения снаряда.

Таким образом, ваш класс Projectile может иметь следующие атрибуты:

float positionX;
float positionY;
float velocityX;
float velocityY;

Скорость вычисляется из targetX, targetY, playerX и playerY следующим образом:

float distance = sqrt(pow(targetX - playerX, 2)+pow(targetY - playerY, 2))
velocityX = (targetX - playerX) * speed / distance;
velocityY = (targetY - playerY) * speed / distance;

Ваша позиция после 20 тиков

x = positionX + 20 * velocityX;
y = positionY + 20 * velocityY;

Время, необходимое для достижения цели, равно ticksToTarget = расстояние/скорость;

Расположение точки половинного пути

halfWayX = positionX + velocityX * (tickToTarget / 2);
halfWayY = positionY + velocityY * (tickToTarget / 2);
person Ivo Bosticky    schedule 04.11.2010