You are on page 1of 31

29/08/13

The world of Bouncing Balls - An introduction to Java Game Programming

yetanotherinsignificantprogrammingnotes...|HOME

Java Game Programming


Introduction - The World Of Bouncing Balls

Letusbeginbygettingsomeballsbouncing,asanintroductiontogameprogramming.

Demos
ClicktheimagetoruntheDEMOApplets:

Example 1: Getting Started with One Bouncing Ball


BouncingBallSimple.java: Writing a single ball bouncing inside a rectangular container box is straight forward,
andcanbeaccomplishedwithveryfewlinesofcodes,asfollows:
importjava.awt.*
importjava.util.Formatter
importjavax.swing.*
/**
*Oneballbouncinginsidearectangularbox.
*Allcodesinonefile.Poordesign!
*/
//ExtendsJPanel,soastooverridethepaintComponent()forcustomrenderingcodes.
publicclassBouncingBallSimpleextendsJPanel{
//Containerbox'swidthandheight
privatestaticfinalintBOX_WIDTH=640
privatestaticfinalintBOX_HEIGHT=480

//Ball'sproperties

www.ntu.edu.sg/home/ehchua/programming/java/J8a_GameIntro-BouncingBalls.html

1/31

29/08/13

The world of Bouncing Balls - An introduction to Java Game Programming

privatefloatballRadius=200//Ball'sradius
privatefloatballX=ballRadius+50//Ball'scenter(x,y)
privatefloatballY=ballRadius+20
privatefloatballSpeedX=3//Ball'sspeedforxandy
privatefloatballSpeedY=2

privatestaticfinalintUPDATE_RATE=30//Numberofrefreshpersecond

/**ConstructortocreatetheUIcomponentsandinitgameobjects.*/
publicBouncingBallSimple(){
this.setPreferredSize(newDimension(BOX_WIDTH,BOX_HEIGHT))

//Starttheballbouncing(initsownthread)
ThreadgameThread=newThread(){
publicvoidrun(){
while(true){//Executeoneupdatestep
//Calculatetheball'snewposition
ballX+=ballSpeedX
ballY+=ballSpeedY
//Checkiftheballmovesoverthebounds
//Ifso,adjustthepositionandspeed.
if(ballXballRadius<0){
ballSpeedX=ballSpeedX//Reflectalongnormal
ballX=ballRadius//Repositiontheballattheedge
}elseif(ballX+ballRadius>BOX_WIDTH){
ballSpeedX=ballSpeedX
ballX=BOX_WIDTHballRadius
}
//Maycrossbothxandybounds
if(ballYballRadius<0){
ballSpeedY=ballSpeedY
ballY=ballRadius
}elseif(ballY+ballRadius>BOX_HEIGHT){
ballSpeedY=ballSpeedY
ballY=BOX_HEIGHTballRadius
}
//Refreshthedisplay
repaint()//CallbackpaintComponent()
//Delayfortimingcontrolandgiveotherthreadsachance
try{
Thread.sleep(1000/UPDATE_RATE)//milliseconds
}catch(InterruptedExceptionex){}
}
}
}
gameThread.start()//Callbackrun()
}

/**CustomrenderingcodesfordrawingtheJPanel*/
@Override
publicvoidpaintComponent(Graphicsg){
super.paintComponent(g)//Paintbackground

//Drawthebox
g.setColor(Color.BLACK)
g.fillRect(0,0,BOX_WIDTH,BOX_HEIGHT)

//Drawtheball
g.setColor(Color.BLUE)
g.fillOval((int)(ballXballRadius),(int)(ballYballRadius),
(int)(2*ballRadius),(int)(2*ballRadius))

//Displaytheball'sinformation
g.setColor(Color.WHITE)
g.setFont(newFont("CourierNew",Font.PLAIN,12))
StringBuildersb=newStringBuilder()
Formatterformatter=newFormatter(sb)
formatter.format("Ball@(%3.0f,%3.0f)Speed=(%2.0f,%2.0f)",ballX,ballY,
ballSpeedX,ballSpeedY)
g.drawString(sb.toString(),20,30)
}

/**mainprogram(entrypoint)*/

www.ntu.edu.sg/home/ehchua/programming/java/J8a_GameIntro-BouncingBalls.html

2/31

29/08/13

The world of Bouncing Balls - An introduction to Java Game Programming

publicstaticvoidmain(String[]args){
//RunGUIintheEventDispatcherThread(EDT)insteadofmainthread.
javax.swing.SwingUtilities.invokeLater(newRunnable(){
publicvoidrun(){
//Setupmainwindow(usingSwing'sJframe)
JFrameframe=newJFrame("ABouncingBall")
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE)
frame.setContentPane(newBouncingBallSimple())
frame.pack()
frame.setVisible(true)
}
})
}
}

Dissecting BouncingBallSimple.java:
IassumethatyouunderstandJavaGraphicsprogramming(AWT/Swingandcustompainting),andthemultithreading
issuesinvolved.
OurmainclassextendstheJPanel,soastooverridethepaintComponent()forourcustomrenderingcodes.
Intheconstructor,wesetuptheUIcomponents(setthepreferredsizefortheJPanel).Wethenstartanewthread
torunthegameupdate(movingtheball).
Foreachupdatestep,wemovetheballaccordingtoitsspeedandcheckforcollision.Ifitexceedsthebound,we
reactbyadjustingtheball'snewpositionandspeed.Inthissimplecase,theballreflectshorizontallyifitexceedsthe
xbound,andreflectsverticallyifitexceedstheybound.Wetheninvokerepaint()torefreshthescreen,whichin
turncallsthepaintComponent().
We override paintComponent() to perform our custom rendering. We use fillRect() to draw the rectangular
containerboxfillOval()todrawtheroundball,anddrawString()todisplayastatusmessage.
Inthemain()method,weconstructaJFrameastheapplication'smainwindow.WesetourcustomJPanelasthe
content pane for the JFrame. The UI codes are run in the Event Dispatcher Thread (EDT), via
javax.swing.SwingUtilities.invokeLater(),asrecommendedbySwingdevelopers.
This program, although works, is poor in design (in terms of modularity, reusability and expansibility). Moreover, the
collisiondetectionandresponsealgorithmiscrude.Thereisalsonotimingcontrol.

Example 2: Bouncing Ball in Object-Oriented Design


Click the image to run the DEMO. Click HERE to download the source codes for this example (unzip the downloaded
JARfile).

LetusrewritethebouncingballprogramwithproperObjectOrientedDesign,whichconsistsofthefollowingclasses:

The Box Class

www.ntu.edu.sg/home/ehchua/programming/java/J8a_GameIntro-BouncingBalls.html

3/31

29/08/13

The world of Bouncing Balls - An introduction to Java Game Programming

TheBoxclasshasthefollowinginstancevariables:
minX,minY,maxXandmaxY,whichrepresentthebox'sbounds,withpackageaccess.
Take note that Java (and also Windows) Graphics Coordinates is inverted vertically, with origin (0, 0) at the topleft
corner,asshown:

