First of all, organisation. I want to make a ship, and I know that I’ll be making several different ships, so let’s create a new package. Again, my creativity abounds as I decide to call this package “ships”. So, how does this work?
Well, in my spaceGame directory I create a new directory called “ships”. Therein I’ll create a new actionScript file called “Ship.as”.
Inside the actionScript file I’ll write the following:
package ships
{
import flash.display.Sprite
public class Ship extends Sprite
{
public function Ship()
{
trace("hello world!");
}
}
}
Notice the capitalisation – it’s important. To keep things consistent, I’ll be starting class names with a capital letter, and instance names with a lower case one (I believe this is the convention). What’s an instance? Read on…
So now I’ve written “package ships”. When it comes to compiling, Flash will look for a directory called “ships” whenever this class is imported from another file. How do we import from another file? Well, back in our SpaceGame.as file we’ll replace the code with this:
package
{
import flash.display.MovieClip
import ships.Ship
public class SpaceGame extends MovieClip
{
public function SpaceGame()
{
var myShip:Ship = new Ship();
}
}
}
So what has changed? Well, as well as importing the MovieClip class, we’re now importing our own new class called Ship, from the package “ships”. Then, within the SpaceGame constructor we’re going to make a new variable.
var myShip:Ship = new Ship(): variables can be all sorts of things – hopefully you should know what they are already… things like integers, Numbers, Arrays etc. Well, instances of a class are variables also, and here we’re declaring a new variable, called “myShip” (but it could be called anything: “myShip27”, “i”, “Jennifer”) and it is of type “Ship” (the class we’ve just written).
The command new Ship() means that we create an instance (a specific individual as opposed to a general definition) of the class Ship, and this will run the constructor of class Ship – which in this case just means tracing “hello world” again. If we run it, hopefully nothing will have changed since the last version.
Great Awoogamuffin, I hear you say, what was the point of that? Well, I just wanted to show how to organise the code using packages and whatnot. Let’s actually make a ship, yeah?
The fun bit is next – design a ship. Here’s where flash comes into its own – you create a new symbol (movieClip) in the flash document and do whatever you want with it – you could make it animated or just a simple image. I’m going to keep things simple because in the end I want to have loads of these things flying around.
I won’t go into detail on the drawing and animation aspects of this game – there are plenty of resources online to help you with that – this blog is predominately about programming. But I am going to tell you that in the properties panel for your newly created ship MovieClip, you need to tick the box that says, “Export for ActionScript”. This means that now the ship Image symbol is a class you can use within your code.
notice that you can select what the Base Class is... this is useful if you want to create something which doesn't need a timeline (such as a Sprite or Shape - these are easier on the memory). I'm keeping this image as a movieClip because I'll need the timeline for things like animating the ship exploding, or for more complex ships that change their state - like aggressive to defensive or something awesome like that
[note - as of tutorial 6 some aspects have changed - the blueLightFighterImage is now a sprite, and we have to uncheck the "export on first frame" option. All is explained in tutorial 6. Things change, I guess]
Now let’s go back to the Ship class. We’re going to make it more ship-like (as in, it’s functionality will not be limited to the admittedly unimpressive “hello world”). The ship class extends Sprite, which means it has a position, rotation, and can have other objects added to it. Ultimately, our ship will have all sorts of attributes, such as maximum speed, acceleration, turn rate, ammunition etc.
But first of all we want to attach the image of a ship we made earlier. So now the ship class is going to look like this:
package ships
{
import flash.display.*;
public class Ship extends Sprite
{
private var _shipImage:MovieClip;
public function Ship()
{
_shipImage = new blueLightFighterImage();
addChild(_shipImage);
x = 275;
y = 200;
}
}
}
A few small differences here – let’s have a look at what’s going one.
First of all, I’ve been lazy, and written import flash.display.*. I believe they call the asterisk the wildcard or something (by “they” I mean real programmers), which is to say that it represents everything and anything. In this case it means importing every class in the flash.display package. Strictly speaking you should only import the specific classes you need to avoid confusion involving scary things like namespaces and other things I daren’t read about for fear of getting even more confused on the subject than I already am.
Luckily, we’re just writing a game, and not an incredibly flexible, re-usable framework (they talk about this in the intimidating wikipedia entry on design patterns). I feel guilty and pathetic, but I’m lazy so nyah!
Anyway, we now declare a variable before the constructor.
private var _shipImage:MovieClip: “private” is an access modifier (this link has a nice metaphor involving beer), and it means that access to this variable is restricted to this particular class. The underscore before the name is a naming convention (as far as I can tell) for all variables that are not public. I’ve read a few confused accounts on the internet explaining why this convention exists, but nothing of the blinding clarity needed for me to understand. If anyone reading this wishes to educate me, please do!
Anyway, this _shipImage variable is of the type MovieClip. We then call new blueLightFighterImage() which means we're making an instance of the symbol we created earlier within the spaceGame.fla file. Now, for this to actually appear within our Ship class we need to call addChild() which is a function within the flash.display.displayObject class (hence my importing everything out of sheer lethargy). All visible objects in Flash inherit from displayObject (I think). Now our blueLightFighterImage is visible in the Ship class.
I also set the Ship x and y values (these are inherited from the Sprite class) to 275 and 200 so that the ship appears at the centre of the screen ([0,0] is the top left corner in Flash – I know, that's weird. In flash, y increases as you go down, unlike all those Cartesian maths you did at school. I think Adobe just wants to screw with our heads).
But if we run the game now, we still don't see the ship! That's because we also need to add the myShip instance we created in SpaceGame.as to the display list. So now in the SpaceGame.as class we should add the following:
package
{
import flash.display.*;
import ships.Ship;
public class SpaceGame extends MovieClip
{
public function SpaceGame()
{
var myShip:Ship = new Ship();
addChild(myShip);
}
}
}
Notice I've also been lazy and wild-carded the import statement; that's because the displayObject class is needed for the addChild function, so let's just import everything and hope something fits...
Now run the game, and we should have a spaceGame.swf file that does this:
Yes, that's right. Absolutely nothing. On to tutorial 3!
Some people, for whatever reason, feel obligated to encode multiple meanings into their variables names. Generally, though not usually perfectly correct, this is referred to as "Hungarian notation". Microsoft used this heavily in the past, but has sense smartened up about it. An example of a private member variable string in C++ might look something like this:
ReplyDeletem_szTheString
The prefix here being m_ for "member varaible" and "sz" for a specific type of string. Ultimately this practice makes for rigid, ugly code and the community as a whole seems to be migrating away from it.
The straight underscore prefix is still around, but I believe this is due to programmer ignorance. I think the main reason it exists is because many times you'll want to pass some argument into your constructor, and immediately assign the argument to a member variable of the same name. So:
private var position:Vector2D;
public function classConstructor(position:Vector2D)
{
position = position; // ?? this is a problem!
}
...so people will tack an underscore onto the beginning of the variable name in order to make the assignment more reasonable. What you SHOULD do, and this is in line with the Flex coding standards as well if you're intent on following those, is use the "this." prefix for the assignment:
private var position:Vector2D;
public function classConstructor(position:Vector2D)
{
this.position = position; // works fine
}
I generally use the 'this.' prefix anytime I reference any variables or functions in-class to make things more explicit. No point in "m_" (or just "_") when the language has this sort of thing built in.
Thanks for the informative comment!
ReplyDeleteI've also heard that people use the underscore for variables that have getters and setters applied to them because the name of the getter function and that of the variable can't be the same - it causes an error.
I use getters and setters because it makes life much easier in the future - Before the Tragedy I wasn't using them, and it caused all sorts of nightmares as I realised more and more things needed to be done when a change was made to a variable, and I was writing "changeTarget()" functions when really it could have just been a setter. (as in Player.target = someShip as opposed to Player.changeTarget(someship)).
Though this might not be true for all my private variables, I prefer to be safe. If I ever get to a final version of this game, I might make some variables public if they don't need any extra actions performed when changed, because getters and setters are more expensive, from what I've been able to gather...
Yeah, the getter/setter thing is something I had thought about, but hoped wouldn't come up. I happen to be one of the guys who dislikes getters/setters on a near-religious level. At their most basic, they're restrictive functions with misleading semantics. In a higher, Object Oriented Design respect, they break encapsulation. "Getting" and "setting" actions should be minimized as much as possible in place of more abstract, complex (as in more complex than "return value;" and nothing else) functions.
ReplyDeleteIt sounds like you may have considered public member variables in classes at some point. There's a basic rule to follow here: If your class has only member variables and no member functions, make them all public. If your class has member variables AND member functions, all variables should be private. This comes from the Open/Closed principle: http://www.objectmentor.com/resources/articles/ocp.pdf
Other core principles of Object Oriented Design include Single Responsibility, Liskov Substitution, Dependency Inversion, and Interface Segregation. Similar articles can be found on each of these at objectmentor.com under the "resources" section. It's vital that everyone understand each of those principles in depth.
Thanks for all the input Dough, seriously appreciated. I'm slowly but surely getting to grips with proper OOP, but at the same time I'm trying to avoid over-designing this particular game.
ReplyDeleteAs for getters and setters, most of them I plan not to simply return a value. What do you think of my use of getters and setters for my Vector2D class (tutorial 4) whereby they're getting and setting variables that don't actually exist in the class - such as angle...
Also, I'd love it if you could have a look at my preloader issue (tutorial 6) and inform me of a way I could do it without the awkward main timeline method.
Already learning lots from this guide and you've helped me solved my annoying problem of trying to access the stage when it wasn't around. It felt too messy to just pass it along as a reference from the first frame of the movie. ActionScript? On MY timeline? No thank you! =)
ReplyDelete