ホームゲームつくろー!衝突判定編

2D衝突編
その0 2Dプリミティブの型定義


 衝突判定をする上で「基本プリミティブ」を定義すると実装が楽になります。この章ではそんなプリミティブを定義してみます。下記のプリミティブの具体的な実装はprimitive.hとして公開致します(^-^)。プリミティブは沢山考えられるため、必要に応じて定義も増やしていきます:


 あ、基本この章、3D衝突編その0「3Dプリミティブの型定義」のコピペでございます(^-^;;;



○ 浮動小数点誤差

 衝突判定では浮動小数点の誤差が問題になります。数学的には0のはずなのに計算誤差で0.000001みたいな小さな誤差が出て衝突していないと判定される事があります。これによりプルプル震えたりすり抜けたりと割と散々なのです。その為「このくらい小さい値になったら0とみなそう」という範囲を決めると安定します:

#define _OX_EPSILON_ 0.000001f // 誤差

イプシロン(ε:epsilon)は科学系の文献で誤差として良く利用されるギリシア文字です。



○ 2次元ベクトル(Float2)

 点やベクトルなどを表す基本中の基本の型になります。欲しい機能としては加算、減算、スカラー(係数)との掛け算、割り算はまぁ必須として、ベクトルとしてとらえた時の長さ(length)、べき乗長さ(lengthSq)、後は内積と外積と正規化はしょっちゅう使いますので機能に加えたい所です:

// 2成分float
struct Float2 {
    float x, y;

    Float2();
    Float2( float x, float y );
    ~Float2();

    Float2 operator +( const Float2 &r );
    Float2 operator -( const Float2 &r ) const;
    Float2 operator -() const;
    Float2 operator *( const Float2 &r ) const;
    Float2 operator /( const Float2 &r ) const;
    Float2 operator *( float r ) const;
    Float2 operator /( float r ) const;
    friend Float2 operator *( float l, const Float2 &r );
    friend Float2 operator /( float l, const Float2 &r );
    float dot( const Float2 &r ) const;
    float cross( const Float2 &r ) const;
    float length() const;
    float lengthSq() const;
    void norm();
    Float2 getNorm() const;
};

 getNormメソッドは非破壊に標準化したFloat2を取得します。「正規化値が欲しいけど成分そのままで…」というケースは結構あります。



○ 点(Point2D)とベクトル(Vec2D)

 点はFloat2型として扱った方が良いので、typedefで再定義するくらいで良いと思います。一方ベクトルはFloat2の機能を踏襲しつつ他方のベクトルと垂直・平行かどうかの判定くらいの機能はあって良いかなと思いますのでFloat2を継承します:

// 点
typedef Float2 Point2D;


// 2Dベクトル
struct Vec2 : public Float2 {
    Vec2();
    Vec2( float x, float y ) : Float2( x, y );
    Vec2( const Float2 &r ) : Float2( r );
    ~Vec2();

    Vec2& operator =( const Float2 &r );

    // 標準化
    void norm();

    // 垂直関係にある?
    bool isVertical( const Vec2 &r ) const;

    // 平行関係にある?
    bool isParallel( const Vec2 &r ) const;

    // 鋭角関係?
    bool isSharpAngle( const Vec2 &r ) const;
};

Vec2はFloat2を継承していますが、Float2(Point2D)で値を代入したい事もありますので、コピーコンストラクタと代入演算子を定義しています。


○ 直線(Line2D)

 直線(無限に伸びた線)は「直線上の一点と方向ベクトル」で定義できます。点上の座標を取得する機能はあって良いかなと思います:

// 直線
struct Line2D {
    Point2D p;
    Vec2 v; // 方向ベクトル
    Line2D();
    Line2D( const Point2D &p, const Vec2 &v );
    ~Line2D();

    // 点上の座標を取得
    // ベクトルに掛け算する係数
    Point2D getPoint( float t ) const;
};

機能は小さいものなら追加で加えても良いと思いますが、とりあえず値定義だけで今は良いかなと。


○ 線分(Segment2D)

 線分は2つの点を結ぶ線ですが、レイ(方向性を持った線分)などでも使えるようにするために始点とベクトルで定義しておきます。よって終点座標を算出する機能を持たせます:

// 線分
struct Segment2D : public Line2D {

    Segment2D() {}
    Segment2D( const Point2D &p, const Vec2 &v );
    Segment2D( const Point2D &p1, const Point2D &p2 );

    // 終点を取得
    Float2 getEndPoint() const;
};



○ 円(Circle2D)

 円は中心点と半径のみで定義できる軽量なプリミティブです。対称性が極めて高いため衝突判定が簡単になるのが嬉しいプリミティブです:

// 円
struct Circle2D {
    Point2D p;
    float r; // 半径
    Circle2D();
    Circle2D( const Point2D &p, float r );
    ~Circle2D();
};



○ カプセル(Capsule)

 カプセルは線分上を半径rの球がスウィープした図形です。定義としては線分に半径が追加された物になります:

// カプセル
struct Capsule2D {
    Segment2D s;
    float r; // 半径
    Capsule2D();
    Capsule2D( const Segment2D &s, float r );
    Capsule2D( const Point2D &p1, const Point2D &p2, float r );
    ~Capsule2D();
};



○ 三角形(Triangle)

 三角形はいわずもがな3点で定義される図形です:

// 三角形
struct Triangle2D {
    Point2D p[ 3 ]; // 頂点座標

    Triangle2D() {}
    ~Triangle2D() {}

    // 辺の長さを取得
    float len( int i ) const {
        // i = 0は辺p1p2など点piの対辺の長さ
        return vec( i ).length();
    }

    // 辺ベクトル(非正規化)を取得
    Vec2 vec( int i ) const {
        // i = 0はベクトルp1p2など点piの対辺の方向ベクトル
        // i = 0 : p1p2
        // i = 1 : p2p0
        // i = 2 : p0p1
        int idx[] = { 1, 2, 0, 1 };
        return p[ idx[ i + 1 ] ] - p[ idx[ i ] ];
    }
};