TheBoxclasshasthefollowingpublicmethods:
Aconstructorwhichacceptthetopleftcorner(x,y),width,heightandcolor. It is safer to use x,y,width and
height,insteadofminX,minY,maxXandmaxY,todefinearectangle.
Aset()methodtosetorresetitsbounds.
Adraw(g)method,todrawitselfwiththegivengraphicscontextg(ofjava.awt.Graphics).
importjava.awt.*
/**
*Arectangularcontainerbox,containingthebouncingball.
*/
publicclassContainerBox{
intminX,maxX,minY,maxY//Box'sbounds(packageaccess)
privateColorcolorFilled//Box'sfilledcolor(background)
privateColorcolorBorder//Box'sbordercolor
privatestaticfinalColorDEFAULT_COLOR_FILLED=Color.BLACK
privatestaticfinalColorDEFAULT_COLOR_BORDER=Color.YELLOW

/**Constructors*/
publicContainerBox(intx,inty,intwidth,intheight,ColorcolorFilled,ColorcolorBorder){
minX=x
minY=y
maxX=x+width1
maxY=y+height1
this.colorFilled=colorFilled
this.colorBorder=colorBorder
}

/**Constructorwiththedefaultcolor*/
publicContainerBox(intx,inty,intwidth,intheight){
this(x,y,width,height,DEFAULT_COLOR_FILLED,DEFAULT_COLOR_BORDER)
}

/**Setorresettheboundariesofthebox.*/
publicvoidset(intx,inty,intwidth,intheight){
minX=x
minY=y
maxX=x+width1
maxY=y+height1

www.ntu.edu.sg/home/ehchua/programming/java/J8a_GameIntro-BouncingBalls.html

4/31

29/08/13

The world of Bouncing Balls - An introduction to Java Game Programming

}
/**Drawitselfusingthegivengraphiccontext.*/
publicvoiddraw(Graphicsg){
g.setColor(colorFilled)
g.fillRect(minX,minY,maxXminX1,maxYminY1)
g.setColor(colorBorder)
g.drawRect(minX,minY,maxXminX1,maxYminY1)
}
}

The Ball Class

TheBallclasshasthefollowinginstancevariables:
x,y,radiusandcolor,whichrepresenttheball'scenter(x,y)coordinates,radiusandcolor,respectively.
speedXandspeedY,whichrepresentthespeedinthexandydirections,measuredinpixelspertimestep.
Internally, all numbers are expressed in float to ensure smoothness in rendering, especially in the trigonometric
operations.Thenumberswillbetruncatedtointegralpixelvaluesfordisplay.(32bitsingleprecisionfloatissufficient
formostofthegames,doubleisprobablyanoverkill!)
TheBallclasshasthefollowingpublicmethods:
A constructor that accepts x, y,radius, velocity in the polar coordinates of speed and moveAngle (because it is
easierandmoreintuitiveforusertospecifyvelocitythisway),andcolor.

