Contact Me or check out the Producerism Blog

Tom-as3-gotchi (Flash Game Tutorial) Part 3

05/21/2010


If you haven't been following along, make sure to check out part 1 and part 2 of this tutorial first.  In the third part of this tutorial, I will cover the whole concept of getter/setter functions, along with the benefits of doing so, instead of using public variables.  A new class will be created too, Food.as.  Keep in mind that any or all of this code can change, move around or get scrapped completely, since this is somewhat of an "organic" tutorial.

To get started, let's have a look at what's changed in the Tom.as class. First, look at the getter function:

public function get weight():Number {
	// when returning weight, round to 1 decimal
	// this way "weight" getter function will return 201.5
	// while private variable _weight will still be accurate
	return Math.round(_weight * 10) / 10;
}


We could have easily made _weight a public variable, but then it will return a very long number (201.12395323). Most digital scales aren't even that accurate, nor do most people need to know their weight to that many decimal points. Programming wise, we want to make sure Tom's weight is as accurate as possible, so behind the scenes, we are storing the entire decimal within the private variable _weight, but whenever something outside of the Tom.as class requests to know Tom's weight, Tom will return his weight rounded to the nearest single decimal through the weight getter method (which to outsiders, looks like a variable instead of a function). You may be thinking, "but I could just get Tom's weight, then round it myself!" That's true, however I prefer doing this within the getter method, so that I can take care of formatting in one place, instead of many different places.

I also added the rest of the simple getter functions, in case I want to create similar functions to the weight function above.

// getter functions for private variables
public function get health():String { return _health; }
public function get mood():String 	{ return _mood; }
public function get hunger():uint { return _hunger; }
public function get thirst():uint { return _thirst; }
public function get boredom():uint { return _boredom; }
public function get age():uint { return _age; }

The previous tutorial (Part 2) covered getter functions, so this shouldn't seem brand new. We haven't covered setter functions yet though, so let's take a look at that:

// example setter function
public function set weight(value:Number):void
{
	// we shouldn't allow the weight variable to be changed
	// unless it's through the eat() or drink() functions
	throw new Error("Hey, no cheating your diet!");
}

Let's take a step back for a second. Right now, we have a "Tom" class, that has a private variable _weight, which can only be accessed from within the Tom.as file. One way to think about it is this: since _weight is private, it's almost like a secret that "Tom" is keeping to himself. Unless there is a public function created to expose that variable to the rest of the world, there will be no way to alter or check on Tom's weight. If we make _weight a public variable, then it solves the problem, but creates another: Tom should only be able to gain or lose weight by eating or exercising, not by magically adding or removing X pounds.

Being able to write the following should not be possible, because you can't do it in the real world (and therefore, we won't allow it in this game).

tom.weight = 100; // this is cheating!
tom.weight = tom.weight - 50; // losing 50 pounds is that easy?
tom.weight++; // where did those extra lbs just come from?

In the weight setter function, we throw an error whenever code tries to change the _weight variable directly.  All three of the lines posted above will throw the following error:

throw new Error("Hey, no cheating your diet!");

Instead, Tom will only be able to gain or lose weight through new functions that we'll create, starting with eat():

Tom.as

// public functions
public function eat($food:Food):void
{
	_weight += $food.calories / Food.CALORIES_IN_A_POUND;
	trace("After eating that " + $food.name + ", Tom now weighs " + weight);
}

Here you can see the only place within the Tom.as class which alters the _weight variable. To restate one more time, the only way to change Tom's weight outside of the Tom.as class, is to call this new eat() function. Also, whenever this eat() function is called, Tom will trace out a message that states what he ate, and how much he weighs after eating. To fully understand what's going on, we also need to analyze the new Food.as class, since the eat() function is expecting you to pass it a Food variable:

Food.as:

package
{
	public class Food
	{

		public var calories:uint;
		public var name:String;

		public static const CALORIES_IN_A_POUND:uint = 3500;

		public function Food($name:String, $calories:uint)
		{
			name = $name;
			calories = $calories;
		}
	}
}

That is the entire class so far.  Right away you may notice that I'm using public variables this time! I did this for a couple of reasons: to show how public variables can be easily used instead of private variables with getter/setter methods, and also because it just made more sense in this case. Also notice the constant I've created, called CALORIES_IN_A_POUND, which is equal to 3500.  Using all capital letters for constants, and underscores for spaces is a programming standard.  It's not required by flash, but this is a good habit to get into.

This is also the first function in which we are accepting arguments:

Food.as:

public function Food($name:String, $calories:uint)

Remember, that within this Food.as file, since the "Food" function has the same name of the class, "Food," it's called a constructor function, and is called when a new Food item is created. The $name:String and $calories:uint within the parenthesis denote that in order to create a new Food item, a name must be supplied in string format ("Apple"), and calories must be provided as a positive (unsigned) integer (165). The dollar sign in front of those argument variables is a new feature to AS3 which I've recently discovered, and is a preference thing. Feel free to omit the dollar signs, I like to use them for all of my argument variables, which helps prevent conflicts with duplicate variable names.

The very next line stores the supplied arguments ($name and $calories) into private variables within the Food item. To create a Food item, this is the code:

var someFood:Food = new Food("Burrito", 500);

Which creates a new Food item with the name "Burrito," along with storing 500 calories within that Food item (declared someFood).

And that's it for the Tom.as and Food.as classes. The only other changes to cover are within the TomAS3Gotchi.as class:

TomAS3Gotchi.as:

// feed Tom some food
_tom.eat(new Food("apple", 500));
_tom.eat(new Food("cheeseburger", 3000));

// this will trigger an error
_tom.weight = 100;

The first line is calling our new public eat() function within the Tom.as class. Since eat() requires a Food variable, one is created on that same line, using Food("apple", 500). This creates a new Food object with the name "apple" and 500 calories. The next line is basically the same, but as you can see the Cheeseburger has many more calories. Doing some quick mental math, you can see that 3000+500 = 3500, which is the same number of calories in a pound. This should show us that Tom gains a full pound after eating both items.

The next line is a deliberate attempt to create an error.  We added code in the weight setter function of our Tom.as class, so that an Error will be thrown whenever Tom's weight is changed by any method other than the eat() function.  Let's see what happens:

Just as expected, an error was thrown with our custom message, "Hey, no cheating your diet!"

If you read the full error message in the pop-up window, you can see exactly how that error was thrown.  Start from the bottom and work your way up.  The last line says "at TomAS3Gotchi()[D:\projects\producerism\Tom-as3-gotchi\src\TomAS3Gotchi.as:26]" which means that the error all started with line #26 of TomAS3Gotchi.as:

line #26 of TomAS3Gotchi.as:

_tom.weight = 100;

The line above that is "at Tom/set weight()[D:\projects\producerism\Tom-as3-gotchi\src\Tom.as:50]"

line #50 of Tom.as:

throw new Error("Hey, no cheating your diet!");

And there you have it - first, the TomAS3Gotchi.as class tried to set Tom's weight through the setter property on line #26. Next, within the setter property of Tom.as (line 50), we throw an error. Of course, this wasn't a huge revelation since we knew this error would happen, and caused it to error on purpose. The point is, you may run into an error in another project somewhere, and need to figure out where it's coming from. By following these lines from the bottom to the top, you will be able to figure out the source of most errors.

And that's it for now! I promised some buttons and a crude GUI, so that will be covered in the next part of this series.

Tom-as3-gotchi Flash Game Tutorial - zip, git, or wonderfl. (SVN will be posted soon)


Leave a Reply