PAC-CAT!

For this project we are going to do a Pac-Man knockoff. Pac-Man is a classic video game first released about thirty-five years ago.

The wikipedia article on PAC-MAN You can play at least one version of Pac-Man on the web: Google Pac-man knockoff

For this project I fantasized that we would replace Pac-Man's circular head with a cartoon of a cat head, the candies with mice, and the ghosts with dogs.

Of course, you don't have to use my pictures, or even my fantasies. You're free to use the basic Pac-man motif however you want. But the point is only half-way to come up with a great game to play -- there are at least a dozen versions of Pac-Man on the Android Play site. The real point is to learn how to build one.

Animation

We sort of know how to do animation; we did a ticking stopwatch in class. I put a version up on https://github.com/StephenETaylor/Ticker, I think after I finished debugging it... Anyhow, the animation for that app consists of two parts. Part one is a thread with the following run() method:
public void run(){ while (true) { try { sleep(1000); }catch(Exception e){e.printStackTrace();} if (b.getText().equals(stopText)) { context.runOnUiThread(new Runnable() { @Override public void run() { faceView.invalidate(); } }); } if (stopThread) { context.timePasser = null; return; } }// ends while true } // end of run()
Part two is the FaceView class, which features the following onDraw() method.
public void onDraw (Canvas canvas){ int w = getWidth(); int h = getHeight(); canvas.drawColor(Color.CYAN); double radius = Math.min(w,h)/3.0; long now = System.currentTimeMillis(); time.set(now-startTime); double angle = -Math.PI / 2 + ((time.second * Math.PI * 2) / 60); float dx = (float)(radius* 0.9 * Math.cos(angle)); float dy = (float)(radius* 0.9 * Math.sin(angle)); float cx = w/2; float cy = h/2; paint.setStyle(Paint.Style.STROKE); canvas.drawCircle(cx,cy,(float)radius, paint); canvas.drawLine(cx,cy , cx+dx, cy+dy, paint); }
Of course we can extend this to animate pretty much anything we want. But there is a catch, as I discovered when I wrote my first pass at PAC-CAT.

Performance

It takes 60 ms to naively draw all those lines and mouse bitmaps on my screen. If I want 16 frames a second, I don't have time for anything else on the phone, and the game feels sluggish. How do I know it takes 60ms? I used Traceview. Traceview works well in ADT/Eclipse, but has been replaced in Android Studio by an ugly, inferior product. Anyway...

How to speed things up?

Two obvious ways:
  1. fewer refreshes will leave the CPU available to pay attention to the buttons. (Of course, that will make the motion choppier.)
  2. Redo the animation so that only the parts that change need to be drawn on each iteration.

And a couple more things...

Any time you have an animation going on, it takes a lot of CPU time. So it doesn't make sense to do the work if the application isn't visible. But since there are lots of applications which can do useful work even if they aren't visible, the Android system doesn't stop execution of the app just because the screen is invisible. Instead, you have to do it yourself. A good time to stop refreshing the screen is in onPause() which is called when the screen is about to become invisible. But if you do this, you want to make sure that the animation starts up again when the screen becomes visible again. That would be in onResume(). You could ask the timing thread to commit suicide in onPause(), in which case you need to start a new thread in onResume(). Or you could make sure that the thread is inactive while the screen is invisible by having it wait() instead of committing suicide, and then doing a notify() in onResume(). That last is a little tricky, because the first time through, you want to make sure that the thread has time to wait() before onResume() calls notify(); otherwise the notify will fall on the floor. Using a semaphore works better than wait()/notify() because the semaphore has a count, which can be pre-bumped.

What should happen if you get a phone call while playing?

I can imagine a game in which the play continued while you were not paying attention, so that you could come back from a phone call and discover that the cat had eaten all the mice while you weren't looking. There was an early computer game called Empire which worked like that. You would set up your forts and array your armies, and the game actions took place in something like real time, so if you were marching your army twenty miles, and you checked back in half an hour, they still had 19 miles to go. But for PAC-CAT, I think that would be painfully frustrating. You get a phone call, and you're just about guaranteed that the cat will get stuck in some corner, and if the call goes on long enough the dogs will catch it. And also, not stopping the play would make my onPause()/onResume comments rather pointless. So the game should pick up where it left off, and because by the time you finish the phone call, maybe add the caller to your contacts, make an entry in your calendar, etc. the phone may have run out of memory and stopped your process, you need to save and restore the game state. You save the game state in onSaveInstanceState(Bundle). You have two chances to restore the game state, either in onCreate(Bundle) or in onRestoreInstanceState(Bundle) My game state consists of quite a lot of information: the maze dimensions, where the walls are in the maze, which mice have already been eaten, where the cat and dogs currently are. The Bundle lets me store my datastructures, with (for example) bundle.putByteArray(maze) but it doesn't have a bundle.putCat(cat) method to store my Cat objects; I have to tear my classes down and rebuild them from elementary data.

Some starter code

I'm going to give you my first, broken version of Pac-Cat. You don't have to use it, but it could be a place to start. Here's what it does: Here's what it doesn't do:

My source files


BoardView.java
Cat.java
Drifter.java
PacCat.java
activity_pac_cat.xml

You will turn in:

  1. A beautiful journal, describing which features you decided to fix/include, and how you did it. I pay a lot of attention to the journal, so you should, too.
  2. A copy of your code. You should put comments at the beginning of classes and methods that you wrote, and you should comment things that you changed or fixed. Put your name on each of these comments, so that I can find them easily.