Adraw(g)methodtodrawitselfwiththegivengraphicscontext.
AtoString()todescribeitself,whichisusedinprintingtheball'sstatus.
A moveOneStepWithCollisionDetection(Boxbox) which moves the ball by one step, with collision detection
andresponse.
BelowisthelistingoftheBallclass:
importjava.awt.*
importjava.util.Formatter
/**
*Thebouncingball.
*/
publicclassBall{

www.ntu.edu.sg/home/ehchua/programming/java/J8a_GameIntro-BouncingBalls.html

5/31

29/08/13

The world of Bouncing Balls - An introduction to Java Game Programming

floatx,y//Ball'scenterxandy(packageaccess)
floatspeedX,speedY//Ball'sspeedperstepinxandy(packageaccess)
floatradius//Ball'sradius(packageaccess)
privateColorcolor//Ball'scolor
privatestaticfinalColorDEFAULT_COLOR=Color.BLUE

/**
*Constructor:Foruserfriendliness,userspecifiesvelocityinspeedand
*moveAngleinusualCartesiancoordinates.NeedtoconverttospeedXand
*speedYinJavagraphicscoordinatesforeaseofoperation.
*/
publicBall(floatx,floaty,floatradius,floatspeed,floatangleInDegree,
Colorcolor){
this.x=x
this.y=y
//Convert(speed,angle)to(x,y),withyaxisinverted
this.speedX=(float)(speed*Math.cos(Math.toRadians(angleInDegree)))
this.speedY=(float)(speed*(float)Math.sin(Math.toRadians(angleInDegree)))
this.radius=radius
this.color=color
}
/**Constructorwiththedefaultcolor*/
publicBall(floatx,floaty,floatradius,floatspeed,floatangleInDegree){
this(x,y,radius,speed,angleInDegree,DEFAULT_COLOR)
}

/**Drawitselfusingthegivengraphicscontext.*/
publicvoiddraw(Graphicsg){
g.setColor(color)
g.fillOval((int)(xradius),(int)(yradius),(int)(2*radius),(int)(2*radius))
}

/**
*Makeonemove,checkforcollisionandreactaccordinglyifcollisionoccurs.
*
*@parambox:thecontainer(obstacle)forthisball.
*/
publicvoidmoveOneStepWithCollisionDetection(ContainerBoxbox){
//Gettheball'sbounds,offsetbytheradiusoftheball
floatballMinX=box.minX+radius
floatballMinY=box.minY+radius
floatballMaxX=box.maxXradius
floatballMaxY=box.maxYradius

//Calculatetheball'snewposition
x+=speedX
y+=speedY
//Checkiftheballmovesoverthebounds.Ifso,adjustthepositionandspeed.
if(x<ballMinX){
speedX=speedX//Reflectalongnormal
x=ballMinX//Repositiontheballattheedge
}elseif(x>ballMaxX){
speedX=speedX
x=ballMaxX
}
//Maycrossbothxandybounds
if(y<ballMinY){
speedY=speedY
y=ballMinY
}elseif(y>ballMaxY){
speedY=speedY
y=ballMaxY
}
}

/**Returnthemagnitudeofspeed.*/
publicfloatgetSpeed(){
return(float)Math.sqrt(speedX*speedX+speedY*speedY)
}

/**Returnthedirectionofmovementindegrees(counterclockwise).*/
publicfloatgetMoveAngle(){
return(float)Math.toDegrees(Math.atan2(speedY,speedX))

www.ntu.edu.sg/home/ehchua/programming/java/J8a_GameIntro-BouncingBalls.html

6/31

29/08/13

The world of Bouncing Balls - An introduction to Java Game Programming

/**Returnmass*/
publicfloatgetMass(){
returnradius*radius*radius/1000f//Normalizebyafactor
}

/**Returnthekineticenergy(0.5mv^2)*/
publicfloatgetKineticEnergy(){
return0.5f*getMass()*(speedX*speedX+speedY*speedY)
}

/**Describeitself.*/
publicStringtoString(){
sb.delete(0,sb.length())
formatter.format("@(%3.0f,%3.0f)r=%3.0fV=(%2.0f,%2.0f)"+
"S=%4.1f\u0398=%4.0fKE=%3.0f",
x,y,radius,speedX,speedY,getSpeed(),getMoveAngle(),
getKineticEnergy())//\u0398istheta
returnsb.toString()
}
//ReusetobuildtheformattedstringfortoString()
privateStringBuildersb=newStringBuilder()
privateFormatterformatter=newFormatter(sb)
}

The Control Logic and Main Display Class BallWorld

TheBallWorldclassprovidestheControlLogic(C)(gameStart(),gameUpdate()),aswellasthePresentationView
(V)byextendingtheJPanel.
importjava.awt.*
importjava.awt.event.*
importjava.util.Random
importjavax.swing.*
/**
*Thecontrollogicandmaindisplaypanelforgame.
*/
publicclassBallWorldextendsJPanel{
privatestaticfinalintUPDATE_RATE=30//Framespersecond(fps)

privateBallball//AsinglebouncingBall'sinstance
privateContainerBoxbox//Thecontainerrectangularbox

privateDrawCanvascanvas//Customcanvasfordrawingthebox/ball
privateintcanvasWidth

www.ntu.edu.sg/home/ehchua/programming/java/J8a_GameIntro-BouncingBalls.html

7/31

29/08/13

The world of Bouncing Balls - An introduction to Java Game Programming

privateintcanvasHeight

/**
*ConstructortocreatetheUIcomponentsandinitthegameobjects.
*Setthedrawingcanvastofillthescreen(givenitswidthandheight).
*
*@paramwidth:screenwidth
*@paramheight:screenheight
*/
publicBallWorld(intwidth,intheight){

canvasWidth=width
canvasHeight=height

//Inittheballatarandomlocation(insidethebox)andmoveAngle
Randomrand=newRandom()
intradius=200
intx=rand.nextInt(canvasWidthradius*220)+radius+10
inty=rand.nextInt(canvasHeightradius*220)+radius+10
intspeed=5
intangleInDegree=rand.nextInt(360)
ball=newBall(x,y,radius,speed,angleInDegree,Color.BLUE)

//InittheContainerBoxtofillthescreen
box=newContainerBox(0,0,canvasWidth,canvasHeight,Color.BLACK,Color.WHITE)

//Initthecustomdrawingpanelfordrawingthegame
canvas=newDrawCanvas()
this.setLayout(newBorderLayout())
this.add(canvas,BorderLayout.CENTER)

//Handlingwindowresize.
this.addComponentListener(newComponentAdapter(){
@Override
publicvoidcomponentResized(ComponentEvente){
Componentc=(Component)e.getSource()
Dimensiondim=c.getSize()
canvasWidth=dim.width
canvasHeight=dim.height
//Adjusttheboundsofthecontainertofillthewindow
box.set(0,0,canvasWidth,canvasHeight)
}
})

//Starttheballbouncing
gameStart()
}

/**Starttheballbouncing.*/
publicvoidgameStart(){
//Runthegamelogicinitsownthread.
ThreadgameThread=newThread(){
publicvoidrun(){
while(true){
//Executeonetimestepforthegame
gameUpdate()
//Refreshthedisplay
repaint()
//Delayandgiveotherthreadachance
try{
Thread.sleep(1000/UPDATE_RATE)
}catch(InterruptedExceptionex){}
}
}
}
gameThread.start()//InvokeGaemThread.run()
}

/**
*Onegametimestep.
*Updatethegameobjects,withpropercollisiondetectionandresponse.
*/
publicvoidgameUpdate(){

www.ntu.edu.sg/home/ehchua/programming/java/J8a_GameIntro-BouncingBalls.html

8/31

29/08/13

The world of Bouncing Balls - An introduction to Java Game Programming

ball.moveOneStepWithCollisionDetection(box)
}

/**Thecustomdrawingpanelforthebouncingball(innerclass).*/
classDrawCanvasextendsJPanel{
/**Customdrawingcodes*/
@Override
publicvoidpaintComponent(Graphicsg){
super.paintComponent(g)//Paintbackground
//Drawtheboxandtheball
box.draw(g)
ball.draw(g)
//Displayball'sinformation
g.setColor(Color.WHITE)
g.setFont(newFont("CourierNew",Font.PLAIN,12))
g.drawString("Ball"+ball.toString(),20,30)
}

/**Calledbacktogetthepreferredsizeofthecomponent.*/
@Override
publicDimensiongetPreferredSize(){
return(newDimension(canvasWidth,canvasHeight))
}
}
}

TheclassBallWorldextendsJPanel,asthemasterview panel. The master panel may contain many subpanels. In


thisexample,itcontainsonlyacustomdrawingpanel.Ishallincludeacontrolpanelinthelaterexercises.
TheBallWorldalsocontainsthecontrollogic:gameStart(),gameUpdate().
Ithasthreeinstancevariables:aBall,aBox,andaDrawCanvas(fordrawingthebouncingball).
The constructor sets up the UI components, game objects, and starts the ball bouncing by invoking gameStart(). It
acceptstwoargument,widthandheight,whichisusedtosetthedimensionofthethedrawingcanvas,viacallback
method getPreferredSize() in the DrawCanvas. I also included the codes for handling window resize in
componentResized()callbackhandler,whichshallresizetheboxtofillthewindow.
Custom drawing is carried out in a DrawCanvas, which is derived from JPanel, by overriding the
paintComponent(Graphics)methodtoprogramcustomrendering.paintComponent()isnotsupposedtobecalled
directly,butcalledbackviarepaint().DrawCanvasisdesignedtobeaninnerclassofthemainclass,soastoaccess
theprivatevariablesofouterclassinparticular,thegameobjects.
ThegameStart()startthegameloopinaseparatethread.Thegamelooprepetitively(a)detectcollisioninthecoming
timestepandcomputetheproperresponses,(a)synchronouslyupdatethepositionandstateofallthegameobjects,
(c)rendergraphicsandrefreshthedisplay,(d)delaytocontroltherefreshtimingandyieldcontroltootherthreads.

Thegameloopisrunninginitsownthread(GameThread),bysubclassingThreadandoverridingtherun()methodto
programtherunningbehavior.Multithreadisnecessaryforgameprogramming,asthegraphicssubsystemusesaso

www.ntu.edu.sg/home/ehchua/programming/java/J8a_GameIntro-BouncingBalls.html

9/31

29/08/13

The world of Bouncing Balls - An introduction to Java Game Programming

called Event Dispatch Thread (EDT) to monitor the input events (such as keypress, mouseclick), run the event
handlers, and refreshes the display. If the Event Dispatch Thread is starved (e.g., the GameThread does not yield
control), the screen freezes and no inputs can be processed, resulted in the infamous unresponsive userinterface
problem.
Method run() is not supposed to be called directly, but calledback via the Thread's methodstart(). The static
methodThread.sleep()suspendsthisgamethreadbythespecifiedmilliseconds.Thesleep()servestwopurposes:
Itprovidesthenecessarydelaytoachievethetargetupdate(orrefresh)rate,andalsoyieldscontroltootherthreadsto
do their assigned tasks, in particular, the GUI Event Dispatch Thread which refreshes the screen and processes the
inputs.
Thegameloop,inthiscase,isstraightforward.Foreachstep,itmovestheballandchecksifcollisionoccurs.Ifso,it
computestheproperresponse.Itrefreshesthedisplaybyinvokingrepaint(),whichcallsbackpaintComponent()of
theDrawCanvastocarryoutthecustomdrawing.

The Main class


importjavax.swing.JFrame
/**
*MainProgramforrunningthebouncingballasastandaloneapplication.
*/
publicclassMain{
//Entrymainprogram
publicstaticvoidmain(String[]args){
//RunUIintheEventDispatcherThread(EDT),insteadofMainthread
javax.swing.SwingUtilities.invokeLater(newRunnable(){
publicvoidrun(){
JFrameframe=newJFrame("AWorldofBalls")
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE)
frame.setContentPane(newBallWorld(640,480))//BallWorldisaJPanel
frame.pack()//PreferredsizeofBallWorld
frame.setVisible(true)//Showit
}
})
}
}

