Why do objects stick to the edge when their sign is reversed? - javascript

I'm learning a little bit of javascript and found that when you draw an object and want to set it to "bounce" off the border multiplying it (var *= -1) doesn't work. The object sticks to the border and stops moving.
In this example the ball reaches the top of the page and moves up and down repeatedly like it's "stuck" on the border:
// position of the ball
var y = 0;
// how far the ball moves every time
var speed = 2;
draw = function() {
background(127, 204, 255);
fill(66, 66, 66);
ellipse(200, y, 50, 50);
// move the ball
if (y > 375 || y < 25){
speed *= -1;
}
y = y + speed;
};

If the ball's y is either below 23 or greater than 377, inversing the speed will not get it into the accepted range again, and the direction will be inversed again, so it keeps jumping up and down. You could change the condition to:
if (y > 375 && speed > 0 || // if reaching upper broder and moving up or
y < 25 && speed < 0 // if reaching lower border and moving down
) speed *= -1;

Related

Problem tying a variable to a boolean in P5JS (Simple bouncing ball exercise)

TL;DR Why isn't the ball bouncing off of the edge in the link below?
I'm currently going through the CodingTrain P5JS videos. What I'm trying to achieve is a simple toggle button:
https://editor.p5js.org/S_Oda/sketches/uF_dgcQP-
When the button is 'on' it changes to red and the ball moves until it hits the left or right edge where it will bounce off and change to the opposite direction. When the button is toggled 'off' the ball will freeze in place, it will resume movement when the button is pressed again.
My problem is, when the ball hits the edge, it seems to glitch in place. The bouncing was working before I introduced the toggle button. The resoning for my code is that if I tie the 'speed' variable of the ball to be dependant on a boolean ("ballState"), then the button can just change whether 'ballState' is true or false, i.e. move or not move.
So I'm wondering if the problem is here somewhere:
//Tying speed to ballState so that I can toggle the ball movement with the button
if (!ballState) {
speed = 0;
} else if (ballState) {
speed = 8;
}
//Why does this not result in the ball bouncing off of the edge of the canvas?
if (x >= width || (x <= -1 && ballState)) {
speed = speed * -1;
}
I'd appreciate if someone could tell me what I'm doing wrong, without introducing any new concepts/workarounds, as I'm trying to learn things one step at a time. Thanks very much in advance!
Full code:
var btn = false;
var ballState = false;
var x = 0;
var speed = 0;
function setup() {
createCanvas(600, 400);
background(0);
rectMode(CENTER);
}
function draw() {
background(0);
stroke(255);
//Button
if (btn) {
stroke(255, 0, 0);
fill(255, 0, 0);
push();
translate(0, 180);
text("ON", width / 2, height / 2, 20, 20);
pop();
} else {
noFill(0);
push();
translate(0, 180);
text("OFF", width / 2, height / 2, 20, 20);
pop();
}
rect(width / 2, 330, 120, 60);
//Ball
x = x + speed;
push();
noStroke();
fill(0, 0, 255);
ellipse(x, height / 2, 100, 100);
pop();
//Tying speed to ballState so that I can toggle the ball movement with the button
if (!ballState) {
speed = 0;
} else if (ballState) {
speed = 8;
}
//Why does this not result in the ball bouncing off of the edge of the canvas?
if (x >= width || (x <= -1 && ballState)) {
speed = speed * -1;
}
}
//Button toggle
function mousePressed() {
if (mouseX >= 239 && mouseX <= 360 && mouseY >= 300 && mouseY <= 360) {
btn = !btn;
ballState = !ballState;
}
}
The problem is mostly this, which runs every frame in the draw function:
// ...
else if (ballState) {
speed = 8;
}
...where ballState is more aptly named isBallMoving.
You may think speed = speed * -1; reverses the ball, but on the very next rendering frame, ballState being true means that speed = 8; in the above code block puts it right back in the forward direction, overriding your intent.
Another issue to consider is this: if x >= width and you do speed *= -1 to reverse the direction, will the ball's x still be >= width on the next frame? If so, all things being equal, the speed *= -1 will occur again and the ball will just vibrate in place, reversing direction infinitely.
I'll leave it to you to work out how best to solve these problems, but at a high level, you'll want to capture and check something more than ballState for your speed setting logic. You may also need a way for objects that have collided with a wall to "push" themselves back out of the wall until any collision has been resolved.

