Tuesday, December 09, 2008

Flash Tutorial: Part 5 of 21

Classes

Today we're going to talk about classes. Don't worry, we're still talking about Flash... and while you can program Flash like you do C++, we won't, and instead we'll use "class" as something we make up. So what's our definition of class?

Well of course it's similar to a C++ class. A class is a group of functions and methods and properties that are common and often used when dealing with a specific subject. So for example, we could have a Particle Class, Enemy Class, Item Class, and so on. Then the specifics of that class (a specific enemy for example) will "kinda" inherit these methods from the class. But we we're not going to deal with inheritance, and we will kinda define a constructor but you don't have to look at it that way.

We're going to make this lesson pretty brief... actually, what I mean to say is, I'm not going to write a lot, and a lot of the lesson will be you trying out different things, and reading code to see if you can understand it.

Today we're going to cover the Particle Class in detail, and then a little bit about the Enemy Class and the Item Class.

Particle Class

What is a particle? A particle is a piece of graphic that is made, and then it usually travels in some direction, and then it dies. So it could be a fireball, or a rocket, a glitter, an explosion, and more. So to figure out what the Particle Class should look like, we need to figure out what a particle generally does.

All or most particles are created dynamically; most of them travel in a direction; and most of them die. Some of them might change colours, or accelerate, or rotate along their path, or create other particles, or attempt to hit the player or enemies.

So particles have this kind of life cycle:
(0 - initialize)
1 - come to life
2 - live
3 - die
And sometimes, it has death animation, so it might have
4 - death animation
5 - die for real

Perhaps there's also birth animation, but we won't deal with it now. If you really want, you can do that later.

Once again, like we did on the first lesson, we'll use less-efficient methods right now. I might not even teach you the more efficient method.

Anyways, you should already have a particle. You know which one? That's right, the bullet. The bullet is a particle. Make a copy of the bullet movieclip in your library, and call it "particle_class".

The bullet is a good start. On the first frame, it probably already has something like

var xSpeed;
var ySpeed;

And on the second frame you have something like

stop();

this.onEnterFrame = function()
{
        _x+=xSpeed;
        _y+=ySpeed;
        //(and then you have some boundary conditions)
}

And you already have a constructor, or the thing that "constructs" it: the CreateBullet function.

So first, you want to be able to make particles... so copy the CreateBullet function, and call it createParticle, or whatever you like. Give the proper linkage to the particle_class object. Also, in the function, include parameters about the origin of the particle, because it's unlikely that all particles will be things that come out of your body.

Now, remove the boundary condition, or cut and paste it into a temporary place, because chances are, the boundary condition wouldn't be used in all particles.

Okay... so let's do this and test this in steps. For now, we'll make particles when we make bullets, so instead of calling CreateBullet, call CreateParticle. Remember to update parameters as necessary.

Test 1: Test to make sure it still works. It won't have the boundary conditions, but the rest should be okay.

Now, if you already changed your player to have that "graphic" movieclip, the next step shouldn't be too hard. You want to make it so that when you fire a bullet, it will randomly fire either a blue bullet or a red bullet. They will both behave the same way, except one is blue and one is red.

Here's the code I want you to put in, and if you remember your previous lessons, you'll be able to apply it quite easily.

var particleType = 1+random(2);

newbie tip: random(2) will choose a random integer, either 0 or 1. random(1) will always return 0, I think random(0) also returns 0, and so on. So in this case, particleType will receive a value of either 1 or 2.

this variable can be anywhere.. ideally, in the end, this will be a variable you pass into the CreateParticle function.

On Frame 1 of the particle, you'll want another variable... if you want. Something like

var particleType;

In your particle class, move the onEnterFrame frame to frame 3, and on frame 2, you'll need something like

graphic.gotoAndStop(particleType);

Test 2: Try it now. If done properly, you'll get the desired result of randomly shooting red bullets and randomly shooting blue bullets.

Now, go into the graphic of your particle, and you should have 2 frames. Make a new layer, and put actionscript in each of the two frames:

function EveryFrame()
{        trace("whee");
}

Actually, for one frame, put trace("whee");
and in the other frame, put trace("homunculus");

And... can you predict the next step?




In your particle, in your onEnterFrame, alter it so it looks something like

_x+=xSpeed;
_y+=ySpeed;
graphic.EveryFrame();

Test 3: Run the program. If done right... well, you should know by now what it means.

Change the everyframe's to something more practical. NOTE this:

function EveryFrame()
{        _parent._y++;
}

You have to use _parent, or else you're just modifying the graphic, and not the actual position of the particle.

And thus, if you want you can get back the boundary condition:

function EveryFrame()
{        if (_parent._x > 640)
        {        removeMovieClip(_parent);
        }
//and so on
}