TheMainclassprovidesthemain()methodtostarttheapplication,whichallocatesandsetsupaJFrame.Aninstance
ofBallWorldisconstructedandsetasthecontentpanefortheJFrame.
Themain()usesthestandardproceduretoconstructtheUIintheEventDispatchThreadtoensurethreadsafety(see
JavaSwingonlinetutorial@http://java.sun.com/docs/books/tutorial/uiswing).
Forthisexample,youcouldincludethemain()methodintheBallWorldclass,anddiscardtheMainclass.
Tryrunningtheprogramandresizingthewindow.

Running as an Applet
InsteadoftheMainclass,wecoulduseaMainAppletclasstorunthisprogramasanJavaapplet.
importjavax.swing.JApplet
/**
*MainProgramtorunasanapplet
*Thedisplayareais640x480.
*/
publicclassMainAppletextendsJApplet{
@Override
publicvoidinit(){
//RunUIintheEventDispatcherThread
javax.swing.SwingUtilities.invokeLater(newRunnable(){
publicvoidrun(){
setContentPane(newBallWorld(640,480))//BallWorldisaJPanel
}
})
}
}

AppletextendsfromJApplet(orApplet),andusesinit()insteadofmain()tobeginoperation.

www.ntu.edu.sg/home/ehchua/programming/java/J8a_GameIntro-BouncingBalls.html

10/31

29/08/13

The world of Bouncing Balls - An introduction to Java Game Programming

Distribute Application/Applet in a JAR file


Ourprogramcontainsmanyclasses.Howtodistributetheseclasses?TheanswerisviaasingleJARfile.JARissimilar
toZIP,whichcompressesthecontents(youcanuseWinZiporWinRARtounzipaJARfile).Furthermore,JREcanrun
directlyfromaJARfile,withoutexplicitlydecompressit.
WeshallprepareaJARfilesuchthatwecanrunourprogramasastandaloneapplication(viaMain.class)aswellas
anapplet(viaMainApplet.class).

Distribute Stand-alone Application in a JAR file:Torunastandaloneprogramfromajarfile,weneedto


prepareasocalledmanifest(says"BallWorld.manifest") to specify the application's main class, as follows (On the
otherhand,applet'smainclassisspecifiedintheattribute"code"ofthe<applet>tag,nomanifestneededforapplet):
ManifestVersion:1.0
MainClass:Main

RuntheJDK'sjarutilityfromacmdshelltojarupalltheclassesasfollow(thecommandlineoptionsare:'c'forcreate,
'v'forverbose,'m'formanifest,'f'forjar'sfilename):
...changethecurrentworkingdirectorytotheapplication'sbasedirectory...
>jarcvmfBallWorld.manifestballworld.jar*.class

In Eclipse, rightclick the project Export... Java JAR file Next Check "Export generated class files and
resources"(youmayalsoexportthesourcefiles)NextNextCheck"Generatethemanifestfile"In"Mainclass",
enteryourmainclassFinish.
YoucanusetheJavaruntimewith"jar"optiontorunthestandaloneapplicationfromtheJARfile(asfollow)orsimply
doubleclicktheJARfile.Theembeddedmanifestspecifiesthemainclasstostarttheapplication.
>javajarballworld.jar

If your program (in the JAR file) requires other JAR files, include a "ClassPath" in the manifest as follows. The JAR
filesareseparatedbyspace(?!).Thereisnoneedtoincludethecurrentdirectory(?!).
ManifestVersion:1.0
MainClass:Main
ClassPath:collisionphysics.jaranother.jar

Distribute Applet in a JAR file: To run an applet from a JAR file, provide an HTML script with attribute
"archive"selectingtheJARfileandattribute"code"selectingtheappletclass:
<html>
<head><title>ABouncingBall</title></head>
<body>
<h2>ABouncingBall</h2>
<appletcode="MainApplet.class"
width="640"height="480"
archive="ballworld.jar">
</applet>
</body>
</html>

YoumayincludeadditionalJARfilesinthearchiveattribute.Thejarfilesareseperatedbycommas.

Summary:Forastandaloneapplication,themainclassisspecifiedinthemanifestforanapplet,themainclassis
specifiedinthe<applet>'scodeattribute,nomanifestneeded.Foranapplication,additionalJARfilesarespecifiedin
themanifestforanapplet,theyareaddedinthe<applet>'sarchiveattribute.

Running in Full-Screen Mode


importjava.awt.*
importjava.awt.event.*
importjavax.swing.*
/**
*MainProgramforrunningthebouncingballasastandaloneapplication,
*inFullScreenmode(iffullscreenmodeissupported).
*UseESCKeytoquit(needtohandlekeyevent).
*/
publicclassMainFullScreenOnlyextendsJFrame{

/**ConstructortoinitializeUI*/
publicMainFullScreenOnly(){

www.ntu.edu.sg/home/ehchua/programming/java/J8a_GameIntro-BouncingBalls.html

11/31

29/08/13

The world of Bouncing Balls - An introduction to Java Game Programming

//Getthedefaultgraphicdeviceandtryfullscreenmode
GraphicsDevicedevice=GraphicsEnvironment.getLocalGraphicsEnvironment()
.getDefaultScreenDevice()
if(device.isFullScreenSupported()){//Goforfullscreenmode
this.setUndecorated(true)//Don'tshowtitleandborder
this.setResizable(false)
//this.setIgnoreRepaint(true)//IgnoreOSrepaintrequest
device.setFullScreenWindow(this)
}else{//Runinwindowedmodeiffullscreenisnotsupported
Dimensiondim=Toolkit.getDefaultToolkit().getScreenSize()
this.setSize(dim.width,dim.height40)//minustaskbar
this.setResizable(true)
}

//Allocatethegamepaneltofillthecurrentscreen
BallWorldballWorld=newBallWorld(this.getWidth(),this.getHeight())
this.setContentPane(ballWorld)//SetascontentpaneforthisJFrame

//Tohandlekeyevents
this.addKeyListener(newKeyAdapter(){
@Override
publicvoidkeyPressed(KeyEvente){
intkeyCode=e.getKeyCode()
switch(keyCode){
caseKeyEvent.VK_ESCAPE://ESCtoquit
System.exit(0)
break
}
}
})
this.setFocusable(true)//Toreceivekeyevent

this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE)
this.setTitle("AWorldofBalls")
this.pack()//Packtopreferredsize
this.setVisible(true)//Showit
}

/**Entrymainprogram*/
publicstaticvoidmain(String[]args){
//RunUIintheEventDispatcherThread(EDT),insteadofMainthread
javax.swing.SwingUtilities.invokeLater(newRunnable(){
publicvoidrun(){
newMainFullScreenOnly()
}
})
}
}

The above main program runs the bouncing ball in fullscreen mode, if fullscreen mode is supported. It first queries
GraphicsDevice.isFullScreenSupported(),
and
switches
into
fullscreen
mode
via
GraphicsDevice.setFullScreenWindow(this).
If fullscreen mode is not available, the program runs in windowed mode, using the maximum window size. We use
Toolkit.getDefaultToolkit().getScreenSize()tofindthecurrentscreensize.
Ialsosetupthekeyeventhandler,sothatwecoulduseESCkeytoquittheprograminfullscreenmode.

Example 3: Collision Detection and Response


ClickHEREtodownloadthesourcecodesforthisexample(unzipthedownloadedJARfile).
Let'stakeacloserlookatcollisiondetectionandresponseforoursinglebouncingball.Inourspecialcase,thebounds
areeitherhorizontalorvertical.
Theballcollideswiththecontainerifittouchesormovesoveritsbounds,intheupcomingtimestep.
Detectingcollisionofamovingcirclewithaverticallineorahorizontallineissimple(averyspecialcase!)asillustrated
below.Computingtheresponseforthisspecialcaseisalsotrivialsimplyreflecttheballeitherhorizontallyorvertically.
If collision occurs, we need to position the ball at the point of impact. Do not cross the bound, which may result in a
deadlockwheretheballstickstothewallorsimplyfliesawayinthenextstep.

www.ntu.edu.sg/home/ehchua/programming/java/J8a_GameIntro-BouncingBalls.html

12/31

29/08/13

The world of Bouncing Balls - An introduction to Java Game Programming

Asillustrated,theradiusoftheballeffectivelyreducestheboundariesoftheboxbythatamount,orshortenthecollision
time.Hence,bymovingtheboundariesofthebox,wecansimplifytheballtoapoint.Thissimplificationisimportantina
complexsituation.

Package collisionphysics: In order to handle collision detection and response for many situations, I created a
packagecalledcollisionphysics,whichincludesstaticmethodsfordetectingcollisionandcomputingresponses.

