11-hour video of this lecture

the lost actionscript 3.0 weekend

with james paterson, hoss gifford

shot in california by the ocean, in hd

free course content:

  • course 1 introduction
  • object references and garbage collection
  • inheritance
  • graphics programming with flash player engineer jim corbett
  • flash/flex workflow
  • welcome to the lost actionscript weekend

    thanks o'reilly, adobe, and fitc

    notes online at http://moock.org/lectures/groundUpAS3

    final code at http://moock.org/lectures/groundUpAS3/examples/virtualzoo/

    flash platform overview

    programming tools

    languages

    frameworks

    runtimes

    Flash platform programming tools

    a text editor (Notepad, TextEdit, vi) + standalone compiler (mxmlc)

    Flash authoring tool

    Flex Builder

  • an "IDE" (Integrated Development Environment)
  • pure code editing, or design view
  • Built on Eclipse
  • Mac, Windows, Linux (alpha)
  • FDT

  • ActionScript IDE
  • Built on Eclipse
  • Commercial (free for open source projects)
  • Mac, Windows
  • Flash Develop

  • ActionScript IDE
  • open source, free
  • Windows only
  • Flash platform languages

    ActionScript

  • raw programming
  • MXML

  • GUI layout (compare with HTML)
  • compiles to ActionScript
  • Flash platform frameworks

    Flex framework

  • user interface controls
  • data connectors
  • programming utilities
  • Flash authoring components

  • user interface controls
  • smaller than Flex framework
  • easy to skin in Flash authoring tool
  • Industry frameworks

  • commercial component sets
  • open source libraries (http://osflash.org)
  • Flash platform runtime environments

    ActionScript programs run inside applications called "runtime environments"

  • Flash Player
  • Adobe AIR
  • Flash Lite
  • ActionScript virtual machine (AVM)

  • the specific section of a runtime that executes ActionScript code
  • AVM1: ActionScript 1.0 & 2.0
  • AVM2: ActionScript 3.0
  • what is an ActionScript program?

  • just code, "pure ActionScript" (+ assets)
  • code + .fla (+ assets)
  • code is "compiled" into a .swf file

    our focus: pure object-oriented program

    lessons also apply to timeline scripting

    if you're just starting out...

    find a mentor

    learning to program is a life-long process

    if it does what you want, it's "right"

    experiment with basics in Flash timeline scripts

    building an airplane

    think of writing a program like building an airplane

    first step: blueprints

    one blueprint per part (wheels, wings, seats...)

    each blueprint describes a physical thing conceptually

    one to one

    some blueprints correspond to one thing (e.g., windshield)

    one to many

    some blueprints correspond to many things (e.g., seats)

    master blueprint

    master blueprint describes how parts fit together

    interoperation

    interoperation of the assembled parts produces the airplane's behavior

    connected parts depend on and "know about" each other

    classes and objects

    airplane in flight

  • group of interoperating parts based on a set of blueprints
  • program running

  • group of interoperating objects, based on a set of classes
  • objects

    objects represent the "things" in a program

  • number in a calculation
  • button in an interface
  • point in time
  • blur effect on an image
  • classes

    class describes the characteristics and behavior of a type of object

    custom vs built-in classes

    some classes are written from scratch ("custom classes")

  • car in a game
  • order form in shopping app
  • some classes are provided by ActionScript and Flash runtimes

  • text, sound, images, network operations
  • to create an object-oriented program...

  • create custom classes
  • make objects from custom and built-in classes
  • tell the objects what to do
  • what the objects do determines the behavior of the program

    main class

    every program must have a main class, like a master blueprint

    main class provides program starting point

    to start program

  • load .swf into Flash runtime
  • Flash runtime auto-creates main class instance
  • the virtual zoo

    a simulated zoo game with virtual pets

    player must feed pets, like a Tamagotchi

    why a game?

    a "pet" is a well known system; focus on code-based representation

    game dev work is complex, leading-edge, challenging

    games include all aspects of application development:

  • ui, graphics, skinning
  • data management
  • audio
  • server connectivity
  • perhaps most important: pets die

  • teaches object life cycle
  • program folder

    store program files in /virtualzoo/

    store ActionScript source files in /virtualzoo/src/

    the main class: VirtualZoo

    program main class is VirtualZoo

    place class code in a text file named VirtualZoo.as

    filename must match class name

    packages

    potential problem:

  • "VirtualZoo" name might conflict with built-in class
  • to prevent name conflicts, use packages

    package lengthens a class's name

    person's full name: first name + last name

    class's full name: package name + class name

    package names

    each package has a lowercase name:

  • game
  • physics
  • networking
  • packages can be nested:

  • game.vehicles
  • physics.2d
  • packages usually start with reverse domain:

  • com.yourdomain.game
  • org.moock.utils
  • classes in packages

    a class in a package adopts the package name

    e.g., class Player in package game becomes:

    game.Player

    a dot (.) separates the classname from the package name

    creating a package

    create packages using package definition directive

    directive means program instruction

    a definition creates something, such as a class or a package

    "define" means "create" (so does "declare")

    package definition directive

    here's a package definition directive:

    package packageName { }

    definitions start with a keyword (package)

    followed by the name of the thing being defined

    the { and } mark the start and end of the package contents

    code between { and } is the "package body" or "package block"

    the zoo package

    create a package, zoo, to hold our app's classes

    package zoo { }

    move VirtualZoo.as to a new folder, /zoo/

    place source files in a folder structure matching package name

    e.g., org/moock/zoo/VirtualZoo.as

    defining a class

    create classes using class definition directive:

    class NameOfClass { }

    definition starts with class keyword

    convention: class names are capitalized

    the { and } mark the start and end of the class

    code between { and } is the "class body" or "class block"

    defining VirtualZoo

    package zoo { class VirtualZoo { } }

    the VirtualPet class

    pets in the zoo will be represented by instances of the VirtualPet class

    new source file: VirtualPet.as

    package zoo { class VirtualPet { } }

    save in /virtualzoo/src/zoo/

    zoo package spans multiple files

    convention: one class per file

    the public attribute

    by default, a class in a package can only be used in that package

    to make class available outside package, use public attribute

    package zoo { public class VirtualZoo { } }

    program's main class must be public

    the internal attribute

    to indicate that a class should be used within its package only, use internal

    package zoo { internal class VirtualPet { } }

    same as:

    package zoo { class VirtualPet { } }

    public and internal are known as access control modifiers

    constructor method

    constructor method initializes instances of a class

    create constructor method with a function definition

    package zoo { public class VirtualZoo { function VirtualZoo () { } } }

    start with keyword function

    then class's name

    then parameters (none in this case)

    then "constructor body": { and }

    constructors must be public

    in ActionScript 3.0, all constructors must be public

    package zoo { public class VirtualZoo { public function VirtualZoo () { } } }

    initializing instances

    constructor's instructions carried out when an object is created

    main class constructor plays special role:

  • trigger program's startup code
  • making VirtualPet objects

    to put pets in the zoo, we create VirtualPet objects

    general approach:

    new ClassName

    to make a VirtualPet object:

    new VirtualPet

    to make two VirtualPet objects:

    new VirtualPet new VirtualPet

    making objects with literals

    some built-in classes have special object-creation syntax

    to create a Number object:

    25.4

    to create a String object (representing text):

    "hello"

    to create a Boolean object representing the logical state true:

    true

    to create a Boolean object representing the logical state false:

    false

    putting pets in the program

    add a VirtualPet object to the zoo program:

    package zoo { public class VirtualZoo { public function VirtualZoo () { new VirtualPet } } }

    VirtualPet referred to by class name, without package name

    classes outside the zoo package must be imported before use

    package zoo { import flash.media.Sound public class VirtualZoo { public function VirtualZoo () { new Sound } } }

    the lost pet

    problem: program currently cannot refer to the VirtualPet object

    so program cannot control VirtualPet object

    use variables to refer to an object after creating it

    variables and values

    every object is a piece of data called a value

    a variable is an identifier (i.e., a name) that refers to a value

  • submitBtn refers to a Button object
  • productDescription refers to a String, "toothpaste"
  • use variables to keep track of objects

    remember: variables are not containers! (details later)

    local variables

    four kinds of variables:

  • local variables, instance variables, dynamic instance variables, static variables
  • use local variables to track information within methods and functions

    creating a local variable

    create variables using variable definition

    var identifier = value;

    the "= value" is the variable initializer

    specifying value is known as "assigning" or "setting"

  • when omitted a default value is assigned
  • avoid "container" metaphor ("put value in" is wrong)

    variables do not store values

    the pet variable

    local variable for accessing the VirtualPet object

    note: not using datatypes yet (dynamic language; need to understand inheritance before using datatypes)

    package zoo { public class VirtualZoo { public function VirtualZoo () { var pet = new VirtualPet; } } }

    can now control the VirtualPet object through pet

    but the pet can't do anything yet...needs characteristics and behaviors

    tracking object characteristics

    recall that a class describes:

  • the characteristics of a type of object
  • the behavior of a type of object
  • a characteristic is a value describing something about the object

    to keep track of characteristics, use instance variables

    instance variables

    instance variable: a variable attached to an object

  • width refers to the Number 150
  • address refers to the String "55 Main St"
  • create instance variables with a class-level variable definition

    class SomeClass { var identifier = value; }

    pet nicknames

    use an instance variable to track each pet's name

    package zoo { internal class VirtualPet { var petName = "Unnamed Pet"; } }

    every pet is initially named "Unnamed Pet"

    assigning a new name

    assign new name by changing petName's value

    object.instanceVariable = value

    set petName's value to "Stan"

    package zoo { public class VirtualZoo { public function VirtualZoo () { var pet = new VirtualPet; pet.petName = "Stan"; } } }

    variable access-control modifiers

    public: access allowed everywhere

    internal: access allowed within variable's package only

    protected: access allowed within variable's class and descendant classes only

    private: access allowed within variable's class only

    default is internal

    variable access example

    encapsulation

    an object's variables are its own business

    should be defined private to prevent external modification

    characteristics should be altered via methods (discussed later)

    for now, we'll make petName internal, later private

    package zoo { internal class VirtualPet { internal var petName = "Unnamed Pet"; } }

    initializing an object

    so far, we initialize VirtualPet object characteristics like this:

    var pet = new VirtualPet; pet.petName = "Stan";

    for improved convenience, use constructor parameters

    constructor parameters

    constructor parameter: local variable defined in constructor header

    class SomeClass { function SomeClass (identifier = value) { } }

    define multiple constructor parameters

    class SomeClass { function SomeClass (identifier1, identifier2, identifier3) { } }

    setting constructor parameter values

    set parameter value with constructor argument:

    new SomeClass(someValue)

    set default value with an initializer:

    class SomeClass { function SomeClass (identifier = value) { } }

    if no initializer, argument is required

    a VirtualPet constructor parameter

    required parameter, name:

    package zoo { internal class VirtualPet { internal var petName = "Unnamed Pet"; public function VirtualPet (name) { } } }

    name's value assigned via constructor argument:

    package zoo { public class VirtualZoo { public function VirtualZoo ( ) { var pet = new VirtualPet("Stan"); } } }

    assigning one variable another's value

    within VirtualPet constructor, name's value is "Stan"

    we want to assign that value to petName

    object.instanceVariable = value

    this.petName = name

    this is the VirtualPet object being created

    package zoo { internal class VirtualPet { internal var petName = "Unnamed Pet"; public function VirtualPet (name) { this.petName = name; } } }

    remove petName's initializer

    petName will always receive a value via VirtualPet constructor

    remove redundant petName initializer

    package zoo { internal class VirtualPet { internal var petName; public function VirtualPet (name) { this.petName = name; } } }

    copies and references

    the following assignment can have two results:

    this.petName = name;

    if name's value is a String, Boolean, Number, int, or uint

  • ActionScript copies the object, and assigns the copy to petName
  • if name's value is any other object

  • both variables refer to the same object
  • multiple references to the same object

    when two variables refer to the same object

  • either variable can be used to change the object
  • changes made through one variable are reflected by the other
  • var a = new VirtualPet("Stan"); // a.petName yields "Stan" var b = a; b.petName = "Tom" // a.petName now yields "Tom"

    variables don't "contain" objects, they merely refer to them

    instance variable for our pet

    need a reference to the pet after VirtualZoo constructor finishes

    package zoo { public class VirtualZoo { private var pet; public function VirtualZoo () { this.pet = new VirtualPet("Stan"); } } }

    behaviors for our pet

    instance methods define an object's behavior

    instance method is:

  • a set of instructions related to some object
  • examples:

  • Sound class's play() method
  • TextField class's setSelection() method
  • we want to give VirtualPet an eat() method

    creating an instance method

    use a function definition within the class:

    class SomeClass { function identifier () { } }

    code between { and } is the "method body"

    running an instance method

    execute the code in an instance method:

    object.methodName()

    exactly like running a function

    can't eat without a stomach

    new instance variable: currentCalories

    set default value with initializer (= 1000)

    package zoo { internal class VirtualPet { internal var petName; private var currentCalories = 1000; public function VirtualPet (name) { this.petName = name; } } }

    the eat() method

    instance method eat() will increase currentCalories

    eventually, user input will trigger eat()

    eat() skeleton code:

    package zoo { internal class VirtualPet { internal var petName; private var currentCalories = 1000; public function VirtualPet (name) { this.petName = name; } function eat () { // Empty for now... } } }

    invoking eat()

    object.methodName()

    package zoo { public class VirtualZoo { private var pet; public function VirtualZoo () { this.pet = new VirtualPet("Stan"); // Invoke eat() this.pet.eat(); } } }

    increasing the pet's calories

    add 100 to currentCalories

    this.currentCalories = this.currentCalories + 100;

    this is the object doing the action (e.g., eating)

    more convenient approach:

    this.currentCalories += 100;

    updated VirtualPet code

    package zoo { internal class VirtualPet { internal var petName; private var currentCalories = 1000; public function VirtualPet (name) { this.petName = name; } function eat () { this.currentCalories += 100; } } }

    instance-method access-control modifiers

    same as for instance variables

    public: access allowed everywhere

    internal: access allowed within method's package only

    protected: access allowed within method's class and descendant classes only

    private: access allowed within method's class only

    default is internal

    black box principle

    think of an object as a "black box"

    box is controlled by external knobs

    box's internal operations are unknown to person using knobs

    public methods are the knobs

    non-public methods are internal operations

    car as a black box example

    driver doesn't need to know how engine works

    just press gas pedal

    can change engine without affecting use of car

    oops! "gas" should be "acceleration"

  • don't name public methods after internal implementation
  • can change private methods without affecting public methods

    API

    public methods and variables are the class's API

    Application Programming Interface

    "Interface" used to control the instances of the class

    as much as possible, API should remain fixed

    changes to API force changes to code that uses the class

    add eat() to VirtualPet's API

    package zoo { internal class VirtualPet { internal var petName; private var currentCalories = 1000; public function VirtualPet (name) { this.petName = name; } public function eat () { this.currentCalories += 100; } } }

    configurable meal size

    parameter for "how much to eat"

    package zoo { internal class VirtualPet { internal var petName; private var currentCalories = 1000; public function VirtualPet (name) { this.petName = name; } public function eat (numberOfCalories) { this.currentCalories += numberOfCalories; } } }

    eat 50 calories

    package zoo { public class VirtualZoo { private var pet; public function VirtualZoo () { this.pet = new VirtualPet("Stan"); this.pet.eat(50); } } }

    omitting this

    'this' takes time to write and adds clutter

    public function eat (numberOfCalories) { currentCalories += numberOfCalories; }

    when ActionScript sees an identifier, it searches:

  • local variable, parameter, outer functions, instance variable
  • numberOfCalories: ActionScript finds the parameter

    currentCalories: ActionScript finds the instance variable

    known as identifier resolution (see chapter 16 of Essential ActionScript 3.0)

    note: small runtime performance cost for omitting this

    disambiguating parameter/variable name conficts

    in the code below:

  • calories means parameter
  • this.calories means instance variable
  • package zoo { internal class VirtualPet { private var calories = 1000; public function eat (calories) { this.calories += calories; } } }

    modifying state directly is bad

    pet lives too long:

    somePet.currentCalories = 1000000;

    possible program malfunction:

    somePet.currentCalories = -46;

    moderate state changes through methods

    restrict currentCalories value to a maximum of 2,000

    public function setCalories (newCurrentCalories) { if (newCurrentCalories > 2000) { currentCalories = 2000; } else if (newCurrentCalories < 0) { currentCalories = 0; } else { currentCalories = newCurrentCalories; } }

    update eat():

    public function eat (numberOfCalories) { setCalories(currentCalories + numberOfCalories); }

    known as modifier method (also, mutator or setter)

    moderate state retrieval through methods

    retrieve number of calories:

    public function getCalories () { return currentCalories; }

    retrieve "state of hunger":

    public function getHunger () { return currentCalories / 2000; }

    known as retriever method (also, accessor or getter)

    methods for name modification and retrieval

    limit length of assigned pet name

    public function setName (newName) { // If the proposed new name has more than 20 characters... if (newName.length > 20) { newName = newName.substr(0, 20); } else if (newName == "") { return; } // new name validated, so assign it petName = newName; }

    retrieve name:

    public function getName () { return petName; }

    restrict access to petName:

    private var petName;

    getName() and setName() usage

    package zoo { public class VirtualZoo { private var pet; public function VirtualZoo () { pet = new VirtualPet("Stan"); // Assign the pet's old name to the local variable oldName var oldName = pet.getName(); // Give the pet a new name pet.setName("Marcos"); } } }

    package zoo { internal class VirtualPet { private var petName; private var currentCalories = 1000; public function VirtualPet (name) { setName(name); } // ...remainder of class not shown } }

    static variables

    variables that relate to a class (not an instance)

    track information relating to entire class

  • default size of a dialog box
  • maximum speed of a car
  • class SomeClass { private static var identifier = value; }

    accessing static variables

    Class.variable

    when Class is omitted, current class is used

    EBTI: Explicit is Better Than Implicit

    magic values

    static variables are great for cleaning up "magic values"...

    unexplained literal values

    20 in setName() and 2000 in setCalories(), getHunger()

  • purpose is not self-evident
  • decentralized, so error-prone
  • static variables for VirtualPet

    pet name's maximum length

    pet "stomach size" (maximum calories)

    package zoo { internal class VirtualPet { private static var maxNameLength = 20; private static var maxCalories = 2000; // Remainder of class not shown... } }

    replace magic values with static variables

    centralize code with static variables:

    package zoo { internal class VirtualPet { private static var maxNameLength = 20; private static var maxCalories = 2000; private var petName; // Give each pet 50% of the maximum possible calories to start with. private var currentCalories = VirtualPet.maxCalories/2; public function VirtualPet (name) { setName(name); } public function eat (numberOfCalories) { setCalories(currentCalories + numberOfCalories); } public function setCalories (newCurrentCalories) { if (newCurrentCalories > VirtualPet.maxCalories) { currentCalories = VirtualPet.maxCalories; } else if (newCurrentCalories < 0) { currentCalories = 0; } else { currentCalories = newCurrentCalories; } } public function getCalories () { return currentCalories; } public function getHunger () { return currentCalories / VirtualPet.maxCalories; } public function setName (newName) { if (newName.length > VirtualPet.maxNameLength) { newName = newName.substr(0, VirtualPet.maxNameLength); } else if (newName == "") { return; } petName = newName; } public function getName () { return petName; } } }

    static methods

    methods that relate to a class (not an instance)

    e.g., convert data to a given class:

    Point.polar()

  • returns Cartesian point (x, y) for a polar point (distance, angle)
  • relates to all points, not just one Point object
  • static-method-only classes

    Some classes exist solely to define static methods

    Group related functionality

    Mouse.show(), Mouse.hide()

    a static-method alternative: functions

    standalone set of instructions (independent of any class or object)

    structurally created like methods

    function identifier (param1, param2, param3) { }

    package-level functions

    function defined in a named package

    package packageName { internal_or_public function identifier () { } }

    must be internal or public

    e.g., flash.utils.setTimeout()

    if public, must reside in file with matching name

    global functions (function in unnamed package)

    function defined in the unnamed package

    package { public function identifier () { } }

    accessible anywhere in a program

    e.g., trace()

    consuming food

    new VirtualPet method to reduce calories: digest()

    amount to digest governed by static variable: caloriesPerSecond

    private static var caloriesPerSecond = 100;

    private function digest () { trace(getName() + " digested some food."); setCalories(getCalories() - VirtualPet.caloriesPerSecond); }

    triggering digest()

    updated VirtualPet constructor:

    public function VirtualPet (name) { setName(name); // Call digest() once per second digestIntervalID = setInterval(digest, 1000); }

    digestIntervalID refers to a number used to cancel the interval

    private var digestIntervalID;

    pet death

    when calories run out, stop digesting:

    public function setCalories (newCurrentCalories) { if (newCurrentCalories > VirtualPet.maxCalories) { currentCalories = VirtualPet.maxCalories; } else if (newCurrentCalories < 0) { currentCalories = 0; } else { currentCalories = newCurrentCalories; } // Calculate percentage of calories left var caloriePercentage = Math.floor(getHunger()*100); trace(getName() + " has " + currentCalories + " calories" + " (" + caloriePercentage + "% of its food) remaining."); if (caloriePercentage == 0) { clearInterval(digestIntervalID); trace(getName() + " has died."); } }

    dead pets don't eat

    prevent eat() from running when pet has died

    public function eat (numberOfCalories) { // If this pet is dead... if (currentCalories == 0) { // ...quit this method without modifying currentCalories trace(getName() + " is dead. You can't feed it."); return; } trace(getName() + " ate some food."); setCalories(currentCalories + numberOfCalories); }

    inheritance

    relationship between 2+ classes where one borrows (or inherits) the variable and method definitions of another

    lets one class use the code in another class

    provides conceptual tool for representing hierarchies

  • types of vehicles
  • types of bank accounts
  • follow program needs, not real world

  • Wrong: Person > Male, Female > MaleStudent, FemaleStudent
  • Right: Student class with gender variable
  • inheritance example

    types of food

    pets can eat sushi and apples

    new classes: Sushi and Apple

    Sushi and Apple class functionality is nearly identical

    put shared functionality in a superclass, Food

    Sushi and Apple will extend Food

    the Food class

    package zoo { public class Food { private var calories; private var name; public function Food (initialCalories) { setCalories(initialCalories); } public function getCalories ( ) { return calories; } public function setCalories (newCalories) { calories = newCalories; } public function getName ( ) { return name; } public function setName (newName) { name = newName; } } }

    the Apple class

    Apple inherits from Food

    Apple class sets food name and default calories

    package zoo { public class Apple extends Food { // Set the default number of calories for an Apple object to 100 private static var DEFAULT_CALORIES = 100; public function Apple (initialCalories = 0) { if (initialCalories <= 0) { initialCalories = Apple.DEFAULT_CALORIES; } // Run Food constructor. // (Should come before assigning instance vars so superclass // doesn't overwrite assigned values.) super(initialCalories); setName("Apple"); } } }

    the Sushi class

    Sushi inherits from Food

    Sushi class sets food name and default calories

    package zoo { public class Sushi extends Food { private static var DEFAULT_CALORIES = 500; public function Sushi (initialCalories = 0) { if (initialCalories <= 0) { initialCalories = Sushi.DEFAULT_CALORIES; } // Run Food constructor super(initialCalories); setName("Sushi"); } } }

    updated eat() method

    revise eat() to accept Food objects

    public function eat (foodItem) { if (currentCalories == 0) { trace(getName() + " is dead. You can't feed it."); return; } trace(getName() + " ate the " + foodItem.getName() + "."); setCalories(currentCalories + foodItem.getCalories()); }

    feeding time!

    try the new food out...

    package zoo { public class VirtualZoo { private var pet; public function VirtualZoo () { pet = new VirtualPet("Stan"); pet.eat(new Apple()); // Feed Stan an apple pet.eat(new Sushi()); // Feed Stan some sushi } } }

    wormy apples

    add custom functionality for one type of food

    randomly give 50% of all apples a worm

    package zoo { public class Apple extends Food { private static var DEFAULT_CALORIES = 100; private var wormInApple; public function Apple (initialCalories = 0) { if (initialCalories <= 0) { initialCalories = Apple.DEFAULT_CALORIES; } super(initialCalories); wormInApple = Math.random() >= .5; setName("Apple"); } public function hasWorm () { return wormInApple; } } }

    pets don't eat worms

    update eat() method to reject wormy apples

    public function eat (foodItem) { if (currentCalories == 0) { trace(getName( ) + " is dead. You can't feed it."); return; } if (foodItem is Apple) { if (foodItem.hasWorm()) { trace("The " + foodItem.getName() + " had a worm. " + getName() + " didn't eat it."); return; } } trace(getName() + " ate the " + foodItem.getName() + "."); setCalories(currentCalories + foodItem.getCalories()); }

    preparing for screen display

    application main class must extend Sprite or MovieClip

    allows main class instance to be added to display list

    more display information coming later

    for now, make VirtualZoo extend Sprite so we can compile

    package zoo { import flash.display.Sprite; public class VirtualZoo extends Sprite { private var pet; public function VirtualZoo () { pet = new VirtualPet("Stan"); pet.eat(new Apple()); pet.eat(new Sushi()); } } }

    compiling in Flash Authoring

    select File > New

    select Flash File (ActionScript 3.0), then OK

    select File > Save As

    save in /virtualzoo/src folder

    for File name, enter VirtualZoo.fla, then OK

  • in Flash CS4 and higher: Properties panel, under Publish > Class, enter zoo.VirtualZoo
  • in Flash CS3: Properties panel, under Document class, enter zoo.VirtualZoo
  • select Control > Test Movie

    Flex Builder: "main class in package" issues

    in Flex Builder 3, can't leave VirtualZoo in package "zoo"

  • package folders included in bin-debug (bug 14145)
  • "ActionScript Applications" does not list main application file (bug 14144)
  • therefore, move VirtualZoo class from zoo package to the unnamed package...

    Flash Builder: code preparatation

    move VirtualZoo.as from /virtualzoo/src/zoo to /virtualzoo/src

    in VirtualZoo.as:

  • add import zoo.*;
  • change package zoo { to package {
  • in VirtualPet.as, change internal class to public class

    Flash Builder: create a project

    select File > New > ActionScript Project

    for Project name, enter "virtualzoo"

    under Project Contents, uncheck "Use default location"

    under Project Contents > Folder, select virtualzoo folder

    click Next

    for Main source folder, enter "src"

    for Main application file, enter VirtualZoo.as

    click Finish

    Flash Builder: configure project

    build project in standalone player instead of browser:

  • Project > Properties > ActionScript Compiler > HTML wrapper > uncheck "Generate HTML wrapper file"
  • prevent VirtualZoo.fla from being copied to /bin-debug/ folder:

  • Project > Properties > ActionScript Compiler > uncheck Copy non-embedded files to output folder
  • refresh build:

  • Project > Clean...
  • Flash Builder: compile and run

    Flash Builder constantly compiles in background (optional)

    to run program:

  • in the Package Explorer (formerly "Flex Navigator") panel, select any class in the virtualzoo project
  • select Run > Debug VirtualZoo
  • reference errors

    accessing a nonexistent method or variable causes a reference error

    pet.eatt(new Sushi()) // typo

    pet.jump() // no such feature

    ReferenceError: Error #1069: Property eatt not found on zoo.VirtualPet and there is no default value. at VirtualZoo$iinit()[C:\data\virtualzoo\src\VirtualZoo.as:8]

    type annotations

    reference errors happen at runtime

    to detect reference errors at compile time, use type annotations

    type annotation: tells the compiler the type of value referenced by a variable, parameter, or method return

    var identifier:type = value;

    function identifier (param:type):type { }

    type is any "datatype"...

    datatypes

    datatype: set of values

    null is a datatype (set is null)

    void is a datatype (set is undefined)

    every class is a datatype

  • set is direct instances and instances of descendants
  • Food datatype

  • instances of Food, Apple, and Sushi
  • Apple datatype

  • instances of Apple, but not Sushi instances, or direct Food instances
  • type annotation example

    var meal:Food = new Food();

    var meal:Food = new Apple();

    strict-mode type mismatch compiler error:

    var meal:Food = new VirtualPet("Hoss");

    Implicit coercion of a value of type zoo:VirtualPet to an unrelated type zoo:Food. VirtualZoo.as /virtualzoo/src line 13

    detecting reference errors at compile-time

    var pet:VirtualPet = new VirtualPet("Stan");

    pet.eatt(new Sushi());

    compiler error:

    1061: Call to a possibly undefined method eatt through a reference with static type zoo:VirtualPet.

    virtual zoo program with datatypes

    package { import flash.display.Sprite; import zoo.*; public class VirtualZoo extends Sprite { private var pet:VirtualPet; public function VirtualZoo () { pet = new VirtualPet("Stan"); pet.eat(new Apple()); pet.eat(new Sushi()); } } }

    package zoo { import flash.utils.setInterval; import flash.utils.clearInterval; public class VirtualPet { private static var maxNameLength:int = 20; private static var maxCalories:int = 2000; private static var caloriesPerSecond:int = 100; private var petName:String; private var currentCalories:int = VirtualPet.maxCalories/2; private var digestIntervalID:int; public function VirtualPet (name:String):void { setName(name); digestIntervalID = setInterval(digest, 1000); } public function eat (foodItem:Food):void { if (currentCalories == 0) { trace(getName() + " is dead. You can't feed it."); return; } if (foodItem is Apple) { if (foodItem.hasWorm()) { trace("The " + foodItem.getName() + " had a worm. " + getName() + " didn't eat it."); return; } } trace(getName() + " ate the " + foodItem.getName() + "."); setCalories(currentCalories + foodItem.getCalories()); } public function setCalories (newCurrentCalories:int):void { if (newCurrentCalories > VirtualPet.maxCalories) { currentCalories = VirtualPet.maxCalories; } else if (newCurrentCalories < 0) { currentCalories = 0; } else { currentCalories = newCurrentCalories; } // Calculate percentage of calories left var caloriePercentage:int = Math.floor(getHunger()*100); trace(getName() + " has " + currentCalories + " calories" + " (" + caloriePercentage + "% of its food) remaining."); if (caloriePercentage == 0) { clearInterval(digestIntervalID); trace(getName() + " has died."); } } public function getHunger ():Number { return currentCalories / VirtualPet.maxCalories; } public function getCalories ():int { return currentCalories; } public function setName (newName:String):void { if (newName.length > VirtualPet.maxNameLength) { newName = newName.substr(0, VirtualPet.maxNameLength); } else if (newName == "") { return; } // Assign the new, validated name to petName petName = newName; } public function getName ():String { return petName; } private function digest ():void { trace(getName() + " digested some food."); setCalories(getCalories() - VirtualPet.caloriesPerSecond); } } }

    package zoo { public class Food { private var calories:int; private var name:String; public function Food (initialCalories:int) { setCalories(initialCalories); } public function getCalories ():int { return calories; } public function setCalories (newCalories:int):void { calories = newCalories; } public function getName ():String { return name; } public function setName (newName:String):void { name = newName; } } }

    package zoo { public class Apple extends Food { private static var DEFAULT_CALORIES:int = 100; private var wormInApple:Boolean; public function Apple (initialCalories:int = 0) { if (initialCalories <= 0) { initialCalories = Apple.DEFAULT_CALORIES; } super(initialCalories); wormInApple = Math.random() >= .5; setName("Apple"); } public function hasWorm ():Boolean { return wormInApple; } } }

    package zoo { public class Sushi extends Food { private static var DEFAULT_CALORIES:int = 500; public function Sushi (initialCalories:int = 0) { if (initialCalories <= 0) { initialCalories = Sushi.DEFAULT_CALORIES; } super(initialCalories); setName("Sushi"); } } }

    casting

    consider this code (from VirtualPet.eat()):

    foodItem.hasWorm();

    to strict-mode compiler, foodItem is a Food instance

  • at runtime, foodItem might be Apple or Sushi
  • Food does not define hasWorm(), so error
  • solution? first, check if foodItem is an Apple object:

    if (foodItem is Apple) { ... }

    then use cast to tell compiler the object's actual class:

    if (foodItem is Apple) { if (Apple(foodItem).hasWorm()) { trace("The " + foodItem.getName() + " had a worm. " + getName() + " didn't eat it."); return; } }

    managing display

    could add display code to VirtualPet

  • would make class bulky and inflexible
  • better to separate logic from display, as in "model/view/controller"...

    model/view/controller

    model: logic, state (data)

    view: render model

    controller: receive input, update model

    MVC in the virtual zoo app:

  • model: VirtualPet
  • view+controller: VirtualPetView (new class)
  • real-world mvc use case

    chat room interface: Room (model) and ChatRoomView (view)

    user changes rooms, underlying Room object (model) changes

    ChatRoomView is assigned the new room, updates automatically

    separation benefits:

  • makes the room easy to change
  • easily add other kinds of views (display the same data in multiple ways)
  • e.g., avatar chat, user-list only, audio view
  • implementing mvc in the zoo

    modify VirtualPet to support four states: full, hungry, starving, dead

    use events to tell VirtualPetView about state changes

    implementing states in VirtualPet

    static variables to represent possible states

    instance variable to track current pet state

    modifier/retriever methods for pet state

    update pet state when calories change

    state variables

    public static const PETSTATE_FULL:int = 0; public static const PETSTATE_HUNGRY:int = 1; public static const PETSTATE_STARVING:int = 2; public static const PETSTATE_DEAD:int = 3;

    private var petState:int;

    state modifier and retriever methods

    private function setPetState (newState:int):void { if (newState == petState) { return; } petState = newState; }

    public function getPetState ():int { return petState; }

    set state when calories change

    update setCalories()

    private function setCalories (newCurrentCalories:int):void { if (newCurrentCalories > VirtualPet.maxCalories) { currentCalories = VirtualPet.maxCalories; } else if (newCurrentCalories < 0) { currentCalories = 0; } else { currentCalories = newCurrentCalories; } var caloriePercentage:int = Math.floor(getHunger()*100); trace(getName() + " has " + currentCalories + " calories" + " (" + caloriePercentage + "% of its food) remaining."); // Set pet state if (caloriePercentage == 0) { // The pet has no food left. Set state to dead. setPetState(VirtualPet.PETSTATE_DEAD); clearInterval(digestIntervalID); } else if (caloriePercentage < 20) { // The pet needs food badly. Set state to starving. setPetState(VirtualPet.PETSTATE_STARVING); } else if (caloriePercentage < 50) { // The pet needs food. Set state to hungry. setPetState(VirtualPet.PETSTATE_HUNGRY); } else { // The pet doesn't need food. Set state to full. setPetState(VirtualPet.PETSTATE_FULL); } }

    code cleanup: centralize death

    setCalories() does death, but shouldn't (not its responsibility)

    externalize this code...

    if (caloriePercentage == 0) { setPetState(VirtualPet.PETSTATE_DEAD); clearInterval(digestIntervalID); }

    ...to new method, die()

    if (caloriePercentage == 0) { die(); }

    add new die() method:

    private function die ():void { // Moved from setCalories() setPetState(VirtualPet.PETSTATE_DEAD); clearInterval(digestIntervalID); trace(getName() + " has died."); }

    most classes have a "death" method that releases resources

    code cleanup: updated eat() method

    eat() uses states to check for death

    public function eat (foodItem:Food):void { // If the pet is dead, abort if (petState == VirtualPet.PETSTATE_DEAD) { trace(getName() + " is dead. You can't feed it."); return; } // If the food item is an apple, check it for worms. If it has a worm, // don't eat it. if (foodItem is Apple) { if (Apple(foodItem).hasWorm()) { trace("The " + foodItem.getName() + " had a worm. " + getName() + " didn't eat it."); return; } } // Display a debugging message indicating what the pet ate trace(getName() + " ate the " + foodItem.getName() + " (" + foodItem.getCalories() + " calories)."); setCalories(getCalories() + foodItem.getCalories()); }

    code cleanup: setting initial calories

    set calories in constructor so state is set

    setCalories(VirtualPet.maxCalories/2);

    events and event handling

    system for one object to tell other objects that something happened

    event: the state change

    event target: the VirtualPet object

    event listeners: methods that register to be notified of the "event"

    the event cycle

    dispatch state change events

    extend EventDispatcher:

    import flash.events.*; public class VirtualPet extends EventDispatcher {

    give event a name:

    public static const STATE_CHANGE:String = "STATE_CHANGE";

    request event dispatch from setPetState():

    private function setPetState (newState:int):void { if (newState == petState) { return; } petState = newState; dispatchEvent(new Event(VirtualPet.STATE_CHANGE)); }

    VirtualPetView class

    new class to display pet on screen

    package zoo { import flash.display.*; import flash.events.*; public class VirtualPetView extends Sprite { // The pet being displayed private var pet:VirtualPet; public function VirtualPetView (pet:VirtualPet) { // Retrieve a reference to the pet being displayed this.pet = pet; // Register to be notified when the pet's condition changes pet.addEventListener(VirtualPet.STATE_CHANGE, petStateChangeListener); // NOTICE that the model doesn't control the view directly. // Instead, the view responds to generic events from the model. } private function petStateChangeListener (e:Event):void { renderCurrentPetState(); } private function renderCurrentPetState ():void { var state:int = pet.getPetState(); switch (state) { case VirtualPet.PETSTATE_FULL: trace(pet.getName() + " is now full."); break; case VirtualPet.PETSTATE_HUNGRY: trace(pet.getName() + " is now hungry."); break; case VirtualPet.PETSTATE_STARVING: trace(pet.getName() + " is now starving."); break; case VirtualPet.PETSTATE_DEAD: trace(pet.getName() + " is now dead."); break; } } } }

    give our pet a VirtualPetView

    new instance variable in VirtualZoo:

    private var petView:VirtualPetView;

    create VirtualPetView in VirtualZoo constructor:

    petView = new VirtualPetView(pet);

    run the code to test state change event

    an event for name changes

    event constant in VirtualPet:

    public static const NAME_CHANGE:String = "NAME_CHANGE";

    dispatch event from setName():

    public function setName (newName:String):void { if (newName.length > VirtualPet.maxNameLength) { newName = newName.substr(0, VirtualPet.maxNameLength); } else if (newName == "") { return; } // Assign the new, validated name to petName petName = newName; dispatchEvent(new Event(VirtualPet.NAME_CHANGE)); }

    handle name change events

    VirtualPetView name-change listener and name renderer:

    private function petNameChangeListener (e:Event):void { renderCurrentPetName(); } private function renderCurrentPetName ():void { trace("Pet name is now: " + pet.getName()); }

    register for event in VirtualPetView constructor:

    pet.addEventListener(VirtualPet.NAME_CHANGE, petNameChangeListener);

    initialize name in VirtualPetView constructor:

    renderCurrentPetName();

    a note on weak listeners

    if a program doesn't control a listener's lifespan directly, then:

  • if the listener is weak, it is removed implicitly
  • if the listener is strong, it is never removed (leads to memory leak)
  • moral: forget weak or strong, instead always control listener lifespan

    moral: don't use weak as a default because:

  • strong listeners can cause memory accumulation, easy to find in a profiler
  • weak listeners can cause random failures, hard to find
  • graphics!

    pet logic now works, time to add display

    first, learn about the display api

    display api

    divides display functionality into three tiers

  • display
  • interactivity
  • containment
  • display api core classes

    DisplayObject: base display class

    InteractiveObject: adds mouse/keyboard functionality

    DisplayObjectContainer: adds containment for grouping

    Sprite: adds dragability, button-style interaction features

    MovieClip: adds timeline control

    display object creation

    var t:TextField = new TextField()

    var s:Sprite = new Sprite()

    the display list

    hierarchy of all objects currently on screen

    hierarchy's root is the Stage instance

    Stage instance is a container

    Stage's first child is auto-added: .swf's main class instance

    any display children added to the main class instance appear on screen

    containers and children

    DisplayObjectContainer methods:

  • addChild()
  • removeChild()
  • addChildAt()
  • removeChildAt()
  • display objects can exist detached from the display list

    object state (x, y, rotation) retained even when detached

    display api example

    package { import flash.display.*; import flash.text.TextField; public class GreetingApp extends Sprite { public function GreetingApp() { // Create a rectangle var rect:Shape = new Shape(); rect.graphics.lineStyle(1); rect.graphics.beginFill(0x0000FF, 1); rect.graphics.drawRect(0, 0, 75, 50); // Create a text message var greeting_txt:TextField = new TextField(); greeting_txt.text = "Hello world"; greeting_txt.x = 60; // Add assets to the display list addChild(greeting_txt); addChild(rect); } } }

    displaying the pet: architecture

    displaying the pet

    download graphics

    updates to VirtualPetView class:

  • add a general container for loaded graphics
  • load pet graphics from external .gif files
  • dispatch COMPLETE event when graphics are done loading
  • text field for name
  • graphics container for loaded graphics

    new VirtualPetView instance variable:

    private var graphicsContainer:Sprite;

    new method:

    private function createGraphicsContainer ():void { graphicsContainer = new Sprite(); addChild(graphicsContainer); }

    update constructor:

    createGraphicsContainer(); renderCurrentPetName();

    loading architecture

    variables for loading graphics

    use Loader objects to load graphics

    import flash.net package:

    import flash.net.*;

    one object per graphic

    one variable per object

    private var petAlive:Loader; // The pet in its alive state private var petDead:Loader; // The pet in its dead state private var foodHungry:Loader; // An icon for the hungry state private var foodStarving:Loader; // An icon for the starving state

    variables to count loaded graphics

    keep track of how many graphics have loaded

    when all graphics have loaded, trigger COMPLETE event

    static private var numGraphicsToLoad:int = 4; private var numGraphicsLoaded:int = 0;

    method to manage loading

    why contentLoaderInfo?

  • central location for event handling by Loader and .content
  • security: separates loader from loadee
  • private function loadGraphics ():void { // Graphic showing the pet in its alive state petAlive = new Loader(); petAlive.load(new URLRequest("pet-alive.gif")); petAlive.contentLoaderInfo.addEventListener(Event.COMPLETE, completeListener); petAlive.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, ioErrorListener); // Graphic showing the pet in its dead state petDead = new Loader(); petDead.load(new URLRequest("pet-dead.gif")); petDead.contentLoaderInfo.addEventListener(Event.COMPLETE, completeListener); petDead.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, ioErrorListener); // The "needs food" icon foodHungry = new Loader(); foodHungry.load(new URLRequest("food-hungry.gif")); foodHungry.contentLoaderInfo.addEventListener(Event.COMPLETE, completeListener); foodHungry.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, ioErrorListener); foodHungry.x = 15; foodHungry.y = 100; // The "needs food badly" icon foodStarving = new Loader(); foodStarving.load(new URLRequest("food-starving.gif")); foodStarving.contentLoaderInfo.addEventListener(Event.COMPLETE, completeListener); foodStarving.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, ioErrorListener); foodStarving.x = 15; foodStarving.y = 100; }

    listener triggered when a graphic loads

    private function completeListener (e:Event):void { // Increase (by one) the count of the total number of graphics loaded numGraphicsLoaded++; // If all the graphics have loaded... if (numGraphicsLoaded == numGraphicsToLoad) { // Render pet, then trigger COMPLETE event renderCurrentPetState(); dispatchEvent(new Event(Event.COMPLETE)); } }

    listener triggered when graphic load fails

    private function ioErrorListener (e:IOErrorEvent):void { trace("Load error: " + e); }

    update pet renderer

    private function renderCurrentPetState ():void { // Clear all graphics while (graphicsContainer.numChildren > 0) { graphicsContainer.removeChildAt(0); } // Check the pet's current state var state:int = pet.getPetState(); // Display appropriate graphics switch (state) { case VirtualPet.PETSTATE_FULL: graphicsContainer.addChild(petAlive); break; case VirtualPet.PETSTATE_HUNGRY: graphicsContainer.addChild(petAlive); graphicsContainer.addChild(foodHungry); break; case VirtualPet.PETSTATE_STARVING: graphicsContainer.addChild(petAlive); graphicsContainer.addChild(foodStarving); break; case VirtualPet.PETSTATE_DEAD: graphicsContainer.addChild(petDead); break; } }

    update VirtualPetView constructor

    load the graphics when a VirtualPetView is created

    createGraphicsContainer(); loadGraphics(); renderCurrentPetName();

    don't start pet until graphics have loaded

    new VirtualPet methods: stop() and start()

    move clearInterval() call from die() to stop()

    public function stop ():void { clearInterval(digestIntervalID); }

    private function die ():void { // Stop digesting stop(); setPetState(VirtualPet.PETSTATE_DEAD); trace(getName() + " has died."); }

    move setInterval() call from constructor to start()

    public function start ():void { stop(); digestIntervalID = setInterval(digest, 1000); }

    start pet when graphics have loaded

    import events package

    import flash.events.*;

    register for "graphics loaded" notification in VirtualZoo constructor:

    petView.addEventListener(Event.COMPLETE, petViewCompleteListener);

    when graphics load, start pet and display graphics

    public function petViewCompleteListener (e:Event):void { addChild(petView); pet.start(); }

    display pet's name

    in VirtualPetView, import the flash.text package

    import flash.text.*;

    new variable for a TextField object

    private var petName:TextField;

    create name text field

    new method to create TextField object:

    private function createNameTag ():void { petName = new TextField(); petName.defaultTextFormat = new TextFormat("_sans",14,0x006666,true); petName.autoSize = TextFieldAutoSize.CENTER; petName.selectable = false; petName.x = 250; petName.y = 20; addChild(petName); }

    call createNameTag() from VirtualPetView constructor:

    createGraphicsContainer(); loadGraphics(); createNameTag(); renderCurrentPetName();

    display name on screen

    update renderCurrentPetName():

    private function renderCurrentPetName ():void { petName.text = pet.getName(); }

    interactivity

    buttons for feeding the pet

    new class: FoodButton

    package zoo { import flash.display.* import flash.events.*; import flash.text.*; // The FoodButton class represents a simple clickable-text button public class FoodButton extends Sprite { // The text to be clicked private var text:TextField; // The formatting of the text when it is *not* under the mouse pointer private var upFormat:TextFormat; // The formatting of the text when it *is* under the mouse pointer private var overFormat:TextFormat; // Constructor public function FoodButton (label:String) { // Enable the "hand" mouse pointer for direct interactions with this object // (The buttonMode variable is inherited from Sprite.) buttonMode = true; // Disable mouse events for this object's children. Otherwise, mouse events // would target the textfield, and the hand pointer wouldn't appear. // (The mouseChildren variable is inherited // from DisplayObjectContainer.) mouseChildren = false; // Define the text formatting used when this object is *not* // under the mouse pointer upFormat = new TextFormat("_sans",12,0x006666,true); // Define the text formatting used when this object *is* // under the mouse pointer overFormat = new TextFormat("_sans",12,0x009999,true); // Create the clickable text field, and add it to this object's // display hierarchy text = new TextField(); text.defaultTextFormat = upFormat; text.text = label; text.autoSize = TextFieldAutoSize.CENTER; text.selectable = false; addChild(text); // Register to be notified when the mouse moves over this object addEventListener(MouseEvent.MOUSE_OVER, mouseOverListener); // Register to be notified when the mouse moves off of this object addEventListener(MouseEvent.MOUSE_OUT, mouseOutListener); } // Disables mouse event notifications for this object public function disable ():void { // (The mouseEnabled variable is inherited from InteractiveObject.) mouseEnabled = false; } // Triggered when the mouse moves over this object public function mouseOverListener (e:MouseEvent):void { // Apply the "mouse over" text format text.setTextFormat(overFormat); } // Triggered when the mouse moves off of this object public function mouseOutListener (e:MouseEvent):void { // Apply the "mouse not over" text format text.setTextFormat(upFormat); } } }

    food button variables

    in VirtualPetView, one variable for "Feed Sushi" button, one for "Feed Apple"

    private var appleBtn:FoodButton; private var sushiBtn:FoodButton;

    create food buttons

    method to create buttons

    private function createUI ():void { // The Feed Apple button appleBtn = new FoodButton("Feed Apple"); appleBtn.y = 170; appleBtn.addEventListener(MouseEvent.CLICK, appleBtnClick); addChild(appleBtn); // The Feed Sushi button sushiBtn = new FoodButton("Feed Sushi"); sushiBtn.y = 190; sushiBtn.addEventListener(MouseEvent.CLICK, sushiBtnClick); addChild(sushiBtn); }

    call createUI() from VirtualPetView constructor:

    createGraphicsContainer(); loadGraphics(); createNameTag(); renderCurrentPetName(); createUI();

    button click handlers

    private function appleBtnClick (e:MouseEvent):void { // Feed the pet an apple pet.eat(new Apple()); } private function sushiBtnClick (e:MouseEvent):void { // Feed the pet some sushi pet.eat(new Sushi()); }

    disable interface when pet dies

    new VirtualPetView method:

    private function disableUI ():void { appleBtn.disable(); sushiBtn.disable(); }

    update petStateChangeListener():

    private function petStateChangeListener (e:Event):void { if (pet.getPetState() == VirtualPet.PETSTATE_DEAD) { disableUI(); } renderCurrentPetState(); }

    was it worth it?

    timeline vs oop:

  • timeline faster for small projects
  • oop more flexible, extensible, resusable
  • oop benefits:

  • highly modularized: each component can be developed and tested individually
  • separates artwork from code; developer works separately from designer
  • runtime skinning
  • multiple representations: text, visual, audio
  • scales up well: a "pet manager" could control dozens of pets
  • bonus round 1: the flex debugger

    select Window > Perspective > Flex Debugging

    select Run > Debug VirtualZoo

    break points (app also pauses for errors)

    inspecting variable values

    stepping through code (check flow, branches, and values)

    bonus round 2: the flex profiler

    select Run > Profile VirtualZoo, then Resume

    memory leaks: check "Live Objects"

  • Instances (number of objects in memory)
  • Cumulative Instances (number of objects ever created)
  • Run Garbage Collector button
  • Take Memory Snapshot, then double-click any class to see references to instances
  • method execution bottlenecks

  • Capture Performance Profile button
  • Calls: number of executions
  • Avg. Cumulative Time: ms this method, and all nested calls, took per call
  • Cumulative Time: ms this method, and all nested calls, took for all calls
  • helps expose the "expensive" code
  • bonus round 3: flash authoring/builder workflow

    create graphical assets in flash, code in flex

  • assign classes to symbols
  • export .swc file
  • link to .swc file using library path
  • compile-time integration: merge into code
  • run-time integration: external, then load .swf into app domain (megaphone example)
  • bonus round 4: source control

    subversion or cvs

    subclipse plug-in

    free hosting options available for small projects

    benefits:

  • revert to old versions
  • revision history, with comments
  • guaranteed backup
  • work on multiple machines
  • make changes offline, auto-upload differences when online
  • merge changes by multiple developers
  • that's all folks!

    audience.thank()

    things to try:

  • add sounds
  • make a multi-pet zoo
  • use .swfs for animated graphics
  • add another type of food