protip: I recommend keeping certain particles separate from the particle class, including bullets. If you have enemies checking for bullets the way I have it, you'd rather have the enemy check for 1-10 bullets rather than 1-200 particles. But for now, keep it so that when you press Space, a particle appears rather than a bullet.

protip 2: For testing purposes, feel free to add more frames wherever necessary in the particle class. Later when you want to make it functionally better and also look better, you can change things around.

Challenge 1: Make a new particle that's like an explosion... you can do this in several ways, but I want a circular, yellowish-orange object that grows bigger and fades, and then disappears.

Challenge 2: Make it so that when you fire a red bullet, an explosion appears by the bullet. Do not modify moveTimer or globalcode for this. You may need to use Protip 2 above, as well as make a new function.

Challenge 3: This is probably easier than Challenge 2, but make it so that when you fire a blue bullet, it leaves a trail of explosions. Also, for the blue bullet, make sure it has a death condition so you can get rid of it.

protip 3: For particles, I usually have the x,y spot in the center of the particle, rather than the top left as I do with most of my other objects. Generally, if an object rotates, it's easier if the x,y is in the center.

protip 4: One of the things that keep the game from massively slowing down is the proper removal of movieclips, or in this case, particles. Make sure objects remove themselves properly. If you experience massive slowdown, one cause might be improper movieclip removal.

Challenge 4: If you got Challenge 2, you can probably do this one. Make it when the red bullet dies, it creates an explosion where the bullet is.

http://www.baomon.com/blog/particle_sample_0004.swf

This was a sample project I put together that shows how my particle class works. Note it has many more variables than your particle class.

particle_tutorial_flash8.fla

Here is the FLA - it should be openable in whatever flash you have. Note that if you run it, you'll probably get some errors, but you can see most of the programming - check the globalcode, and check the particle_class.

Enemy Class

enemy_tutorial_flash8.fla

This is an example of the enemy class that I'm using in my current game. Note if you run the program, nothing should happen, but you should still try to understand what the code does. I omitted one enemy, that guy from MM3 that throws the ball and you can only hit him when his eye is open, because his code is somewhat unique and different. For a basic lesson, having additional code for one enemy could be confusing.

That said, this enemy class is a good starting point but it might not be good enough for your game - maybe you have a lot of guys that are "different" like the green MM3 guy.

Note how the "extra" variables are dealt with in this Enemy Class as opposed to the Particle Class. Part of it is personal preference, part of it is how enemies are coded into the game as opposed to particles.

Item Class

Challenge: Create an item class.

What's an item? An item is an object your player can get, like a piece of food to recover health, or a gold piece to add to your inventory. What it does is it is put on the map (usually programmed by the programmer, or have an enemy drop it), and it stays there and waits to be touched by the player. Some items disappear after some time of nothing happening.

Challenge 2: Have it so that when the game starts and you start moving around, 3 different items appear on the map, each of which do something different.

Brain Logic Challenge Time

Here are a series of challenges that will help you learn some concepts and useful things in programming. After this Brain Logic Challenge, you'll learn something useful. It might be frustrating while you try to learn it, but afterwards you'll feel good and you'll have something useful.

Final Challenge Result: Create a game where you have a turret, and you can move the mouse around, and then when you press space, a bullet will fly out of the turret and travel toward the mouse cursor.

Task 1: Make a new project. Put a dot at 200,200 and call it "turret". Put another dot at 400,300 and call it "enemy". Make it so that when you run the project, it will trace("the project is running");

Challenge 1: Make it trace the x distance between the two objects.
Challenge 2: Make it trace the y distance between the two objects.
Challenge 3: Make it trace the diagonal distance between the two objects. Positive or negative, doesn't matter.

Task 2: Make a button, click on it, and open up the actionscript window. Note that this window is different than the timeline actionscript. Type this:

NOTE: when you edit the button below, the autocode will help you out. I can't put "left-arrow SPACE right-arrow" in html so I can't write it. You can do it yourself.

on (keyPress "")
{        trace("space is being pressed");
}

Run the project, and hope it works.

Task 3: Move the code into the button, so that when you press Space, all that trace happens.

Task 4: Instead of using the enemy, we're going to use the mouse now. So references to the enemy's x and y are now replaced with

_root._xmouse
and
_root._ymouse

protip: In your actual game, you might use _root.main._xmouse, or whatever.

Test the project. Move the mouse around, and hope it works.

Task 5: Put a nose on your turret.

Challenge 4: Make it so when you press Space, the turret rotates so that it points to your mouse cursor.

This challenge might be tough, or easy. You may need Math.Sin, Math.Cos, Math.PI, and if statements. Also I hope you remember how to convert from radians to degrees.