CollisionResponse.java:Ifcollisionoccurs,thecollisiontimeandresponses(newSpeedX,newSpeedY)arekeptin
an object of CollisionResponse. My collision detection algorithems are based on ray tracing and uses parametric
equationtofindtheearliestpositivecollisiontime.
packagecollisionphysics
/**
*Ifcollisionoccurs,thisobjectstoresthecollisiontimeand
*thecomputedresponses,newspeed(newSpeedX,newSpeedY).
*/
publicclassCollisionResponse{
/**Detectedcollisiontime,resettoFloat.MAX_VALUE*/
publicfloatt
//Timethresholdtobesubtractedfromcollisiontime
//topreventmovingoverthebound.Assumethatt<=1.
privatestaticfinalfloatT_EPSILON=0.005f

/**Computedspeedinxdirectionaftercollision*/
publicfloatnewSpeedX
/**Computedspeedinydirectionaftercollision*/
publicfloatnewSpeedY

/**Constructorwhichresetsthecollisiontimetoinfinity.*/
publicCollisionResponse(){
reset()//Resetdetectedcollisiontimetoinfinity
}

/**Resetthedetectedcollisiontimetoinfinity.*/
publicvoidreset(){
this.t=Float.MAX_VALUE
}

/**Copythisinstancetoanother,usedtofindtheearliestcollision.*/
publicvoidcopy(CollisionResponseanother){
this.t=another.t
this.newSpeedX=another.newSpeedX
this.newSpeedY=another.newSpeedY
}

/**Returnthexpositionafterimpact.*/
publicfloatgetNewX(floatcurrentX,floatspeedX){

www.ntu.edu.sg/home/ehchua/programming/java/J8a_GameIntro-BouncingBalls.html

13/31

29/08/13

The world of Bouncing Balls - An introduction to Java Game Programming

//Subtractbyasmallthreadtomakesurethatitdoesnotcrossthebound.
if(t>T_EPSILON){
return(float)(currentX+speedX*(tT_EPSILON))
}else{
returncurrentX
}
}

/**Returntheypositionafterimpact.*/
publicfloatgetNewY(floatcurrentY,floatspeedY){
//Subtractbyasmallthreadtomakesurethatitdoesnotcrossthebound.
if(t>T_EPSILON){
return(float)(currentY+speedY*(tT_EPSILON))
}else{
returncurrentY
}
}
}

Themethodreset()setsthecollisiontimetopositiveinfinity(Float.MAX_VALUE).
In a complex systems (e.g., mutliple balls), we need to look for the earliest collision in the entire system. The copy()
methodcanbeusedtotransferthecurrentresponsetotheearliestresponse,ifithasasmallertime.

CollisionPhsyics.java: The main class (modeled after java.lang.Math), which provides static methods for
collisiondetectionandresponse.
packagecollisionphysics
publicclassCollisionPhysics{
//Workingcopyforcomputingresponseinintersect(ContainerBoxbox),
//toavoidrepeatedlyallocatingobjects.
privatestaticCollisionResponsetempResponse=newCollisionResponse()

/**
*Detectcollisionforamovingpointbouncinginsidearectangularcontainer,
*withinthegiventimeLimit.
*IfcollisionisdetectedwithinthetimeLimit,computecollisiontimeand
*responseinthegivenCollisionResponseobject.Otherwise,setcollisiontime
*toinfinity.
*TheresultispassedbackinthegivenCollisionResponseobject.
*/
publicstaticvoidpointIntersectsRectangleOuter(
floatpointX,floatpointY,floatspeedX,floatspeedY,floatradius,
floatrectX1,floatrectY1,floatrectX2,floatrectY2,
floattimeLimit,CollisionResponseresponse){

response.reset()//Resetdetectedcollisiontimetoinfinity

//Aouterrectangularcontainerboxhas4borders.
//Needtolookfortheearliestcollision,ifany.

//Rightborder
pointIntersectsLineVertical(pointX,pointY,speedX,speedY,radius,
rectX2,timeLimit,tempResponse)
if(tempResponse.t<response.t){
response.copy(tempResponse)//Copyintoresultantresponse
}
//Leftborder
pointIntersectsLineVertical(pointX,pointY,speedX,speedY,radius,
rectX1,timeLimit,tempResponse)
if(tempResponse.t<response.t){
response.copy(tempResponse)
}
//Topborder
pointIntersectsLineHorizontal(pointX,pointY,speedX,speedY,radius,
rectY1,timeLimit,tempResponse)
if(tempResponse.t<response.t){
response.copy(tempResponse)
}
//Bottomborder
pointIntersectsLineHorizontal(pointX,pointY,speedX,speedY,radius,
rectY2,timeLimit,tempResponse)

www.ntu.edu.sg/home/ehchua/programming/java/J8a_GameIntro-BouncingBalls.html

14/31

29/08/13

The world of Bouncing Balls - An introduction to Java Game Programming

if(tempResponse.t<response.t){
response.copy(tempResponse)
}
}

/**
*Detectcollisionforamovingpointhittingahorizontalline,
*withinthegiventimeLimit.
*/
publicstaticvoidpointIntersectsLineVertical(
floatpointX,floatpointY,floatspeedX,floatspeedY,floatradius,
floatlineX,floattimeLimit,CollisionResponseresponse){

response.reset()//Resetdetectedcollisiontimetoinfinity

//NocollisionpossibleifspeedXiszero
if(speedX==0){
return
}

//Computethedistancetotheline,offsetbyradius.
floatdistance
if(lineX>pointX){
distance=lineXpointXradius
}else{
distance=lineXpointX+radius
}

floatt=distance/speedX//speedX!=0
//Accept0<t<=timeLimit
if(t>0&&t<=timeLimit){
response.t=t
response.newSpeedX=speedX//Reflecthorizontally
response.newSpeedY=speedY//Nochangevertically
}
}

/**
*@seemovingPointIntersectsLineVertical().
*/
publicstaticvoidpointIntersectsLineHorizontal(
floatpointX,floatpointY,floatspeedX,floatspeedY,floatradius,
floatlineY,floattimeLimit,CollisionResponseresponse){
response.reset()//Resetdetectedcollisiontimetoinfinity

//NocollisionpossibleifspeedYiszero
if(speedY==0){
return
}

//Computethedistancetotheline,offsetbyradius.
floatdistance
if(lineY>pointY){
distance=lineYpointYradius
}else{
distance=lineYpointY+radius
}

floatt=distance/speedY//speedY!=0
//Accept0<t<=timeLimit
if(t>0&&t<=timeLimit){
response.t=t
response.newSpeedY=speedY//Reflectvertically
response.newSpeedX=speedX//Nochangehorizontally
}
}
}

The pointIntersectsLineXxx() methods take a moving ball (currentX, currentY, speedX, speedY, radius), a
vertical/horizontal line (lineX|lineY), a time limit (timeLimit), and a CollisionResponse object. If a collision is
detected within the given time limit, it computes the collision time and responses (newSpeedX,newSpeedY) and stores

www.ntu.edu.sg/home/ehchua/programming/java/J8a_GameIntro-BouncingBalls.html

15/31

29/08/13

The world of Bouncing Balls - An introduction to Java Game Programming

thembacktothegivenCollisionResponseobject.
ThepointIntersectsRectangleOuter()usestheabovemethodstodetecttheearliestcollisiontothe4bordersof
the container box. Only the first collision matters, which nullifies all the subsequent detected collisions. It positions the
ballaccuratelyafterthecollisionatthepointofimpact.

Ball.java: Weshallprepareforthemultipleballcase,whereonlytheearliestcollisionmatters.Eachballshalldetect
probablecollisiontoalltheotherobjectsinthesystem.Itshallmaintaininformationaboutitsearliercollisiondetected.

