Processing:Примеры/Неперпендикулярное отражение 2

Материал из Онлайн справочника
Перейти к навигацииПерейти к поиску

Перевод: Максим Кузьмин
Проверка/Оформление/Редактирование: Мякишев Е.А.


Описание[1]

Этот скетч-пример основан на примере из книги «Основы анимации с помощью AutoScript: Двигай это!» Кита Питерса.

Пример

Orb orb;

PVector gravity = new PVector(0,0.05);
// «земля» - это массив объектов «Ground»:
int segments = 40;
Ground[] ground = new Ground[segments];

void setup(){
  size(640, 360);
  // объект «orb», который будет падать и отскакивать от поверхности:
  orb = new Orb(50, 50, 3);

  // рассчитываем высоту «гор»: 
  float[] peakHeights = new float[segments+1];
  for (int i=0; i<peakHeights.length; i++){
    peakHeights[i] = random(height-40, height-30);
  }

  /* Значение «segs» типа «float» необходимо
     для расчета ширины «гор», чтобы «земля» растянулась
     на весь экран независимо от количества «гор». */
  float segs = segments;
  for (int i=0; i<segments; i++){
    ground[i]  = new Ground(width/segs*i, peakHeights[i], width/segs*(i+1), peakHeights[i+1]);
  }
}


void draw(){
  // фон:
  noStroke();
  fill(0, 15);
  rect(0, 0, width, height);
  
  // перемещаем и показываем шар:
  orb.move();
  orb.display();
  // проверяем, сталкивается ли шар с границами экрана:
  orb.checkWallCollision();

  // проверяем, сталкивается ли шар с «горами»:
  for (int i=0; i<segments; i++){
    orb.checkGroundCollision(ground[i]);
  }

// рисуем «землю»:
  fill(127);
  beginShape();
  for (int i=0; i<segments; i++){
    vertex(ground[i].x1, ground[i].y1);
    vertex(ground[i].x2, ground[i].y2);
  }
  vertex(ground[segments-1].x2, height);
  vertex(ground[0].x1, height);
  endShape(CLOSE);
}



class Orb {
  // у шара есть позиция и направление движения:
  PVector position;
  PVector velocity;
  float r;
  // скорость шара будет замедляться на 20%
  // после столкновения с «землей»: 
  float damping = 0.8;

  Orb(float x, float y, float r_) {
    position = new PVector(x, y);
    velocity = new PVector(.5, 0);
    r = r_;
  }

  void move() {
    // перемещаем шар:
    velocity.add(gravity);
    position.add(velocity);
  }

  void display() {
    // рисуем шар:
    noStroke();
    fill(200);
    ellipse(position.x, position.y, r*2, r*2);
  }
  
  // проверяем, сталкивается ли шар с границами экрана:
  void checkWallCollision() {
    if (position.x > width-r) {
      position.x = width-r;
      velocity.x *= -damping;
    } 
    else if (position.x < r) {
      position.x = r;
      velocity.x *= -damping;
    }
  }

  void checkGroundCollision(Ground groundSegment) {

    // считываем расстояние между шаром и «землей»:
    float deltaX = position.x - groundSegment.x;
    float deltaY = position.y - groundSegment.y;

    // заранее рассчитываем тригонометрические значения:
    float cosine = cos(groundSegment.rot);
    float sine = sin(groundSegment.rot);

    /* Определяем углы склонов «гор»
       и направление движения шара,
       чтобы рассчитать их столкновение. */
    float groundXTemp = cosine * deltaX + sine * deltaY;
    float groundYTemp = cosine * deltaY - sine * deltaX;
    float velocityXTemp = cosine * velocity.x + sine * velocity.y;
    float velocityYTemp = cosine * velocity.y - sine * velocity.x;

    /* Столкновение с «землей»: 
     определяем, есть ли столкновение с поверхностью,
     а также то, находится ли шар
     в пределах левого/правого края «горы». */
    if (groundYTemp > -r &&
      position.x > groundSegment.x1 &&
      position.x < groundSegment.x2 ) {
      // не даем шару провалиться под «землю»:
      groundYTemp = -r;
      // выполняем отскок и замедление шара:
      velocityYTemp *= -1.0;
      velocityYTemp *= damping;
    }

    // обновляем значения для расстояния между шаром и «землей»,
    // а также для направления движения и позиции шара:
    deltaX = cosine * groundXTemp - sine * groundYTemp;
    deltaY = cosine * groundYTemp + sine * groundXTemp;
    velocity.x = cosine * velocityXTemp - sine * velocityYTemp;
    velocity.y = cosine * velocityYTemp + sine * velocityXTemp;
    position.x = groundSegment.x + deltaX;
    position.y = groundSegment.y + deltaY;
  }
}

class Ground {
  float x1, y1, x2, y2;  
  float x, y, len, rot;

  // конструктор:
  Ground(float x1, float y1, float x2, float y2) {
    this.x1 = x1;
    this.y1 = y1;
    this.x2 = x2;
    this.y2 = y2;
    x = (x1+x2)/2;
    y = (y1+y2)/2;
    len = dist(x1, y1, x2, y2);
    rot = atan2((y2-y1), (x2-x1));
  }
}

См.также

Внешние ссылки