Force, acceleration, and velocity with Javascript - javascript

I am attempting to make objects move on screen based on force on the x-axis and y-axis. It seems to work properly when a force is applied to an object, but when the force is no longer applied, instead of the object continuing at the same velocity and direction, it goes off in a different direction with a different velocity. Is the error in this section of code? This is the only part I can think would be the problem.
var update = function (modifier) {
// rock going up/down wh
if(rocks[i].y > 0 && rocks[i].y < worldSizeY){
if(rocks[i] != null){
rocks[i].accelerationy = rocks[i].forcey/rocks[i].mass;
rocks[i].velocityy += (modifier*1000)*rocks[i].accelerationy;
rocks[i].y += (modifier*1000)*rocks[i].velocityy;
}
}
// rock going right/left
if(rocks[i].x < worldSizeX && rocks[i].x > 0){
if(rocks[i] != null){
rocks[i].accelerationx = rocks[i].forcex/rocks[i].mass;
rocks[i].velocityx += (modifier*1000)*rocks[i].accelerationx;
rocks[i].x += (modifier*1000)*rocks[i].velocityx;
}
}
rocks[i].forcex = 0;
rocks[i].forcey = 0;
}

Related

Generate random divs without overlapping

I am trying to create a simple project, using TypeScript and React, to be able to generate a new <div> that has random width and height (min/max 50/300px), and is randomly placed inside a wrapper (which for this example is 1920x1080).
Goal is to check if newly generated div rectangle is overlapping or not existing divs.
If not, create the element, else generate new position and size and check again, goal being to fill up the wrapper as much as possible since min/max size of div is set and have no overlapping ones.
So far I managed to write code for generating random positions and sizes, but I am stuck at checking collisions and sending message when empty space isn't left.
const [count,setCount]=useState(0)
const wrapper = document.getElementById('wrapper');
var posX:number,posY:number,divSizeH:number,divSizeW:number;
var willOverlap:boolean=false;
function createRandomRectangle(){
divSizeW = Math.round(((Math.random()*250) + 50));
divSizeH = Math.round(((Math.random()*250) + 50));
if (wrapper!=null) {
const width = wrapper.offsetWidth , height = wrapper.offsetHeight;
posX = Math.round( (Math.random() * ( width - divSizeW )) );
posY = Math.round( (Math.random() * ( height - divSizeH )) );
//checking collision
document.querySelectorAll('.Rectangle').forEach(element=>{
var r2 = element.getBoundingClientRect();
//my attempt //if(((posX>=r2.x&&posX<=r2.right)&&(posY>=r2.top&&posY<=r2.bottom))||((posX+divSizeW>=r2.x&&posX+divSizeW<=r2.right)&&(posY>=r2.top&&posY<=r2.bottom))||((posX>=r2.x&&posX<=r2.right)&&(posY+divSizeH>=r2.top//&&posY+divSizeH<=r2.bottom))||((posX+divSizeW>=r2.x&&posX+divSizeW<=r2.right)&&(posY+divSizeH>=r2.top&&posY+divSizeH<=r2.bottom))){
//copied from someone elses code for checking collisions
if((posX <= r2.x && r2.x <= posX+divSizeW) && (posY <= r2.y && r2.y <= posY+divSizeH) ||
(posX <= r2.x && r2.x <= posX+divSizeW) && (posY <= r2.bottom && r2.bottom <= posY+divSizeH) ||
(posX <= r2.x+r2.height && r2.x+r2.height <= posX+divSizeW) && (posY <= r2.y+r2.width && r2.y+r2.width <= posY+divSizeW) ||
(posX <= r2.x+r2.height && r2.x+r2.height <= posX+divSizeW) && (posY <= r2.y && r2.y <= posY+divSizeW)){
willOverlap=true;
while(willOverlap){
posX = Math.round((Math.random() * ( width- divSizeW)));
posY = Math.round((Math.random() * ( height- divSizeH)));
divSizeW = Math.round(((Math.random()*250) + 50));
divSizeH = Math.round(((Math.random()*250) + 50));
if(!(((posX>=r2.x&&posX<=r2.right)&&(posY>=r2.top&&posY<=r2.bottom))||((posX+divSizeW>=r2.x&&posX+divSizeW<=r2.right)&&(posY>=r2.top&&posY<=r2.bottom))||((posX>=r2.x&&posX<=r2.right)&&(posY+divSizeH>=r2.top&&posY+divSizeH<=r2.bottom))||((posX+divSizeW>=r2.x&&posX+divSizeW<=r2.right)&&(posY+divSizeH>=r2.top&&posY+divSizeH<=r2.bottom)))){
willOverlap=false;
}
}
}
})
}
}
//if there is no more place send message and dont create....
const newDiv = document.createElement('div');
newDiv.classList.add('Rectangle');
newDiv.style.width=divSizeW+"px";
newDiv.style.height=divSizeH+"px";
newDiv.style.left=posX+"px";
newDiv.style.top=posY+"px";
boxxy?.appendChild(newDiv);
setCount(count+1);
}
As you started to guess, you need to repeat your code (here new position & size + check collision) in case you find a collision.
But since you implement the repetition within the forEach loop of your list of already existing Rectangles, the newly generated position & size is not re-checked against the previous Rectangles (beginning of the list). Hence you may end up with still colliding elements.
Another issue, much more difficult, is to detect when your wrapper is "full", or rather when the empty space can no longer take any new of your Rectangles, given their minimum size. Because this step is so much more complex, let's leave it aside for now, and use a much more simplistic approach, by using a maximum number of repetitions.
With this simplification, your algorithm could look like:
const existingRectangles = document.querySelectorAll('.Rectangle');
let repCount = 0; // Number of repetitions
do {
var overlapping = false;
var newPositionAndSize = generateRandomPositionAndSize();
// Check for collision with any existing Rectangle.
// Use a classic for loop to be able to break it
// on the first collision (no need to check further)
for (let i = 0: i < existingRectangles.length; i += 1) {
if (checkCollision(existingRectangles[i], newPositionAndSize)) {
overlapping = true;
repCount += 1;
break; // No need to check the rest of the list
}
}
} while (overlapping && repCount < 1000);
if (overlapping) {
// If still overlapping, it means we could not find
// a new empty spot (here because of max repetitions reached)
showMessageWrapperIsFull();
} else {
// If we checked through the entire list without raising
// the overlapping flag, it means we have found
// a suitable empty spot
createNewRectangleAndInsertIt(newPositionAndSize);
}

