Ultrashock Tutorials > Photoshop / Flash 8 > Pixel Art - Part IV  
 
by Peter Hansén, Abscess.se
 
Pixel Art - Part IV: Interactive Isometrics in Flash
 

 Introduction: Pixel Art - Part IV Interactive Isometrics in Flash  
 Step 01: Code Overview  
 Step 02: Animation Graphics
 Step 03: Building a User
 Step 04: User Class  
 Step 05: Conclusion

 

Author:
Peter Hansén

  Abscess.se

- discuss this tutorial -

Introduction:
Pixel Art - Part IV: Interactive Isometrics in Flash

Now it is time to add some interactivity to our project! In this last part we are going to add a user and some other small details to the yogurt shop. We will add code to make the user movable by using the arrow keys. This is based on the previous parts of the tutorial series and requires the fla from part three.

1. Code overview

The user is added as a separate object with its own class file (User.as). It has a constructor that requires the IsoWorld object it should be added to, the start tiles x and y value, the user speed and the user width as numbers. It also has two methods, one private called checkTiles() that takes the x and y value of a tile, and one public called moveUser() that takes the requested direction as a string.

1
   4.1. Members of the User class

2. Animation graphics

But before we add a user object or any code we have to make some more graphics. It would be kind of silly to have a user moving around looking like he is floating and always facing the same direction. But since I do not want to spend too much time on this moment I will keep it simple and make just a three image walk animation.

We begin by taking the man we made in part two and putting him in a new Photoshop document. The steps below correspond to image 4.2.

  1. The first step is to remove the arms and feet, putting them on separate layers. I have turned the legs into outlines so we are able to see things behind.
  2. Next we want to determine where the feet should go in a step. Select the Move Tool and the layer with the users right foot. By using the arrow keys move the foot six pixels right and three down (one pixel down and two across is the standard isometric line as learned in the first part). The left foot should go four pixels left and two up.
  3. Drawing the legs is a bit tricky. I did this the same way as I drew the whole man in the second part, numbers three and four of image 2.12. The whole torso should also be lowered one pixel so we get a little bouncing in the movement.
  4. The same goes for the arms, both should be bent just a little bit. In this case where the right leg is in front the left arm should be moved forward and the right back.
  5. Fill the pants with grey color and move the tweaked arms back to their original positions and your man is done! Well, at least one of the six different poses we need.
  6. The last image is what we would have ended up with if we had started with the opposite leg.

2
   4.2. Creating a walk animation cycle

When you have done both of the users step images above you have one angle of the walking sequence ready (we have the standing user image from before). To create the opposite angle we can just make a copy of the men, put them on a new layer and select Edit->Transform->Flip Horizontal in the menu. Unfortunately you can not get the two remaining angles by flipping; you have to draw it yourself. Start by drawing a man standing with his back turned, and then follow the six instructions above again.

Poses
4.3. All images used to create the walking sequence with a 300% sample to the right

The image above shows all different poses you need to make the walk animation. This is what you must have before you start shading the user because if you shade before duplicating and flipping you get incorrect shadows in the end. I also included an enlarged animation.

You could spend days tweaking these pixels, making the animation just right, but we have to finish the tutorial some time, so let us move on.

3. Building a User

Now when you have everything read for creating our user we can open up the flash from the previous part. Start by creating a new movie clip in the library; give it “user” as a linkage identifier name.

Open the movie clip to edit and add four frames, the first one should contain a stop() action. Each frame should also have a frame label: “right”, “left”, “up” and “down”. Next we add the user graphics. Each frame should hold a movie clip with the Instance name “man” that contains the animation sequence for the angle stated in the frame label.

The first frame, with label “right”, should then contain the sequence where the user faces down and to the right. The number of frames for each image is something you have to test to get right. I used two frames for the standing images and three for each step image.

4. User class

Now we have everything we need to program the user. Start by creating a class file called User.as.

