Design 20 points; code 40 points.
In this assignment you will begin to design and implement a class for your ``player'' -- the little character that goes running around on your screen. (If your game doesn't have something fitting that description then you should come to talk to me about what you need to do for this assignment.) For this assignment the player needs to do two things:
The design for this assignment will include a description of the player class you will write and its methods. The comments at the top of the class should describe (1) how your player responds to mouse or keyboard input (e.g., ``my player moves up, down, and sideways when the user presses the arrow keys'') and (2) how your player interacts with your block classes.
To fit into the game framework properly, the class needs to implement two interfaces: the ``general entity type'' (YourEntity) you wrote for the previous assignment, and also the framework's Player interface (parameterized with your block and entity types -- i.e., this class should implement Player<YourBlock,YourEntity>).
As before, for the design step you can just provide skeleton or stub code for the methods. You should also look again at your descriptions of classes you wrote for previous homeworks and see if they need to be improved or updated. Remember that I want at least a short comment about every class and every method. Writing these comments before writing the code encourages you to ``think first, then code''. They also help human readers of your code understand how everything fits together.
As with the previous homework, for this assignment you will be writing and editing a fair bit of code, but most if not all of it will be in classes you have already written. The class for your player could easily be the largest class in your game. However, you don't have to write all of it now; you just need to make a start on implementing the functionality you want. Over time you will probably also write other private and public methods for your player that help it get things done and allow it to interact with other parts of your game. See the next section (``Classes for this assignment'') for details; this section just summarizes the overall procedure.
Most if not all of the coding you do for this assignment will consist of filling in methods in your player class. As with the screen class in Homework 2, the methods fall into several groups, and you may find it easiest to implement them group by group in the following order. I suggest doing this in two phases -- one in which you do the minimum needed for the game to run, and one in which you make the player respond to keyboard and/or mouse input.
editPropertiesPanel(): As with Homework 2, just return null from this method; you will almost certainly not use this method for this class.
getGameStatusPanel(): As mentioned in class, eventually you will have the option of adding panels to any or all of the four edges of your playing field. This method is how you do that. For now just return null.
getImage(), partialSizeX(), partialSizeY(): These methods control the image drawn for your player. getImage() should do something similar to what getImage() in your block classes does. The other two methods control the player's size. To make it occupy one block, you can return Location.getPartialsInWhole(). (More about ``partials'' later.)
followLinks(): The game framework allows you to define ``teleport links'' between blocks. This method should return true if you think you might want to do that, and you want the player to be teleported, otherwise false.
getLocation(), setLocation(), and constructor: The first two methods get and set the player's location. The starter code also includes a constructor that takes the player's initial location as a parameter. You probably should keep this method, with suitable changes. (So, yes, you almost surely want an instance variable to hold the location.) Locations are represented using the game framework Location class and include a reference to a Screen and coordinates of a block within that screen.
The value passed to setPartialsInWhole (in BasicGameSetup or elsewhere) determines whether entities move around in whole-block units or in smaller steps. A value of 1 makes things such as collision detection easier, but a larger value gives you smoother motion.
Notice that Location, like many of the game-framework classes, is a generic class that takes two type parameters (so locations normally have type Location<YourBlock,YourEntity > rather than just Location).
gameStatus(): This method tells the framework whether the game is still running or has ended (in success or failure). You should return one of the constants defined in Player.GameStatus.
getUpdateTime() and update() methods: The game framework keeps a queue of entities, in order by when they next want to be updated, as determined by what is returned by getUpdateTime(). Currently the only entity on this queue is the player, which in the starter game is done by code in BasicGameSetup. As you create additional entities that move and/or change, you will put them on this same queue, and later in the semester you will write a replacement for the queue itself. At each game tick, the framework looks at this queue, pulls off the entities that say they want to be updated at the current time, calls their update() methods, and puts them back on the queue. ``Time'' is just a count of game ticks.
So, if you want the player to be updated at every tick: Initially getUpdateTime() should return 0 or 1. update() will then be called initially with a parameter (time) of 0. After that, getUpdateTime() should return 1. It may be called multiple times before update() is called again, but eventually update() will be called with time==1. After that, getUpdateTime() should return 2. How to manage this? You will almost certainly need an instance variable to manage this. (More hints on request.)
update method is where the game entity's state, including position, should be altered. More about that in the notes about phase two.
When you have written code for all of the above methods, you should be able to start the game and have it come up without crashing or freezing, though the player will not (yet!) respond to input.
Your player also needs to be able to accept and deal with inputs from the user. The game framework makes sure that when the user does something (with the keyboard or mouse) it will be passed to your player class. How it does this is something that we will discuss in more depth later. For now what you need to know is that if you want your player to respond to keyboard input you need to implement the interface java.awt.event.KeyListener, and if you want your player to respond to mouse input you need to implement java.awt.event.MouseListener and/or java.awt.event.MouseMotionListener. Look at the Java library API documentation to see what methods you need for these interfaces and what information you have to work with. For keyboard input, you probably should use keyPressed and possibly keyReleased rather than keyTyped because this gives you more control over what is happening. For mouse input, the game framework class MainDisplay provides a nice method getClickLocation to help you convert the position as known to the Java library classes into a game-framework Location. (See me for more details if this sounds like something you want to use.) You will need to think a bit about the interaction between these methods and update, because the listener methods are called when the user does the appropriate thing with the keyboard or mouse, but you probably want all changes to the actual player to be made in update. I recommend that you have the listener methods just set variables (preferably booleans or ints) in your player class and have update determine how to move by looking at these values. (E.g., for keyboard input, you might want a boolean for each key you want to use, representing whether it has been pressed.)
update() (or some method it calls) is where most of the interesting things happen. This is where you will put code to look at the variables set in the listener methods and decide how to move, based on user input and context (e.g., are you trying to move into a wall?). As noted above, position is represented as a Location, which includes a reference to a Screen and coordinates of a block within that screen. This allows your player to figure out what kind of block it currently occupies and what kinds of blocks are nearby. You move by changing the player's location. The partialMove method is useful for moving entities around without having to keep track of whether you are about to run off the edge of your screen. Notice that it returns a new location rather than changing the object it is applied to.
The combination of update and listener methods should give you a player class that takes input from the keyboard or mouse and uses it to move around any screens that you have built. The movement should be appropriate for the blocks that are present -- for example, if you have wall blocks or other obstacles, the player should not be able to move through them. In a side-view game the player should also fall if not supported by something, and you might want to implement some way for the user to make the player jump. If you have blocks that are supposed to change when the player moves onto them, you should implement that as well in this homework.
This might also be a good time to start using Dr. Lewis's screen editor (links to information on starting and using it below) to lay out screen(s) for your game. (You don't have to, but the alternative is to create the layout with code in the constructor(s) of your screen class, which can get tedious for all but the simplest layouts.) This tool saves the layout you create in a file (name decided by you); commented-out code in BasicGameSetup shows how to then read from this file into your game. I recommend that you not invest a great deal of time right now making elaborate layouts; files you create may become unreadable if you make certain kinds of changes to your classes, so you should save this kind of polishing until later in the semester.