[work 117] Collision Simulator without physics engine

[work 117] Collision Simulator without physics engine

Movie

Source code

about

パーティクル衝突シミュレータを物理エンジンを使わずに作る(ただし、だいぶ簡略しています)

file

  • 上部にあるファイル名が表示されているボタンを押すと、表示されるファイルが切り替わります
  • 別ウィンドウ表示したい時や行番号などが無いRawMode表示したい時は、コード内右上のボタンを押してください(ボタンはマウスオーバーすると表示されます)
#include "ofMain.h"
#include "ofApp.h"

//================================
int main( ){

    // 4K:4096x2160
    // 2K:2048x1080
    // FullHD:1920x1080
    // HD:1440x1080
    // HD720p:1280x720
    // DVD:720x480
    // setup the GL context
    ofSetupOpenGL(1280, 720, OF_WINDOW);

	// this kicks off the running of my app
	// can be OF_WINDOW or OF_FULLSCREEN
	// pass in width and height too:
	ofRunApp( new ofApp());

}
#pragma once

#include "ofMain.h"
#include "ofxGui.h"
#include "Mover.hpp"


class ofApp : public ofBaseApp{
public:
    ofApp();
    ~ofApp();
    
    void setup();
    void update();
    void draw();
    
    void keyPressed(int key);
    void keyReleased(int key);
    void mouseMoved(int x, int y);
    void mouseDragged(int x, int y, int button);
    void mousePressed(int x, int y, int button);
    void mouseReleased(int x, int y, int button);
    void mouseEntered(int x, int y);
    void mouseExited(int x, int y);
    void windowResized(int w, int h);
    void dragEvent(ofDragInfo dragInfo);
    void gotMessage(ofMessage msg);
    
private:
    void makeMover();
    std::vector<std::shared_ptr<Mover>> movers;
    ofRectangle area;
    int moversSize;
};
#include "ofApp.h"


ofApp::ofApp(){
    
}

ofApp::~ofApp(){
    
}

//--------------------------------------------------------------
void ofApp::setup(){
    double fps = 30;
    
    ofSetFrameRate(fps);
    ofBackground(ofColor::white);
    ofSetBackgroundAuto(true);
    ofSetVerticalSync(true);
    
    float w = 800;
    float h = 400;
    area = ofRectangle((ofGetWidth() / 2) - (w / 2), (ofGetHeight() / 2) - (h / 2), w, h);
    
    moversSize = 100;

    makeMover();
}

//--------------------------------------------------------------
void ofApp::update(){
    
    for (shared_ptr<Mover> &m : movers) {
        m->update();
        m->checkEdge(area);
    }
    
    for (shared_ptr<Mover> &m0 : movers) {
        for (shared_ptr<Mover> &m1 : movers) {
            if (m0 != m1) {
                m0->impact(m1);
            }
        }
    }
}

void ofApp::makeMover()
{
    ofVec2f pos = ofVec2f(area.getCenter());
    MoverStatus status = MoverStatus::HUNTER;
    movers.push_back(std::make_shared<Mover>(status, pos));
    
    while (movers.size() < moversSize) {
        float offset = 8;
        pos = ofVec2f(ofRandom(round(area.getLeft()) + offset, round(area.getRight() - offset)), ofRandom(round(area.getTop()) + offset, round(area.getBottom()) - offset));
        
        bool create = true;
        float dist = (offset * 2) + 1;
        for (std::shared_ptr<Mover> &m : movers) {
            ofVec2f p = m->getLoc();
            if ((pos - p).length() < dist) {
                create = false;
                break;
            }
        }
        
        if (create) {
            status = MoverStatus::ESCAPER;
            float fix = (float)ofRandom(50);
            movers.push_back(std::make_shared<Mover>(status, pos, fix));
        }
    }
}


//--------------------------------------------------------------
void ofApp::draw(){
    
    ofPushMatrix();
    ofPushStyle();
    ofNoFill();
    ofSetLineWidth(2);
    ofSetColor(ofColor::black);
    ofDrawRectangle(area);
    ofPopStyle();
    ofPopMatrix();
    
    for (shared_ptr<Mover> &m : movers) {
        m->display();
    }
}
#ifndef Mover_hpp
#define Mover_hpp

#include <stdio.h>
#include "ofMain.h"

enum class MoverStatus {
    HUNTER = 0,
    ESCAPER
};


class Mover {
public:
    Mover() = delete;
    Mover(MoverStatus &s, ofVec2f &l);
    Mover(MoverStatus &s, ofVec2f &l, float &fix);
    ~Mover();
    void update();
    void checkEdge(ofRectangle &area);
    void display();
    bool impact(std::shared_ptr<Mover> &m);
    ofVec2f getLoc();
    float getRadius();
    ofVec2f getVelocity();
    MoverStatus getStatus();
    
    
private:
    void setup(MoverStatus &s, ofVec2f &l);
    
