This is part of a series of articles that go over the steps I’m taking to learn Flash CS3. The project’s goal is to develop an RPG similar to those published by Square in the mid/late 1990′s. (Secret of Mana, Final Fantasy VI, etc..)
To recap – for this milestone I’ll be creating our hero and will be able to move him around the screen.
Prototype 01 – Old Tricks
My first prototype was built around trying to remember all of the tricks I used to use back in Flash 4/5. Back in the day we coded straight on the timeline.
I encapsulated the Neco movieClip within a parent movieClip (NecoParent). NecoParent was 2 frames to setup a game loop. Frame 1 would have the game loop logic and Frame 2 had a simple “gotoAndPlay(1);”
My first stumbling block arose when I found in CS3 you can no longer apply actions to button objects. I had planned to create a key logger object that would pass movement information to NecoParent. So instead I put the keylogger logic directly into frame 1 of NecoParent. The flow worked like this…
- Flash movie was started, NecoParent was added to the stage.
- NecoParent Frame 1 would check for a key press.
- If a key was pressed (e.g. Key_right), Neco.gotoAndStop(“walkRight”) was called.
- NecoParent.x and/or NecoParent.y were incremented or decremented depending on the key pressed.
- I also had some really dumb logic that would ensure the full animation was played before stopping on the Stand animations.
Result: necoTest01
Basically crap. Poor response to key presses and the animation is sluggish. But at least I’ve gotten myself familiar with Flash again.
Prototype 02 – Classes and .as Files
Shortly after completing prototype 1 I stumbled across an excellent set of tutorials at asgamer.com. AS3 Flash Games for Beginners. They walked through how to use .as files and setup your movieClips with proper classes. I decided to give it a go.
My Flash file necoTest02.fla used Engine.as while my Neco movieClip was linked to Hero.as. Engine.as simply setup my stage and created an instance of the Hero object.
Within Hero.as I had keypress logic within a function that was called on every ENTER_FRAME event.
- On every frame we check for a key press
- If a key is pressed, move the Hero object around the stage by modifying its x and y values
- If animating= false or if direction is changed, then gotoAndStop(walkingAnimation) and set animating=true
- If a key is released then gotoAndStop(standingAnimation)
Result: necoTest02
Much better! Very responsive control and smooth animation. Plus now my code is significantly cleaner existing in two .as files.
Prototype 3 – Collision Detection & Depth Indexing
Again using the great tutorials on asgamer.com I was able to built a pretty quick collision detection and sprite sorting system.
For collision detection, I made sure that every “Enemy” had a hitbox named “hit”. Within my Engine.as I added each enemy to an array (“enemyList”). Within Hero.as I then added a function that was called on each frame that tested the Hero.hit object against all of the enemyList[i].hit objects (where “i” stepped through the entire array).
I went one step further. When a hit was detected, 4 additional hit objects within the Hero object were tested. This allowed me to determine if I was hitting something on each of the four sides. If Hero.hitRight was colliding with Enemy1.hit, then the Hero would not be allowed to move right, but all other directions continued to be valid.
Depth indexing was even easier. Within Engine.as I simply added all of the objects I wanted sorted to an array (“sortedItems”). On every frame I called a function arrange() that sorted the array based on the individual item’s y value. I then step through each of the objects setting the index accordingly.
Result: necoTest03
Prototype 04 – Making it look more like a game
For my final prototype I decided to extend the concept to make things look a little more game like. I grabbed a screenshot from Seiken Densetsu 3 and created a couple of layers. One layer would be the “ground layer” and would display beneath the Hero at all times. The other layer had sorted items (like the tree or house) that would display above or behind the Hero depending on y values. I threw in some hit boxes for collision detection and voila – and quick and dirty town.
I did have to tweak my collision detection logic a bit when I found a bug, but more of less my code stayed the same since Prototype 3.
Result: necoTest04 (cheesy music warning)
So ends Milestone 1. We have our Hero, now we need to build a real world for him to wander through.
Code
I’m providing the complete code for Hero.as and Engine.as
Hero.as
package com.refrag.necotest03 { //Import our packages import flash.display.MovieClip; import flash.display.Stage; import com.senocular.utils.KeyObject; import flash.ui.Keyboard; import flash.events.Event; public class Hero extends MovieClip { //Setup our class-wide variables private var stageRef:Stage; private var key:KeyObject; private var facing = "down"; private var oldfacing; private var xv = 0; private var yv = 0; private var animating = false; private var canMoveRight=true; private var canMoveLeft=true; private var canMoveUp=true; private var canMoveDown=true; public function Hero(stageRef:Stage) { this.stageRef = stageRef; key = new KeyObject(stageRef); addEventListener(Event.ENTER_FRAME, loop, false, 0, true); } public function loop(e:Event) : void { //If I'm not hitting anything, let me move everywhere! if (!hitTest()) { canMoveRight=true; canMoveLeft=true; canMoveUp=true; canMoveDown=true; } //Keyboard catches if (key.isDown(Keyboard.UP)){ if (canMoveUp) y -= 2; facing = "up"; yv=1; } else if (key.isDown(Keyboard.DOWN)){ if (canMoveDown) y += 2; facing = "down"; yv=1; } else yv=0; if (key.isDown(Keyboard.LEFT)){ if (canMoveLeft) x -= 2; facing = "left"; xv=1; } else if (key.isDown(Keyboard.RIGHT)){ if (canMoveRight) x += 2; facing = "right"; xv=1; } else xv=0; //If I'm not currently animating, or if I've changed directions, lets walk! if (oldfacing!=facing || animating==false){ walk(facing); animating=true; } //If my movement velocity hits 0, I'm going to stop. if (yv==0 && xv==0){ stand(facing); animating=false; } oldfacing=facing; } //My Walk function. Moves the timeline to the right keyframe which contains each animation public function walk(d) { if (d=="left") gotoAndStop("WalkLeft"); else if (d=="right") gotoAndStop("WalkRight"); else if (d=="up") gotoAndStop("WalkUp"); else if (d=="down") gotoAndStop("WalkDown"); else trace("Invalid argument passed to Hero.walk()"); } //My Stand function. Same as walk, but for my standing frames. public function stand(d) { if (d=="left") gotoAndStop("StandLeft"); else if (d=="right") gotoAndStop("StandRight"); else if (d=="up") gotoAndStop("StandUp"); else if (d=="down") gotoAndStop("StandDown"); else trace("Invalid argument passed to Hero.stand()"); } //My hittest function private function hitTest() : Boolean { //Let's pretend that I'm not hitting anything. Let the function prove me wrong. var leftTest = 0; var rightTest = 0; var upTest = 0; var downTest = 0; var hit = false; //Like in the court of law, I can move unless proven guilty of collision. canMoveRight=true; canMoveLeft=true; canMoveUp=true; canMoveDown=true; //Loop through all of the objects in the engine enemy list for (var i:int = 0; i < Engine.enemyList.length; i++) { //If a hit is detected, test my specific hit boxes so I know where I can and cant go if (hitTestObject(Engine.enemyList[i].hit)) { hit=true; if (hitRight.hitTestObject(Engine.enemyList[i].hit)) rightTest++; if (hitLeft.hitTestObject(Engine.enemyList[i].hit)) leftTest++; if (hitUp.hitTestObject(Engine.enemyList[i].hit)) upTest++; if (hitDown.hitTestObject(Engine.enemyList[i].hit)) downTest++; } } //Based on the individual hit tests, set my movability options if (rightTest>0){ canMoveRight=false; x-=2; } if (leftTest>0){ canMoveLeft=false; x+=2; } if (upTest>0){ canMoveUp=false; y+=2; } if (downTest>0){ canMoveDown=false; y-=2; } if (hit==true) return true; else return false; } } }
Engine.as
package com.refrag.necotest03 { import flash.display.MovieClip; import flash.display.Stage; import flash.events.Event; import flash.display.DisplayObject; public class Engine extends MovieClip { public static var enemyList:Array = new Array(); public static var sortedItems:Array = new Array(); public function Engine() { //Create our Hero! var ourHero:Hero = new Hero(stage); sortedItems.push(ourHero); //add it to the display list stage.addChild(ourHero); ourHero.x = 250; ourHero.y = stage.stageHeight /2; //create our "enemies" for (var i:int = 0; i < 4; i++) { var enemy:Enemy = new Enemy(stage, ourHero); //add our enemy to the enemyList enemyList.push(enemy); sortedItems.push(enemy); stage.addChild(enemy); } addEventListener(Event.ENTER_FRAME, loop, false, 0, true); } public function loop(e:Event) : void { //We only do 1 thing every frame. Ensure our depth indexing is right. arrange(); } //Our depth indexing function private function arrange():void { sortedItems.sortOn("y", Array.NUMERIC); var i:int = sortedItems.length; while(i--){ if (stage.getChildIndex(sortedItems[i]) != i) { stage.setChildIndex(sortedItems[i], i); } } } } }







{ 4 comments… read them below or add one }
Hey thanks man, I’m excited to see someone take those tutorials and apply them to something completely different and really learn something from them. You did a great job, the RPG is already looking really slick, nice work.
@Par No thank you! Your tutorials are very well written, clear and concise. Exactly what I needed to get my hands dirty with AS3. My next challenge will be applying your collision detection method to my tile engine. I’ll be sure to post my solution and code samples.
How do you connect the Engine to the Hero? Im new to Adobe Flash CS4 and dont quite understand it.
Thanks, Zach
Hey Zach,
Depends on what you mean. In the Engine.as code, I’m instantiating the Hero with this line:
var ourHero:Hero = new Hero(stage);
Hero (in the example on this page) is a MovieClip I created in my Flash library.
ASGamer has some great examples on connecting playable characters to an Engine. My code on this page is mostly just an extension of what they cover here: http://asgamer.com/2009/flash-game-design-basics-adding-library-objects-to-stage-with-as3
Hope this helps!