I'm trying to make a little game where the user can click on a place on the page, the circle will follow to the pointer's position, and from there, the user can drag their mouse to make the circle move like a slingshot. I want the circle to be able to bounce off the walls.
However, it seems that on the circle's collision with a wall, the circle seems to 'stick' to the wall instead of bouncing right off. I think it might be because of the circle changing directions too quickly due to a rounding error? Also the top wall's collision hasn't been implemented yet because I don't think I declared the top wall div object properly.
let windowHeight = window.innerHeight;
let windowWidth = window.innerWidth;
//console.log(`Window height: ${windowHeight}, Window width: ${windowWidth}`);
var $ = document.querySelector.bind(document);
var $on = document.addEventListener.bind(document);
var mouseX, mouseY;
$on('mousedown', function (e){
mouseX = e.clientX || e.pageX;
mouseY = e.clientY || e.pageY;
initialX = mouseX;
initialY = mouseY;
//console.log('mousedown');
});
$on('mouseup', function (e){
//var d = Math.hypot(e.clientX - mouseX, e.clientY - mouseY);
var movebyX = (Math.abs(e.clientX - mouseX));
var movebyY = (Math.abs(e.clientY - mouseY));
// Move puck in opposite direction
if (e.clientX > mouseX){
mouseX -= movebyX;
}
else if(e.clientX < mouseX){
mouseX += movebyX;
}
if (e.clientY > mouseY){
mouseY -= movebyY;
}
else if(e.clientY < mouseY){
mouseY += movebyY;
}
//console.log('mouseup');
});
var circle = $('#circle');
var top = $('#top');
var bottom = $('#bottom');
var left = $('#left');
var right = $('#right');
var top = $('#top');
var x = void 0,
y = void 0,
dx = void 0;
dy = void 0;
v0x = void 0;
v0y = void 0;
accelScalar = 0.3;
key = -1;
velocityScalar = 0.05;
teleport = 0.1;
isCollide = false;
swap = false;
initialX = void 0;
initialY = void 0;
var followMouse = function followMouse(){
key = requestAnimationFrame(followMouse);
//tester();
if(!x || !y){
x = mouseX;
y = mouseY;
}
else {
makeMove(findVelocity().dx,findVelocity().dy);
}
};
function tester() {
console.log(top.getBoundingClientRect());
}
function findVelocity(){
v0x = (mouseX - x) * velocityScalar
v0y = (mouseY - y) * velocityScalar
return {
dx: v0x * accelScalar,
dy: v0y * accelScalar,
};
}
function makeMove(vx,vy){
//console.log(`x: ${x}, mouseX: ${mouseX}, vx: ${vx}`);
// teleport, avoid asymptote
if(Math.abs(vx) + Math.abs(vy) < teleport) {
x = mouseX;
y = mouseY;
vx = 0;
vy = 0;
}
// update position if collision
if (x-41 < (left.getBoundingClientRect().x)){
vx = -vx
vy = -vy
}
if (x+41 > (right.getBoundingClientRect().x)){
vx = -vx
vy = -vy
}
if (y+41 > (bottom.getBoundingClientRect().y)){
vx = -vx
vy = -vy
}
x += vx;
y += vy;
// show circle position
circle.style.left = (x-20) + 'px';
circle.style.top = (y-20) + 'px';
}
followMouse();
html, body {
margin: 0;
height: 100%;
background: #161616;
}
.wrap {
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
overflow: hidden;
}
@keyframes animation {
0% {
transform: scale(0.9)
}
25% {
transform: scale(1.1)
}
50% {
transform: scale(0.9)
}
75% {
transform: scale(1.1)
}
100% {
transform: scale(0.9)
}
}
#circle {
width: 50px;
height: 50px;
background: none;
border: 5px solid aqua;
border-radius: 50%;
position: absolute;
left: 50%;
top: 50%;
margin: -10px 0 0 -10px;
pointer-events: none;
/*animation: animation 5s infinite
*/
}
#top, #bottom, #left, #right {
background: #a5ebff;
position: fixed;
}
#left, #right {
top: 0; bottom: 0;
width: 10px;
}
#left { left: 0; }
#right { right: 0; }
#top, #bottom {
left: 0; right: 0;
height: 10px;
}
#top { top: 0; }
#bottom { bottom: 0; }
<!DOCTYPE html>
<html>
<head>
<title> Test Project </title>
<link rel = "stylesheet" href = "main.css">
</head>
<body>
<div class = "wrap">
<div id = "circle"></div>
</div>
<div id="left"></div>
<div id="right"></div>
<div id="top"></div>
<div id="bottom"></div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script type = "text/javascript" src = "./main.js"> </script>
</body>
</html>
Two issues:
top is a reserved variable for the top level frame. Conflicts with trying to access element with id top
, (which you should avoid depending on, use getElementById instead)
Just inverting velocity will not work, because you are calculating the velocity every frame using the last mouse clicked position (which is how your easing into position works by decaying velocity as it gets closer to mouseX/Y).
The easiest way to make this work is to use the distance between mouse click outside of borders to set a new mouse clicked position inside the borders offset by distance. (Basically having the same effect as inverting the position using the border as an axis. In this case it also adds in half of the circles width to this axis that it inverts on. Using hardcoded numbers, but if wanted as an improvement calculating from actual dimensions can also be done.)
let windowHeight = window.innerHeight;
let windowWidth = window.innerWidth;
//console.log(`Window height: ${windowHeight}, Window width: ${windowWidth}`);
var $ = document.querySelector.bind(document);
var $on = document.addEventListener.bind(document);
var mouseX, mouseY;
$on('mousedown', function (e){
mouseX = e.clientX || e.pageX;
mouseY = e.clientY || e.pageY;
initialX = mouseX;
initialY = mouseY;
//console.log('mousedown');
});
$on('mouseup', function (e){
//var d = Math.hypot(e.clientX - mouseX, e.clientY - mouseY);
var movebyX = (Math.abs(e.clientX - mouseX));
var movebyY = (Math.abs(e.clientY - mouseY));
// Move puck in opposite direction
if (e.clientX > mouseX){
mouseX -= movebyX;
}
else if(e.clientX < mouseX){
mouseX += movebyX;
}
if (e.clientY > mouseY){
mouseY -= movebyY;
}
else if(e.clientY < mouseY){
mouseY += movebyY;
}
//console.log('mouseup');
});
var circle = $('#circle');
var top = $('#top');
var bottom = $('#bottom');
var left = $('#left');
var right = $('#right');
var top = $('#top');
var x = void 0,
y = void 0,
dx = void 0;
dy = void 0;
v0x = void 0;
v0y = void 0;
accelScalar = 0.3;
key = -1;
velocityScalar = 0.05;
teleport = 0.1;
isCollide = false;
swap = false;
initialX = void 0;
initialY = void 0;
var followMouse = function followMouse(){
key = requestAnimationFrame(followMouse);
//tester();
if(!x || !y){
x = mouseX;
y = mouseY;
}
else {
makeMove(findVelocity().dx,findVelocity().dy);
}
};
function tester() {
console.log(top.getBoundingClientRect());
}
function findVelocity(){
v0x = (mouseX - x) * velocityScalar
v0y = (mouseY - y) * velocityScalar
return {
dx: v0x * accelScalar,
dy: v0y * accelScalar,
};
}
function makeMove(vx,vy){
//console.log(`x: ${x}, mouseX: ${mouseX}, vx: ${vx}`);
// teleport, avoid asymptote
if(Math.abs(vx) + Math.abs(vy) < teleport) {
x = mouseX;
y = mouseY;
vx = 0;
vy = 0;
}
// update position if collision
if (x-41 < (left.getBoundingClientRect().x)){
mouseX = Math.abs(mouseX - x) + 41
}
if (x+31 > (right.getBoundingClientRect().x)){
mouseX = right.getBoundingClientRect().x - Math.abs(mouseX - x) - 31
}
var top = document.getElementById('top')
if (y-41 < (top.getBoundingClientRect().y)){
mouseY = Math.abs(mouseY - y) + 41
}
if (y+31 > (bottom.getBoundingClientRect().y)){
mouseY = bottom.getBoundingClientRect().y - Math.abs(mouseY - y) - 31
}
x += vx;
y += vy;
// show circle position
circle.style.left = (x-20) + 'px';
circle.style.top = (y-20) + 'px';
}
followMouse();
html, body {
margin: 0;
height: 100%;
background: #161616;
}
.wrap {
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
overflow: hidden;
}
@keyframes animation {
0% {
transform: scale(0.9)
}
25% {
transform: scale(1.1)
}
50% {
transform: scale(0.9)
}
75% {
transform: scale(1.1)
}
100% {
transform: scale(0.9)
}
}
#circle {
width: 50px;
height: 50px;
background: none;
border: 5px solid aqua;
border-radius: 50%;
position: absolute;
left: 50%;
top: 50%;
margin: -10px 0 0 -10px;
pointer-events: none;
/*animation: animation 5s infinite
*/
}
#top, #bottom, #left, #right {
background: #a5ebff;
position: fixed;
}
#left, #right {
top: 0; bottom: 0;
width: 10px;
}
#left { left: 0; }
#right { right: 0; }
#top, #bottom {
left: 0; right: 0;
height: 10px;
}
#top { top: 0; }
#bottom { bottom: 0; }
<!DOCTYPE html>
<html>
<head>
<title> Test Project </title>
<link rel = "stylesheet" href = "main.css">
</head>
<body>
<div class = "wrap">
<div id = "circle"></div>
</div>
<div id="left"></div>
<div id="right"></div>
<div id="top"></div>
<div id="bottom"></div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script type = "text/javascript" src = "./main.js"> </script>
</body>
</html>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With