1
\$\begingroup\$

I am making a game like this :

Smiley Game

Yellow smiley has to escape from red smileys, when yellow smiley hits the boundary game is over, when red smileys hit the boundary they should bounce back with the same angle they came, like shown below:

Bounce angle

Every 10 seconds a new red smiley comes in the big circle, when red smiley hits yellow, game is over, speed and starting angle of red smileys should be random. I control the yellow smiley with arrow keys. The biggest problem I have reflecting the red smileys from the boundary with the angle they came. I don't know how I can give a starting angle to a red smiley and bouncing it with the angle it came. After some tips I did the reflection, looks reasonable but I am not sure if it is working because sometimes it just bounces to the way they came from.

Now the problem is after each bounce the speed of the red smiley increases and after 4-5 bounces they the speed goes to infinity and balls disappear.

How can I overcome this?

My js source code :

//Smiley.js var canvas = document.getElementById("mycanvas"); var ctx = canvas.getContext("2d"); var vx; var vy; var twiceProjFactor; // Object containing some global Smiley properties. var SmileyApp = { radius: 15, xspeed: 0, yspeed: 0, xpos:200, // x-position of smiley ypos: 200 // y-position of smiley }; var SmileyRed = { radius: 15, xspeed: 0, yspeed: 0, xpos:350, // x-position of smiley ypos: 67 // y-position of smiley }; var SmileyReds = new Array(); for (var i=0; i<5; i++){ SmileyReds[i] = { radius: 15, xspeed: 0, yspeed: 0, xpos:350, // x-position of smiley ypos: 67 // y-position of smiley }; SmileyReds[i].xspeed = Math.floor((Math.random()*50)+1); SmileyReds[i].yspeed = Math.floor((Math.random()*50)+1); } function drawBigCircle() { var centerX = canvas.width / 2; var centerY = canvas.height / 2; var radiusBig = 300; ctx.beginPath(); ctx.arc(centerX, centerY, radiusBig, 0, 2 * Math.PI, false); // context.fillStyle = 'green'; // context.fill(); ctx.lineWidth = 5; // context.strokeStyle = '#003300'; // green ctx.stroke(); } function lineDistance( positionx, positiony ) { var xs = 0; var ys = 0; xs = positionx - 350; xs = xs * xs; ys = positiony - 350; ys = ys * ys; return Math.sqrt( xs + ys ); } function drawSmiley(x,y,r) { // outer border ctx.lineWidth = 3; ctx.beginPath(); ctx.arc(x,y,r, 0, 2*Math.PI); //red ctx.fillStyle="rgba(255,0,0, 0.5)"; ctx.fillStyle="rgba(255,255,0, 0.5)"; ctx.fill(); ctx.stroke(); // mouth ctx.beginPath(); ctx.moveTo(x+0.7*r, y); ctx.arc(x,y,0.7*r, 0, Math.PI, false); // eyes var reye = r/10; var f = 0.4; ctx.moveTo(x+f*r, y-f*r); ctx.arc(x+f*r-reye, y-f*r, reye, 0, 2*Math.PI); ctx.moveTo(x-f*r, y-f*r); ctx.arc(x-f*r+reye, y-f*r, reye, -Math.PI, Math.PI); // nose ctx.moveTo(x,y); ctx.lineTo(x, y-r/2); ctx.lineWidth = 1; ctx.stroke(); } function drawSmileyRed(x,y,r) { // outer border ctx.lineWidth = 3; ctx.beginPath(); ctx.arc(x,y,r, 0, 2*Math.PI); //red ctx.fillStyle="rgba(255,0,0, 0.5)"; //yellow ctx.fillStyle="rgba(255,255,0, 0.5)"; ctx.fill(); ctx.stroke(); // mouth ctx.beginPath(); ctx.moveTo(x+0.4*r, y+10); ctx.arc(x,y+10,0.4*r, 0, Math.PI, true); // eyes var reye = r/10; var f = 0.4; ctx.moveTo(x+f*r, y-f*r); ctx.arc(x+f*r-reye, y-f*r, reye, 0, 2*Math.PI); ctx.moveTo(x-f*r, y-f*r); ctx.arc(x-f*r+reye, y-f*r, reye, -Math.PI, Math.PI); // nose ctx.moveTo(x,y); ctx.lineTo(x, y-r/2); ctx.lineWidth = 1; ctx.stroke(); } // --- Animation of smiley moving with constant speed and bounce back at edges of canvas --- var tprev = 0; // this is used to calculate the time step between two successive calls of run function run(t) { requestAnimationFrame(run); if (t === undefined) { t=0; } var h = t - tprev; // time step tprev = t; SmileyApp.xpos += SmileyApp.xspeed * h/1000; // update position according to constant speed SmileyApp.ypos += SmileyApp.yspeed * h/1000; // update position according to constant speed for (var i=0; i<SmileyReds.length; i++){ SmileyReds[i].xpos += SmileyReds[i].xspeed * h/1000; // update position according to constant speed SmileyReds[i].ypos += SmileyReds[i].yspeed * h/1000; // update position according to constant speed } // change speed direction if smiley hits canvas edges if (lineDistance(SmileyApp.xpos, SmileyApp.ypos) + SmileyApp.radius > 300) { alert("Game Over"); } for (var i=0; i<SmileyReds.length; i++){ if (lineDistance(SmileyReds[i].xpos, SmileyReds[i].ypos) + SmileyReds[i].radius > 300) { // Red Smiley collusion //SmileyReds[i].xpos //SmileyReds[i].xspeed //SmileyReds[i].ypos //SmileyReds[i].yspeed // r = v − [2 (n · v) n] formula //n calculation nx = 350 - SmileyReds[i].xpos ; ny = 350 - SmileyReds[i].ypos ; nx = nx / (Math.sqrt(nx * nx + ny * ny)); ny = ny / (Math.sqrt(nx * nx + ny * ny)); //new calc v_newx = SmileyReds[i].xspeed - (2 *( nx * SmileyReds[i].xspeed + ny * SmileyReds[i].yspeed ) ) * nx; v_newy = SmileyReds[i].yspeed - (2 *( nx * SmileyReds[i].xspeed + ny * SmileyReds[i].yspeed ) ) * ny; SmileyReds[i].xspeed = v_newx; SmileyReds[i].yspeed = v_newy; //to calculate "n," you do (626/L, 282/L) where L=sqrt(xpos^2+ypos^2) } } /* Square canvas if ((SmileyApp.xpos + SmileyApp.radius > canvas.width) || (SmileyApp.xpos - SmileyApp.radius) < 0) { SmileyApp.xspeed = -SmileyApp.xspeed; } if ((SmileyApp.ypos + SmileyApp.radius > canvas.height) || (SmileyApp.ypos - SmileyApp.radius) < 0) { SmileyApp.yspeed = -SmileyApp.yspeed; } */ // redraw smiley at new position ctx.clearRect(0,0,canvas.height, canvas.width); drawBigCircle(); drawSmiley(SmileyApp.xpos, SmileyApp.ypos, SmileyApp.radius); for (var i=0; i<SmileyReds.length; i++){ drawSmileyRed(SmileyReds[i].xpos, SmileyReds[i].ypos, SmileyReds[i].radius); } } // uncomment these two lines to get every going // SmileyApp.speed = 100; run(); // --- Mouse wheel event handler to grow and shrink smiley function mousewheelCB(event){ // alert("Hallo"); event.preventDefault(); event.stopPropagation(); SmileyApp.radius += event.wheelDelta/40; } canvas.addEventListener('mousewheel', mousewheelCB, false); // --- Control smiley motion with left/right arrow keys function arrowkeyCB(event) { event.preventDefault(); if (event.keyCode === 37) { // left arrow SmileyApp.xspeed = -100; SmileyApp.yspeed = 0; } else if (event.keyCode === 39) { // right arrow SmileyApp.xspeed = 100; SmileyApp.yspeed = 0; } else if (event.keyCode === 38) { // up arrow SmileyApp.yspeed = -100; SmileyApp.xspeed = 0; } else if (event.keyCode === 40) { // right arrow SmileyApp.yspeed = 100; SmileyApp.xspeed = 0; } } document.addEventListener('keydown', arrowkeyCB, true); /* function run(){ console.log("Here is run"); console.log(Date.now()); ctx.clearRect(0,0,canvas.width, canvas.height); xpos = 200; drawSmiley2(100,100,20); xpos = xpos-50; // decent animation 30 pictures per second } setInterval(run, 50); */ 