......
publicclassBall{
......

//Forcollisiondetectionandresponse
//Maintaintheresponseoftheearliestcollisiondetected
//bythisballinstance.(packageaccess)
CollisionResponseearliestCollisionResponse=newCollisionResponse()

......

//Workingcopyforcomputingresponseinintersect(ContainerBoxbox),
//toavoidrepeatedlyallocatingobjects.
privateCollisionResponsetempResponse=newCollisionResponse()

/**
*Checkifthisballcollideswiththecontainerboxinthecomingtimestep.
*
*@parambox:container(obstacle)forthisball
*/
publicvoidintersect(ContainerBoxbox){
//CallmovingPointIntersectsRectangleOuter,whichreturnsthe
//earliestcollisiontooneofthe4borders,ifcollisiondetected.
CollisionPhysics.pointIntersectsRectangleOuter(
this.x,this.y,this.speedX,this.speedY,this.radius,
box.minX,box.minY,box.maxX,box.maxY,
1.0f,tempResponse)
if(tempResponse.t<earliestCollisionResponse.t){
earliestCollisionResponse.copy(tempResponse)
}
}

/**
*Updatethestatesofthisballforonetimestep.
*Moveforonetimestepifnocollisionoccursotherwisemoveupto
*theearliestdetectedcollision.
*/
publicvoidupdate(){
//Checktheearliestcollisiondetectedforthisballstoredin
//earliestCollisionResponse.
if(earliestCollisionResponse.t<=1.0f){//Collisiondetected
//Thisballcollided,getthenewpositionandspeed
this.x=earliestCollisionResponse.getNewX(this.x,this.speedX)

www.ntu.edu.sg/home/ehchua/programming/java/J8a_GameIntro-BouncingBalls.html

16/31

29/08/13

The world of Bouncing Balls - An introduction to Java Game Programming

this.y=earliestCollisionResponse.getNewY(this.y,this.speedY)
this.speedX=(float)earliestCollisionResponse.newSpeedX
this.speedY=(float)earliestCollisionResponse.newSpeedY
}else{//Nocollisioninthiscomingtimestep
//Makeacompletemove
this.x+=this.speedX
this.y+=this.speedY
}
//Clearforthenextcollisiondetection
earliestCollisionResponse.reset()
}
}

BallWorld.java:
......
publicclassBallWorldextendsJPanel{
......

publicvoidgameUpdate(){
//Detectcollisionforthisballwiththecontainerbox.
ball.intersect(box)
//Updatetheball'sstatewithpropercollisionresponseifcollided.
ball.update()
}
}

Run this example and compare with previous example. Closely observe the collision by reducing the refresh rate and
increasetheball'sspeed.

Example 4: Timing Control


ClickHEREtodownloadthesourcecodes(UnzipthedownloadedJARfile).
Sofar,wehaveignoredthetimingcontrol.Inmanycomputergames,wedefineasocalledtimestep,whichisclosely
relatedtothescreenrefreshrate,tosynchronouslyupdateallthegameobjectsbeforerefreshthedisplay.
For our bouncing ball, it probably hits the box in between the timestep, and need to continuously move in the new
directionfortheremainingofthetimestepforaccuratetimingcontrol.Collisionsmayagainoccurintheremainingofthe
timestep.
Normalizeeachtimestepto1,weneedtocomputethefractionoftimes(t)takenuptothefirstcollision,andlettheball
continuesitsjourneyfortheremainingtimefraction1t.
Inacomplexsituation(e.g.,multiplemovingballs),itisimportanttodetectthe"earliest"collisionamongallthemoving
objects,andmovealltheobjectsuptothiscollisiontime,andrepeattheprocessuntilthetimestepisover.Takenote
thatonlythefirstcollisionmatters!

Ball.java:
......
publicclassBall{
......
//Forcollisiondetectionandresponse
//Maintaintheresponseoftheearliestcollisiondetected
//bythisballinstance.Onlythefirstcollisionmatters!
CollisionResponseearliestCollisionResponse=newCollisionResponse()
......
//Workingcopyforcomputingresponseinintersect(box,timeLimit),
//toavoidrepeatedlyallocatingobjects.
privateCollisionResponsetempResponse=newCollisionResponse()
/**
*Checkifthisballcollideswiththecontainerboxintheinterval
*(0,timeLimit].
*/
publicbooleanintersect(ContainerBoxbox,floattimeLimit){
//CallmovingPointIntersectsRectangleOuter,whichreturnsthe
//earliestcollisiontooneofthe4borders,ifcollisiondetected.

www.ntu.edu.sg/home/ehchua/programming/java/J8a_GameIntro-BouncingBalls.html

17/31

29/08/13

The world of Bouncing Balls - An introduction to Java Game Programming

CollisionPhysics.pointIntersectsRectangleOuter(x,y,speedX,speedY,radius,
box.minX,box.minY,box.maxX,box.maxY,timeLimit,tempResponse)
if(tempResponse.t<earliestCollisionResponse.t){
earliestCollisionResponse.copy(tempResponse)
}
}

publicvoidupdate(floattime){
//Checkifthisballisresponsibleforthefirstcollision?
if(earliestCollisionResponse.t<=time){
//Thisballcollided,getthenewpositionandspeed
this.x=earliestCollisionResponse.getNewX(this.x,this.speedX)
this.y=earliestCollisionResponse.getNewY(this.y,this.speedY)
this.speedX=(float)earliestCollisionResponse.newSpeedX
this.speedY=(float)earliestCollisionResponse.newSpeedY
}else{
//Thisballdoesnotinvolveinacollision.Movestraight.
this.x+=this.speedX*time
this.y+=this.speedY*time
}
//Clearforthenextcollisiondetection
earliestCollisionResponse.reset()
}
}

BallWorld.java: Modify the gameUpdate() method in the class BallWorld to make use of the intersect() to
consume one full timestep of movement, even after possibly multiple collisions. Also modify the game loop to control
onetimestepprecisely.
......
publicclassBallWorldextendsJPanel{
privatestaticfinalfloatEPSILON_TIME=1e2f//Thresholdforzerotime
......

publicvoidgameStart(){
//Runthegamelogicinitsownthread.
ThreadgameThread=newThread(){
publicvoidrun(){
while(true){
longbeginTimeMillis,timeTakenMillis,timeLeftMillis
beginTimeMillis=System.currentTimeMillis()

//Executeonegamestep
gameUpdate()
//Refreshthedisplay
repaint()

//Providethenecessarydelaytomeetthetargetrate
timeTakenMillis=System.currentTimeMillis()beginTimeMillis
timeLeftMillis=1000L/UPDATE_RATEtimeTakenMillis
if(timeLeftMillis<5)timeLeftMillis=5//Setaminimum

//Delayandgiveotherthreadachance
try{
Thread.sleep(timeLeftMillis)
}catch(InterruptedExceptionex){}
}
}
}
gameThread.start()//InvokeGaemThread.run()
}

/**
*Onegametimestep.
*Updatethegameobjects,withpropercollisiondetectionandresponse.
*/
publicvoidgameUpdate(){
floattimeLeft=1.0f//Onetimesteptobeginwith

//Repeatuntiltheonetimestepisup
do{
//Needtofindtheearliestcollisiontimeamongallobjects

www.ntu.edu.sg/home/ehchua/programming/java/J8a_GameIntro-BouncingBalls.html

18/31

29/08/13

The world of Bouncing Balls - An introduction to Java Game Programming

floatearliestCollisionTime=timeLeft
//Specialcasehereasthereisonlyonemovingball.
ball.intersect(box,timeLeft)
if(ball.earliestCollisionResponse.t<earliestCollisionTime){
earliestCollisionTime=ball.earliestCollisionResponse.t
}
//UpdatealltheobjectsforearliestCollisionTime
ball.update(earliestCollisionTime)

//TestingOnlyShowcollisionposition
if(earliestCollisionTime>0.05){//Donotdisplaysmallchanges
repaint()
try{
Thread.sleep((long)(1000L/UPDATE_RATE*earliestCollisionTime))
}catch(InterruptedExceptionex){}
}

timeLeft=earliestCollisionTime//Subtractthetimeconsumedandrepeat
}while(timeLeft>EPSILON_TIME)//Ignoreremainingtimelessthanthreshold
}
}

