「Android Studio」円と四角形の衝突

今回は円と四角形の衝突の実装をします。

まずは以下のヘッダーファイルengine.hの中身です。

衝突に関する記述はCircle構造体のcollision_with_rect関数にあります。

#include "DxLib.h"
#include <cmath>

using namespace std;

int Static = 1; //動かない物体
int Dynamic = 2; //動く物体
int CIRCLE = 3; //円
int RECTANGLE = 4; //四角形
int LINE = 5; //線分
int CENTER = 6; //線分の中心で回転させるか
int START = 7; //線分の始点で回転させるか

struct Vector2D
{
    double x;
    double y;

    Vector2D(double _x, double _y)
    {
        x = _x;
        y = _y;
    }

    Vector2D add(Vector2D v)
    {
        return Vector2D(x + v.x, y + v.y);
    }

    Vector2D mul(double _x, double _y)
    {
        return Vector2D(x * _x, y * _y);
    }

    int dot(Vector2D v)
    {
        return x * v.x + y * v.y;
    }

    int cross(Vector2D v)
    {
        return x * v.y - v.x * y;
    }

    double length()
    {
        return sqrt(x * x + y * y);
    }

    Vector2D unit()
    {
        return Vector2D(x / length(), y / length());
    }
};

struct Rectangle
{
    int shape;
    int type;
    int color;
    double restitution; //減速具合
    int x;
    int y;
    int w;
    int h;
    bool on_hit;

    Rectangle(int _x, int _y, int _w, int _h, int _color, int _type, double _restitution, bool _on_hit)
    {
        x = _x;
        y = _y;
        w = _w;
        h = _h;
        color = _color;
        type = _type;
        restitution = _restitution;
        shape = RECTANGLE;
        on_hit = _on_hit;
    }

    bool is_hit(int i, int j)
    {
        return (x <= i && i <= x + w && y <= j && j <= y + h);
    }
};

struct Line
{
    int shape = LINE; //形状は線分
    int type; //動的か静的か
    double restitution; //反発係数
    double x0; //始点のx座標
    double y0; //始点のy座標
    double x1; //終点のx座標
    double y1; //終点のy座標
    bool on_hit; //物体と衝突したかどうかの判定

    Line(double _x0, double _y0, double _x1, double _y1)
    {
        x0 = _x0;
        y0 = _y0;
        x1 = _x1;
        y1 = _y1;
    }

    Line(double _x0, double _y0, double _x1, double _y1, int _type, double _restitution, bool _on_hit)
    {
        x0 = _x0;
        y0 = _y0;
        x1 = _x1;
        y1 = _y1;
        type = _type;
        restitution = _restitution;
        on_hit = _on_hit;
    }

    Vector2D center()
    {
        return Vector2D((x0 + x1) / 2, (y0 + y1) / 2);
    }

    Vector2D vec()
    {
        return Vector2D(x1 - x0, y1 - y0);
    }

    double len()
    {
        return sqrt(pow(vec().x, 2) + pow(vec().y, 2));
    }

    Vector2D norm()
    {
        return Vector2D(y0 - y1, x1 - x0).mul(1 / len(), 1 / len()); //その法線方向のベクトルを正規化したもの
    }

    Vector2D unit()
    {
        return Vector2D((x1 - x0) / len(), (y1 - y0) / len());
    }

    void move(double dx)
    {
        x0 += dx;
        x1 += dx;
    }

    void rotation(double theta, int a)
    {

        if(a == CENTER) { //線分の中心を中心に回転
            Vector2D center_point = center();
            double x = x0 - center().x;
            double y = y0 - center().y;
            x0 = (x * cos(theta * M_PI / 180) - y * sin(theta * M_PI / 180)) + center_point.x;
            y0 = (x * sin(theta * M_PI / 180) + y * cos(theta * M_PI / 180)) + center_point.y;

            x1 = (x * cos(M_PI + theta * M_PI / 180) - y * sin(M_PI + theta * M_PI / 180)) + center_point.x;
            y1 = (x * sin(M_PI + theta * M_PI / 180) + y * cos(M_PI + theta * M_PI / 180)) + center_point.y;
        }
        else if(a == START) { //線分の始点を中心に回転
            double x = x1 - x0;
            double y = y1 - y0;

            x1 = (x * cos(theta * M_PI / 180) - y * sin(theta * M_PI / 180)) + x0;
            y1 = (x * sin(theta * M_PI / 180) + y * cos(theta * M_PI / 180)) + y0;
        }

    }

};

