I'm trying to detect if two given div's are too close or collide/overlap .
I have the below codepen which tries to generate 20 random div's and only append them to body if their position isn't too close to other existing div.
That's the idea but it doesn't work as expected where i get div's that get through with close/overlapping positions to existing divs. (run it multiple times if first time is perfect and you should come across it).
http://codepen.io/anon/pen/fHLzj
Can anyone see the mistake and way to make it work?
This is somewhat hard to explain and get..but here goes:
check every div against every div by running for loop.
x,y,h,w
x is top-left corner's distance from left.
y is top-left corner's distance from top.
h is div's height.
w is div's width.
Point to consider... you don't really need to check every div..consider this
there are 10 divs...
First you will check 1st against 9.
Second one against 8.
.............
Eight one against 2.
Ninth one against 1.
And don't the last one.
Also it's a good idea to assign values and check for collisions in data, before assigning them to dom. Dom should be just for rendering final result.
I'll assume you want to keep none of the two colliding divs.
Preview
http://jsfiddle.net/techsin/m4fSf/6/
as expected code is huge
var
div={},
number=10,
size=20,
m = ele('main');
mw= parseFloat(getComputedStyle(m).getPropertyValue("width"))-size,
mh= parseFloat(getComputedStyle(m).getPropertyValue("height"))-size,
f=true,
nn;
var i
for (i = 0; i < number; i++) {
div[i] = {};
var t = true, newX, newY, nn;
if (i!=0){
while (t) {
newX = rand(mw);
newY = rand(mh);
for (nn = 0; nn < i; nn++) {
if (!(((newX > div[nn].x + size+5) || (newY > div[nn].y + size+5)) ||
((newX + size+5 < div[nn].x) || (newY + size+5 < div[nn].y)))) {
break;
}
if (nn == i-1) t = false;
}}} else {
newX = rand(mw);
newY = rand(mh);
}
console.log(newX);
div[i].x = newX;
div[i].y = newY;
}
for (i = 0; i < number; i++) {
render(div[i]);
}
console.log(div);
function render(x){
var d=document.createElement('div');
d.style.position='absolute';
d.style.left=(x.x+'px');
d.style.top=(x.y+'px');
m.appendChild(d);
}
function rand(x) { return Math.random()*x;}
function ele(x){return document.getElementById(x);}
this code is from my collision site...ill try and put it in the code above, but this what's needed to avoid collisions and close gaps.
if (xpost+30>xx.left && xx.left>xpost && xx.top+30>ypost && xx.top<ypost+30) { xspeed = -speed; }
if (xpost<xx.left+30 && xx.left<xpost && xx.top+30>ypost && xx.top<ypost+30) { xspeed = speed; }
if (ypost+30>xx.top && xx.top>ypost && xx.left+30>xpost && xx.left<xpost+30) { yspeed = -speed; }
if (ypost<xx.top+30 && xx.top<ypost && xx.left+30>xpost && xx.left<xpost+30) { yspeed = speed; }
How about using one of these libraries to detect the collisions for you?
http://sourceforge.net/projects/jquerycollision/
http://gamequeryjs.com/
I changed the collision logic. It detects if an object is close to another object by comparing the distance between the objects. I wrapped the logic in a do-while loop as well, so that it will keep attempting to find a position to place the square and you'll have exactly 20 squares.
This works:
var positions = []; //stroe positions of appended divs
var divsize = 20;
var topGap = 40; // gap from top
var leftGap = 80; //gap from left
function generateRandomPositionedDiv(){
for(var c = 0; c < 20; c++){
var color = '#'+ Math.round(0xffffff * Math.random()).toString(16);
$newdiv = $('<div/>').css({
'width':divsize+'px',
'height':divsize+'px',
'background-color': color
});
var posLeft;
var posTop;
var checkObj;
var collide = false;
posLeft = Math.floor((Math.random() * ($(document).width() - divsize)));//.toFixed();
posTop = Math.floor((Math.random() * ($(document).height() - divsize)));//.toFixed();
checkObj = {x: posLeft, y: posTop};
collide = checkForCollisions(checkObj);
if(!collide) {
positions.push({x: posLeft, y: posTop});
$newdiv.css({
'position':'absolute',
'left':posLeft+'px',
'top':posTop+'px'
});
$('body').append($newdiv);
}
}
}
/*function getPositions(box) {
var $box = $(box);
var pos = $box.position();
var width = $box.width();
var height = $box.height();
return [ [ pos.left, pos.left + width + leftGap ], [ pos.top, pos.top + height + topGap ] ];
}*/
function comparePositions(obj1, obj2) {
if(Math.abs(obj1.x - obj2.x) <= (divsize + leftGap) && Math.abs(obj1.y - obj2.y) <= (divsize + topGap)) {
return true;
} else {
return false;
}
}
function checkForCollisions(posObj){
for(var i = 0; i < positions.length; i++){
var match = comparePositions(positions[i], posObj);
if (match) {
//return true if two positions are close or overlapping
return match;
}
}
}
generateRandomPositionedDiv();
Related
I want to put like 30 of <img class="anger"> elements with random size and random position inside the <div> container, but none of the .anger hitting one another.
Is it possible?
This is my code so far:
function loadAngers() {
var wrp = '#angerContainer'; //the container
var rectAvatar = $('#picAvatar')[0].getBoundingClientRect(); //rect of user avatar
var rectWrapper = $(wrp)[0].getBoundingClientRect(); //rect of container
listCoorditaes = [[
rectAvatar.width,
(rectAvatar.left+rectAvatar.right)/2,
(rectAvatar.top+rectAvatar.bottom)/2
]];
$(wrp).find('.anger').remove();
for (var i=0; i<listAnger.length; i++) {
var verb = listAnger[i].replace('assets/img/verb/','').replace('.png','').replace('-',' ');
var anger = $('<img src="'+listAnger[i]+'" class="anger hvr-'+getRandom(listAnim)+'" data-verb="'+verb+'" style="position:absolute">');
var paddingX = 100;
var paddingY = 200;
var wideX = rectWrapper.width - paddingX;
var wideY = rectWrapper.height - paddingY - rectAvatar.top;
var width = Math.round(30 + Math.random() * 70);
var left;
var top;
var x;
var y;
var tubrukan;
var coba = 0;
do { //find the best coordinate
tubrukan = false;
coba++;
x = Math.round(Math.random() * wideX) + paddingX/2;
y = Math.round(Math.random() * wideY) + paddingY/2 + rectAvatar.top;
left = x - width/2;
top = y - width/2;
for (var j=0; j<=i; j++) {
var cekW = listCoorditaes[j][0];
var cekX = listCoorditaes[j][1];
var cekY = listCoorditaes[j][2];
var difX = Math.abs( x - cekX );
var difY = Math.abs( y - cekY );
if (difX < cekW && difY < cekW) {
tubrukan = true;
break;
}
}
}
while(tubrukan && coba<3); //as I give up for eternal loop, I limit the loop with 3 tries.
listCoorditaes.push([width,x,y]);
anger.css('width',width+'px');
anger.css('left',left);
anger.css('top',top);
anger.appendTo(wrp);
}
}
This is the current result:
As we can see, the elements still overlap the other because I limit the loop. If I remove the limit, browser will endure endless loop and become not responding.
Do you have another better way to achieve it?
UPDATE:
My bad, I set the container height only 800px, that's why it can't contain all those <img> without overlapping, thus eternal loop happen. I made it to 2000px to see if it works. But the problem is still takes uncertain number of loops to find the best coordinate, so I still put limit to the loop thus overlap still happen several times.
I am coding a lil js game for a university project.
I have a 2d map and I can move my player with arrows. Enemies are spawned every 5 seconds and they are guided by the function:
enemy.updatePosition = function() {
if(enemy.isAttacking === false) {
var diffX = Math.floor(player.x - enemy.x);
var diffY = Math.floor(player.y - enemy.y);
//security distance by player --> superEnemy type 1 uses arrows
var distance = getDistanceBetweenEntities(player, enemy);
var gap = 20;
enemy.pressingRight = diffX > gap;
enemy.pressingLeft = diffX < -gap;
enemy.pressingDown = diffY > gap;
enemy.pressingUp = diffY < -gap;
enemy.isStopped = false;
if(enemy.speedX < 0)
enemy.speedX = - enemy.speedX;
if(enemy.speedY < 0)
enemy.speedY = - enemy.speedY;
//bumpers check if hitting a wall or end of map
var rightBumper = {x:enemy.x + 15, y:enemy.y};
var leftBumper = {x:enemy.x - 15, y:enemy.y};
var upBumper = {x:enemy.x, y:enemy.y - 25};
var downBumper = {x:enemy.x, y:enemy.y + 20};
if(currentMap.isPositionWall(rightBumper)) {
enemy.x -= 1;
} else {
if(enemy.pressingRight)
enemy.x += enemy.speedX;
}
if(currentMap.isPositionWall(leftBumper)) {
enemy.x += 1;
} else {
if(enemy.pressingLeft)
enemy.x -= enemy.speedX;
}
if(currentMap.isPositionWall(downBumper)) {
enemy.y -= 1;
} else {
if(enemy.pressingDown)
enemy.y += enemy.speedY;
}
if(currentMap.isPositionWall(upBumper)) {
enemy.y += 1;
} else {
if(enemy.pressingUp)
enemy.y -= enemy.speedY;
}
//set position again if the center of the draw
//of enemy goes out of map's limits
if(enemy.x < enemy.width/2)
enemy.x = enemy.width/2;
if(enemy.x > currentMap.width - enemy.width/2)
enemy.x = currentMap.width - enemy.width/2;
if(enemy.y < enemy.height/2)
enemy.y = enemy.height/2;
if(enemy.y > currentMap.height - enemy.height/2)
enemy.y = currentMap.height - enemy.height/2;
}
}
}
So my enemies follow the player with the values of diffX and diffY. Each enemy has it own speedX and speedY, something like:
var random = 1 + Math.random()*7; //from 1 to 8
enemy.speedX = random;
enemy.speedY = random;
The result is that enemies start to overlap, expecially when they are performing an attack(x and y don't changes during attack). Is there a simple way to avoid that without checking a lot of collision? Thanks everyone
There are more options for you, but here is one simple collision detection.
First you will need to make every enemy unique like giving everyone of them a unique name. This doesn't need to be complicated just like enemy1, enemy2, .... enemy223. You can do it at the point where you spawn the enemy, like this:
enemy['name'] = 'enemy' + i++;
so you can access it like this:
enemy.name;
Important: you should write some kind position that updates everytime the 'enemy' changes position or every tick.
enemy['position'] = enemy.x+','+enemy.y;
make an array into that you can write the positions of every enemy. I know this is not the best option but it's simple and will work for now.
var pstns = [];
After that write every enemy into the array (just do it at spawn). I would like to mention that the following is not good practice.
var pstnsObj = {};
pstnsObj[enemy.name] = enemy.position;
pstns.push(pstnsObj);
Next you need to update the position in the array every tick with every enemy. This is only one example you can do it multiple ways or even automate this process.
function updatePstns(id, position){
pstns[id][Object.keys(pstns[id])[0]] = position;
//just in case:
return pstns;
}
//updating first enemy:
updatePstns(0, enemy.position);
now for the collision:
function checkCollision(){
var count = 0;
pstns.forEach(function(e){
for(i=0; i<pstns.length; i++){
if(pstns[e][Object.keys(pstns[e])[0]] == pstns[i][Object.keys(pstns[i])[0]]){
count++;
}
}
if(count > 1){
console.log('enemy ' + pstns[e] + 'collides with ' + count + 'enemies');
}
});
}
So I've been working on re-producing the slider found here https://www.skylight.io/ ( Scroll down to find the price slider ).
So far Ive managed to create something similiar, but some numbers are hard coded, making it difficult to change and not very re-usable.
I've been researching around and I think I need to use Math.log() and Math.exp() together to achieve something like in the link above but I'm not sure.
Heres a jsfiddle of what I have so far https://jsfiddle.net/7wrvpb34/.
I feel that its the maths part of this problem that is halting me I think, so any help would be greatly appreciated.
Javascript code below:
var slider = document.getElementById("slider")
var sliderFill = document.getElementById("slider-fill")
var knob = document.getElementById("knob")
var mouseDown;
var mousePos = {x:0};
var knobPosition;
var minPrice = 20;
var price = 0;
var minRequests = 50;
var requests = 50 + ",000";
var incrementSpeed = 2;
var incrementModifier = 20;
var incrementValue = 1;
var minMillionCount = 1;
var millionCount = 1;
var previousRequestAmount = 0;
document.getElementById("price").innerHTML = price;
document.getElementById("requests").innerHTML = requests;
highlightTable(1);
document.addEventListener('mousemove', function(e) {
if(mouseDown) {
updateSlider(e);
}
})
function updateSlider(event) {
mousePos.x = event.clientX - slider.getBoundingClientRect().left;
mousePos.x -= knob.offsetWidth / 2;
console.log(mousePos.x);
if(mousePos.x < 0) {
knob.style.left = "0px";
sliderFill.style.width = "0px";
price = 0;
requests = 50 + ",000";
document.getElementById("price").innerHTML = price;
document.getElementById("requests").innerHTML = requests;
return
}
if(mousePos.x > slider.offsetWidth - 20) {
return
}
sliderFill.style.width = mousePos.x + 10 + "px";
knob.style.left = mousePos.x + "px";
//Increase requests by using X position of mouse
incrementSpeed = mousePos.x / incrementModifier;
requests = minRequests + (mousePos.x * incrementSpeed);
//Round to nearest 1
requests = Math.round(requests / incrementValue) * incrementValue;
if (requests >= 1000){
var m = requests/ 1000;
m = Math.round(m / 1) * 1;
//Problem, lower the modifier depending on requests
incrementModifier = 20 * 0.95;
document.getElementById("requests").innerHTML = m + " million";
//Adjust Prices
if(( requests >= 1000) && (requests < 10000)) {
var numOfMillions = requests / 100;
//Round to closest 10.
//10 * number of millions
var rounded = Math.round(numOfMillions / 10) * 10;
price = minPrice + rounded;
highlightTable(3);
}
//Adjust Prices
if(requests >= 10000) {
var numOfMillions = requests / 1000;
var rounded = Math.round(numOfMillions / 1) * 1;
var basePrice = minPrice * 6;
price = basePrice + rounded;
highlightTable(4);
}
} else {
incrementModifier = 20;
document.getElementById("requests").innerHTML = requests + ",000"
if(requests < 100) {
highlightTable(1);
price = 0;
} else {
highlightTable(2);
price = 20;
}
}
previousRequestAmount = requests;
document.getElementById("price").innerHTML = price;
}
knob.addEventListener('mousedown', function() {
mouseDown = true;
});
document.addEventListener('mouseup', function() {
mouseDown = false;
});
function highlightTable(rowNum) {
var table = document.getElementById("payment-table")
for(var i = 0; i < table.rows.length; ++i) {
var row = table.rows[i]
if(i == rowNum) {
row.style.background = "grey"
} else {
row.style.background = "white";
}
}
}
Thank you for your time.
If you want it to be reusable you need to create a mathematical function that assigns a result to the number of requests. I will give you a very easy example.
If you want a different result for 1,10,100,100,10000 etc
var d = Math.log10(requests);
if(d<1){
doSomething();
}else if(d<2){
doSomethingElse();
} //etc
This way if you want to change the specific values that create certain results, all you need to do is change the function.
This only works if your tiers of requests follow a math function, if they don't you need to hard code it.
However if say they don't follow a math function, but you know how you would like them to change based on a value then you can do this.
var changingValue = 4;
if(requests < 400*changingValue){
doSomthing();
}else if(requests <= 400*changingValue*changingValue){
doSomethingElse();
}else{// the requests is greater than any of the above
doTheOtherThing();
}
Edit:
For the second one you need to make sure that each condition if always larger than the other from top to bottom.
The description "increasingly increasing" matches an arbitrary number of functions. I assume you also want it to be continuous, since you already have a non-continuous solution.
TL;DR
Use an exponential function.
Generic approach
Assuming imin and imax are the minimal and maximal values of the slider (i for input) and omin and omax are the minimal and maximal values to be displayed, the simplest thing I can think of would be a multiplication by something based on the input value:
f(x)
{
return omin + (omax - omin) * g((x - imin) / (imax - imin));
}
This will pass 0 to g if x == imin and 1 if x == imax.
The return value r of g(y) should be
r == 0 for y == 0
r == 1 for y == 1
0 < r < y for 0 < y < 1
The simplest function that I can think of that fulfills this is an exponential function with exponent > 1.
An exponent of 1 would be a linear function.
An exponent of 2 would be make the middle of the slider display one fourth of the maximum price instead of half of it.
But you really need to find that exponent yourself, based on your needs.
I'm looking for a way to move a div from an array of position with javascript/jquery.
I have trying to do it with jquery.animate but he moved the div with a pause at each iteration of my array.
That could be something like move the div from 0,0 to 120px,230px passing by the 23px,35px;45px,50px etc...
That is for moving an game character on a Tile map
So as requested, some bit of code
First you have a global timer that call a function at short interval to see if it have any action to execute.
In this loop a routine look if some mobile tiles are waiting of any mouvement.
Mobiles are declared as Object class and have a sub function that do the deplacement like that
setPos:function(coord){
var pos = jQuery("#"+this.id).position();
var x = (coord[0] - 32 + this.screenOffX + this.xOffset) - pos.left;
var y =(coord[1] + this.yOffset) - pos.top;
//this.stopAnimation();
//this.startAnimation(this.walkingAnimation);
jQuery("#"+this.id).animate({
left: '+='+ x,
top: '+='+ y
}, 33, function() {
// Animation complete.
});
},
That is a bit messy cause i trying a lot of thing to do the smooth movement that i'm looking for.
so setPos is calling in another place like that
stepMobile:function(mobile){
var wp;/*TEST*/
mobile.changeState("idle");
var ind = mobile.getWayPointIndex();
while(ind < (mobile.getWayPoints()).length - 1){
if (ind < (mobile.getWayPoints()).length - 1) {
wp = (mobile.getWayPoints())[ind + 1];
if (getTime() > wp.time) {
mobile.setWayPointIndex(ind + 1);
ind = ind +1;
}
}
wp = (mobile.getWayPoints())[ind];
var x;
var y = 0;
var z;
x = this.tileWidth * (wp.getTile()).getCol();
z = this.tileHeight * (wp.getTile()).getRow();
var elapsed = getTime() - wp.getTime();
console.log(elapsed);
if (ind == (mobile.getWayPoints()).length - 1) {
console.log('checkForOnStopEvent()');
} else {
//x += 1 * mobile.getWalkSpeed() * mobile.getCosAngle();
//z += 1 * mobile.getWalkSpeed() * mobile.getSinAngle();
}
var coord = this.mapToScreen(x, y, -z);
mobile.setPos(coord);
ind = mobile.getWayPointIndex();
}
},
Again lot of junk code here cause i literally burned my brain but i didn't get any good result.
And you have that global function that run this function over all mobiles waiting for deplacement.
I have a simple javascript animation, where two cowboys (iamges) 'race' each other based on a random interval number.
What I can't find out how to do is for the script to take a decision on who is the winner, meaning if a cowboy reaches a pre-defined distance first, the script will know and will show an alert on who won.
Here is a screen shot to show an example:
This is the code I have so far: http://pastebin.com/Cmt4N8c9
Can give me some directions?
Thanks,
Brian
In your move() function you should do something like
if (x >= dest_x) {
alert('player 1 won');
} else if (x2 >= dest_x2) {
alert('player 2 won');
} else {
... continue the loop ...
}
You'd most likely put that behind
document.getElementById("cowboy").style.top = y+'px';
document.getElementById("cowboy").style.left = x+'px';
document.getElementById("cowboytwo").style.top = y2+'px';
document.getElementById("cowboytwo").style.left = x2+'px';
You might want to check your code on duplicate variables too, by the way.
AFAIK dest_x and dest_x2 are both the same for example.
Simple move
/* Y is not relevant since you only move it on X axis */
var position1 = 100;
var position2 = 100;
var dest = 800; //Or any given value
function move() {
var step1 = Math.floor(1 + (10 * Math.random() ) );
var step2 = Math.floor(1 + (10 * Math.random() ) );
position1 += step1;
position2 += step2;
document.getElementById("cowboy").style.left = position1+'px';
document.getElementById("cowboytwo").style.left = position2+'px';
if(position1 < dest && position2 < dest) {
window.setTimeout('move()',100);
} else {
//We have the winner
if(position1 > dest) alert("Winner is Cowboy1");
if(position2 > dest) alert("Winner is Cowboy2");
//Its also possible that both of them pass target value at the same step
}
}