CSC123 C# Inheritance Assignment 1 CSC123 Halloween Assignment: Professor and Goblins Due Midnight 10/31 I'm assuming in the following that you were present for my demonstrations: For this assignment you are to write a small video game using animated gifs. It's adopted from a Java assignment I used to give to my CSC15 students. The difference is that you will use C#, inheritance and the Observer Pattern. I'll be giving you quite a bit of a head start, and will guide you along the way. But you need to pay careful attention. First, download the files goblins.cs, observer.cs and boxworld.dll from the homepage. Also download the animated gif files (right click on images above) that you wish to use, and (optionally) the .wav sound file. Boxworld is a set of modular, simple 3d-drawing routines I created. The source code for boxworld is in boxworld.cs. You're encouraged to look at it, but if you want to modify and recompile it, you'll need additional microsoft dlls from msdn.microsoft.com. The file observer.cs contains the abstract interfaces for my version of the observer pattern, which your program must implement. goblins.cs is the skeleton program that you will extend. Compile the main program with csc observer.cs goblins.cs /r:boxworld.dll. The program you downloaded runs by itself - you'll see a little human figure ("the professor") in the lower right corner, which you can control using the arrow keys and the space bar. The figure wraps around the edges of the grid. There's also a hovering diamond, and the program ends when the professor gets the diamond (not very interesting, but that's because there are no goblins yet!) The skeleton program contains three classes: "goblins" is the main application class and includes the method "mainloop", which you will modify. Despite appearances, this is actually a tail-recursive function: the call to Invalidate() at the end invokes a windows "event" that triggers the "OnPaint" method, which will then call mainloop() again. Set the global (public static) "stop" variable to true to exit the loop. The "human" class is for the "professor" object and the diamond class is for the treasure. You need to create (at least) two additional classes for two types of goblins, each of which behaves differently, and each class can have several instances. In my demonstration program, one goblin chases the professor and the other tries to intercept the professor in a certain number of steps. It's pretty obvious that the code can benefit from inheritance since the human and diamond classes share much identical code. So your first task is to implement the following inheritance hierarchy:
class gameobject
{
// contains code common to all game "players", including "virtual" default code
// that can be overridden.
}
There should be 4 subclasses of gamobject: human, diamond, goblin1, goblin2. Most of the code can be directly inherited. But each will probably have a "override" move method. Other methods such as "setdirection" should also be overridden depending on the intended behavior of the objects. You should also make the "overlap" function more generic so it can detect collision between any gameobjects, not just human and diamond. Each subclass must have something that distinguishes it from other classes. The goal of this assignment is good OOP design, not just getting something to run. Declare your game object variables inside the main goblins class (where the professor is declared - careful, mainloop's local vars will be recreated on each call!). Create instances of the three classes in the createplayers() method of the goblins public class, and animate them by calling their move() methods from the mainloop() method. The graphical image of the professor is an animated gif. However, internally the object is represented as a circle, and the key attributes of the object are the x,y coordinates of the center of the circle, and the radius. The properties dx and dy is the movement vector of the object. That is, they control how much the x and y coordinates will change each time the move() function is called. The x, y and radius values are set by the constructor while (dx,dy) is set by the "setdirection" method. You'll have to study the program I gave you to understand it fully, though you don't have to worry about how the double-buffered animation and the event handler work. Neither should you worry about the "boxworld" simple 3d drawing routines that I'm using - that's completely self contained. That is, in your program, your objects will have only x and y coordinates - the graphical "rendering" is hidden by the boxworld routines. Here's a summary of the methods and properties available on the human and diamond classes: properties X,Y,R for xcord, ycord, and radius properties DX,DY for movement vector void setdirection(newdx, newdy) for changing movement vector. move() // moves the object according to movement vector and desired behavior of object dist and overlap (currently only in human class): detects if one object has collided with another draw() // draws object, handles animation (don't have to change) In addition, you should be aware of the class variables XBOUND and YBOUND, which the maximum values of xcord and ycord. To stop the game, set the static variable goblins.stop to true. The game ends when the professor gets the treasure or when one of the monsters catches him (that's the purpose of the overlap method). Alternatively, you can make the professor protect the diamond. You can also change the animated gifs after the game ends - so you might need to create some additional state variables in your classes. The most important part of this assignment is how to make the objects interact with eachother. But first ... "This program needs some MATH!" Your goblin classes should each implement a different strategy to catch the professor. To make the goblin chase the professor, adjust the DX and DY values of the goblin to move toward the professor's position: that is, if professor.Y is less than you.Y, then you're above the professor and should give a negative value to you.DY. To make the goblin intercept the professor at a point in the future, assume the goblin is at (x0,y0) and the professor is at (x1,y1) and moving in direction (dx1,dy1). Then to intercept the professor in t number of moves, use the following movement vector: dx = ((x1-x0)/t) + dx1, dy = ((y1-y0)/t) + dy1 If you set t=1, then you'll catch the professor in the very next move, which is hardly fair. So you need to use these formulas judiciously to make a nice simulation (try t = distance to professor / goblins.STEP). The Observer Pattern The observer pattern is an abstract template for objects to communicate with eachother. Let's say we want our goblin to chase the professor. The goblin needs to change its movement vector each time the professor moves, so when the professor.move() method is called, it should "notify" the goblin, which then "updates" its movement vector to match. If our goblin is smarter and tries to intercept the professor at a future, ploted point, then it should only update its movement vector when the professor changes direction. That is the professor should notify the goblin whenever its setdirection method is called. What's also interesting is that the observer pattern imposes a different inheritance structure from the code-sharing inheritance structure described above. Specifically, the "observers" here are your two (or more) goblins, and the "observee" (or subject), is the human-professor object. Your program must implement the observer pattern interfaces (found in observer.cs):
public interface observee // subject
{
void attach(observer obv); // add observer to internal list (ArrayList?)
void detach(observer obv); // remove observer
void notify(); // notify all observers (calls observer.update)
void notify(observer obv); // my addition - notify a single observer
}
public interface observer
{
object update(observee obe); // observee should pass "this" to observer
}
The final inheritance structure of your program will be:
class human : gameobject, observee
class goblin1 : gameobject, observer
class smartgoblin : gameobject, observer
class diamond : gameobject
The human object should call notify at appropriate points (like after move or setdirection). The notify method should invoke the update method of the observer(s), which behaves differently depending on the intended behavior of the objects. One additional point you need to becareful about: The parameter type for update is "observee", not human or goblin, so you'll need to type cast it before using them as such. Also: you should not call the observer pattern methods from mainloop(), they should be called from within the object's methods (like at the end of setdirection()). Let objects determine their own behavior. The observer pattern, like all patterns, offers a more configurable program. It'll be easier, for example, to add a new class of goblins compared to a solution not using a good OOP design. An important principle of OOP is to "let objects decide for themselves." One should not have to manipulate the behavior of objects externally, except by invoking abstract methods such as "move". As extra credit, you can attempt to make the program even more object-oriented. Can you change the program so that nomatter how many players there are and how they should behave, the mainloop method in the goblins class won't have to be changed at all? (hint: define an ArrayList in class goblins and make the goblins class a kind of observer). You're welcome to experiment with the program. You can use different gifs and sounds. To play sound, follow the example of the hahasnd sound (find all lines that pertains to it). Have fun but keep in mind that the main issue here is oop design. No more than two people can collaborate on this assignment To compile the goblins program, make sure all sources and media files are in the same directory, and do
csc observer.cs goblins.cs /r:boxworld.dll
The will produce goblins.exe. Finally, this program requires a reasonably powerful computer (celerons and atoms may choke on the double-precision math). It also requires the Microsoft directX runtime installed if you're going to play sound.