struct Circle
{
    int shape = CIRCLE;
    int type;
    double restitution;
    double  deceleration;
    double x;
    double y;
    int radius;
    Vector2D velocity = Vector2D(0, 0);
    Vector2D accel = Vector2D(0, 0);

    Circle(double _x, double _y, int _r, Vector2D _v, Vector2D _a)
    {
        x = _x;
        y = _y;
        radius = _r;
        velocity = _v;
        accel = _a;
    }

    Circle(double _x, double _y, int _r, Vector2D _v, Vector2D _a, int _type, double restituion, double _deceleration)
    {
        x = _x;
        y = _y;
        radius = _r;
        velocity = _v;
        accel = _a;
        type = _type;
        restitution = restituion;
        deceleration = _deceleration;
    }

    void move(double dx, double dy)
    {
        x += dx;
        y += dy;
    }

    void transfer(Vector2D gravity)
    {
        if(type == Dynamic)
        {
            velocity = velocity.add(gravity);
            velocity = velocity.add(accel);
            velocity = velocity.mul(deceleration, deceleration);
            x += velocity.x;
            y += velocity.y;
        }

    }

    bool is_hit(double _x, double _y)
    {
        double dd = pow(x - _x, 2) + pow(y - _y, 2);
        return dd < pow(radius, 2);
    }

    void collision_with_rect(Rectangle &rect)
    {

        double nx = fmax(rect.x, fmin(x, rect.x + rect.w));
        double ny = fmax(rect.y, fmin(y, rect.y + rect.h));

        if(!is_hit(nx, ny))
        {
            return;
        }

        rect.on_hit = true;

        if(rect.color < 4)
        {
            rect.color++;
        }

        double dd = pow(nx - x, 2) + pow(ny - y, 2);
        double overlap = fabs(radius - sqrt(dd));

        double dx = 0;
        double dy = 0;

        if(fabs(ny - rect.y) < 20) //上面衝突
        {
            dy = -overlap;
        }
        else if(fabs(ny - (rect.y + rect.h)) < 20) //下面衝突
        {
            dy = overlap;
        }
        else if(fabs(nx - rect.x) < 20 ) //左面衝突
        {
            dx = -overlap;
        }
        else if(fabs(nx - (rect.x + rect.w)) < 20) //右面衝突
        {
            dx = overlap;
        }
        else
        {
            dx = -velocity.x;
            dy = -velocity.y;
        }


        move(dx, dy);
        if(dx != 0)
        {
            velocity = velocity.mul(-1 * rect.restitution * restitution, 1 * rect.restitution);
        }
        if(dy != 0)
        {
            velocity = velocity.mul(1 * rect.restitution, -1 * restitution * rect.restitution);
        }

    }

    void collision_with_line(Line &line)
    {
        Vector2D v0 = Vector2D(line.x0 - x + velocity.x, line.y0 - y + velocity.y);
        Vector2D v1 = velocity;
        Vector2D v2 = Vector2D(line.x1 - line.x0, line.y1 - line.y0);
        Vector2D v2_unit = v2.unit();

        double cv1v2 = v1.cross(v2);
        double t1 = v0.cross(v1) / cv1v2;
        double t2 = v0.cross(v2) / cv1v2;
        bool crossed = (0 <= t1 && t1 <= 1) && (0 <= t2 && t2 <= 1);

        if(crossed)
        {
            move(-velocity.x, -velocity.y);
            double dot0 = velocity.dot(line.norm());
            Vector2D vec0 = line.norm().mul(-2 * dot0, -2 * dot0); //正射影ベクトル
            velocity = vec0.add(velocity);
            velocity = velocity.mul(line.restitution * restitution, line.restitution * restitution);
            line.on_hit = true;
        }

    }

};

