PART SIXTEEN
BUTTONS
MY FIRST BUTTON
Button Class
We'll start by creating a Button class. The full code for this class is provided below.
A few highlights in this code:
We're creating an abstract method called onClick() which will define what each specialized button does when it is pressed. This is being managed by the mousePressed() method.
Much like in the Messages section, we're overloading the constructor for ease-of-use later on.
In render() we'll selectively draw each element. This allows subclasses to create buttons that are transparent, have images, or text.
Next Day Button
Create a new class called NextDayButton
The full code for this is class is shown on the right as a model.
Let's notice a few things:
It uses the simpler Button constructor, only needing an X and a Y. We can make most of our buttons a uniform size.
Note: The position is essentially hardcoded in the call to the super constructor. In my code, I use a little math to put it where I want it to be in the bottom right corner of the screen.
We need a reference back to the Game object so that we can call nextDay()
Adding It To Game
In class Game
Create a NextDayButton object
In the init() method, initialize it
In the render() method, tell it to render itself
In the mousePressed() method, call its mousePressed(x, y)
Since we have a button now and are near release, make your hotkey for nextDay() a less prominent hotkey, like enter. When a user actually plays the game, we want to avoid them mashing spacebar accidentally.
CHECKPOINT
Run your code and see...
That your next day button starts a new day in your program
STATE BUTTONS
A List Of Buttons
Using the example above, we'll create three new buttons
StartButton
Placed on the title screen, moves you to Gameplay
EnterShopButton
Placed in the Gameplay state, moves you to Shop
LeaveShopButton
Placed in the Shop state, moves you to Gameplay
Hint: They'll all want to take a StateBasedGame as a parameter, so you can use the enterState() method.
So many button subclasses!
Notice that we have to keep adding very specific subclasses for buttons. This is one of the limits of using an exclusively object oriented approach with buttons.
There is a better way to implement buttons using something called functional programming, but for now stick with this approach until we're ready to take that step!
RESPONSIVENESS
Rollover
When the mouse is over a button, change
To do so, we'll need to make the Button's render method take a GameContainer as a parameter. This allows us to use polling to check the state of the mouse in render.
You have a few options for a rollover:
Change which image you draw
Tint the image a different color by adding another parameter to the draw method.
Sound Effect
In class Button,
Modify the mousePressed method to register a click sound before calling onClick().
You can't make something more white, since white is the default color filter. In my example, when the mouse is over it I make it very slightly yellow. When the mouse isn't over it, I darken the image slightly.
SHOP ITEM BUTTONS
A Major Refactor
In this next section we are going to undergo a refactor, which means a major revision to your code.
This often will result in disabling some functionality, deleting old code, and at times going longer without having your code compile.
You may want to backup an old, stable version before making huge changes!
Class ItemButton
Class ItemButton is going to be a little more complicated than other buttons.
In the constructor, we'll provide it with a few things:
X and Y coordinates on a scale of about 0-8, representing which "slot" it occupies on a simple grid in our screen.
This is going to be modified by INDENT and SPACING and SIZE values to help us easily adjust how we dense and large the buttons should be.
Feel free to change these values as much as you like!
An item that this button represents
A reference to a shop object so we can later use methods like buildItem() and buy()
Refactoring Shop
In class Shop, We're going to carefully and thoughtfully replace references to Item with ItemButtons
We'll start by making four changes, three of which are shown on the right:
Declare itemButtons instead of items
Initialize itemButtons instead of items
Render itemButtons insetad of drawing labels
Delete or disable the code in keyPressed that deals with items
It should look roughly like the image below:
At the top of the class
In init()
In renderWares()
CHECKPOINT
Run your code and see...
Stop and make sure your code is displaying correctly, looking roughly like the image above
Mouse Pressed
In class ItemButton
Implement the onClick() method:
It should check if the user has money equal to the cost AND has space available. If so...
Create a new item of that type
Buy the new item
In class Shop
In the mousePressed() method,
Call each itemButton's mousePressed() method
Removing Unique Items
In class Shop,
Add a method called removeItemButton(ItemButton i)
It should remove the parameter from the list of buttons
In class IShopItemButton
After buying an item, if it is unique...
It should tell shop to remove itself from the shop's list of buttons by calling removeItemButton()
Displaying Costs
In class ItemButton
After buying an item, if it is unique...
It should tell shop to remove itself from the shop's list of buttons by calling removeItemButton()
CHECKPOINT
Run your code and see...
Purchase each item from the shop.
Make sure each one works and that unique items are being removed.
ITEMBAR BUTTONS
A Major Refactor
This section of the project is tricky, because how we initially developed this project made a few simplifications.
We created a list of items which drew themselves directly. The ItemBar class managed an index which kept track of which item was selected. A few of these solutions are a bit clunky.
This was a good stepping stone to show you how to build a project like this. But imagine you wanted a more robust toolbar from scratch. You'd plan something out that's a little more powerful.
To fix this up, we'll do a major refactor. This means rebuilding most of ItemBar and creating an ItemBarButton class.
Rather than writing out directions to change each individual line, which would be quite complicated, I am providing my new versions here with commentary. You can copy this code, but take time to understand how it's different (and better) than the original.
Class ItemBarButton
Commentary
Notice that the data methods in my class are neatly organized into Data, Constructors, Accessors, and Mutators. I recommend this!
The static reference to selectedButton may seem a little weird at first. Let's break it down:
This is a static variable, meaning it is shared by all members of the class. So there's only ever one selectedButton.
That is super helpful for us, because we only ever want to have a single item selected at a time. Cool!
Don't get too confused by there being "an ItemBarButton inside of the ItemBarButton class." This is just a reference. Think about how you, an object of class Person, might have a reference to another object of class Person called bestFriend.
Notice that we're keeping track of the index.
An ItemBarButton doesn't know it's inside an ArrayList inside another class.
But we'll need to have data so it knows where to draw itself.
We will update its index each frame in class ItemBar.
The render() method is a modified version of what we used to do in class Item. You can safely remove the render code from Item.
Class ItemBar (Revised)
Commentary
This new version may actually be shorter than the old version of ItemBar, and it certainly is less complex.
Some of these methods, like clearSelection() and getSelectedItem() are just passthrough methods. That means they don't really do much on their own, but call code from another class we wanted abstracted from other parts of our program. This means we don't have to do much change to the Game or Shop class to implement the internal changes to ItemBar.
This class no longer tracks a selectedIndex. It just asks the ItemBar class which one of its members is currently selected.
During cleanup(), we make sure to update the index of each button since we know this is getting called each frame. Ideally, I would put this in a seperate update() method, but this approach having to change code outside of ItemBar.
CHECKPOINT
Run your code and see...
Check that you can click on each item from your bar OR use hotkeys