|
Comp201: Principles of Object-Oriented Programming I
|
(A summary of the students' final projects can be found here).
For our final project, we will return to BallWorld, but this time we will put it on steroids (legally, of course!).
The final project will be to take the upgraded BallWorld framework (rename "BallWar") and to build a game of your own design from it.
You are allowed to work in teams of at most two people for this project. Upload a copy of the finished product to EACH PARTNER's submission.
(Note that there is a lab that involves this project)
Check out the demo! (may take a little while to load, includes a short "users manual")
Download the code for BallWar (collision detection deactivated--see accompanying lab)
Strong recommendation: Keep It SIMPLE!! You can always add features later if you've built it right in the first place. You are not being graded on the elaborateness of your game.
Your game description information will be posted to the Comp201 web site under the Final Project Student Project Descriptions
Point breakdown and percentages subject to change.
When you have completed your final project, zip up the entire directory, including all non-JVM packages needed to run your system and all documentation and upload it from the usual upload site.
Be sure that both you and your partner's names are in the documentation!!
Here is some background information on the architecture of the supplied code, which you are free to change, subject to the above guidelines.
The BallWar system uses paint strategies to paint the desired shapes and images corresponding to a Ball on the screen. This is very similar to the way that update strategies are used for the Ball's behaviors. Please carefully read the detailed web page on painting strategies.
Important note regarding image files: In the demo, the image files are in the directory ballwar\model\paint\images in the src directory. This directory must be manually added to the classes directory structure--DrJava will not automatically copy it from the src directory to the classes directory. Also, when creating a jar file using DrJava., this directory will not be put into the jar file. You will need to use a zip tool such as WinZip or WinRAR to open the jar file up and insert the images directory in the correct location. If you configured your system to read files solely from the file system on the disk, then the images will need to be stored outside of the jar file. See the internal code documentation in ImagePaintStrategy.java for more information.
The BallWar system is what is referred to as a "component-framework" system. A framework is a the invariant portion of a system that provides services for and management of the variant portions of the system, the components. We can think of components as "plugging into" the framework. Examples of component-framework systems are Netscape (the framework) and it plugins (e.g. RealPlayer, the components), an XBox console (the framework) and its game cartridges (components), a computer motherboard (the framework) and its memory chips (components), and the electric power delivery system in a house (the framework) and all the appliances plugged into it (the components).
Component-framework systems are important because they enable a complex system to be stable, but yet to grow and be modified, even at run-time. The invariant framework only knows about the abstract component, and thus can handle any concrete component that is installed at any time. Components know that they can rely on an invariant framework to provide management, communications and other services and are thus freed from those tasks. Components remain decoupled from each other by the framework, reducing the internal complexity of the system making it more understandable, more extensible, more flexible and more robust.
But "variant" and "invariant" are relative terms. What is variant at on level of abstraction may be invariant when viewed from another, name from a level of lower abstraction. BallWar thus has several component-framework systems imbedded within it, some nested within each other:
See if you can find more examples of component-framework systems buried in the BallWar system!
The BallWar system uses a "command dispatching" system for communicating with all the balls that are in the system. This system is an extension of the Observer-Observable design pattern. The Observer-Observable pattern is used when GUI components (Observerables) have listeners (Observers) added to them to respond to mouse clicks, key strokes, etc.
The BallModel has a Dispatcher object (the "Observable")to which all the balls ("Observers") have been added, as they all implement the necessary Observer interface. When the Dispatcher's notifyAll method is called, each of the ball's update methods are called. The balls are called one at a time and in no guaranteed sequence.
The Dispatcher's notifyAll method allows a parameter to be passed to each ball's update method. In order to make this simple pattern allow us to do anything we wish with the balls, we pass an abstract IBallCmd object (Command design pattern) to the ball. All the ball's update method does is to call the command's apply method, handing itself as the context. The command object's apply method now has the ability to call whatever methods it desires on the ball and accomplish whatever task it so desires.
The two IBallCmd commands that are currently used by the system are for updating the state of each ball and for detecting collisions between any given ball and all the other balls.
Collisions are handled in a straightforward, brute-force and not particularly efficient manner in the current BallWar system. Essentially, when a ball moves, it may collide with another ball. While the ball may be painted using any arbitrary shape or image, the collision system simply approximates the ball to be...a ball. That is, collision detection is calculated as if the ball were painted as a circle of the ball's radius, even though it's actual painted representation may be quire different. More sophisticated collision detection is possible but beyond the scope of this class.
A collision is defined as the centers of two balls being less than the sum of their radii. Note that since the system involves discrete-sized movements, a ball may move will inside of the collision radius before being detected. Since the dispatcher processes only one ball at a time, we are assured that only one ball has moved and thus possibly collided with another. It is thus important to maintain a set protocol as to which ball is the one that was moved at that moment.
The dispatcher can be used to send a IBallCmd command out to each ball that can be used to check if a given ball (the one that is in the middle of updating its state) has collided with another ball. Ball provides three methods for this purpose:
From a design standpoint, notice how each method only takes care of very specific
tasks and delegates to other methods to perform tasks that are beyond its scope.
For instance, checkCollide only
does the checking and collidedWith
only deals with modifying the original, "context" ball while collidedWithHelp
only deals with modifying the target ball. This "separation of responsibility"
is crucial in creating complex systems that are understandable and maintainable.
The BallWar system uses a Model-View-Controller design pattern ("MVC") to separate the user interface (the "view") from the underlying processing (the "model"). This is because,the view the is a variant with respect to the model while the model is a variant with respect to the view. That is, for a given way of processing something, there are many different ways of presenting its input and output to the user. Likewise, for any given way of presenting input and output of a computation to the user, there are many ways in which that computation can be implemented.
In general, we replace concrete variants with abstract invariants, so we do that with both sides, on the model side looking towards the view and from the view side looking towards the model. If you look at the UML diagram labeled "Model-View-Controller" in the documentation, you will see that the model and the view do not communicate directly, but rather through "adapters". Each adapter, supports an interface that is invariant with respect to whomever is using it. Thus when the model needs to communicate to the view, it only talks to what it thinks is an invariant interface, IViewCtrlAdapter. Likewise, when the view needs to communicate to the model, it talks only to invariant interfaces, here separated into 3 interfaces grouped according to functionality: IGameCtrlAdapter, IBallCreateAdapter,and IBallCtrlAdapter. The adapters do no significant processing themselves and serve chiefly to translate the call being made from the model (view) to the methods available on the view (model). Thus the model and the view are decoupled from each other.
The role of the "controller" (BallControl) is to be the one and only one object in the system that knows about both the concrete model and the concrete view. The controller will instantiate both the model and the view, instantiate all the necessary adapters and connect the model and the view using those adapters. Since the controller knows both the model and the view, it can translate the calls to the adapters to the calls to the respective model or view. Do not think of the controller as a "mediator" but rather as a "setter upper". Once the controller has instantiated and connected the model and the view, its job is done. One thing to note that the controller does take advantage however is that since it is using anonymous inner classes to create the adapters, it can utilize the closure of those anonymous inner classes to enable types of communication that would otherwise be very difficult.
In an MVC system, if one changes the model or the view, one only need to change the controller and not all the rest of the system. This greatly enhances maintainability of the system.
One quandary that was encountered while designing the BallWar system (actually, this was in the original BallWorld system as well) was that the model expects to be given IUpdateStrategies with which to construct new Balls. On the other hand, the BallGUI has no and should not have any knowledge of IUpdateStrategies. The naive solution is to simply pass a String with the strategy's class name from the view to the model and let the model dynamically load the strategy from that name. But the system inherently supports dynamically composed strategies (e.g. using MultiStrategy) and since the model treats these composed strategies identically to uncomposed ones, there should not be any special methods on the model to deal with such special cases. But the view doesn't know about composed strategies any more than it knows about any other strategy. So we have a dilemma: the view needs functionality that the model doesn't support.
Here is where the controller, BallControl, with its intimate knowledge of both the model and the view steps in. The controller defines a private factory for instantiating strategies, IStrategyFac. This factory takes a string name and instantiates the corresponding strategy when its make method is called. Let's see how this simple little interface with very small amounts of code neatly solves our problem. (If this doesn't hook you on factories, nothing will!)
Please reference the code for BallControl and BallGUI while reading the following sections.
So what about composed strategies? No problem:
Feel the power of OOP. This is why we do it.
Players add unique challenges to the BallWar system because they exert global influence across the entire system. Players have a score associated with them that may be updated due to interactions between specific balls which, to the system, are equivalent to any other ball. Likewise, a player is associated with a particular set of keys used to control movements, etc of the balls and those keys may only affect particular balls in the system.
The pieces used to implement players are as follows:
To create a player:
To create a "moveable" ball:
While slick, this mechanism could use improvements. For instance, at the moment a player can only control one ball at a time and only a freshly made ball at that. Get your thining caps on!
The Java Development Kit (JDK) comes with a tool called JavaDoc. This tool will generate documentation for Java source code with comments written in accordance with the Java documentation style. The following links show more examples. You do need to spend time outside of the lab to study them.
Use DrJava to look at both the code and the comments in the BallWar system, which follow the Javadoc conventions. The following is a very short summary of the Javadoc conventions.
Creating javadoc using DrJava:
In DrJava,
Now change the javadoc Access level in the javadoc Preferences to private and generate javadoc again. What is the difference?
Last Revised Thursday, 03-Jun-2010 09:50:16 CDT
©2007 Stephen Wong and Dung Nguyen