Increasing the speed of a ball in javascript - javascript

What I'm trying to do is simply make a ball rebound from a wall. Everything works OK, except the fact I want to be able to increase the speed of movement. Literally, the speed is how much 'x-value' is added (measured in px) to the ball's current position. The thing is, when I'm increasing the var speed, the ball floats out of the bounds, because the rebounding is checked by the difference between the bound and the current position of the ball.
--------------------------------------update-----------------------------------------
I've used the technique suggested by Mekka, but still did something wrong.The ball doesn't float outside anymore, yet something "pushes it out" of the bounds for several pixels/"doesn't let the ball float several more pixels to reach the bounds".
My new code looks like this:
// the bounds-describing object
var border={
X:[8,302], // left and right borders in px
Y:[8,302], // top and bottom borders in px
indX:1, //border index for array Х
indY:0, //border index for array Y
changeInd:function(n){return this[n] = +!this[n]; } // function to change the index
};
if($("#ball").position().left + speed > border.X[1] || $("#ball").position().left + speed < border.X[0]){
var distX = "+=" + (border.X[border.indX] - $("#ball").position().left);
var distY = "-=" + ((border.X[border.indX] - $("#ball").position().left) * k);
$("#ball").css("left", distX);
$("#ball").css("top", distY);
border.changeInd("indX");
speed = -speed;
}
if($("#ball").position().top + k > border.Y[1] || $("#ball").position().top + k < border.Y[0]){
var distX = "+=" + ((border.Y[border.indY] - $("#ball").position().top) / k);
var distY = "+=" + (border.Y[border.indY] - $("#ball").position().top);
$("#ball").css("left", distX);
$("#ball").css("top", distY);
border.changeInd("indY");
k = -k;
}
Another problem is that my code's math is incorrect sometimes, the reason of which I absolutely can't figure out. To test it, try 45 degrees with different speed.
The question is: how can I improve the 'collision-checking' process or even apply some other technique to do this?
the whole code can be found here:
http://jsfiddle.net/au99f/16/

You're very close! The answer is actually hinted at in your question. You're currently using the absolute value of the distance to the boundary to determine when to change direction. This defines a "magic zone" where the ball can change direction that is about 6 pixels wide (given your speed of 3). When you increase speed to something higher (like 10), you could jump right over this magic zone.
A better way to do this would be to test if the next jump would put the ball completely outside the bounds. So this check is not based on a constant (like 3) but on the speed of the ball itself. You can also see how much the ball would have travelled out of bounds to determine how far to move the ball in the opposite direction. In other words, if your speed is 10, and the ball is 3 pixels from the right edge on step 8, then on step 9, the ball would be 7 pixels from the right edge, traveling left. Be wary of edge cases (ball could land exactly on bounds).

Related

Fixing simple collision detection with velocity based 2d movement