#endif //BLOG_ENGINE_H

上記のヘッダーファイルengine.hをインクルードして以下のプログラムを実行。

#include "DxLib.h"
#include "engine.h"
#include <thread>
#include <chrono>
#include <vector>

// プログラムは android_main から始まります

int android_main( void )
{
    if( DxLib_Init() == -1 )		// DXライブラリ初期化処理
    {
        return -1 ;			// エラーが起きたら直ちに終了
    }

    SetGraphMode(1080, 2220, 32); //1080×2220にサイズ変更

    int light_b = GetColor(0, 255, 255);
    int white = GetColor(255, 255, 255);

    Line line1 = Line(800, 1200, 300, 1700, Static, 1, false); //始点のx座標 始点のy座標 終点x座標 終点y座標 (動的か静的か) 反発係数 衝突したかの判定
     /*
    Line line2 = Line(500, 1000, 600, 1100);
    */

    Vector2D gravity = Vector2D(0, 0);

    Circle ball = Circle(0, 1000, 30, Vector2D(15, -15), Vector2D(0, 0), Dynamic, 1, 1); //x座標 y座標 半径 速度 加速度 (動的か静的か) 反発係数 減速係数

    Rectangle window[4] = {
            Rectangle(-20, -20, 1120, 20, white, Static, 1, false),
            Rectangle(1080, -20, 20, 2240, white, Static, 1, false),
            Rectangle(-20, 2220, 1120, 20, white, Static, 1, false),
            Rectangle(-20, -20, 20, 2220, white, Static, 1, false)
    };

    vector<Rectangle> box;
    for(int i = 0; i < 3; i++)
    {
        for(int j = 0; j < 3; j++)
        {
            box.push_back(Rectangle(200 + 300 * j, 100 + i * 300, 70, 70, white, Static, 1, false));
        } //左上のx座標 左上のy座標 幅 高さ 色 動的か静的か 反発係数 衝突判定
    }
    //this_thread::sleep_for(std::chrono::milliseconds(5000));

    while(ScreenFlip() == 0 && ProcessMessage() == 0 && ClearDrawScreen()==0)
    {
        /*
        DrawLine(line1.x0, line1.y0, line1.x1, line1.y1, light_b, TRUE);
        line1.rotation(1, CENTER);

        DrawLine(line2.x0, line2.y0, line2.x1, line2.y1, light_b, TRUE);
        line2.rotation(1, START);
        */

        this_thread::sleep_for(std::chrono::milliseconds(10));

        ball.transfer(gravity);

        ball.collision_with_line(line1);
        for(int i = 0; i < 4; i++)
        {
            ball.collision_with_rect(window[i]);
        }

        for(int i = 0; i < 3; i++)
        {
            for(int j = 0; j < 3; j++)
            {
                double x = box[3 * i + j].x;
                double y = box[3 * i + j].y;
                double w = box[3 * i + j].w;
                double h = box[3 * i + j].h;
                DrawBox(x, y, x + w, y + h, white, TRUE);
                ball.collision_with_rect(box[3 * i + j]);
            }
        }
        DrawCircle(ball.x, ball.y, ball.radius, light_b, TRUE);
        DrawLine(line1.x0, line1.y0, line1.x1, line1.y1, white, TRUE);

    }

    /*
    DrawLine(0, 0, 1079, 0, light_b, TRUE);
    DrawLine(1079, 0, 1079, 2219, light_b, TRUE);
    DrawLine(1079, 2219, 0, 2219, light_b, TRUE);
    DrawLine(0, 2219, 0, 0, light_b, TRUE);
    */

    WaitKey() ;				// キー入力待ち

    DxLib_End() ;				// DXライブラリ使用の終了処理

    return 0 ;					// ソフトの終了
}

実行結果です!

コメント

タイトルとURLをコピーしました