The constructor of the User class is pretty similar to the createWorld() method in the IsoWorld class. We start by assigning the new values from world object, speed and width to class variables.

   class User {
       public function User (currentWorld:IsoWorld, startTileX:Number, startTileY:Number, 
                             speed:Number, startWidth:Number) {
          userWorld = currentWorld;
          userSpeed = speed;
          userWidth = startWidth;

Next we calculate the users position by multiplying the x and y start value with the tile width from userWorld. These values are then used to calculate the isometric position like we did in the last part.

  userX = startTileX *   userWorld.tileWidth;
  userY = startTileY *   userWorld.tileWidth;                          
  userXiso = userX - userY;
  userYiso = (userX +   userY) / 2;

Then we want to attach the movie clip we added to the library. It should be added to the same path as the closed tiles did in the createWorld method and to make sure the user is not added in the back (it will be if the last tile in the world array is an open tile) we have to add a line in the end of the createWorld method in the Isoworld class file:

   path = target_mc.front;

We can now attach an instance of our user using userWorld.path.attachMovie() with char as idName and newName and the depth calculated as when we added tiles.

   userWorld.path.attachMovie("user", "user",   userYiso * 100 + userXiso);

Next we create a variable holding the path to the user to make things easier. The x and y value is then set to the corresponding isometric x/y values we calculated before.

   userMC =   userWorld.path.user;                        
   userMC._x = userXiso;
   userMC._y = userYiso;

Finally we tell the user not to move right away by adding a stop function on the man movie clip.

     userMC.man.stop();
     }
   }

Next we have the public moveUser method. It takes just a single string as an argument and moves the user according to that.

   public function moveUser (direction:String):Void {

There are five if statements to check the content of the string received, one for each direction and a fifth for no movement. If we have movement (here we move up) we call the checkTile() method on the tile directly in front of the user by passing the x and y values together with the userSpeed value.

    if(direction == "up"){
        checkTile(userX, userY - userSpeed);

Next we have a second if statement to see if the tile checked is open or not.

    if(leftTileOpen &&   upTileOpen){

If it is we subtract the userSpeed value to the userY variable, tell the user movie clip to go to the “up” label and play.

         userY +=   -userSpeed;
         userMC.gotoAndStop("up");
         userMC.man.play();
     }

The three other if statements for checking movement are almost the same as the one above. The difference is the direction string, the way we calculate what tile to check and what value (x or y) to add or subtract the speed value from.

      } else if(direction == "down")   {
          checkTile(userX, userY +   userSpeed);
          if (downTileOpen &&   rightTileOpen) {
              userY +=   userSpeed;
              userMC.gotoAndStop("down");
              userMC.man.play();
          }
      } else if(direction == "left"){
              checkTile(userX -   userSpeed, userY);
              if (leftTileOpen &&   downTileOpen) {
                  userX +=   -userSpeed;
                  userMC.gotoAndStop("left");
                  userMC.man.play();
               }
       } else if(direction == "right"){
              checkTile(userX +   userSpeed, userY);
              if (upTileOpen &&   rightTileOpen) {
                  userX +=   userSpeed;
                  userMC.gotoAndStop("right");
                  userMC.man.play();
              }

If there is no movement we tell the user movie clip to stop playing its animation.

      } else if(direction == "not"){
             userMC.man.gotoAndStop(1);
      }

What we have done so far is to check what direction to move, look for a solid wall and then change the user position value. Now we use this new value to calculate the isometric position as we did in the constructor.

     userXiso = userX - userY;
     userYiso = (userX +   userY) / 2;

Send the user movie clip to the new x and y value.

     userMC._x = userXiso;
     userMC._y =   userYiso;

Finally we calculate the new depth, also just like before.

      userMC.swapDepths(userYiso * 100 +   userXiso);                     
   }

