Tuesday, November 29, 2011

Pulsate

Решил продолжить тему потрясающе простой и красивой флешки Pulsate, после того как AViktorov сделал её клон на Lua.

Идея данной флешки заключается в следующем: при клике мышкой на поле происходит добавление нового круга. Радиус круга начинает плавно увеличиваться, до столкновения с другим кругом. После столкновении происходит воспроизведение звукового сигнала в соответствии с радиусами кругов и радиусы обеих кругов начинают уменьшаться, до того пока не станут равны нулю или же круги не столкнутся с другими кругами. Таким образом можно сказать, что после столкновения радиусы кругов меняют способ роста на противоположный.

Математика для расчёта столкновений двух кругов подразумевает два варианта.
Первый — когда происходит внешнее столкновение двух кругов, и второй — при котором один круг находится внутри другого, то есть происходит внутреннее столкновение.

В первом варианте определить столкновение можно узнав расстояние d между центрами кругов и проверив является ли сумма радиуса большего круга R и радиуса меньшего круга r больше или равной расстояния d. То есть при столкновении L = 0.
Для того чтобы определить находится ли один круг находится внутри другого нужно определить больший из двух кругов по радиусу и затем проверить меньше ли расстояние d между центрами кругов, чем радиус большего круга R. Если d < R, то один круг находится внутри другого.

Во втором варианте для того чтобы определить столкновение необходим предпринять следующее. Определив расстояние между центрами кругов d, и зная радиусы обеих кругов, нужно проверить является ли сумма d и r больше или равной R. В случае столкновения L = 0.
В ходе экспериментов задав интервалы радиусов и соответствующие им ноты, получаются очень атмосферные мелодии.


Добавив линии соединяющие центры всех кругов получается такой вот красивый рисунок:


Исходный код основных методов столкновений представлен ниже:

struct Circle {

    Circle(
int x, int y) : x(x), y(y), radius(0), color(D3DCOLOR_RGBA(0,155,225,255)), is_grow(true) { }

    
int x;
    
int y;
    
float radius;
    
int color;
    
bool is_grow;
};

const int MAX_CIRCLES_NUM = 32;

std::vector
circles;
//-----------------------------------------------------------------------------
// Checks circles for intersection and update its sizes
//-----------------------------------------------------------------------------
void updateCircles() {
    
for (int i = 0; i < circles.size(); i++) {
        
for (int j = 0; j < circles.size(); j++) {
            
// prevent checking with self
            
if (i == j) continue;

            
// get the squared distances between centers of circles
            
float distance_squared = (circles[i].x - circles[j].x) * (circles[i].x - circles[j].x) + (circles[i].y - circles[j].y) * (circles[i].y - circles[j].y);   
            
// get radiuses and distance
            
float max_radius = max(circles[i].radius, circles[j].radius);
            
float min_radius = min(circles[i].radius, circles[j].radius);
            
float distance = sqrt(distance_squared);
            
float radius =
circles[i].radius + circles[j].radius;

            
// circle inside another circle
            
if (max_radius >= distance) {
                
if (max_radius - (distance + min_radius) <= 0) {
                    
// prevent overgrow
                    
if (circles[i].radius > circles[j].radius) {
                        circles[i].radius = max_radius;
                        circles[j].radius = max_radius - distance;
                    }
                    circles[i].is_grow = !circles[i].is_grow;
                    circles[j].is_grow = !circles[j].is_grow;                               
           
                    playIntersectionSound(max_radius);
                }           
            }
else {               
                
// circles outside
                
if (distance - radius <= 0) {
                    circles[i].is_grow =
false;
                    circles[j].is_grow =
false;
                    playIntersectionSound(max_radius);
                }
            }
        }

        
// update circles
        
if (circles[i].is_grow) {
            circles[i].radius += grow_speed;
        }
else {
            circles[i].radius -= grow_speed;
        }
        
if (circles[i].radius <= 0) {
            circles[i].radius = 0;
            circles[i].is_grow =
true;
        }

    }
}

//-----------------------------------------------------------------------------
// Update Game
//-----------------------------------------------------------------------------
void updateGame() {

    // add one more circle
    
if (mouse_lbutton_clicked) {
        
if (circles.size() < MAX_CIRCLES_NUM) {
            circles.push_back(Circle(mouse_lbutton_down.x,mouse_lbutton_down.y));
        }
    }

    
// crear circles
    
if (rbutton_clicked) {
        circles.clear();
    }

    
// draw circles
    
for (int i = 0; i < circles.size(); i++) {
        primitive->setPenColor(circles[i].color);
        primitive->drawCircle(circles[i].x,circles[i].y,circles[i].radius,50);
// 50 sections
    }

    
// check for intersections and update circles sizes
    updateCircles();
}

* This source code was highlighted with Source Code Highlighter.

No comments:

Post a Comment