In this codePen demo you can move "player" square with arrows, place a light with space and are supposed to be stopped going over blue lines from any direction by being pushed to the opposite direction. "player" uses x and y velocity variables to create movement and multiply them by -1 (+some value) if collision detected.
The problem is that after being pushed away from the wall "player" gets stuck in a position where only moving backward from the wall is possible while appearing stuck on a perpendicular axis to that. (for example - if the wall is on top of player you can move only to bottom and not left or right after hitting the wall)
Theoretically, I would want a smooth sliding collision detection where player stuck at the wall would slowly slide down the left or right side
depending if left or right arrow pressed. (playing around I am able to achieve this but always one direction would "flow" making player slide down certain direction) I thought about using rays or some others way to detect hits, but they seem to require more computational time than just plain approach. Would appreciate any input and any recommendations of building scalable collision detections,
Here is my basic code for movement and collision detection from the demo:
let xVelocity = 0;
let yVelocity = 0;
var blockedMapGrid = [[0,30],[0,50],[0,100],[0,150],[0,200],[0,250],
[50,0],[100,0],[150,0],[200,0],[250,0],[300,0]];
var animate = function() {
if (keyState[37]) {
xVelocity -= 1;
}
if (keyState[38]) {
yVelocity += 1;
}
if (keyState[39]) {
xVelocity += 1;
}
if (keyState[40]) {
yVelocity -= 1;
}
for (var i = 0; i < blockedMapGrid.length; i++) {
if (Math.abs(player.position.x - blockedMapGrid[i][0]) +
Math.abs(player.position.y - blockedMapGrid[i][1]) < 36) {
xVelocity = -xVelocity * 1.2;
yVelocity = -yVelocity * 1.2;
console.log("Blocked by " + blockedMapGrid[i][0])
};
}
player.position.x = player.position.x + xVelocity;
player.position.y = player.position.y + yVelocity;
yVelocity *= 0.80;
xVelocity *= 0.80;
camera.position.x = player.position.x;
camera.position.y = player.position.y;
requestAnimationFrame(animate);
renderer.render(scene, camera);
};
This part of your detector is wrong:
Math.abs(player.position.x - blockedMapGrid[i][0]) +
Math.abs(player.position.y - blockedMapGrid[i][1]) < 36
Basically, here, you approximate distance from player to the point on grid by using added absolute values instead of root of sum of squares. The truth is, you don't need such a complex grid (repeating lines) and distance.
It looks like you are doing Axis-Aligned Bounding Box (AABB) detection. There is plenty of resources on internet how to optimize it.
But general approach would be like this. Your grid array should consist of boxes with (x,y,w,h) measures. Could be thin, long, square, anything.
Let's also assume your player has a bounding box (player.x, player.y, player.w, player.h), then
for (var i = 0; i < grid.length; i++) {
if (player.x < grid[i].x + grid[i].w &&
player.x + player.w > grid[i].x &&
player.y < grid[i].y + grid[i].h &&
player.y + player.h > grid[i].y) {
//collision detected! move player to previous known position
break;
}
}
You can vary, what you do when collision is detected, but finding if two boxes overlap using 4 conditions is the key here.
Update
Another problem arising from the code in the question is "bouncing" or "getting stuck" after collision is detected.
As a rule of thumb, you should never use velocity = -velocity after collision without also making sure the character gets back into the "clear", i.e. player's bounding box is not overlapping with any obstacles. Otherwise you will be stuck in infinite loop collision? -> vel = -vel, pos += vel*t -> collision -> ... with velocity bouncing from negative to positive and back without ever allowing player to get out of the wall.
The easiest way to fix it is to calculate new position of the player in temporary variables first, check if new position is not colliding, and only then make it permanent and call render(), otherwise simply ignore it and render without moving the player.
Another way is to remember last known "good" position and only give back control of the character, when it is returned to this previous position, possibly after animation or a bunch of uncontrollable moves.
There are more elaborate ways, mostly involving some kind of physics emulation to let the character bounce of multiple obstacles, assuming control inputs do not overpower inertia - think of a car on a slippery road or a boat hitting multiple trees. But either way, after you detect collision and before calling "render()" you have to place the character to a physically possible position, or it will be famously "stuck in textures".

detect collision between two circles and sliding them on each other