    MoverStatus status;
    ofVec2f location;
    ofVec2f acceleration;
    ofVec2f velocity;
    float speed;
    float radius;
    bool fix;
    float fixRatio;
    
    std::vector<std::shared_ptr<Mover>> mlist;
};

#endif /* Mover_hpp */
#include "Mover.hpp"


Mover::Mover(MoverStatus &s, ofVec2f &l)
{
    fixRatio = 0;
    
    setup(s, l);
}


Mover::Mover(MoverStatus &s, ofVec2f &l, float &fix)
{
    fixRatio = fix;
    
    setup(s, l);
}


Mover::~Mover()
{
    
}


void Mover::update()
{
    if (fix == false) {
        velocity += acceleration;
        velocity = velocity.normalize() * speed;
        acceleration *= 0;
    }
    location += velocity;
}


void Mover::checkEdge(ofRectangle &area)
{
    int left = round(area.getLeft());
    int right = round(area.getRight());
    int top = round(area.getTop());
    int bottom = round(area.getBottom());
    int x = round(location.x);
    int y = round(location.y);
    int r = round(radius);
    
    if (left > x - r) {
        location.x = (left + r) + (left - (x - r));
        if (velocity.x < 0) {
            velocity.x *= -1;
        }
    } else if (right < x + r) {
        location.x = (right - r) - ((x + r) - right);
        if (velocity.x > 0) {
            velocity.x *= -1;
        }
    }
    
    if (top > y - r) {
        location.y = (top + r) + (top - (y - r));
        if (velocity.y < 0) {
            velocity.y *= -1;
        }
    } else if (bottom < y + r) {
        location.y = (bottom - r) - ((y + r) - bottom);
        if (velocity.y > 0) {
            velocity.y *= -1;
        }
    }
}


void Mover::display()
{
    ofColor c;
    
    if (status == MoverStatus::ESCAPER) {
        c = ofColor::blue;
    } else {
        c = ofColor::red;
    }
    
    ofPushMatrix();
    ofPushStyle();
    ofFill();
    ofSetColor(c);
    ofDrawCircle(location, radius);
    
    ofPopStyle();
    ofPopMatrix();
}


bool Mover::impact(std::shared_ptr<Mover> &m)
{
    ofVec2f dir = m->getLoc() - location;
    int len = round(dir.length());
    bool ret = false;
    
    if (len <= round(m->getRadius() + radius)) {
        ret = true;
        
        bool remove = false;
        for (std::shared_ptr<Mover> &ml : mlist) {
            if (ml == m) {
                remove = true;
                break;
            }
        }
        
        if (remove == false) {
            mlist.push_back(m);
            
            if (fix == false) {
                if (m->getVelocity() == ofVec2f(0, 0)) {
                    float angle = dir.angleRad(getVelocity());
                    float a = getVelocity().length() * std::cos(angle);
                    float str = std::abs(a) * 2;
                    acceleration = str * (dir.normalize() * -1);
                } else {
                    float angle = dir.angleRad(m->getVelocity());
                    float a1 = m->getVelocity().length() * std::cos(angle);
                    angle = dir.angleRad(getVelocity());
                    float a2 = getVelocity().length() * std::cos(angle);
                    float str = std::abs(a1) + std::abs(a2);
                    acceleration = str * (dir.normalize() * -1);
                }
            }
            
            if (m->getStatus() == MoverStatus::HUNTER && status != MoverStatus::HUNTER) {
                status = MoverStatus::HUNTER;
            }
        }
    } else {
        for (auto mitr = mlist.begin(); mitr != mlist.end(); ++mitr) {
            if (*mitr == m) {
                mlist.erase(mitr);
                break;
            }
        }
    }
    
    return ret;
}


ofVec2f Mover::getLoc()
{
    return location;
}


float Mover::getRadius()
{
    return radius;
}


ofVec2f Mover::getVelocity()
{
    return velocity;
}


MoverStatus Mover::getStatus()
{
    return status;
}


void Mover::setup(MoverStatus &s, ofVec2f &l)
{
    status = s;
    location = l;
    speed = 1.5;
    radius = 8;
    
    acceleration = ofVec2f(0, 0);
    
    if (ofRandom(100) < fixRatio) {
        fix = true;
        velocity = ofVec2f(0, 0) * speed;
    } else {
        fix = false;
        velocity = ofVec2f(1, 0).rotate(ofRandom(0, 360)) * speed;
    }
}

Link to the reference page

ソースコードで使用したAPIの中から要点になりそうなものをいくつか選んでリストアップしました。

categoryAPI/Lib
openframeworksofVec2f angleRad
c++round
c++cos
c++abs

Development environment

  • openframeworks 0.10.1
  • c++
  • macOS
  • Xcode