I've been fooling around with JavaScript/JQuery, and decided to create a little program which would animate a ball bouncing around a rectangular boundary area. I believe that the logic of my code should make sense, but for some reason I can't get it to change directions. What is even stranger is that I put the ball's x and y positions as text on it, but it seems statically stuck (it doesn't change), but I see when I inspect the element that it's left and top css parameters are changing over time.
Here's the code:
<!DOCTYPE html>
<html>
<head>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.10.1/jquery.min.js">
</script>
<style>
.ball {
width: 50px;
height: 50px;
background: red;
-moz-border-radius: 50px;
-webkit-border-radius: 50px;
border-radius: 50px;
position: absolute;
}
.boundary {
width: 700px;
height: 500px;
background: #AAAAAA;
border-style:solid;
border-width:5px;
position: absolute;
}
</style>
<script>
$(document).ready(function(){
var b = new ball(1, 1, 10, 10);
for(i = 0; i < 1000; i++)
b.moveBall();
});
function ball(xPos, yPos, xVel, yVel)
{
this.xPos = xPos;
this.yPos = yPos;
this.xVel = xVel;
this.yVel = yVel;
this.rightBound = false
this.leftBound = false;
this.upBound = false;
this.downBound = false;
this.width = 50;
this.height = 50;
this.moveBall = moveBall;
function moveBall()
{
var h = 500;
var w = 700;
//detect if it is at x bounds
if(this.xPos + this.width + this.xVel > w)
{this.rightBound = true;}
else if(this.xPos + this.xVel < -1)
this.leftBound = true;
else
{
this.rightBound = false;
this.leftBound = false;
}
//detect if it is at y bounds
if(this.yPos + this.height + this.yVel > h)
{this.downBound = true;}
else if(this.yPos + this.yVel < -1)
this.upBound = true;
else
{
this.upBound = false;
this.downBound = false;
}
//handle velocity changes for bounds
//so you switch the x direction if x bound is met, same for y
if(this.rightBound || this.leftBound)
this.xVel *= -1;
if(this.upBound || this.downBound)
this.yVel *= -1;
//now give motion
this.xPos += xVel;
this.yPos += yVel;
//now draw
$(".ball").animate({
left:this.xPos + 'px',
top:this.yPos + 'px'
}, 150).text(this.xPos + "," + this.yPos);
}
}
</script>
</head>
<body>
<div class="boundary">
<div class="ball"></div>
</div>
</body>
</html>
The weird thing is that it seems to automatically put the end value of 10,001, 10,001 (assuming it never changes direction) as it's (x,y) coordinates from the very beginning. Anything that could point me in the right direction would be appreciated! And sorry if it's some basic error, I tried to ensure it wasn't but sometimes they slip through!
You're doing your ball.moveBall in a loop, not on a timer. So it's going as fast as computably possible. Yes, computably isn't a word. Moving on.
By calling $.animate you're saying you want jQuery to handle moving the object from one spot to another. jQuery goes way way way way way slower than the computer. Hang on, this is getting confusing. Let me put it simply.
You loop through ball.moveBall 1000 times. How long does that take? Virtually no time at all. That's why the coordinates stay stuck at 1000 the whole time. It actually gets there super super super fast. So fast, it gets there basically before jQuery has time to start moving the ball. And.... then the ball starts moving. Why? Why doesn't it actually move to position 1000,1000 right away? Well, the coordinates do get to 1000,1000. But jQuery is like, "Oh, okay, move the ball to position 1000,1000. I can do that! Really really slowly...". You're probably tired of hearing the explanation, so here's the fix:
Change $(".ball").animate to $(".ball").css. And change your loop to window.setInterval(function(){b.moveBall()},1000) or something like that. I've set up a fiddle for you here: http://jsfiddle.net/uXbwR/
You'll notice it moves really slowly. That's cause I set the interval to 1000 milliseconds, which means it only moves once every second. For a game you'll want something like 1000/60 (once every 60th of a second), but I tried that and it makes the ball move super fast. You've got the ball's speed really high. You might wanna try turning that down a bit.
That's all I've got.
Edit
Computationally. That's the word I was looking for.
You should call the next step of the animation only when the preceding has completed. You are telling animate to take 150ms, but the while loop completes almost instantly, without waiting each step.
[EDIT]
#Samuel answer is complete and already suggested you a good workaround. I guess this is going to be beyond the purposes of your application, but if you're interested in setting up a proper Javascript game mainloop these are some useful resources followed by an implementation of mine:
Fabien Sanglard, Game timers: Issues and solutions
Paul Irish, requestAnimationFrame for smart animating.
var RENDERING_FRAME_TIME = 1000/60; // ms
var PHYSICS_FRAME_TIME = 5; // ms
var currentTime = new Date().getTime();
var accumulator = 0;
(function mainloop(){
newTime = new Date().getTime();
accumulator = newTime - currentTime;
currentTime = newTime;
while (accumulator > PHYSICS_FRAME_TIME) {
integrate(PHYSICS_FRAME_TIME);
accumulator -= PHYSICS_FRAME_TIME;
}
requestAnimationFrame(mainloop);
render();
})();
Related
The Plan
I want to make a animation using JavaScript and CSS, The plan is to make snow fall from the top of the div to the bottom. In short currently I create a div and just append a few child DOM elements that will be created with JavaScript and appended to the already created parent. The snow will also pick up when the browser window changes and then automatically create. The code is done using OOP and it currently will change the CSS properties of each new child DOM element that was created to increase the top and left properties (they will all be set to position absolute) and once the DOM element is off screen it will reset and start over only making sure to stick to use the limit I set in the Object property.
Problem
Currently If I run this method snow.animate in a setInterval - it says that it cant find the length property of the array that I pushed all the created child DOM elements in, but if i console.log() the array and try and get the length of the array then it actually shows me the length. I logged before the for loop and inside it to check if I can get the length of the array and it gets it but not when run through the setInterval.
Question
My question is, am I missing something or will I need to just make separate function and not use a Object to store all the methods?
note - I made a while loop inside the animation method - I felt very uncomfortable doing this. Is this safe or bad practice and is there another way of using the while loop to create the child elements that will be pushed in the array to use in the for loop that comes after.
DEMO Code - JSFIDDLE: https://jsfiddle.net/sb9o0t4m/
HTML
<div id="snowglobe"></div>
CSS
body {
background-color: #000;
margin: 0;
}
/*---------------Snow Flakes--------------*/
#snowglobe {
width: 100%;
min-height: 650px;
position: relative;
top: 0;
left: 0;
overflow: hidden;
}
#snowglobe .flake {
position: absolute;
width: 1px;
height: 1px;
color: rgba(255,255,255,0);
text-shadow: 0 0 20px #fff;
}
JS
var snow = {
//variables
width: window.innerWidth,height: 650,flakeCount: 10,
gravity: 0.7,windSpeed: 20,flakes: [],
currentFlake: 0,snowGlobe: document.getElementById('snowglobe'),
//methods
getRandom: function(min,max){
return Math.floor(Math.random() * (max - min) + min);
},
animate: function(){
//while loop
while (this.currentFlake < this.flakeCount){
var flake = document.createElement('div'),
newFlake;
flake.className = "flake";
flake.style.fontSize = this.getRandom(20,50) + 'px';
flake.style.top = this.getRandom(0,this.height) + 'px';
flake.style.left = this.getRandom(0,this.left) + 'px';
flake.innerHTML = "*";
newFlake = this.snowGlobe.appendChild(flake);
//set a speed property to the newflake obj
newFlake.speed = this.getRandom(1,100);
this.flakes.push(newFlake);
this.currentFlake++;
}
var flakes = this.flakes;
//use the array of dom elements
for(var i = 0; i < flakes.length; i++){
positionX = false;
positionY = false;
//new Y position
positionY = parseFloat(flakes[i].style.top) + (flakes[i].speed / 100) * this.gravity;
if(positionY > this.height){
positionY = 0 - parseInt(flakes[i].style.fontSize);
positionX = getRandom(0, width);
}
//new X position
if (!positionX) positionX = parseFloat(flakes[i].style.left) + Math.sin(positionY / this.windSpeed);
if (positionX < -20) positionX = this.width + 20;
if (positionX > this.width + 20) positionX = -20;
// Set new position
flakes[i].style.top = positionX + 'px';
flakes[i].style.left = positionX + 'px';
}
}
}
//check browser window, did it resize?
window.onresize = function(){
snow.width = window.innerWidth;
console.log(snow.width);
}
setInterval(snow.animate, 1000/60);
//if you want, coment out the above line and uncoment the one below to see the snow loading
//snow.animate();
Thanks in advance
The problem is that this is not set to the proper object. For details, see the "this" problem.
You could call the method like this:
setTimeout( function(){ snow.animate(); }, 1000/60 );
but it's better to use requestAnimationFrame:
var snow = {
...
animate: function() {
...
window.requestAnimationFrame( (function(){ this.animate() }).bind(this) );
}
}
and you could simply start the animation by calling snow.animate().
I also took the liberty of fixing some errors that you wouldn't be aware of because the code wouldn't run (referencing width without this etc..).
Here's an updated fiddle.
I left your initialisation loop as is; since it only runs once, normally you'd put this in a separate method, usually a constructor or initialisation method. The only downside here is that there is the overhead of checking the while condition on each animation frame.
I wrote a little Javascript app to track and display the x- and y- components of the mouse's velocity. It subtracts the cursor's previous position from its current position, and divides by time. Pretty simply stuff. Here is the complete working source:
<!DOCTYPE html5>
<html>
<head>
<meta charset="UTF-8">
<style>
* {
background-color:#000000;
}
html {
width:100%;
height:100%;
}
#readout {
background-color:#FFFFFF;
border: 8px solid #34a1ff;
width: 162px;
height: 100px;
position:absolute;
top:0;
bottom: 0;
left: 0;
right: 0;
margin: auto;
padding: 16px;
}
.text {
background-color:#FFFFFF;
}
</style>
</head>
<body>
<div id="readout">
<span id="xLabel" class="text">X: </span>
<span id="xValue" class="text"></span>
<br>
<span id="yLabel" class="text">Y: </span>
<span id="yValue" class="text"></span>
</div>
<script>
// Where the speed will be displayed
window.xDisplay = document.getElementById("xValue");
window.yDisplay = document.getElementById("yValue");
// Keep track of last time mouse was moved
window.lastTime = new Date().getTime();
window.lastDeltaTime = 0;
window.lastMouseX = 0;
window.lastMouseY = 0;
window.lastVX = 0; // for smoothing
window.lastVY = 0;
// Listen for mouse move event
document.addEventListener('mousemove', function(e){
// Get current mouse position
var currentX = e.clientX || e.pageX;
var currentY = e.clientY || e.pageY;
// Get distance travelled from last mouse position
var deltaX = currentX - lastMouseX;
var deltaY = currentY - lastMouseY;
// Update mouse position
lastMouseX = currentX;
lastMouseY = currentY;
// Get current time
var currentTime = new Date().getTime();
// Get time elapsed since last mouse event
var deltaTime = currentTime - lastTime;
// Update last time
lastTime = currentTime;
// Get velocity components
var xSpeed = deltaX / deltaTime;
var ySpeed = deltaY / deltaTime;
// Smooth out velocity
var xSmooth = (xSpeed*2 + lastVX)/3;
var ySmooth = (ySpeed*2 + lastVY)/3;
// Update previous components
lastVX = xSpeed;
lastVY = ySpeed;
// Display velocity
xDisplay.innerHTML = xSmooth.toFixed(3);
yDisplay.innerHTML = ySmooth.toFixed(3);
}, false);
</script>
</body>
</html>
This will be used in an app that is intended to run in fullscreen mode. The problem I encounter is when the cursor ends up all the way at an edge of the screen, and the user keeps moving the mouse in that direction. (Example: the cursor is all the way at the right edge of the screen, but the user keeps moving their mouse toward the right).
In the above scenario, the app displays a velocity of zero, since the cursor position is not being updated. However, I am in need to a solution that continues to display the actual mouse velocity even after the cursor has reached the edge (none of the similar questions on this site address this issue).
This is important since the use case will be in a WebGL context in which the mouse is used to control rotation from a first person view. The user needs to be able to keep on rotating their view around as many times as they want where rotation velocity is based on mouse velocity (not position!) and this simply doesn't work by calculating mouse speed from cursor position.
This may require some creativity but I'm sure it can be done. Thanks in advance for any solutions!
Found the solution, in case anyone ever stumbles upon this later.
The Pointer Lock API does exactly what I needed.
So I have been trying endlessly to try and do something similar too what this site is doing (http://whois.domaintools.com/). I'm trying to get a webpage, so wherever the mouse moves over the webpage, that kind of effect follows it (I'm sorry I don't know what I would call the effect).
I've read how to ask questions on here, but I don't know what too look for so it's difficult for me to attempt this. So far this link (http://p5js.org/learn/demos/Hello_P5_Drawing.php) I've used the code from this and played around with it but i'm just puzzled as too how I would go about doing this.
Thanks for any help, I've been banging my head against a brick wall for a good couple of days now.
This seems to be some kind of particle system. I would start the following way: First create a class for a particle, it should have a random x and y coordinate, and it should change it's postion periodically to a random new postion. Then create a lot of instances of the particle and distribute them over the page.
http://jsfiddle.net/aggoh0s1/3/
/* each particle will move in a 100px100px square */
var gutterWidth = 100;
/* class definition */
var Particle = function(x, y) {
var t = this;
t.x = x;
t.y = y;
t.elem = $('<div class="particle" />');
t.elem.css({ left: x+"px", top: y+"px"});
$('body').append(t.elem);
/* create a new position every 500-1000 milliseconds */
var milliSecs = 500 + Math.random() * 500;
t.ptinterval = setInterval(function() {
var dx = Math.round(Math.random() * gutterWidth);
var dy = Math.round(Math.random() * gutterWidth);
t.elem.animate({left: (t.x + dx)+"px", top: (t.y + dy) + "px"}, 600);
}, milliSecs);
};
/* create a 1000px1000px area where particles are placed each 100px */
var particles = [];
var newParticle;
for(var x = 0; x < 1000; x = x + gutterWidth) {
for(var y = 0; y < 1000; y = y + gutterWidth) {
newParticle = new Particle(x,y);
particles.push(newParticle);
}
}
CSS:
.particle {
width: 2px;
height: 2px;
background-color: black;
position: absolute;
}
Using this logic, you could also use a canvas to display the particles instead of a html div like it is done on whois.domaintools.com. The next step should be to connect the particles with lines to each other, and after that some code should hide all particles that are some distance away from the mouse position.
I've developed the following solution for the effect which you are referring. This is done using jQuery using the event mousemove(). Bind this event to your body where the content is.
Method :
Create an element with the following css on your body. You can create the element onthefly using jQuery as well.
<div class='hover'></div>
CSS
.hover{
position:absolute;
width:100px;
height:100px;
background-color:#fff;
}
The add the following code to your page.
$('body').mousemove(function(event){
$('.hover').css({
'top' : event.pageY,
'left': event.pageX
})
});
The above code will bind an event to your mouse move and I change the element position according to the mouse coordinates.
This fiddle shows a running example
I've given you the basic idea of the solution! You will have to medle with the css and jquery to add the looks and feels of the effect which you refer to.
See the simple example
<img id="imgMove" src="Images/img1.jpg" height="100" width="100" style="position: absolute;" />
JQuery
$(document).ready(function () {
$(document).mousemove(function (e) {
$("#imgMove").css({ "top": e.pageY - 50, "left": e.pageX - 50 }); // e.pageX - Half of Image height, width
})
})
So I'm trying to make this div animation a little more realistic by implementing a "physics engine" that slightly resembles how it would actually accelerate and decelerate... kinda...
<!DOCTYPE html>
<html>
<head>
<title>Gio is Fay</title>
<link rel="stylesheet" href="sliding.css"/>
</head>
<body>
<div id="button">
<h5>Click me!</h5>
<h4>HARDER!</h4>
</div>
<div id="moving"></div>
<script type="text/javascript">
var sliding = document.getElementById("moving");
var margin = sliding.style.marginTop | 100;
var speeds = [0.5, 1, 3, 6, 8, 9, 10, 10, 10, 10, 10, 10, 9, 8, 5, 3, 1];
var length = speeds.length;
sliding.onclick = move;
function move() {
window.log("Herro!");
for (i = 0; i < length; i++) {
var x = speeds[i];
margin += x * 5;
sliding.style.marginTop = margin + "px";
}
}
</script>
</body>
</html>
When I click on the div, unsurprisingly, nothing happens. I put in an alert box to tell me whether the function was being triggered, and apparently it wasn't. Or at least the alert never showed up. Not sure why. No errors in the console. Help?
I tried your code, and everything but window.log() "worked", so I'm not sure why you weren't getting any results at all.
This really isn't the best method to animate. You should use requestAnimationFrame(), which works a lot like a setTimeout() but is optimized for animation. When I got your code working, you don't see an animation at all because you have only 17 frames, and those 17 passes through the for loop happen SUPER fast. So look into requestAnimationFrame() for animating with js. Here is what your code would look like implementing that change:
var sliding = document.getElementById("moving");
var margin = sliding.style.marginTop | 10;
var speeds = [0.5, 1, 3, 6, 8, 9, 10, 10, 10, 10, 10, 10, 9, 8, 5, 3, 1];
var length = speeds.length;
var frame = 0;
sliding.onclick = function () {
frame = 0;
requestAnimationFrame(move);
}
function move() {
var x = speeds[frame];
margin += x;
sliding.style.marginTop = margin + "px";
if (frame < length)
requestAnimationFrame(move);
frame++;
}
You should also reconsider your method for changing the rate. I would use trig functions. That way you could easily change the duration and distance that the div moves. Here is an example of that, feel free to use it. You can run this snippet to see what it looks like.
var sliding = document.getElementById("moving");
var margin = sliding.style.marginTop | 10;
// a few settings for our animation
var theta = 0.0; //in radians
var distance = 100; //in pixels
var duration = 1; //in seconds
var frameRate = 60; //fps
var down = false; //a switch so we can move up and down, we are starting in the up position
sliding.onclick = function () {
if (down)
//if we need to go up, we start at 1 (cos(PI) = -1)
theta = Math.PI;
else
//otherwise we start at 0
theta = 0.0;
//flip it
down = !down;
//and away we go
requestAnimationFrame(move);
}
function move() {
//the margin is determined with cos
margin = (-1 * Math.cos(theta) + 1) * distance + 10;
//and set
sliding.style.marginTop = margin + "px";
//our theta advances
theta += (Math.PI) / frameRate / duration;
//and we continue if we are not at either end
if ((down && theta <= Math.PI) || (!down && theta <= Math.PI * 2))
requestAnimationFrame(move);
}
#moving {
margin-top: 10px;
padding: 10px;
background: #047;
color: #bbb;
border-radius: 5px;
width: 80px;
text-align: center;
}
<div id="moving">DIV</div>
And finally, jQuery has this stuff built in so you don't have to worry about reinventing the wheel, so look in to that. I hope this helps!
first of all, as mentioned. window.log isnt a function. I do get that error in the console. You will also need to set the position of your moving div to be relative to see it move.
Here is a fiddle of your code (with my modifications)
http://jsfiddle.net/404xk2fo/
when I click on the red div it will instantly disappear off the screen because it's moving quite a bit. Lower the multiplication factor, and it will stay on the screen but still instantly move. The problem is you are trying to animate in this loop without any kind of timer. Executing this code in a loop is so fast that your animation is going to happen instantly.
check out this webpage for some examples on how to do smooth animations in javascript. the key is using requestAnimationFrame
http://creativejs.com/resources/requestanimationframe/
I added a really simple example of requestAnimationFrame http://jsfiddle.net/j4x9dctq/
I'm using a little jquery to select the div. I just prefer jquery syntax. you can use native javascript if you prefer.
I have an image of a ball, a cup, and an inner and outer div that represent a throw power bar.
When the user clicks the ball, the power bar starts to increment and then decrements. When the user clicks the ball a 2nd time, the throw power bar stops and the ball is thrown.
As I have began coding this, I realized certain things are going to be extremely complicated, even though the idea itself is rather simple.
For example, I want the ball to be able to "bounce", meaning I will need to not only keep track of the balls x and y coordinates, but also a z coordinate representing depth.
When the ball falls to bounce, the z coordinate with be decremented, and the image of the ball should be scaled down in size, and when it begins bouncing back up, it should scale up in size, again based on the z coordinate. I also want the ball to bounce off the cup if the z coordinate is below a certain value by the time it reaches the cup, go into the cup if it is a certain sweet spot, or go over the cup if it's z value is over that sweet spot.
In the interest of keeping this somewhere short, I'll just post what I have so far. This example is lacking certain things that I was hoping people here could help me with.
http://jsfiddle.net/7Lsh78nw/
<html>
<head>
<style>
#ball {
position:absolute;
width:75px;
height:75px;
}
#cup1 {
position:absolute;
left:375px;
}
#outerPowerMeter {
position:absolute;
width:25px;
height:100px;
background-color:red;
}
#innerPowerMeter {
position:absolute;
width:25px;
height:100px;
background-color:black;
}
</style>
<script>
window.onload = function() {
var ball = document.getElementById("ball");
var yPos = 500;
var xPos = 400;
var zPos = 100;
var ballWidth = 75;
var ballHeight = 75;
var throwBallInterval;
var changeBallSizeInterval;
ball.style.top = yPos + "px";
ball.style.left = xPos + "px";
var cup1 = document.getElementById("cup1");
var powerMeter = document.getElementById("innerPowerMeter");
var powerMeterValue = 0;
var powerMeterHeight = 100;
var powerMeterActive = false;
var powerMeterInterval;
powerMeter.style.height = powerMeterHeight + "px";
ball.onclick = function() {
if (powerMeterActive == false) {
powerMeterActive = true;
startPowerMeter();
} else {
powerMeterActive = false;
stopPowerMeter();
throwBall();
}
}
function throwBall() {
throwBallInterval = setInterval(function() {
yPos = yPos - 1;
ball.style.top = yPos + "px";
}, 1);
changeBallSizeInterval = setInterval(function() {
zPos = zPos - 1;
ballWidth = ballWidth - 1;
ballHeight = ballHeight - 1;
ball.style.width = ballWidth;
ball.style.height = ballHeight;
}, 100);
}
function startPowerMeter() {
var increment = true;
powerMeterInterval = setInterval(function() {
if (increment == true) {
powerMeterValue = powerMeterValue + 1;
powerMeter.style.height = (powerMeterHeight - powerMeterValue) + "px";
if (powerMeterValue == 100) {
increment = false;
}
} else {
powerMeterValue = powerMeterValue - 1;
powerMeter.style.height = (powerMeterHeight - powerMeterValue) + "px";
if (powerMeterValue == 0) {
increment = true;
}
}
},1);
}
function stopPowerMeter() {
clearInterval(powerMeterInterval);
}
function detectCollision() { }
function detectGoal() { }
}
</script>
</head>
<body>
<img id="cup1" src="http://beerwar.com/game/images/cup.png">
<img id="ball" src="http://beerwar.com/game/images/ball.png">
<div id="outerPowerMeter">
<div id="innerPowerMeter"></div>
</div>
</body>
</html>
Since you posted such a detailed case, i thought i give you some pointers. Mind you: this is mostly vector math. I'm not a physicist either, but vector math isn't that complicated luckily! Some pythagoras here and there and you are set.
A good an fast library for that is glMatrix
A couple of things to get you going. Please note: it is pseudo code, but it does explain the concept of it.
Keep a vector for the position of the ball
Keep a vector for the position of the cup (where the ball should hit)
Keep a vector for the position of 'the camera' (since you want to scale the ball based on distance from the camera. Doesn't have to be accurate, just get the idea across)
Keep a vector for the 'direction of the force' you are going to apply to the ball. (this can be multiplied with the force from your force meter)
Keep a vector for the 'velocity of the ball'
Keep a vector for the 'gravity'
Your 'throw' function would become something along the lines of:
ball.velocity = throw.direction * throw.power
setInterval(tick,50);
Basicly, your 'tick' function (the function you apply every x-time)
ball.velocity += gravity; // we apply gravity to the speed of the ball. Pulling it down
ball.position = ball.position + ball.velocity // we add the velocity to the position every tick
if (ball.position.y < ball.radius) // if the ball is below its radius, it is colliding with the ground
{
ball.position.y = 0 - ball.position.y; // just invert its 'up' position, to make it not collide with the ground anymore
// to make the ball go back up again, invert its 'up' velocity. Gravity will get it down eventually
// dampening applied so it will bounce up less every time. For instance make that 0.9.
ball.velocity.y = 0 - (ball.velocity.y * dampening);
}
// the scale of the ball is not determined by its height, but by its distance from the camera
distanceFromCamera = (camera.position - ball.position).length()
ball.scale = 100 - (distanceFromCamera / scaleFactor);
// to make a simply guess if we are hitting the target, just check the distance to it.
distanceFromTarget = (cup.target.position - ball.position).length()
if (distanceFromTarget <= cup.target.radius) // if we 'hit the target'
handleHit()