The last method is the private checkTile(). It is used to check whether a tile is open or not and takes the coordinates of where the user intends to go.

   private function checkTile (xWall:Number, yWall:Number):Void {

First we calculate what position in the two-dimensional array the y values directly in front and behind has. We do this by dividing the y value with the tile width. The value on the opposite side is calculated the same way but with the user width minus one added to the y value.

   upTile = Math.floor(yWall /   userWorld.tileWidth);
   downTile = Math.floor((yWall + userWidth - 1)   / userWorld.tileWidth);

The x values are calculated exactly the same way.

   leftTile = Math.floor(xWall /   userWorld.tileWidth);
   rightTile = Math.floor((xWall + userWidth -   1) / userWorld.tileWidth);

Next we use the above variables to check if the tiles are open.

   upTileOpen = userWorld.getTile(userWorld.worldGrid[upTile][rightTile]).openTile;
   downTileOpen = userWorld.getTile(userWorld.worldGrid[downTile][leftTile]).openTile;
   leftTileOpen = userWorld.getTile(userWorld.worldGrid[upTile][leftTile]).openTile;
   rightTileOpen = userWorld.getTile(userWorld.worldGrid[downTile][rightTile]).openTile;

We also use this function to check what floor we are walking on.

   wallNumber =   userWorld.getTile(userWorld.worldGrid[upTile][leftTile]).changeWall;

We can then show or hide walls according to that result by calling the userWorlds changeWall() method.

     if(wallNumber == 1){
        userWorld.changeWall(_root.myGrid_wall1, "high")
        userWorld.changeWall(_root.myGrid_wall2, "high")
        userWorld.changeWall(_root.myGrid_wall3, "high")
     }else if(wallNumber == 2){
        userWorld.changeWall(_root.myGrid_wall1, "high")
        userWorld.changeWall(_root.myGrid_wall2, "low")
        userWorld.changeWall(_root.myGrid_wall3, "low")
     }else if(wallNumber == 3){
        userWorld.changeWall(_root.myGrid_wall1,   "low")
        userWorld.changeWall(_root.myGrid_wall2,   "high")
        userWorld.changeWall(_root.myGrid_wall3,   "low")
     }else if(wallNumber == 4){
        userWorld.changeWall(_root.myGrid_wall1, "high")
        userWorld.changeWall(_root.myGrid_wall2, "high")
        userWorld.changeWall(_root.myGrid_wall3, "low")
     }
   }

Finally we have to create a user object in the Flash main timeline. This has to be added after creating the world.

   var myUser:User = new User(world,22,22,2,16);

That is it, we have our user. But we have to add one last function that allows us to control the man through the keyboard.

Use the keyboard
I use a really simple function put directly on the timeline. I do not think it needs any explanation.
   function simpleArrowKeyFunction(){
      if (Key.isDown(Key.UP)){
          myUser.moveUser("UP");
      } else if (Key.isDown(Key.DOWN)){
          myUser.moveUser("DOWN");
      } else if (Key.isDown(Key.LEFT)){
          myUser.moveUser("LEFT");
      } else if (Key.isDown(Key.RIGHT)){
          myUser.moveUser("RIGHT");
      } else {
          myUser.moveUser("NOT");
      }
    }

The function is then called by putting it on the movie clip we used to create the world together with an enterFrame event.

   onClipEvent(enterFrame){
       _root.simpleArrowKeyFunction()
   }

Adding some details Now you have your world and a user walking around in it but I think it looks way to clean and boring. To make things more interesting we will add some details and as you might remember we prepared this when we made the world.

Go to the library and open the tiles movie clip. There you should have an empty layer called “detail”. Create a new movie clip on this layer and give it the instance name “tileDetail”. Open this movie clip and add a stop action on the first frame.

Next we just add the detail graphics we want, a frame label should also be added to each frame so the createWorld method knows what frame to go to. Say you want to put a goldfish bowl on the counter. Then we would add the graphics of the bowl to frame two, add “fish_bowl” as a label and create the tile object like this:

   world.addTile(new Tile("counter_tile", false, 0, "fish_bowl"));

You should now see a bowl with your fish in it when you test the movie.

5. Conclusion

This was the last tutorial for this series. I hope everything is working like it should and that you are satisfied with the series. I enjoyed making it and would love to see what you can do. If you have any comments, questions or problems you can go to this tutorial forum thread.

- discuss this tutorial -
 
©2006 Ultrashock.com - All rights reserved