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;
}
山を複雑にして、軌跡を長くしてみました。
他に「めり込み防止」をコメントアウトするとどうなるか、など試してみると良いとおもいます。