Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JavaScript: change object path if collision detected

I have to create some falling snowflakes in javascript, but they have change current path if they get collision with other flake. Something like on this image: enter image description here

Here is my current code: http://codepen.io/wojtek1150/pen/QyaYdY

var flakePositions = [[]];  
var temp = 0;
// snowflake proto
function Snowflake() {
    this.pos = new Physics();
    // snowflake guid
    this.id = '';
    // inits
    this.MAX_X_START_POS = 100;
    this.X_START_OFFSET = 0;
    this.MAX_Y_START_POS = 50;
    this.Y_START_OFFSET = -50;
    this.MAX_X_SPEED = 4;
    this.MAX_Y_SPEED = 1.2;

    // use to get sin && cos
    this.animationStepsCounter = 0
    this.fallFactor = 100;
    // snowflake html
    this.getId = function () {
        if (this.id == '') {
            this.id = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g,
                function (c) {
                    var r = crypto.getRandomValues(new Uint8Array(1))[0] % 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
                    return v.toString(16);
                });
        }
        return this.id;
    }
    this.initalize = function () {   
        temp++;
        //var size = 5 + Math.random() * 20;
        var size = 20;
        this.flakeDOM.style.width = size + "px";
        this.flakeDOM.style.height = size + "px";
        this.flakeDOM.style.opacity = Math.random();
        this.pos.x = (Math.random() * this.MAX_X_START_POS);
        this.pos.y = this.Y_START_OFFSET+(Math.random() * this.MAX_Y_START_POS);
        this.pos.xSpeed = Math.random() * this.MAX_X_SPEED* Math.sign(-0.5 + Math.random());
        this.pos.ySpeed = Math.random() * this.MAX_Y_SPEED;
      
        //create array
        flakePositions[temp] = [];      
        flakePositions[temp]['id'] = this.id;
        flakePositions[temp]['x'] = this.flakeDOM.style.top;
        flakePositions[temp]['y'] = this.flakeDOM.style.left;
        flakePositions[temp]['width'] = this.flakeDOM.style.width;
        flakePositions[temp]['height'] = this.flakeDOM.style.height;
        flakePositions[temp]['xspeed'] = this.pos.xSpeed;
        flakePositions[temp]['yspeed'] = this.pos.ySpeed
    }
    this.move = function () {
      this.flakeDOM.style.top = (this.pos.y+=this.pos.ySpeed) + "px";
        this.flakeDOM.style.left = (this.pos.x += Math.sin(this.animationStepsCounter/this.fallFactor) * this.pos.xSpeed) + "px";
        this.animationStepsCounter += this.pos.ySpeed;  
      
        //update array
        flakePositions[temp]['x'] = this.flakeDOM.style.top;
        flakePositions[temp]['y'] = this.flakeDOM.style.left;
      
            
        //check position with rest
        for (var i = 1, len = flakePositions.length; i < len-1; i++) {
                        
            var rect1 = flakePositions[i];
            var rect1d = rect1['id'];
            var rect1sx = rect1['xspeed'];
            var rect1sy = rect1['yspeed'];
            var rect1x = parseInt(rect1['x']);
            var rect1y = parseInt(rect1['y']);
            
            for (var j = 2, len = flakePositions.length; j < len; j++) {
                var rect2 = flakePositions[j];
                
                var rect2d = rect2['id'];
                var rect2x = parseInt(rect2['x']);
                var rect2y = parseInt(rect2['y']);
                
                //if(rect1x == rect2x && rect1y == rect2y)
                if(rect1x < rect2x + 10 && rect1x + 10 > rect2x &&
                rect1y < rect2y  + 10 && 10 + rect1y  > rect2y )
                {
                    console.log('collision detected');
                    var t = document.getElementById(rect1d);
                    t.style.top = t.style.top+rect1sx+10;
                    t.style.left = t.style.left+rect1sy-10;
                }
            }
            
            
        }
    }
}


    

function Physics() {
    // pos
    this.x = 0;
    this.y = 0;
    this.z = 0;
    // speed
    this.xSpeed = 0;
    this.ySpeed = 0;
    this.zSpeed = 0;
    // acceleration
    this.xAccel = 1;
    this.yAccel = 1;
    this.zAccel = 1;
}

snowflakes = new Array();
var interval = 0;

function makeThisBoom() {
    // snowflakes container
    snowfield = document.getElementById('snow');
    // snowflakes count
    snoflakesCount = 20;
    for (var i = 0; i < snoflakesCount; i++) {
        snowflakes[i] = new Snowflake();
        var flake = document.createElement('div');
        snowflakes[i].flakeDOM = flake;
        flake.id = snowflakes[i].getId();
        flake.classList.add('sf');
        snow.appendChild(flake);
        snowflakes[i].initalize();
        snowflakes[i].move();
    }    
    interval = setInterval(anime,50);
}