Challenge 5: Make it so that when you press Space, a bullet is created and it travels toward the mouse cursor, at 12 pixels per frame.

And you're done!

And now you know so much about Flash.

In the lessons to come: Arrays, more about user inputs, color, preloader, user interface, a little bit about sounds and graphics, and artists

Wednesday, December 03, 2008

Flash Tutorial: Part 4 of 21

In this Lesson: Multiple Walls, "Quadrants", Scrolling, Flash Placements, and Wait

if (Key.isDown(Key.LEFT))
{            if (CheckWalls(-moveSpeed,0) ????)
            {            MovePlayer(-moveSpeed,0);
            }
}

The ???? up there can be any number of things. It could be == true, == false, or, what I am using right now, is > 20.

Protip: If you have a boolean value, you don't need to compare it to anything in an if statement. I mean, if(amIDoneYet == true) is the same as if(amIDoneYet).

Newbie tip: Useful syntax for if statements include ==, >, <, >=, <=, !=, &&, and ||.

In the last lesson, you used the square brackets to tell flash to modify different objects' properties, where the object's name was a combination of constant and variable name (for example, "enemy"+i).

So if you can modify a property like that, you can surely access the object like that. So instead of using hitTest with ledge, you can now do it with ledge0, ledge1, ledge2, etc. Therefore, you now modify your CheckWalls method to look something like this below. I'm using the method that returns an integer.

function CheckWalls(xs,xy)
{
            var hittingWall = 50;
            _root.main.player._x+=xs;
            _root.main.player._y+=ys;
            for (var i=0; i < _root.gc.numLedges; i++)
            {            if (_root.main.player.graphic.hitbox.hitTest(_root.main["ledge"+i]))
                        {            hittingWall = i;
                        }
                        
            }
            _root.main.player._x-=xs;
            _root.main.player._y-=ys;
            return hittingWall;
}

//you can return true, or false, but I'm returning an integer instead - because it's more useful than just a boolean value. You can do whichever you want.


Protip: This is a tip that I know about, and I SHOULD use more, but I don't... but I should >_>. By now, your code would probably be a bit different than mine. I have _root.main.player.graphic.hitbox, while you might just have _root.main.player. So what can you do? You can use a variable. In your first frame of globalcode, or if you want, first frame of _root, you can try:

var gamePlayer = _root.main.player.graphic.hitbox;

That's right. You can put objects or movieclip refernces into variables. Try it out if you want.


and of course, the MovePlayer function is

function MovePlayer(xs,xy)
{            _root.main.player._x+=xs;
            _root.main.player._y+=ys;
}

Try it out. Make multiple ledges (maybe 4-10) and see if it works. Make them different sizes, make some of them connect, do whatever. Remember to name them properly and assign any variables correctly.

"Quadrants"

I'm not sure how other programmers deal with this, but I call them quadrants. Long story short, if you're making a big game, you'll probably have over a thousand walls.. and you're not going to tell Flash to check for a thousand walls every frame. That's 30,000 operations per second, which is pretty inefficient, considering that you'll probably only see 1-20 walls on the screen at any given time. My solution: Quadrants.

A quadrant is a section of the game, and each quadrant has a finite amount of walls (among other things). So when you do the loop, instead of checking for 1000 walls, maybe instead you'll look for 10, 15, or 20. If you're making a zelda game where each screen is unconnected to other screens, maybe 1 quadrant is one screen. If you're making a sidescroller, maybe your quadrants aren't as specified.

FIRST, if you're already making another game that wouldn't need to use quadrants, save it as something else. If you're going to use this tutorial to the fullest, you're gonna want to make quadrants, but there's no point doing that if you're having a game that doesn't need it. If you've already started on another game idea, you may want to start a second game idea that would use these quadrants.

Now, we can start implementing quadrants. First, go into your globalcode, and use one of these:

var cq = 1;                        //current quadrant
_global.cq = 1;                        //current quadrant

The above is for the sidescroller. If you're make a zelda game, you may want to use this:

var cx = 1;
var cy = 1;

or

_global.cx = 1;
_global.cy = 1;

//note you can use 0 instead of 1, it's up to you

If you're making a zelda game, you'll probably need a 2dimensional quadrant... space... thing...

Next, you'll want to rename your current ledges so they make use of this information. So some ideas are:

q1ledge0
q1l1

or

q1x1yledge0
q1x1yl0
q1x1yw0

or whatever. Ledge, Wall, whatever. Only bad thing about ledge is that the lowercase L looks like a 1, so you may want to start using the w.

FROM THIS POINT ON, WE'RE WORKING WITH A ZELDA GAME, so WE'LL USE THE TWO-DIMENSIONAL QUADRANT SYSTEM.