I'm trying to detect collision between two circles like this:
var circle1 = {radius: 20, x: 5, y: 5}; //moving
var circle2 = {radius: 12, x: 10, y: 5}; //not moving
var dx = circle1.x - circle2.x;
var dy = circle1.y - circle2.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < circle1.radius + circle2.radius) {
// collision detected
}else{
circle1.x += 1 * Math.cos(circle1.angle);
circle1.y += 1 * Math.sin(circle1.angle);
}
Now when collision is detected I want to slide the circle1 from on the circle2 (circle1 is moving) like this:
--circle1---------------------------------circle2-------------------------
I could do this by updating the angle of circle1 and Moving it toward the new angle when collision is detected.
Now My question is that how can I detect whether to update/increase the angle or update/decrease the angle based on which part of circle2 circle1 is colliding with ?? (circle one comes from all angles)
I would appreciate any help
This will depend a bit on how you are using these circles, and how many will ever exist in a single system, but if you are trying to simulate the effect of two bodies colliding under gravity where one roles around to the edge then falls off (or similar under-thrust scenario), then you should apply a constant acceleration or velocity to the moving object and after you compute it's movement phase, you do a displacement phase where you take the angle to the object you are colliding with and move it back far enough in that direction to reach circle1.radius + circle2.radius.
[edit] To get that redirection after falling though (not sure if you intended this or if it's just your sketch), there is probably going to be another force at play. Most likely it will involve a "stickiness" applied between the bodies. Basically, on a collision, you need to make sure that on the next movement cycle, you apply Normal Movement, then movement towards the other body, then the repulsion to make sure they don't overlap. This way it will stick to the big circle until gravity pulls way at enough of a direct angle to break the connection.
[edit2] If you want to make this smoother and achieve a natural curve as you fall away you can use an acceleration under friction formula. So, instead of this:
circle1.x += 1 * Math.cos(circle1.angle);
circle1.y += 1 * Math.sin(circle1.angle);
You want to create velocity properties for your object that are acted on by acceleration and friction until they balance out to a fixed terminal velocity. Think:
// constants - adjust these to get the speed and smoothness you desire
var accelerationX = 1;
var accelerationY = 0;
var friction = 0.8;
// part of physics loop
circle1.velX += (accelerationX * Math.cos(circle1.angle)) - (friction * circle1.velX);
circle1.velY += (accelerationY * Math.sin(circle1.angle)) - (friction * circle1.velX);
circle1.x += circle1.velX;
circle1.y += circle1.velY;
This way, when things hit they will slow down (or stop), then speed back up when they start moving again. The acceleration as it gets back up to speed will achieve a more natural arc as it falls away.
You could get the tangent of the point of contact between both circles, which would indicate you how much to change your angle compared to the destination point (or any horizontal plane).

How to make an object bounce of edge

I'm making a simple game in JavaScript and using the Phaser library. I'm new to this, so hopefully this is not a silly question.
I have made it all work perfectly but I would love to know how to get the rocks to bounce of the walls, rather than go through them and appear on the other side.
It has something to do with this function:
I was told by someone to
"If it hits Width: 940 then x = 940 and you start going back 939, i--, etc. Height will continue as normal. Rather than resetting i.e shot.reset(x, y);.
If you hit the bottom or top then do the same to height, keeping width the same."
However, I am not sure how to implement this into the code. I have tried but failed :) Its very frustrating, so any help on this matter would be amazing.
Thanks.
Usually, I create a velocity vector, wich represents the "speed" of my objects.
On each frame, I add that velocity vector to the position vector. When I want my object to move to the opposite direction, I multiply my vector by -1.
Create a vector like that, and when your object collid an edge, multiply it by -1.
You can make a lot of things with this type of vector, such as smooth speed decrease, inspace-like movements etc...
e.g:
//on init
var velocity = {x: 10; y: 10};
var pos = {x: 10; y:10};
//on frame update
pos.x += velocity.x;
pos.y += velocity.y
//on edge collision
velocity.x = velocity.x * -1;
velocity.y = velocity.y * -1;

How to calculate bezier curve control points that avoid objects?