Editing terrain generated randomly in Javascript with cursor

I'm making an University project and at the moment I have this randomly generated terrain. It simply gets a random value with some limits to it, puts it into an array and then i use lineTo(i,array[i]) to show me the world.
It works as intended, but now I'd like to be able to edit this "map" with my cursor if I enter let's call it "Edit Mode". At the moment I have a button that makes me enter this mode and then I check this:
function mouseMoveHandler(e) {
var relativeX = e.clientX - canvas.offsetLeft;
var relativeY = e.clientY - canvas.offsetTop;
if(relativeX < canvas.width && dragging && editing && relativeY < world[relativeX] + 20 && (relativeY > world[relativeX])) {
removeWorldPart(relativeX);
}
function removeWorldPart(coordinate){
for(var i = 0; i< 5; i++) {
if(coordinate > world[coordinate] + 5 || coordinate > world[coordinate] - 5) {
world[coordinate+i] += 6-i;
world[coordinate-i] += 6-i;
}
}
So what I'm trying to achieve (numbers can be set easily) is that like a little circle from the array that stores all those heights of my random world gets modified a bit.
It does change the heights in "world[]" but not really as a circle and probably I'm missing something else.
Any suggestions?

JavaScript canvas: "Random" errors with collision detection

Working on a simple canvas application where the user can shoot bullets with a gun. (click = new bullet, arrow key = new direction)
Works almost perfectly except there are seemingly "random" occurrences of where if the direction is changed, collision will fail.
Here's my jsFiddle. Relevant snippets:
Bullet.prototype - animate():
animate: function () {
if (gun.velocity.direction === keys.left) {
this.coordinates.x -= this.velocity.speed.x;
} else if (gun.velocity.direction === keys.up) {
this.coordinates.y += this.velocity.speed.y;
} else if (gun.velocity.direction === keys.right) {
this.coordinates.x += this.velocity.speed.x;
} else if (gun.velocity.direction === keys.down) {
this.coordinates.y -= this.velocity.speed.y;
}
return this;
},
Bullet.prototype - collision():
collision: function (str) {
if (str === 'boundary') {
if (this.coordinates.x + this.velocity.speed.x > canvas.width || this.coordinates.x + this.velocity.speed.x < 0) {
this.velocity.speed.x = -this.velocity.speed.x;
} else if (this.coordinates.y + this.velocity.speed.y > canvas.height || this.coordinates.y + this.velocity.speed.y < 0) {
this.velocity.speed.y = -this.velocity.speed.y;
}
}
}
Key handling:
document.onkeydown = function (e) {
e = e.keyCode;
if (e === keys.left) {
gun.velocity.direction = keys.left;
} else if (e === keys.up) {
gun.velocity.direction = keys.up;
} else if (e === keys.right) {
gun.velocity.direction = keys.right;
} else if (e === keys.down) {
gun.velocity.direction = keys.down;
}
};
How do I figure out why this is happening and how I can stop it?
Ok I had a look and found your bug.
You have bullets that you have given the property speed that is two values x, and y You also have a direction. When you animate the bullets you check the direction and move the bullet in the correct direction by adding only to x or y depending on direction. But then when you test if the bullets hit the wall you ignore the direction and test against the bullets speed. The whole time you have the bullets x and y speed both not equal to zero. You collision test is testing bullets moving diagonally.
If you add this bit of code
ctx.strokeStyle = this.color;
ctx.beginPath();
// draw a line in the direction you are testing the wall to be
ctx.moveTo(this.coordinates.x, this.coordinates.y);
ctx.lineTo(this.coordinates.x + this.velocity.speed.x*10, this.coordinates.y + this.velocity.speed.y*10);
ctx.stroke();
where you render the bullets, you will see that the bullets are not traveling in the direction indicated by this.velocity.speed, but you use these values to test for the wall.
There is to much to change for a simple fix.
What to do.
For each bullet
Keep speed as a single number.
Create delta.x, and delta.y as the bullets vector.
Keep direction as a single value. As you already have.
When you shoot you use the direction to set the bullet vector (delta);
up set delta {x:0,y:-1},
down set delta {x:0,y:1},
left set delta {x:-1,y:0},
right set delta {x:1,y:0},
To move the bullet just add the deltas times the speed;
bullet.pos.x += bullet.delta.x * bullet.speed;
bullet.pos.y += bullet.delta.y * bullet.speed;
Speed has nothing to do with direction. It is a positive value describing distance over time.
Delta x and y is really all you need for the bullets direction, but no harm in holding the direction as a single value as well as long as the two match.
To test for the wall
// check if the bullet is moving in the x direction
// then check if it's about to hit the wall
if(bullet.delta.x !== 0 && (bullet.pos.x + bullet.delta.x * bullet.speed > wallLoc ||
bullet.pos.x + bullet.delta.x * bullet.speed < 0)){
bullet.delta.x = -bullet.delta.x; // reverse bullet
}
do the same for the y direction.
I don't know if it is your intention to change all the bullet directions when you press a key. If it is just go through all the bullets and change there deltas when a key is pressed.
Hope this is clear. Do ask if you need more info.

3D Object Turntable Skips Strangely At Around 9

My teacher let me make the website for his class a couple of years ago and I'm still developing for it.
I recently added a 3D turntable (Render out so many frames from a camera spinning around an object) feature. I want buttons on the side so that you can just click and hold and it'll go at a certain rate. (I used a CSS Triangle Generator to create it, but Jquery isn't picking up mousedown events... I'll figure this out ...). There's an HTML5 range in between that's been stylized.
The problem occurs when I get around 9 or 10 depending on the direction arrow clicked. No matter how many frames, it wants to just jump to the end. I assume it has something to do with my logic:
function Turntable(ttMethod) {
var slider;
if(typeof ttMethod !== 'undefined') slider = $('#tt-slider-input');
if(ttMethod == 'rotate') padVal = slider.val();
else if(ttMethod == 'rotate-r') { // ERROR POSSIBLY IN THESE STATEMENTS
padVal = slider.val();
//if(padVal >= slider.attr('max')) padVal = '0000';
if(padVal >= 0 && padVal < slider.attr('max')) padVal++;
else if(padVal == slider.attr('max')) padVal = 0;
else padVal = slider.attr('max');
//console.log(padVal);
slider.val(padVal);
}
else if(ttMethod == 'rotate-l') { // ERROR POSSIBLY IN THESE STATEMENTS
padVal = slider.val();
if(padVal <= slider.attr('max') && padVal > 0) padVal--;
else if(padVal == 0) padVal = slider.attr('max');
else padVal = 0;
//else if(padVal <= 0) padVal = slider.attr('max');
//console.log(padVal);
slider.val(padVal);
}
else return;
padVal = pad_with_zeroes(padVal,4);
$('#turntable').css('background-image','url('+slider.attr('data-folder')+slider.attr('data-base')+padVal+'.'+slider.attr('data-format')+'), url(images/loading.png)');
}
An example of a turntable might look like this: http://jsfiddle.net/ybDy8/4/
(A working version is at http://greengoosemarketing.com/ .. Just click on a thumbnail with a light grey and dark grey circle that resembles a turntable in the bottom left-hand corner. Those are turntables.)

How to track divs scrolling over point on page with JavaScript/jQuery

I'm looking for a very fast solution to a div scrolling problem.
I have a set of divs, like forum posts, that are laid out one on top of the other. As the page scrolls down or up, I'd like to know when one of those divs hit's an arbitrary point on the page.
One way I tried was adding an onScroll event to each item, but as the number of items grow the page really starts to lag.
Anyone know a more efficient way to do this? Thanks /w
Well, I'm new to all this, so may be someone should correct me :)
I propose to
cache posts position
caсhe current
use binary search
Demo: http://jsfiddle.net/zYe8M/
<div class="post"></div>
<div class="post"></div>
<div class="post"></div>
...
var posts = $(".post"), // our elements
postsPos = [], // caсhe for positions
postsCur = -1, // cache for current
targetOffset = 50; // position from top of window where you want to make post current
// filling postsPos with positions
posts.each(function(){
postsPos.push($(this).offset().top);
});
// on window scroll
$(window).bind("scroll", function(){
// get target post number
var targ = postsPos.binarySearch($(window).scrollTop() + targetOffset);
// only if we scrolled to another post
if (targ != postsCur) {
// set new cur
postsCur = targ;
// moving cur class
posts.removeClass("cur").eq(targ).addClass("cur");
}
});
// binary search with little tuning on return to get nearest from bottom
Array.prototype.binarySearch = function(find) {
var low = 0, high = this.length - 1,
i, comparison;
while (low <= high) {
i = Math.floor((low + high) / 2);
if (this[i] < find) { low = i + 1; continue; };
if (this[i] > find) { high = i - 1; continue; };
return i;
}
return this[i] > find ? i-1 : i;
};
You shouldn't bind scroll event to all the divs but only to window instead. Then, you should check whether one of the divs overlap with the target point by making a simple calculation of the element offset values.
$(window).scroll(function(event)
{
var isCaptured = capture();
console.log(isCaptured);
});
function capture()
{
var c = $('.box'); //this is the divs
var t = $('#target'); //this is the target element
var cPos = c.offset(); var tPos = t.offset();
var overlapY = (cPos.top <= tPos.top + t.height() && cPos.top + c.height() >= tPos.top);
var overlapX = (cPos.left <= tPos.left + t.width() && cPos.left + c.width() >= tPos.left);
return overlapY && overlapX;
}
Instead of the $('#target') element, you can pass top and left (X, Y) offset values directly to the function.
Well, here is a dirty demonstration.

Categories

Resources