Now you need to apply it in the code. I would usually apply a CHALLENGE here, but instead, I'll type it out. However, you should have a good idea of what you need to do before you scroll down.

The old usage was: _root.main["ledge"+i]

The new usage is below - try to figure it out before you scroll down:







_root.main["q"+_root.gc.cx+"x"+_root.gc.cy+"y"+"w"+i]

The +"y"+"w"+ is redundant above, but I just wanted to break it down for you. Fix it to just +"yw"+, unless you like it the old way.

After this, try out your game. If done correctly, everything should still work.

Now we're going to test other quadrants. Change the names of the some of the walls so that it has a different quadrant number, so for example, change
q1x1yw3 to q2x1yw3.

Test the game, and you should be able to go through that wall now.

Now, make a new object, like a rectangle or something, put it into your main, and give it the usual onEnterFrame loop, with

if (this.hitTest(_root.main.player.graphic.hitbox))
{            _root.gc.cx = 2;
}

And another one to change it back

if (this.hitTest(_root.main.player.graphic.hitbox))
{            _root.gc.cx = 1;
}

Test the project. If it works properly, then you can just hit the new object and it will change quadrants for you, and thus enable and disable different walls for you.

For the time being, you probably don't have to worry about quadrants too much, unless you want a big playable level. At least, you should do some scrolling first.

Scrolling

Task 1: Go into your main and place your player at the center, which should be about 300,220.

Challenge: Make it so that when you move around, the player stays at the center of the screen, and everything else scrolls.

Hint 1: Edit some code in the moveTimer.

Challenge 2: After you get this to work, you can try to make scrolling work the way you want it to.

Flash Placements

I'm not sure how other programmers deal with this either... but long story short, the flash stage editing document... doesn't have infinite width or height. I mean, if you zoom out, you can see the boundaries. You can try putting some walls around, try moving them off the boundaries, and you'll make the bounds a bit bigger, but eventually, it stops.

Of course, you don't want to stop the level there. You want it to keep going. How do you do it? Mass Moving.

You create a portion of level where you can, and then you use code to move it.

First of all, in main, make a second frame, with stop(); at the top. This second frame is a keyframe for the actionscript, BUT NOT for anything else. Everything else should just be there on frame 2, not have a keyframe.

This is a precaution we're putting in... flash is a bit confusing with things existing in different frames...

For now, this code can be used on frame 1, so we'll put it there. Ya know what... I'll give you some piece of code, and I'll just pretend you can understand it.

for (var i=0; i < _root.gc.numLedges; i++)
{            this["q4x1yw"+i]._x+=3000;
            this["q4x1yw"+i]._y+=700;
}

Quite straight forward I think, or I hope.

Wait

Wait is another function made by me, which I learned from other places. I think you can use it as a function, but I just retype the whole thing whenever I need to use it. We use this when we need to time something, or we have a cutscene and we need it to stop at a part for a few frames, and so on.

Anyway, for this part, make a new project. As usual, Actionscript 2.0, Flash Player 7.0, 30fps, etc.

Make an object (circle or square or whatever, doesn't matter). You can make a moveTimer, or you can put code into the object itself, doesn't matter since it's a test project.

Our goal is to make a little movie, where the circle starts at the top left corner, waits there for 2 seconds, then moves to the right for exactly 2 seconds, and then stops and waits there for 2 seconds, and then goes down for 1 second, and then stops.

You might already know how to do this. Anyway, we're not going to use the moveTimer for this, and we'll just put everything into the movieclip itself.

On the first frame, just declare a variable:

var timeWait = 60;

protip: If you want, you can put the 2nd frame in the first frame, but for organization, we'll do it this way.

When the movie clip plays, it will play the first frame, and then get to the second frame. On the second frame, we stop it, and:

stop();

this.onEnterFrame = function()
{            //do nothing
            timeWait--;
            if (?????)
            {            ?????
                        ?????
            }
}

Take a few minutes or seconds to think about it, then scroll down.



stop();

this.onEnterFrame = function()
{            //do nothing
            timeWait--;
            if (timeWait <= 0)
            {            delete this.onEnterFrame;
                        play();
            }
}

So for 60 frames (2 seconds), you do absolutely nothing, and then you can go onto the next frame, where you might have



timeWait = 60;
stop();
this.onEnterFrame = function()
{            _x+=2;
            timeWait--;
            if (timeWait <= 0)
            {            delete this.onEnterFrame;
                        play();
            }
}

Get it? Good.

Challenge: Complete the rest of the steps to make this move as described, or do whatever you want!

Protip: In all cases so far, we've used this.onEnterFrame following a stop(); You don't need to use stop();, but for our purposes, we've used stop(); Maybe later on you can try it without stop();.