Specifically, I'm working in canvas with javascript.
Basically, I have objects which have boundaries that I want to avoid, but still surround with a bezier curve. However, I'm not even sure where to begin to write an algorithm that would move control points to avoid colliding.
The problem is in the image below, even if you're not familiar with music notation, the problem should still be fairly clear. The points of the curve are the red dots
Also, I have access to the bounding boxes of each note, which includes the stem.
So naturally, collisions must be detected between the bounding boxes and the curves (some direction here would be good, but I've been browsing and see that there's a decent amount of info on this). But what happens after collisions have been detected? What would have to happen to calculate control points locations to make something that looked more like:
Bezier approach
Initially the question is a broad one - perhaps even to broad for SO as there are many different scenarios that needs to be taken into consideration to make a "one solution that fits them all". It's a whole project in its self. Therefor I will present a basis for a solution which you can build upon - it's not a complete solution (but close to one..). I added some suggestions for additions at the end.
The basic steps for this solutions are:
Group the notes into two groups, a left and a right part.
The control points are then based on the largest angle from the first (end) point and distance to any of the other notes in that group, and the last end point to any point in the second group.
The resulting angles from the two groups are then doubled (max 90°) and used as basis to calculate the control points (basically a point rotation). The distance can be further trimmed using a tension value.
The angle, doubling, distance, tension and padding offset will allow for fine-tuning to get the best over-all result. There might be special cases which need additional conditional checks but that is out of scope here to cover (it won't be a full key-ready solution but provide a good basis to work further upon).
A couple of snapshots from the process:
The main code in the example is split into two section, two loops that parses each half to find the maximum angle as well as the distance. This could be combined into a single loop and have a second iterator to go from right to middle in addition to the one going from left to middle, but for simplicity and better understand what goes on I split them into two loops (and introduced a bug in the second half - just be aware. I'll leave it as an exercise):
var dist1 = 0, // final distance and angles for the control points
dist2 = 0,
a1 = 0,
a2 = 0;
// get min angle from the half first points
for(i = 2; i < len * 0.5 - 2; i += 2) {
var dx = notes[i ] - notes[0], // diff between end point and
dy = notes[i+1] - notes[1], // current point.
dist = Math.sqrt(dx*dx + dy*dy), // get distance
a = Math.atan2(dy, dx); // get angle
if (a < a1) { // if less (neg) then update finals
a1 = a;
dist1 = dist;
}
}
if (a1 < -0.5 * Math.PI) a1 = -0.5 * Math.PI; // limit to 90 deg.
And the same with the second half but here we flip around the angles so they are easier to handle by comparing current point with end point instead of end point compared with current point. After the loop is done we flip it 180°:
// get min angle from the half last points
for(i = len * 0.5; i < len - 2; i += 2) {
var dx = notes[len-2] - notes[i],
dy = notes[len-1] - notes[i+1],
dist = Math.sqrt(dx*dx + dy*dy),
a = Math.atan2(dy, dx);
if (a > a2) {
a2 = a;
if (dist2 < dist) dist2 = dist; //bug here*
}
}
a2 -= Math.PI; // flip 180 deg.
if (a2 > -0.5 * Math.PI) a2 = -0.5 * Math.PI; // limit to 90 deg.
(the bug is that longest distance is used even if a shorter distance point has greater angle - I'll let it be for now as this is meant as an example. It can be fixed by reversing the iteration.).
The relationship I found works good is the angle difference between the floor and the point times two:
var da1 = Math.abs(a1); // get angle diff
var da2 = a2 < 0 ? Math.PI + a2 : Math.abs(a2);
a1 -= da1*2; // double the diff
a2 += da2*2;
Now we can simply calculate the control points and use a tension value to fine tune the result:
var t = 0.8, // tension
cp1x = notes[0] + dist1 * t * Math.cos(a1),
cp1y = notes[1] + dist1 * t * Math.sin(a1),
cp2x = notes[len-2] + dist2 * t * Math.cos(a2),
cp2y = notes[len-1] + dist2 * t * Math.sin(a2);
And voila:
ctx.moveTo(notes[0], notes[1]);
ctx.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, notes[len-2], notes[len-1]);
ctx.stroke();
Adding tapering effect
To create the curve more visually pleasing a tapering can be added simply by doing the following instead:
Instead of stroking the path after the first Bezier curve has been added adjust the control points with a slight angle offset. Then continue the path by adding another Bezier curve going from right to left, and finally fill it (fill() will close the path implicit):
// first path from left to right
ctx.beginPath();
ctx.moveTo(notes[0], notes[1]); // start point
ctx.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, notes[len-2], notes[len-1]);
// taper going from right to left
var taper = 0.15; // angle offset
cp1x = notes[0] + dist1*t*Math.cos(a1-taper);
cp1y = notes[1] + dist1*t*Math.sin(a1-taper);
cp2x = notes[len-2] + dist2*t*Math.cos(a2+taper);
cp2y = notes[len-1] + dist2*t*Math.sin(a2+taper);
// note the order of the control points
ctx.bezierCurveTo(cp2x, cp2y, cp1x, cp1y, notes[0], notes[1]);
ctx.fill(); // close and fill
Final result (with pseudo notes - tension = 0.7, padding = 10)
FIDDLE
Suggested improvements:
If both groups' distances are large, or angles are steep, they could probably be used as a sum to reduce tension (distance) or increase it (angle).
A dominance/area factor could affect the distances. Dominance indicating where the most tallest parts are shifted at (does it lay more in the left or right side, and affects tension for each side accordingly). This could possibly/potentially be enough on its own but needs to be tested.
Taper angle offset should also have a relationship with the sum of distance. In some cases the lines crosses and does not look so good. Tapering could be replaced with a manual approach parsing Bezier points (manual implementation) and add a distance between the original points and the points for the returning path depending on array position.
Hope this helps!
Cardinal spline and filtering approach
If you're open to use a non-Bezier approach then the following can give an approximate curve above the note stems.
This solutions consists of 4 steps:
Collect top of notes/stems
Filter away "dips" in the path
Filter away points on same slope
Generate a cardinal spline curve
This is a prototype solution so I have not tested it against every possible combination there is. But it should give you a good starting point and basis to continue on.
The first step is easy, collect points representing the top of the note stem - for the demo I use the following point collection which slightly represents the image you have in the post. They are arranged in x, y order:
var notes = [60,40, 100,35, 140,30, 180,25, 220,45, 260,25, 300,25, 340,45];
which would be represented like this:
Then I created a simple multi-pass algorithm that filters away dips and points on the same slope. The steps in the algorithm are as follows:
While there is a anotherPass (true) it will continue, or until max number of passes set initially
The point is copied to another array as long as the skip flag isn't set
Then it will compare current point with next to see if it has a down-slope
If it does, it will compare the next point with the following and see if it has an up-slope
If it does it is considered a dip and the skip flag is set so next point (the current middle point) won't be copied
The next filter will compare slope between current and next point, and next point and the following.
If they are the same skip flag is set.
If it had to set a skip flag it will also set anotherPass flag.
If no points where filtered (or max passes is reached) the loop will end
The core function is as follows:
while(anotherPass && max) {
skip = anotherPass = false;
for(i = 0; i < notes.length - 2; i += 2) {
if (!skip) curve.push(notes[i], notes[i+1]);
skip = false;
// if this to next points goes downward
// AND the next and the following up we have a dip
if (notes[i+3] >= notes[i+1] && notes[i+5] <= notes[i+3]) {
skip = anotherPass = true;
}
// if slope from this to next point =
// slope from next and following skip
else if (notes[i+2] - notes[i] === notes[i+4] - notes[i+2] &&
notes[i+3] - notes[i+1] === notes[i+5] - notes[i+3]) {
skip = anotherPass = true;
}
}
curve.push(notes[notes.length-2], notes[notes.length-1]);
max--;
if (anotherPass && max) {
notes = curve;
curve = [];
}
}
The result of the first pass would be after offsetting all the points on the y-axis - notice that the dipping note is ignored:
After running through all necessary passes the final point array would be represented as this:
The only step left is to smoothen the curve. For this I have used my own implementation of a cardinal spline (licensed under MIT and can be found here) which takes an array with x,y points and smooths it adding interpolated points based on a tension value.
It won't generate a perfect curve but the result from this would be:
FIDDLE
There are ways to improve the visual result which I haven't addressed, but I will leave it to you to do that if you feel it's needed. Among those could be:
Find center of points and increase the offset depending on angle so it arcs more at top
The end points of the smoothed curve sometimes curls slightly - this can be fixed by adding an initial point right below the first point as well at the end. This will force the curve to have better looking start/end.
You could draw double curve to make a taper effect (thin beginning/end, thicker in the middle) by using the first point in this list on another array but with a very small offset at top of the arc, and then render it on top.
The algorithm was created ad-hook for this answer so it's obviously not properly tested. There could be special cases and combination throwing it off but I think it's a good start.
Known weaknesses:
It assumes the distance between each stem is the same for the slope detection. This needs to be replaced with a factor based comparison in case the distance varies within a group.
It compares the slope with exact values which may fail if floating point values are used. Compare with an epsilon/tolerance

Limit max velocity of a particle in javascript

Im working on a little project, I have some particles i want to move towards target positions without exceeding a max velocity, first i tried capping the X and Y velocities seperately which caused the hypotenuse of the two to be able to go over the max speed, i then remembered my maths classes and attempted this:
var totalVel = Math.sqrt(Math.pow(curVelocity[0],2) + Math.pow(curVelocity[1],2));
if(totalVel > maxSpeed){
//sin(θ) = Opposite / Hypotenuse
var angle = Math.asin(curVelocity[1]/totalVel);
var newHyp = maxSpeed;
var newOp = Math.sin(angle)*newHyp;
var newAdj = Math.sqrt(Math.pow(newHyp,2) - Math.pow(newOp,2));
curVelocity[1] = newOp;
curVelocity[0] = newAdj;
}
(curVelocity is an array where index 0 is X and index 1 is Y)
This works well hald the time, the other time it curves away from the target its trying to reach.. matches it on the Y plane but heads in the whole wrong direction in the X plane. im guessing its something to do with using math.sin when perhaps it no longer applies in the direction its traveling but i wouldnt know where ot begin differentiating what to use, or if that idea is even correct.
A live example of what im talking about can be found at this location here, refreshing the page will change the starting location and target location, the black circle is the particle the green circle is the target location

Categories

Resources