JavaScript object positioning collision logic

#Pointy:
As I said below, the size variable adjusts the size by a percentage. So this number could have, for example, a value of 1, or 1.5, or 2, etc. The default circle diameter is 100 pixels, and the actual diameter of the circle displayed is size*100. When checking for collision I use size*120 because I want to leave some extra room.
To my understanding, the CSS left and top properties positions the object in terms of its left and top bounds, respectively. Thus if I want to check collision on the right side of the object, I'd have to take the left boundary and add the diameter of the circle (which is given by size*100). When checking the collision on the left side, I take the left bound and subtract size*(1.2-1)*100, because I do not need to account for the diameter of the circle as I am starting from the left bound. The same thing applies to the y-axis.
I hope this was understandable, thanks for helping me out.
Original Post:
I have two circles on my screen, a pink one and a purple one. The positions of these circles (x,y) are randomly determined. Initially, only the purple circle is on the page. One second later, the pink circle will appear. I want to check if these two circles will overlap, and if so, reposition the pink circle.
My code:
if(document.getElementById("purple").style.left+(size*120) > x
&& document.getElementById("purple").style.left-(size*20) < x)
{
if(x + 200 <= rightBound) {
x = x+200;
}
else {
x = x-200;
}
}
else if(document.getElementById("purple").style.top+(size*120) > y
&& document.getElementById("purple").style.top-(size*20) < y)
{
if(y+200 <= bottomBound) {
y = y+200;
}
else {
y = y-200;
}
}
document.getElementById("pink").style.left = x+"px";
document.getElementById("pink").style.top = y+"px";
The size variable modifies the circle size by a percentage. The default circle size is 100 pixels, but in my code I specify 120 pixels to leave a bit of extra room.
The rightBound and bottomBound variables dictate the bounds in which the circle must be randomly positioned. My code checks if moving the circle 200 pixels to the right, or 200 pixels down would still position the circle within the bounds. If so, then this is done, otherwise the circle is moved 200 pixels to the left instead.
By the way, in case it is not clear x and y are the randomly generated values for the position of the pink circle.
With this code, however, I have still experienced instances where the pink and purple circle will overlap. I'm wondering what the problem is with my logic, as in my explanation and thinking I feel that everything should be working fine.
Why not just keep trying new pink circles until you get one that's right? Random numbers are cheap, after all:
http://jsfiddle.net/HVX7d/
while(distance(purple, pink) < 120) {
pink = {
x: int_rand(100, 500),
y: int_rand(100, 500)
};
}
Just use a loop to keep generating new positions for pink until you're sure there are no collisions, using Pythagorean to calculate how far apart the circles are.
Original Answer (incorrectly assumed 100 was a radius):
The issue is that you're specifying 120 in your boundary logic but only moving the circle by 200 when you detect it. That means there's a scenario where the circles originally don't overlap, but when you move them by only 200 you've created an overlap that didn't previously exist.
Change your instances of 200 to 240 and you shouldn't see any more issues.
For example: you have purple at 200, 200 and pink at 310, 200. That's enough to trigger your boundary logic because 310 - 200 is only 110, so you choose to move pink. Let's say you move it up because it's near the bottom boundary, so now it's at 110, 200 and you've created overlap.
I think this should do what you need:
function reposition_check(x, y, rightBound, bottomBound, size, elem) {
var c1_size = 120 * size;
var c2_size = 120 * size;
var c1_x = parseInt(elem.style.left.replace("px", "")) + (c1_size / 2); //center the purple x coordinate
var c1_y = parseInt(elem.style.top.replace("px", "")) + (c1_size / 2); //center the purple y coordinate
var c2_x = x + (c2_size / 2); //center the pink x coordinate
var c2_y = y + (c2_size / 2); //center the pink y coordinate
var d_x = c1_x - c2_x; //distance on the x plane
var d_y = c1_y - c2_y; //distance on the y plane
if (Math.sqrt((d_x * d_x) + (d_y * d_y)) < (c1_size / 2) + (c2_size / 2)) { //Pythagorean theorum to determine if overlapping
console.log("is overlapping");
if (Math.abs(d_x) < (c1_size / 2) + (c2_size / 2)) { //if true, the overlap is on the x plane
//if(x + (c2_size) + 200 <= rightBound && d_x > 0) {
if(d_x > 0) {
x = (x + 200 <= rightBound) ? x + 200 : (x - 200 - d_x);
}else{
x = (x + 200 <= rightBound) ? x - d_x + 200 : (x - 200);
}
return {x: x, y: y};
}
if (Math.abs(d_y) < (c1_size / 2) + (c2_size / 2)) { //if true, the overlap is on the y plane
if(d_y > 0) {
y = (y + 200 <= bottomBound) ? y + 200 : (y - 200 - d_y);
}else{
y = (y + 200 <= bottomBound) ? y - d_y + 200 : (y - 200);
}
return {x: x, y: y};
}
}
return {x: x, y: y};
}
//Here's how you would use it:
var purple = document.getElementById("purple");
var values = reposition_check(111, 1, 1000, 800, 1, purple); //x, y, rightBound, bottomBound, size, element
document.getElementById("pink").style.left = values.x + "px";
document.getElementById("pink").style.top = values.y + "px";

