Reflection2

Reflection2

(Topics->Motion->Reflection2)

[Ground]

class Ground {

float x1, y1, x2, y2;

float x, y, len, rot;

// Default constructor

Ground(){

}

// Constructor

//このコンストラクタでは長さと面の傾きを算出します

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

}

}

[Orb]

class Orb{

float x, y, r;

// Default constructor

Orb() {

}

Orb(float x, float y, float r) {

this.x = x;

this.y = y;

this.r = r;

}

}

【改造例】

ボールが落下し、複雑な起伏をもった地面をリアルに跳ねるシミュレーション。

OrbとGroundという2つのクラスを作成しています。

斜めの斜面への衝突時の計算は、回転変換を使って算出しています。

大変勉強になるサンプルです。

[Reflection2]

/**

* Non-orthogonal Collision with Multiple Ground Segments

* by Ira Greenberg.

*

* Based on Keith Peter's Solution in

* Foundation Actionscript Animation: Making Things Move!

*/

Orb orb; //球体のクラス

PVector velocity; //速度はPVector型で扱っています

float gravity = .05, damping = 0.8; // 重力と反発係数

int segments = 40; //山の分割数

Ground[] ground = new Ground[segments]; //地面のクラス。山の起伏を一つ一つ、セグメント(分割)とした配列で扱います

float[] peakHeights = new float[segments+1]; //それぞれの山の頂点を保存してある配列

void setup(){

size(640, 200);

smooth();

orb = new Orb(50, 50, 3);

velocity = new PVector(.5, 0);

// Calculate ground peak heights

// 山の高さをランダムで生成

for (int i=0; i<peakHeights.length; i++){

peakHeights[i] = random(height-40, height-30);

}

/* Float value required for segment width (segs)

calculations so the ground spans the entire

display window, regardless of segment number. */

float segs = segments;

for (int i=0; i<segments; i++){

//Groundクラスのコンストラクタに山の位置XY、幅、標高を与えます

ground[i] = new Ground(width/segs*i, peakHeights[i],

width/segs*(i+1), peakHeights[i+1]);

}

}

void draw(){

// Background

noStroke();

fill(0, 15);

rect(0, 0, width, height);

// Move orb

orb.x += velocity.x;

velocity.y += gravity;

orb.y += velocity.y;

// Draw ground

fill(127);

//Groundのx1,y1,x2,y2から頂点を使って山を描いていきます

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

// Draw orb

noStroke();

fill(200);

ellipse(orb.x, orb.y, orb.r*2, orb.r*2);

// Collision detection

// 衝突判定。まず壁との判定を行います。

checkWallCollision();

for (int i=0; i<segments; i++){

checkGroundCollision(ground[i]); //セグメントごとに当たり判定を実施しています

}

}

//衝突判定の本体:壁とボール

void checkWallCollision(){

if (orb.x > width-orb.r){

orb.x = width-orb.r;

velocity.x *= -1;

velocity.x *= damping;

}

else if (orb.x < orb.r){

orb.x = orb.r;

velocity.x *= -1;

velocity.x *= damping;

}

}

//衝突判定の本体:地面の1セグメントとの衝突判定

void checkGroundCollision(Ground groundSegment) {

// Get difference between orb and ground

// ボールと床のセグメントとの差分を算出

float deltaX = orb.x - groundSegment.x;

float deltaY = orb.y - groundSegment.y;

// Precalculate trig values

// 三角関数を事前に計算しておきます

float cosine = cos(groundSegment.rot);

float sine = sin(groundSegment.rot);

/* Rotate ground and velocity to allow

orthogonal collision calculations */

// 直交衝突の計算を可能にするために、地面と加速度を回転させます(回転変換)

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;

/* Ground collision - check for surface

collision and also that orb is within

left/rights bounds of ground segment */

// 地面の衝突−面衝突と左右端のチェック

if (groundYTemp > -orb.r &&

orb.x > groundSegment.x1 &&

orb.x < groundSegment.x2 ){

// keep orb from going into ground

// めり込み防止

groundYTemp = -orb.r;

// bounce and slow down orb

// 衝突による速度の減衰

velocityYTemp *= -1.0;

velocityYTemp *= damping;

}

// Reset ground, velocity and orb

// 地面と速度とボールの逆変換

deltaX = cosine * groundXTemp - sine * groundYTemp;

deltaY = cosine * groundYTemp + sine * groundXTemp;

velocity.x = cosine * velocityXTemp - sine * velocityYTemp;

velocity.y = cosine * velocityYTemp + sine * velocityXTemp;

orb.x = groundSegment.x + deltaX;

orb.y = groundSegment.y + deltaY;

}

山を複雑にして、軌跡を長くしてみました。

他に「めり込み防止」をコメントアウトするとどうなるか、など試してみると良いとおもいます。