JSFiddle : http://jsfiddle.net/gj4Q7/2/

\$\endgroup\$

2 Answers 2

1
\$\begingroup\$

You can do this using vector math. There's a standard formula for reflecting an incoming vector (v₁) off a normal, which you can see derived on this page. The formula is

v₂ = v₁ - 2 (v₁ · n) n

Since it's a circle, the normal is just the normalized vector from the collision point toward the center of the circle. This is a good formula to put in a utility function in your math library, since it will come in handy all over the place when programming games.

\$\endgroup\$
14
  • \$\begingroup\$ thanks for the reply firstly but in my Red Smileys I only have x,y coordinates, nothing else ... Don't we need at least 2 points for calculating a vector? From my x,y how can I do this? Or does my Red Smiley object need more variables? I'm not good at maths :( \$\endgroup\$ Commented Nov 10, 2013 at 21:32
  • 1
    \$\begingroup\$ @Anarkie The velocity x,y already is a vector. You might want to read this article series to learn the basics of vectors. They're not very complicated, and you'll use them all the time in game programming. \$\endgroup\$ Commented Nov 10, 2013 at 21:34
  • \$\begingroup\$ I read the link you gave, but still can't figure out, can you give an example calculation for example (x=340,y=320) when the smiley hits the boundary? \$\endgroup\$ Commented Nov 10, 2013 at 22:07
  • \$\begingroup\$ in this example, the vector v1 is (xspeed,yspeed) and n=(circlex-xpos,circley-ypos), where (circlex,circle) is the position of the centre of the circle. v2 will be the new (xspeed,yspped) \$\endgroup\$ Commented Nov 11, 2013 at 17:49
  • \$\begingroup\$ @Ken if I understood correct my center position is 350,350 so n should be : (350-xposition, 350-yposition)? After a little bit better understnading the vectors my questions is "Lets say a ball with xspeed: 14, yspeed: 16 hits the circular edge at xposition:626 yposition:382" then how would we calculate this? Do wee need to find out teta?If yes how :( \$\endgroup\$ Commented Nov 11, 2013 at 21:20
1
\$\begingroup\$

I wanted to comment on the answer given by @Nathan Reed (which is pretty good, btw - I do not wish to make his answer less relevant by answering again), but I do not have enough reputation yet. Anyway, I just wanted to point out that you can try to use this Vector2D class from Kevin Lindsey: http://www.kevlindev.com/gui/math/vector2d/index.htm Include that class in your project and then just do as Nathan suggested. You can find many other implementations of vector math, just google for "Vector2D Javascript".

Also, it might be useful for you to study a little about steering behaviours. These links have relevant information:

http://gamedev.tutsplus.com/tutorials/implementation/understanding-steering-behaviors-seek/ http://www.red3d.com/cwr/steer/

\$\endgroup\$

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.