Drawing an X shape on a HTML5 canvas and checking for collisions with a border

I am working on a maze game in HTML 5.
Here is the function which I use to draw an "X" on the canvas (using the touchpad, the user will navigate the X through the labyrinth).
dim = 8;
function rect(x,y,xdim){
ctx.beginPath();
ctx.moveTo(x - xdim, y - xdim);
ctx.lineTo(x + xdim, y + xdim);
ctx.moveTo(x + xdim, y - xdim);
ctx.lineTo(x - xdim, y + xdim);
ctx.strokeStyle = '#FF5000';
ctx.stroke();
}
The problem is in the checkcollision function, mainly in this piece of code:
ctx.getImageData(checkx, checky, 16, 16);
I am looking for an optimal way to check all the space that is taken up by the "X" on the canvas.
There are more comments in the code i used.
Issues:
1. Sometimes, the X goes a little passed the border
2. Sometimes, the X doesn't get close, letting a few pixels between it and the border.
function checkcollision(checkx, checky){
var collision = 0;
/*
User presses an arrow key
Calculate where that would move the square to.
Look at all of the pixels on the maze image that the square will cover
in its new position (our square is 15x15 so we look at 225 pixels)
Are any of the pixels black (the maze is white with black borders.
If a pixel is black that means the square would collide with a border)
YES: then do not move
NO: then move
*/
var imgd = ctx.getImageData(checkx, checky, 15, 15);
pix = imgd.data;
for (var i = 0; n = pix.length, i < n; i += 4){
if (pix[i] == 0) {
collision = 1;
} else if(pix[i] == 255 && pix[i + 1] == 0){
winner = "yes";
}
}
return collision;
}
I believe the getImageData gets a rectangle which starts from the x,y position in a corner.
So maybe there is a way to draw it using the checkx and checky as the coordinates for the center and retrieve a square 16 pixels wide
Wow, writing the question made it clear:
var imgd = ctx.getImageData(checkx - xdim, checky - xdim, 16, 16);
Ty guys

Movement for out-of-bound objects, canvas

