Reputation: 107
I have a small game where the player is moving a circle within a canvas. I am having issues with detecting the edges of the canvas, disallowing a player to move past those edges, and allowing the user to navigate away from any given edge.
Currently, the player can hit canvas edges and move away successfully, most of the time. However, with a combination of certain movements and collision along an edge, the player will become "stuck".
How can I go about creating simple canvas edge collision detection and allow the user to move freely after the event?
My code:
var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
var x = canvas.width / 2;
var y = canvas.height - 30;
var dx = 2;
var dy = -2;
var ballRadius = 10;
var rightPressed = false;
var leftPressed = false;
var upPressed = false;
var downPressed = false;
document.addEventListener("keydown", keyDownHandler, false);
document.addEventListener("keyup", keyUpHandler, false);
function keyDownHandler(e) {
if (e.keyCode == 65) {
rightPressed = true;
}
if (e.keyCode == 68) {
leftPressed = true;
}
if (e.keyCode == 87) {
upPressed = true;
}
if (e.keyCode == 83) {
downPressed = true;
}
}
function keyUpHandler(e) {
if (e.keyCode == 65) {
rightPressed = false;
}
if (e.keyCode == 68) {
leftPressed = false;
}
if (e.keyCode == 87) {
upPressed = false;
}
if (e.keyCode == 83) {
downPressed = false;
}
}
function drawBall() {
ctx.beginPath();
ctx.arc(x, y, ballRadius, 0, Math.PI * 2);
ctx.strokeStyle = "black";
ctx.stroke();
ctx.closePath();
}
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawBall();
if (x + dx > canvas.width - ballRadius || x + dx < ballRadius - 3) {
dx = -dx;
}
if (y + dy > canvas.height - ballRadius || y + dy < ballRadius - 3) {
dy = -dy;
}
if (rightPressed) {
x -= dx;
}
if (leftPressed) {
x += dx;
}
if (upPressed) {
y += dy;
}
if (downPressed) {
y -= dy;
}
}
setInterval(draw, 10);
<canvas id="myCanvas"></canvas>
Upvotes: 2
Views: 901
Reputation: 107
Thanks for the help from the two of you who answered my question. Because of you two, I was able to successfully find a solution.
Originally, I was using this to determine the edges of the canvas, and adjust direction accordingly:
if (x + dx > canvas.width - ballRadius || x + dx < ballRadius - 3) {
dx = -dx;
}
if (y + dy > canvas.height - ballRadius || y + dy < ballRadius - 3) {
dy = -dy;
}
However, as pointed out, that reverses the movement direction, making it basically useless for my intended purpose. Not only that, but all 4 sides of the canvas act in a slightly different manor from each other.
Using the feedback from the other answer, here's the solution I am using now, which works wonderfully:
if (x + dx > canvas.width - ballRadius) {
var fx = x - dx;
x = fx;
}
if (x + dx < ballRadius) {
var fx = x + dx;
x = fx;
}
if (y + dy > canvas.height - ballRadius) {
var fy = y + dy;
y = fy;
}
if (y + dy < ballRadius) {
var fy = y - dy;
y = fy;
}
This way, I am detecting all 4 sides successfully, stopping user motion on contact, and allowing the player to move away from the side of the canvas.
Upvotes: 1
Reputation: 3978
Well i have to admit that have been long time since i don't touch older libraries stored in the bault. Luckley i got the sources at hand, so after taking a look i will tell a method that works for me regarding how to detect the colision of a ball object against a wall.
I guess it will fit to you needs, don't know if the better or worse solution, but works!. Let start by defining how we represent the data for each ball, recall our friends of unix that says that part of the complexity is in the data structure as it is part of the algorithm as a whole... but enought chat, going to the matters.. by using this kind of data structure for representing a Ball
class Ball
{
radio : number
x : number
y : number
}
So you can draw the ball this way:
function draw (Graphics g)
{
int r = ball.getRadio ();
int diam = rad * 2;
int px = (int) ball.getPx () - r;
int py = (int) ball.getPy () - r;
g.setColor (niceColor);
g.fillArc (px,py,diameter,diameter,0,360); // Fills a circular or elliptical arc covering the specified rectangle
}
// Disclaimer, i don't know well the primitives for graphical routines in canvas, but i would assume that you will have somethign to draw a circle with all of that. You got px, py, x, y, radio, diameter.
function isAHorizontalCollision (ball c,int width)
{
return (c.x > width - c.radio || c.x < radio);
}
function isAVerticalCollision (ball c,int height)
{
return (c.y > height - c.radio || c.y < radio);
}
// Assume that enclosing rectangle where the ball can move is between (0,width) for horizontal, and (top,0) vertical.
Is important to advise that this work only if the x values goes incrementally from left to right and the y goes decrementally from top to bottom.
Hope my typescript alike code fits well for explaining. I have a lot of source files in java for this. If need more. (For example collision between two circles).
If you have time i would recommend to check this out, very very powerfull stuff in there.
Upvotes: 3
Reputation: 17594
You complicated things a bit with all the Boolean variables...
The same can be done with just a the dx
and dy
those are your speed set them to 0
to stop.
Also it look like you where inverting the direction of the keys that was the lines:
if (x + dx > canvas.width - ballRadius || x + dx < ballRadius - 3) {
dx = -dx;
}
if (y + dy > canvas.height - ballRadius || y + dy < ballRadius - 3) {
dy = -dy;
}
When those condition are true the up is now down and the right is left, not sure if that was your intended behavior, when it worked it looked like the ball was bouncing from the edge but from that point on the key was inverted... I remove that from my fix.
Here is my approach, I added a bit of math fun to animate your player:
var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
var x = canvas.width / 2;
var y = canvas.height / 2;
var dx = 0;
var dy = 0;
var ballRadius = 10;
document.addEventListener("keydown", keyDownHandler, false);
document.addEventListener("keyup", keyUpHandler, false);
function keyDownHandler(e) {
if (e.keyCode == 65) dx = -2
if (e.keyCode == 68) dx = 2
if (e.keyCode == 87) dy = -2
if (e.keyCode == 83) dy = 2
}
function keyUpHandler(e) {
if (e.keyCode == 65) dx = 0
if (e.keyCode == 68) dx = 0
if (e.keyCode == 87) dy = 0
if (e.keyCode == 83) dy = 0
}
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawBall();
var fx = x + dx
var fy = y + dy
if (ballRadius < fx && fx < canvas.width - ballRadius) x = fx;
if (ballRadius < fy && fy < canvas.height - ballRadius) y = fy;
}
setInterval(draw, 10);
var hr = ballRadius / 2 - 1
var pi2 = Math.PI * 2
var i = 0
function drawBall() {
ctx.beginPath();
ctx.arc(x, y, ballRadius, 0, pi2);
ctx.strokeStyle = "black";
ctx.stroke();
// draw some small moving circles inside
ctx.beginPath();
ctx.fillStyle = "red";
ctx.arc(x + hr * Math.sin(i), y + hr * Math.cos(i), hr, 0, pi2);
ctx.fill();
ctx.beginPath();
ctx.fillStyle = "blue";
ctx.arc(x - hr * Math.sin(i), y - hr * Math.cos(i), hr, 0, pi2);
ctx.fill();
ctx.closePath();
i += 0.1 + Math.abs(dx)/15 + Math.abs(dy)/15
}
<canvas id="myCanvas" style="border:1px solid #000000;"></canvas>
Upvotes: 1