Again, compare the output of example 2, 3, and 4. Closely observe the collisions by reducing the refresh rate and
increasetheball'sspeed.

Example 5: Control Panel


Letusaddacontrolpanel,witha"pause"checkbox,a"speed"slideranda"radius"slider.

Click the image to run the DEMO. Click HERE to download the source codes for this example (unzip the downloaded
JARfile).

Modify the BallWorld class to include a ControlPanel inner class, which maintains the control UI components
(checkbox,slider,button):
publicclassBallWorldextendsJPanel{
......
......

privateControlPanelcontrol//Thecontrolpanelofbuttonsandsliders.

/**ConstructortocreatetheUIcomponentsandinitthegameobjects.*/
publicBallWorld(){
.......

//Controlpanel
control=newControlPanel()

//Layoutthedrawingpanelandcontrolpanel
this.setLayout(newBorderLayout())
this.add(canvas,BorderLayout.CENTER)
this.add(control,BorderLayout.SOUTH)

//Starttheballbouncing
gameStart()

www.ntu.edu.sg/home/ehchua/programming/java/J8a_GameIntro-BouncingBalls.html

19/31

29/08/13

The world of Bouncing Balls - An introduction to Java Game Programming

......
......

/**Thecontrolpanel(innerclass).*/
classControlPanelextendsJPanel{

/**ConstructortoinitializeUIcomponentsofthecontrols*/
publicControlPanel(){
//Acheckboxtotogglepause/resumemovement
JCheckBoxpauseControl=newJCheckBox()
this.add(newJLabel("Pause"))
this.add(pauseControl)
pauseControl.addItemListener(newItemListener(){
@Override
publicvoiditemStateChanged(ItemEvente){
paused=!paused//Togglepause/resumeflag
}
})

//Asliderforadjustingthespeedoftheball
intminSpeed=2
intmaxSpeed=20
JSliderspeedControl=newJSlider(JSlider.HORIZONTAL,minSpeed,maxSpeed,
(int)ball.getSpeed())
this.add(newJLabel("Speed"))
this.add(speedControl)
speedControl.addChangeListener(newChangeListener(){
@Override
publicvoidstateChanged(ChangeEvente){
JSlidersource=(JSlider)e.getSource()
if(!source.getValueIsAdjusting()){
intnewSpeed=(int)source.getValue()
intcurrentSpeed=(int)ball.getSpeed()
ball.speedX*=(float)newSpeed/currentSpeed
ball.speedY*=(float)newSpeed/currentSpeed
}
}
})

//Asliderforadjustingtheradiusoftheball
intminRadius=10
intmaxRadius=((canvasHeight>canvasWidth)?canvasWidth:canvasHeight)/28
radiusControl=newJSlider(JSlider.HORIZONTAL,minRadius,
maxRadius,(int)ball.radius)
this.add(newJLabel("BallRadius"))
this.add(radiusControl)
radiusControl.addChangeListener(newChangeListener(){
@Override
publicvoidstateChanged(ChangeEvente){
JSlidersource=(JSlider)e.getSource()
if(!source.getValueIsAdjusting()){
floatnewRadius=source.getValue()
ball.radius=newRadius
//Repositiontheballsuchasitisinsidethebox
if(ball.xball.radius<box.minX){
ball.x=ball.radius+1
}elseif(ball.x+ball.radius>box.maxX){
ball.x=box.maxXball.radius1
}
if(ball.yball.radius<box.minY){
ball.y=ball.radius+1
}elseif(ball.y+ball.radius>box.maxY){
ball.y=box.maxYball.radius1
}
}
}
})
}
}
}

www.ntu.edu.sg/home/ehchua/programming/java/J8a_GameIntro-BouncingBalls.html

20/31

29/08/13

The world of Bouncing Balls - An introduction to Java Game Programming

ModifythegameThreadofBallWorldclasstoprogramtosupportpause/resumeoperation:
publicclassBallWorld{
......
......
publicvoidgameStart(){
ThreadgameThread=newThread(){
publicvoidrun(){
while(true){
longbeginTimeMillis,timeTakenMillis,timeLeftMillis
beginTimeMillis=System.currentTimeMillis()

if(!paused){
//Executeonegamestep
gameUpdate()
//Refreshthedisplay
repaint()
}
......
......
}

Example 6: Many Balls of Different Sizes


Letusnowconsiderthecaseofmanyballsofdifferentsizes.ClicktheimagetoruntheDEMO.ClickHEREtodownload
thesourcecodesforthisexample(unzipthedownloadedJARfile)andCollisonPhysiscspackage.

Collision Detection
Collisionoccursifthedistancebetweenthetwoballsisequaltothesumoftheirradii.

www.ntu.edu.sg/home/ehchua/programming/java/J8a_GameIntro-BouncingBalls.html

21/31

29/08/13

The world of Bouncing Balls - An introduction to Java Game Programming

Collision Response

www.ntu.edu.sg/home/ehchua/programming/java/J8a_GameIntro-BouncingBalls.html

22/31

29/08/13

The world of Bouncing Balls - An introduction to Java Game Programming

Wefirstdissolvethevelocities(V1andV2)alongtheaxesofcollision,pandq(asillustrated).Wethenapplythelawsof
conservation of momentum and energy to compute the velocities after collision, along the axis of collision p. The
velocitiesperpendiculartotheaxisofcollisionqremainsunchanged.

Laws of Conservation of Momentum & Energy


Tocomputethetwovelocitiesaftercollision(v3andv4),twoformulasarerequired.Thetwoformulascanbefoundfrom
thelawofconservationofmomentum(alongthecollisionaxis),andthelawofconservationofkineticenergy(inscalar
quantities).Wemakethefollowingassumptions:
Energylosses(duetofriction)areignored.
Theballsarerigidbody.
Theballshavethesamedensity.Hence,theirmassareproportionaltotheirvolume(=4/3r^3).

www.ntu.edu.sg/home/ehchua/programming/java/J8a_GameIntro-BouncingBalls.html

23/31

29/08/13

The world of Bouncing Balls - An introduction to Java Game Programming

Collision

Detection and Response for Two Moving Balls: In CollisionPhysics, method


movingPointIntersectsMovingPoint()
and
its
helper
methods
movingPointIntersectsMovingPointDetection()andmovingPointIntersectsMovingPointResponse().
Ball.java:Amethodcalledintersect(Ballanother,floattimeLimit)isaddedtodetectcollisionbetweentwo
balls(thisandthegivenanother),andcomputetheproperresponseifcollisionisdetected.
publicclassBall{
......
......
//Workingcopyforcomputingresponseinintersect(Ball,timeLimit),
//toavoidrepeatedlyallocatingobjects.
privateCollisionResponsethisResponse=newCollisionResponse()
privateCollisionResponseanotherResponse=newCollisionResponse()

/**
*Checkifthisballcollideswiththegivenanotherballintheinterval
*(0,timeLimit].
*/
publicvoidintersect(Ballanother,floattimeLimit){
//CallmovingPointIntersectsMovingPoint()withtimeLimit.
//UsethisResponseandanotherResponse,astheworkingcopies,tostorethe
//responsesofthisballandanotherball,respectively.

www.ntu.edu.sg/home/ehchua/programming/java/J8a_GameIntro-BouncingBalls.html

24/31

29/08/13

The world of Bouncing Balls - An introduction to Java Game Programming

//Checkifthiscollisionistheearliestcollision,andupdatetheball's
//earliestCollisionResponseaccordingly.
CollisionPhysics.pointIntersectsMovingPoint(
this.x,this.y,this.speedX,this.speedY,this.radius,
another.x,another.y,another.speedX,another.speedY,another.radius,
timeLimit,thisResponse,anotherResponse)

if(anotherResponse.t<another.earliestCollisionResponse.t){
another.earliestCollisionResponse.copy(anotherResponse)
}
if(thisResponse.t<this.earliestCollisionResponse.t){
this.earliestCollisionResponse.copy(thisResponse)
}
}
}

BallWorld.java: The gameUpdate() method is modified to detect collision between any pair of balls and collision
betweentheballthethecontainerbox.Thecontrolsarealsomodified.Anewbuttonisaddedtolaunchnewballs.
publicclassBallWorldextendsJPanel{
......
......
//Balls
privatestaticfinalintMAX_BALLS=25//Maxnumberallowed
privateintcurrentNumBalls//Numbercurrentlyactive
privateBall[]balls=newBall[MAX_BALLS]

/**ConstructortocreatetheUIcomponentsandinitthegameobjects.*/
publicBallWorld(){
......
......
currentNumBalls=11
balls[0]=newBall(100,410,25,3,34,Color.YELLOW)
balls[1]=newBall(80,350,25,2,114,Color.YELLOW)
balls[2]=newBall(530,400,30,3,14,Color.GREEN)
balls[3]=newBall(400,400,30,3,14,Color.GREEN)
balls[4]=newBall(400,50,35,1,47,Color.PINK)
balls[5]=newBall(480,320,35,4,47,Color.PINK)
balls[6]=newBall(80,150,40,1,114,Color.ORANGE)
balls[7]=newBall(100,240,40,2,60,Color.ORANGE)
balls[8]=newBall(250,400,50,3,42,Color.BLUE)
balls[9]=newBall(200,80,70,6,84,Color.CYAN)
balls[10]=newBall(500,170,90,6,42,Color.MAGENTA)

//Therestoftheballs,thatcanbelaunchedusingthelaunchbutton
for(inti=currentNumBallsi<MAX_BALLS++i){
balls[i]=newBall(20,CANVAS_HEIGHT20,15,5,45,Color.RED)
}
......
}

/**Updatethegameobjects,detectcollisionandprovideresponse.*/
publicvoidgameUpdate(){
floattimeLeft=1.0f//Onetimesteptobeginwith

//Repeatuntiltheonetimestepisup
do{
//FindtheearliestcollisionuptotimeLeftamongallobjects
floattMin=timeLeft

//Checkcollisionbetweentwoballs
for(inti=0i<currentNumBalls++i){
for(intj=0j<currentNumBalls++j){
if(i<j){
balls[i].intersect(balls[j],tMin)
if(balls[i].earliestCollisionResponse.t<tMin){
tMin=balls[i].earliestCollisionResponse.t
}
}
}
}
//Checkcollisionbetweentheballsandthebox

www.ntu.edu.sg/home/ehchua/programming/java/J8a_GameIntro-BouncingBalls.html

25/31

29/08/13

The world of Bouncing Balls - An introduction to Java Game Programming

for(inti=0i<currentNumBalls++i){
balls[i].intersect(box,tMin)
if(balls[i].earliestCollisionResponse.t<tMin){
tMin=balls[i].earliestCollisionResponse.t
}
}

//UpdatealltheballsuptothedetectedearliestcollisiontimetMin,
//ortimeLeftifthereisnocollision.
for(inti=0i<currentNumBalls++i){
balls[i].update(tMin)
}

timeLeft=tMin//Subtractthetimeconsumedandrepeat
}while(timeLeft>EPSILON_TIME)//Ignoreremainingtimelessthanthreshold
}
......
......
/**Thecontrolpanel(innerclass).*/
classControlPanelextendsJPanel{

/**ConstructortoinitializeUIcomponents*/
publicControlPanel(){
//Acheckboxtotogglepause/resumealltheballs'movement
JCheckBoxpauseControl=newJCheckBox()
this.add(newJLabel("Pause"))
this.add(pauseControl)
pauseControl.addItemListener(newItemListener(){
@Override
publicvoiditemStateChanged(ItemEvente){
paused=!paused//Togglepause/resumeflag
}
})

//Asliderforadjustingthespeedofalltheballsbyafactor
finalfloat[]ballSavedSpeedXs=newfloat[MAX_BALLS]
finalfloat[]ballSavedSpeedYs=newfloat[MAX_BALLS]
for(inti=0i<currentNumBalls++i){
ballSavedSpeedXs[i]=balls[i].speedX
ballSavedSpeedYs[i]=balls[i].speedY
}
intminFactor=5//percent
intmaxFactor=200//percent
JSliderspeedControl=newJSlider(JSlider.HORIZONTAL,minFactor,maxFactor,100)
this.add(newJLabel("Speed"))
this.add(speedControl)
speedControl.addChangeListener(newChangeListener(){
@Override
publicvoidstateChanged(ChangeEvente){
JSlidersource=(JSlider)e.getSource()
if(!source.getValueIsAdjusting()){
intpercentage=(int)source.getValue()
for(inti=0i<currentNumBalls++i){
balls[i].speedX=ballSavedSpeedXs[i]*percentage/100.0f
balls[i].speedY=ballSavedSpeedYs[i]*percentage/100.0f
}
}
}
})

//Abuttonforlaunchingtheremainingballs
finalJButtonlaunchControl=newJButton("LaunchNewBall")
this.add(launchControl)
launchControl.addActionListener(newActionListener(){
@Override
publicvoidactionPerformed(ActionEvente){
if(currentNumBalls<MAX_BALLS){
++currentNumBalls
if(currentNumBalls==MAX_BALLS){
//Disablethebutton,asthereisnomoreball
launchControl.setEnabled(false)
}

www.ntu.edu.sg/home/ehchua/programming/java/J8a_GameIntro-BouncingBalls.html

26/31

29/08/13

The world of Bouncing Balls - An introduction to Java Game Programming

}
}
})
}
}
}

Example 6a: Circular Container


Click the image to run the DEMO. Click HERE to download the source codes for this example (unzip the downloaded
JARfile)andCollisonPhysiscspackage.

Collision Detection and Response

www.ntu.edu.sg/home/ehchua/programming/java/J8a_GameIntro-BouncingBalls.html

27/31

29/08/13

The world of Bouncing Balls - An introduction to Java Game Programming

Example 7: More Shapes and Obstacles


Click the image to run the DEMO. Click HERE to download the source codes for this example (unzip the downloaded
JARfile)andCollisonPhysiscspackage.

Collision - A Moving Ball hitting a Line Segment

www.ntu.edu.sg/home/ehchua/programming/java/J8a_GameIntro-BouncingBalls.html

28/31

29/08/13

The world of Bouncing Balls - An introduction to Java Game Programming

Collision - A Moving Ball hitting an End-Point of a Line Segment

www.ntu.edu.sg/home/ehchua/programming/java/J8a_GameIntro-BouncingBalls.html

29/31

29/08/13

The world of Bouncing Balls - An introduction to Java Game Programming

[PENDING]SomeExplanation?

A Pong Game

[PENDING]

Brick, Billiards, Pinball


[PENDING]

REFERENCES & RESOURCES

www.ntu.edu.sg/home/ehchua/programming/java/J8a_GameIntro-BouncingBalls.html

30/31

29/08/13

The world of Bouncing Balls - An introduction to Java Game Programming

[PENDING]

Latestversiontested:JDK1.6
Lastmodified:October,2010
Feedback,comments,corrections,anderratacanbesenttoChuaHockChuan(ehchua@ntu.edu.sg)|HOME

www.ntu.edu.sg/home/ehchua/programming/java/J8a_GameIntro-BouncingBalls.html

31/31

You might also like