I have a space ship in a canvas. It has velocities, ship.vx and ship.vy. When it's 30px away from the canvas borders I set ship.vx & ship.vy to 0 and move the background objects in ship's opposite direction. At this moment the ship is stuck at a point. That's all good. Now if I try to move it left-right(stuck at top/bottom) or top-down(stuck at left/right) it doesn't since it is stuck in the point where vx & vy are set to 0.
If i accelerate in it's opposite direction it takes like 5 seconds to pick it's velocity (around 2), so it's basically at the same point for 5 seconds.
I tried not to set vy to 0 when out of x-axis and vice-versa but the ship keeps moving slowly in the other axis.
So what i'm trying to achieve is the ship'll get stuck when it's 30 px from border, but if i try to move or accelerate in other 3 directions it'll pretend as if it's not stuck.
Any of you know any mechanisms?
Thanks.
function stuckShip(){
if(
(ship.x - ship.width < 0) ||
(ship.x + ship.width > w) ||
(ship.y + ship.height > h) ||
(ship.y - ship.height < 0))
{
ship.vx = 0;
ship.vy = 0;
}
}
function againAndAgain(){
var angle = ship.rotation;
var x = Math.cos(angle);
var y = Math.sin(angle);
var ax = x*thrust,
ay = y*thrust;
ship.vx += ax;
ship.vy += ay;
stuckShip();
ship.draw(context);
}
document.addEventListener('keydown', function(e){
switch(e.keyCode){
case 38:
thrust = 0.35;
break;
case 37:
ship.rotation -= 3;
break;
case 39:
ship.rotation += 3;
break;
}
}
Manipulating the velocity of an object based on it's position on the screen for display-purposes is never the best idea.
Often you use Parent-based systems, so you have one main-container and all objects (including the ship) are child to that container and move relatively to the main-container. Now you can update the container's position, if the ship's global position is in that 30px-band, to make it "lock" on the edge of the screen.
Ha ha, simple, just set ship's position to 31px from border,
if(ship.x <= 30){
ship.x = 30 + 1;
}
What it does is, when the ship is 30px from the left, it'll set ship.x to 31, so it'll never be stuck, just swinging 1px back and forth. i am not sure if it's a perfect solution, but it doesn't pull back the ship for 5 seconds.

How to convert a point to polar coordinates in ECMAScript?

I need to turn a click location into a polar coordinate.
This is my current algorithm. Location is the location on the canvas of the click ({x:evt.clientX, y:evt.clientY}), center is the offset of the origin from 0,0. For example, if the circle is centered on 250, 250, center is {x:250, y:250}. Scale is the scale of the radius. For example, if the radius of a circle from the center would normally be 50 and the scale is .5, the radius becomes 25. (it's for zooming in/out)
this.getPolarLocation = function(location){
var unscaledFromCenter = {
x: location.x - center.x,
y: location.y - center.y
};
var angle = this.getAngleOnCircle(unscaledFromCenter);
var dist = Math.sqrt(unscaledFromCenter.x * unscaledFromCenter.x + unscaledFromCenter.y * unscaledFromCenter.y) * this.ds.scale;
return {
angle:angle,
dist:dist,
toString: function(){
return "Theta: ".concat(angle).concat("; dist: ").concat(dist);
}
};
}
this.getAngleOnCircle = function(location){
var x = location.x;
var y = location.y;
if(x == 0 && y > 0)
return Math.PI / 2;
if(x == 0 && y < 0)
return 3 * Math.PI / 2;
if(y == 0 && x > 0)
return 0;
if(y == 0 && x < 0)
return Math.PI;
var angle = Math.atan(y/x);
if(x > 0 && y > 0)
return angle;
if(x < 0)
return Math.PI + angle
return Math.PI * 2 + angle;
}
Screenshots of the issue. The left is what happens zoomed out (and is not supposed to happen). The right is zoomed in (scale >= 1), and is what is supposed to happen.
I'm under the impression that my center coordinates are being shifted slightly off. It seems to work fine for scale >= 1, but not for scale < 1
Source:
circos.html: http://pastie.org/private/cowsjz7mcihy8wtv4u4ag
circos.js: http://pastie.org/private/o9w3dwccmimalez9fropa
datasource.js: http://pastie.org/private/iko9bqq8eztbfh8xpvnoaw
Run in Firefox
So my question is: why doesn't this work?
For some reason, the program automagically works when I close firebug. It doesn't seem to work on Firefox 5, only the version I have (in the 3s somewhere). Either way, I'm scrapping the project for something more object oriented. There's no way the current algorithm could handle a genome. (which is exactly what I'm going to be mapping)
UPDATE:
I figured out the problem... I was measuring the distance from the top left of the page, not the top left of the canvas. Thus, when firebug was enabled, the screen was shifted, making the problems worse. The solution is the use canvas.offsetLeft and canvas.offsetTop to calculate the position on the canvas.

Categories

Resources