function anime() {
    for (var flake of snowflakes) {
        flake.move();
    }
}

function setInterface() {
    document.getElementById('startstop').onclick = function () {
        if (interval != 0) {
            clearInterval(interval);
            interval = 0;
        } else interval = setInterval(anime, 50);
    }
}
document.addEventListener("DOMContentLoaded", makeThisBoom);
document.addEventListener("DOMContentLoaded", setInterface);
.sf{
  position:absolute;
  z-index:9999999;
  /*background: -moz-radial-gradient(center, ellipse cover, rgba(255,255,255,1) 0%, rgba(255,255,255,0) 100%);
  background: -webkit-radial-gradient(center, ellipse cover, rgba(255,255,255,1) 0%,rgba(255,255,255,0) 100%);
  background: radial-gradient(ellipse at center, rgba(255,255,255,1) 0%,rgba(255,255,255,0) 100%);
  filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffffff', endColorstr='#00ffffff',GradientType=1 );
  */
  border-radius:50%;
  display:block;
  width:20px; height:20px;
  /* FOR DEV ONLY */
  background:#FFF;
  opacity:1!important;
}
body{
  background:#222;
  overflow:hidden;
}
#snow {
    position: absolute;
    width: 100%;
    height: 100%;
    overflow: hidden;
}
#startstop{
  width:100px;
  height:30px;
  border:0;
  background:rgb(61, 95, 123);
  color:#FFF;
  outline:none;
}
<button id="startstop">Start/stop</button>
	<div id="snow">	</div>

I already know how to get positions and I got if statement to check if there is any collision. But I don't know how to change path in proper way, even just bounce it :(

Any suggestions?

like image 406
Wojtek1150 Avatar asked Jun 17 '26 05:06

Wojtek1150


1 Answers

One of the key things for you to think about when doing this, are the cost/reward of implementing this kind of feature. I don't think these collisions will help you create the illusion of snowfalling. When the snowflakes in your current iteration miss each other it gives the illusion of 3d. If they were to hit each other and bounce it might give the incorrect illusion of balls falling in a 2d plane.

That being said, to implement what you are asking without using a library would be a huge time sync. I would recommend taking a look at PhysicsJS or matter-js.

Below you can see the function I personally use in a library I was working on. You can adapt most of it for your use. The truth is this a complicated ask.

define( 'detect/detectCircleCircleCollision' , [ 'lib/underscore' ] , function ( _ ) {

    return function detectCircleCircleCollision (   circlePositionA,
                                                    circleRadiusA,
                                                    circleDisplacementA,
                                                    circlePositionB,
                                                    circleRadiusB,
                                                    circleDisplacementB,
                                                    boolean,
                                                    normalBody ) {

        boolean = _.isUndefined( boolean ) || boolean ? true : false;

        normalBody = _.isUndefined( normalBody ) || normalBody ? true : false;

        var
            relativePosition            = circlePositionA.subtract.new( circlePositionB ),
            combineRadius               = circleRadiusA + circleRadiusB,
            relativePositionDotProduct  = relativePosition.lengthSqr(),
            relativeDisplacement        = circleDisplacementA.subtract.new( circleDisplacementB ),
            a,b,c,r,t,newCircleOnePosition,newCircleTwoPosition,newCirclePositionDifference,collisionPoint;

        if ( relativePositionDotProduct < combineRadius * combineRadius ) {

            if ( boolean ) return true;

            return collision(   0,//Time
                                circlePositionB.add.new( vector( relativePosition ).magnitude.set( circleRadiusA ) ),//point
                                relativePosition.normalize(),//Normal
                                normalBody,//normalbody
                                vector( relativePosition ).magnitude.set( circleRadiusA + circleRadiusB - relativePosition.magnitude() ) );//intersection

        }

        a = relativeDisplacement.dotProduct( relativeDisplacement );
        b = relativePosition.dotProduct( relativePosition );
        c = relativePositionDotProduct - combineRadius * combineRadius;
        r = b * b - a * c;

        if ( r < 0 ) return false;

        t = -b - r * r / a;

        if ( t > 1 || t < 0 ) return false;
        else if ( boolean ) return true;

        newCircleOnePosition        = circleDisplacementA.scale.new( t ).add( circlePositionA );
        newCircleTwoPosition        = circleDisplacementB.scale.new( t ).add( circlePositionB );
        newCirclePositionDifference = newCircleTwoPosition.subtract.new( newCircleOnePosition ).normalize();
        collisionPoint              = newCirclePositionDifference.scale.new( circleRadiusA );

        return collision(   t,
                            collisionPoint.add( newCircleOnePosition ),
                            newCirclePositionDifference,
                            normalBody,
                            false );



    };

} );
like image 97
hyphnKnight Avatar answered Jun 19 '26 17:06

hyphnKnight



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!