Pixel Manipulation With AS3
A bit of code that I developing for creating my level select screen involves manipulating individual pixels from an image. Here’s what my level select screen looks like right now.
To properly explain how I came up with these images, I should explain how I store my maps. I have a standard map sprite sheet that I use for all the levels. Each level map is stored as an array of tiles, with each tile being identified by that tile’s image position on my sprite sheet. Here’s what that sprite sheet looks like.
So what I wanted to do was come up with a color for each tile on the sprite sheet and just use that one color to represent that on the mini-map. I didn’t want to have to recalculate this color every time I changed the sprite sheet, so I created some code to average the RGB values of each sprite. Here’s the code I used to do that.
var tempBitmap:Bitmap = new LevelRegistry.Img_Map1(); for (var y:int = 0; y < tempBitmap.height; y += BaseLevel.yWidth) { for (var x:int = 0; x < tempBitmap.width; x += BaseLevel.xWidth) { var rValue:Number = 0; var gValue:Number = 0; var bValue:Number = 0; for (var altX:int = 0; altX < BaseLevel.xWidth; altX++) { for (var altY:int = 0; altY > 16 & 255; var tempPixel = tempBitmap.bitmapData.getPixel(x + altX, y + altY); rValue += tempPixel >> 16 & 255; gValue += tempPixel >> 8 & 255; bValue += tempPixel >> 0 & 255; } } var pixelCount:int = BaseLevel.xWidth * BaseLevel.yWidth; var newR:uint = uint(rValue / pixelCount) * 0x10000; var newG:uint = uint(gValue / pixelCount) * 0x100; var newB:uint = uint(bValue / pixelCount) * 0x1; var newColor:uint = 0xff000000 + newR + newG + newB; this.averageMapColorArray.push(newColor); } }
So this starts with the upper-left corner of each sprite and then separately totals the R, G and B values. The >> operator is the right shift operator that shifts pixel’s RGB values by 16, 8 or 0 bits. Then the & is a bit-wise “and” operator. Doing an “& 255” gives us only the first 8 bits, which is the total length of individual RGB values in AS3. Then we average the RGB values and add them back together along with 0xff000000 which is a solid alpha channel in flixel. The last step after this is just to read the tile map data and create a new mini-map image for each level.
var output:Bitmap; for ( var i:int = 1; i <= LevelRegistry.levelCount; i++) { if(LevelRegistry["CSV_Map".concat(i)]) { var tempMap:String = new LevelRegistry["CSV_Map".concat(i)]; var rowArray:Array = tempMap.split("n"); output = new Bitmap(new BitmapData(rowArray[0].split(",").length * 3 + 2 (rowArray.length - 1) * 3 + 2, true, 0xff000000)); for (var rowNum:int = 0; rowNum < (rowArray.length - 1); rowNum++) { var pixelArray:Array = rowArray[rowNum].split(","); for (var colNum:int = 0; colNum < pixelArray.length; colNum++) { var currentColor:uint = averageMapColorArray[pixelArray[colNum]]; var colorArray:BitmapData = new BitmapData(3, 3, true, currentColor); output.bitmapData.copyPixels(colorArray, new Rectangle(0, 0, 3, 3), new Point(colNum * 3 + 1, rowNum * 3 + 1)); } } j = (i - ((i - 1) % 5) - 1) / 5; var tempButton:BitmapButton = new BitmapButton(40 + ((i - 1) % 5) * 70, 100 + j * 70, 0xff000000, output, this.startGame, i); add(tempButton); } }
One thing to note in this is “LevelRegistry[“CSV_Map”.concat(i)]”. LevelRegistry is my class that stores all the references to level maps, enemy lists, enemy paths, level counts, etc. Calling “ClassName[variableName]” returns a reference to the static variable in ClassName that is named variableName. It’s a nice way to programmatically cycle through a list of static variable names. This way I can add or remove levels to LevelRegistry without having to reprogram the rest of the game.
Debug Menu in Flixel
So for the first feature to review, let’s look at the last one I added, a debug menu.
The textbox on the left displays the details of the current enemy wave list. It’s only there for informational purposes. The one next to it is where the real business is. It can be used to edit and then reset the current wave list. Assuming my towers are already balanced (they’re not), I can use this to tweak the difficulty curve of each level while still in the game. This saves having to re-compile every time I need to update the enemy list for a level.
The other things I can do from this level are remove all the towers from the current level and reset the funds available in the current level. Again, these is used to reset the level state for testing.
The other menus I’d like to add would let me change tower and enemy stats on the fly in order to tweak game balance without having to re-compile. Obviously this menu won’t be in the production release, but it will help immensely during development.
Now, on to the code. My flixel game state is set to a zoom level of 200%. This makes the sprites look a lot better, but text tends to look like crap. My solution is to code all the debug textboxes as regular AS3 textfields overlain on top of the actual game. Here’s the constructor on my DebugMenu class.
public class DebugMenu extends FlxGroup { public var bgSprite:FlxSprite; public var waveDisplay:TextField; public var waveEntry:TextField; public var moneyText:TextField; public var resetTowerButton:AltButton; // Custom button class public var resetWaveButton:AltButton; // extending FlxSprite public var setMoneyButton:AltButton; // public static var width:int = 400; public static var height:int = 150; public function DebugMenu() { bgSprite = new FlxSprite(0, 150, null); bgSprite.makeGraphic(DebugMenu.width, DebugMenu.height, 0x00cccccc); var gfx:Graphics = FlxG.flashGfx; gfx.clear(); gfx.lineStyle(1, 0x000000, 1); gfx.beginFill(0xffffff, 1); gfx.drawRoundRect(0, 0, DebugMenu.width - 1, DebugMenu.height - 1, 3); gfx.endFill(); bgSprite.pixels.draw(FlxG.flashGfxSprite); bgSprite.dirty = true; bgSprite.exists = true; this.add(bgSprite); waveEntry = new TextField(); waveEntry.x = 220; waveEntry.y = 310; waveEntry.width = 200; waveEntry.height = 200; waveEntry.setTextFormat(new TextFormat(null, 6, 0x000000)); waveEntry.selectable = true; waveEntry.border = true; waveEntry.type = TextFieldType.INPUT; waveEntry.multiline = true; FlxG.stage.addChild(waveEntry); //same as above for waveDisplay and moneyText resetTowerButton = new AltButton(215, 185, 0xcccccc, "Reset Towers", 50, 30, this.destroyAllTowers); resetTowerButton.label.color = 0x000000; this.add(resetTowerButton); resetWaveButton = new AltButton(215, 215, 0xcccccc, "Reset Waves", 50, 30, this.resetWaveList); resetWaveButton.label.color = 0x000000; this.add(resetWaveButton); setMoneyButton = new AltButton(265, 155, 0xcccccc, "Set Funds", 50, 30, this.setFunds); setMoneyButton.label.color = 0x000000; this.add(setMoneyButton); }
As you can see, all the Flixel elements are added directly to DebugMenu, an extension of FlxGroup. This makes it really easy to show or hide them. On the textfields, making them selectable allows copy/paste operations. The type has to be set to TextFieldType.INPUT in order to alter the contents.
Since I’m using a mix of Flixel objects and AS3 objects, I needed to write an additional function to show/hide the debug menu. If I was just using Flixel objects, setting Registry.debugMenu.exists = false; would hide all the elements. Here’s that code:
public function showHide(makeVisible:Boolean):void { if (makeVisible) { this.exists = true; if(!this.waveEntry.stage) { FlxG.stage.addChild(this.waveEntry); } if(!this.waveDisplay.stage) { FlxG.stage.addChild(this.waveDisplay); } if(!this.moneyText.stage) { FlxG.stage.addChild(this.moneyText); } } else { this.exists = false; if(this.waveEntry.stage) { FlxG.stage.removeChild(this.waveEntry); } if(this.waveDisplay.stage) { FlxG.stage.removeChild(this.waveDisplay); } if(this.moneyText.stage) { FlxG.stage.removeChild(this.moneyText); } FlxG.stage.focus = null; } }
Above is a technique I learned recently that I wish I had known when I was coding this whole thing from scratch in AS3. On a textfield, .stage will be null if the object has not been placed and non-null otherwise. This is the most surefire way to make sure you’re not using .removeChild on an object that hasn’t been placed and that you’re not using .addChild on an object that’s already on the stage.
One last interesting piece of code to deal with textfields in AS3. I thought I could just use .text.split(“n”) to divide the text into an array of lines. Unfortunately, .text removes the new line characters, but using .getRawText().split(“n”); gives you the behavior you want in order to split up the text by line.
In order to get all this stuff working the way I wanted, I needed to rewrite my PlayState class in order to update certain parts of the game when the debug menu is visible, other parts when the game is paused and basically everything when the game is running. I might have shown how I did the last two parts before, but here’s what my PlayState.update() function looks like now.
override public function update():void { //NUMPADPERIOD is used to show and hide the debug menu if (FlxG.keys.justPressed("NUMPADPERIOD")) { trace("Toggling Debug Menu"); Registry.debugMenu.showHide(!Registry.debugMenu.exists); this.debugActive = !this.debugActive; } //Freezes everything once the level is over if (gameIsOver) { if (FlxG.keys.justPressed("ENTER") || FlxG.keys.justPressed("P")) { FlxG.switchState(new MenuState()); } } //Only update the debug menu when it's visible else if (this.debugActive) { Registry.debugMenu.update(); } //Update the game otherwise else { //PlayState.update gets run at 30FPS //Scaling gameSpeed allows the game to run //At 2x/4x speed in order to fast forward the game //This is a common tower defense device for (var i:int = 0; i < gameSpeed; i++) { super.update(); } //Runs things like checking for mouse clicks, //button presses and allowing tower placement //while the game is paused this.runNonGameUpdates(); } }
So that’s pretty much it for my debug menu. If you’re curious how the AltButton class is set up, I can answer questions in the comments or do a separate post.
Finding Motivation to Finish a Game
It’s been a long time since I posted, so here’s a related joke.
Q: How many people does it take to NOT develop a game? A: Hundreds. One person to not develop it and hundreds to make all the games and books that the one person plays with instead of working on their game.
I’ve been slowly adding things to the game over the last 4 months, but I just couldn’t work up the motivation to make a post until now. I’ll try to post several updates in the near future talking about some of the difficult or fun bits of code I wrote in the last few months. This post is just to talk about the ups and downs of seriously working on a video game.
I have heard many people say that the most important thing for an indie developer to do is to release your games. They said that you will want to keep working on your game until it’s perfect. They said you will have trouble seeing past all the flaws in your game. That you’ll never think it’s good enough. I thought they were all full of shit.
I was wrong.
I went into working on this game saying that I had a reasonable set of features I wanted to include. I was so proud of the first few steps, that I didn’t think I’d ever feel ashamed of showing my game to the public. But as I got closer and closer to having an actual “Game” on my hands, I started to feel less and less confident. When you’re still thinking of your game as a prototype, everything that isn’t broken is a point of pride.
As soon as you start thinking about releasing it, every single flaw sticks out like a sore thumb.
At some point, you have to learn your limits. I can get the buttons and text to look a little better. I can add sound effects and find some music to play during the game. I can even animate the sprites that I currently have. But I can’t make sprites that look much better than they currently do. I can’t design a beautiful looking menu. I can’t write a stirring soundtrack that everyone will want to listen to.
So that’s what my game is going to be. Coherent looking menus, crappy but functional sprites, basic sound effects, basic music and a decent selection of levels. It’s a first game. It will work and I’ll be happy with it. And I will get it released.
I’ve been working on my own game instead of playing other people’s games the last week or two, and I’m very happy with the progress I’ve made in that time. The best advice I can give to anyone else that gets stuck on their first game is to keep pushing through it, because it gets better. My game isn’t ready yet, but I’ve accepted the limits of how good it’s going to be and I know what it’s going to take to get it finished.
Reading Data from a csv file
I’ve got a working tower data importer for this game. Upgrades are also working and I managed to fix some bugs. Here’s what the game looks like right now.
My tower data is stored in a csv now. CSV stands for comma-separated values. It’s a simple spreadsheet format that gets stored as a text file, which makes it really easy to read. Writing the code to read the file wasn’t hard at all, though I did have some weird issues with Excel. For some reason, the first time I save a file as a csv, Excel saved it as tab-delimited instead of comma-delimited, so it didn’t work at first. Once I updated the csv manually, it worked perfectly. Here’s a sample of my data csv:
3,levels,bulletType Standard,4,0 fireRate,damage,range,cost,image 1000,0,0,0,StdTowerSprite 15,10,50,100,StdTowerSprite 12,13,60,200,StdTowerSprite 10,20,70,400,StdTowerSprite
So, to explain how I set it up: The first value is the number of towers available. The second and third were just headers for the data below it so I could keep track. The second line is the name of the first tower, followed by the number of levels and the type of bullet it produces. The next row is just headers again so I could actually read the csv without confusing myself. The next 4 rows are the actual data for each level of tower. I include fire rate, which is actually the cooldown between shots, damage, range and cost. Last is the image to use for that level, so that I can change the tower’s appearance as the tower levels up.
And now the code to read the file:
var rawStatsString:String = new TowerStatsCsv; var currentRow:int = 0; var rawStatsArray:Array = rawStatsString.split("n"); var rawHeaderArray:Array = rawStatsArray[0].split(","); for (i = 0; i < rawHeaderArray[0]; i++) { currentRow++; currentHeaderArray = rawStatsArray[currentRow].split(","); currentRow++; towerStats.push(new Array()); towerStats[i]['bulletType'] = currentHeaderArray[2]; towerStats[i]['name'] = currentHeaderArray[0]; for (j = 0; j < currentHeaderArray[1]; j++) { currentRow++; currentRowArray = rawStatsArray[currentRow].split(","); towerStats[i][j] = new Array(); towerStats[i][j]['damage'] = currentRowArray[damageCol]; towerStats[i][j]['fireRate'] = currentRowArray[fireRateCol]; towerStats[i][j]['range'] = currentRowArray[rangeCol]; towerStats[i][j]['cost'] = currentRowArray[costCol]; towerStats[i][j]['image'] = currentRowArray[imageCol]; } }
Most of this is pretty self-explanatory. First I read the contents of the csv into a string. .split(“n”) splits the string into an array of rows. .split(“,”) gives me an array containing each of the “cell” values of that row.
I’m struggling with what to work on next. I’m definitely thinking I want to get a functional main menu with level select and a save system to track level progress. Definitely thinking about adding some sound back in to the game, but not sure if I want placeholder stuff again, or something that could actually be used for the final game. I’m not sure if I want to add some abilities for the player to use during the game to give the player something else to do. Also considering adding a couple different enemy types and maybe one additional tower type. Any feedback on what I should add next?
Moving Groups of FlxSprites
Now we’re getting somewhere. I’ve got enemy and tower status menus working. This is about as much as I had before switching to Flixel, so from here on out it’s real progress again. Here’s a video of the current game.
The status menus were a real challenge. In regular AS3, I would just add bunch of different objects to a sprite, and those objects would all move with the sprite. You can’t add objects onto a FlxSprite like a regular AS3 sprite. I thought that maybe I could create a FlxGroup and add all my objects to that and they would all move together, but I was wrong.
Fortunately, FlxGroups were the right way to head. You can iterate over all objects added to a FlxGroup. If you create a baseline FlxSprite that will function as your upper-left corner, you can move that object and then move all the objects in your FlxGroup in relation to that sprite. Here’s the code I used to move my StatusWindow group around:
public function move(point:FlxPoint):void { var startPointX:int = this.bgSprite.x; var startPointY:int = this.bgSprite.y; for (var i:int = 0; i < this.length; i++) { this.members[i].x = point.x - startPointX + this.members[i].x; this.members[i].y = point.y - startPointY + this.members[i].y; } this.upgradeButton.label.x = this.upgradeButton.x; this.upgradeButton.label.y = this.upgradeButton.y + 3; }
Interesting note about that, FlxButtons can be moved using their x and y, but for some reason, the labels don’t move with them and have to be moved separately.
This worked perfectly to move the window to based on a FlxPoint. In order to get the window to follow an enemy as it moves, I had to add a target variable within the StatusWindow class. This tells the window which object to follow around the screen. Unfortunately, x and y coordinates are Numbers (AS3 version of a Float) and they get rounded to an int before being drawn. For some reason, this can cause stuttering when you’re telling one object to follow another. To fix this, I round the x and y coordinates of the followed object before telling StatusWindow to follow it. Here’s the code for that:
public function findBestLocation():FlxPoint { return new FlxPoint(Math.round(this.target.x - 50), Math.round(this.target.y - 70)); }
This function will later be used to keep the window on the screen, without flowing onto the menus or off the edge of the screen. For now, it just offsets the location of the target object.
Oh, and I almost forgot that I added win and loss conditions to the level. You can see at the end of the video that a little text appears. It says, “You win!!! Press enter to return to menu.” That’ll have to get changed at some point, but it works. There’s also a loss condition when your invisible health drops to 0 that also directs you to return to the menu. They both switch a gameIsOver Bool to true in my PlayState. This causes the game to skip the update loop and instead checks to see if the user is pressing enter to return to the menu. Here’s the top of the PlayState’s update code now:
override public function update():void { if (gameIsOver) { if (FlxG.keys.justPressed("ENTER") || FlxG.keys.justPressed("P")) { FlxG.switchState(new MenuState()); } } else { for (var i:int = 0; i < gameSpeed; i++) { super.update(); } this.runNonGameUpdates();
I’ll be adding the rest of the stats to the bottom menu, along with actual buttons to press to change game speed. Then I think I’ll export my tower stats to a csv file or something and make the game import the stats. That will make it easier to balance the game later. While setting that up, I’m going to add an ability to declare several levels for each tower so they can be upgraded.
Overriding Inherited Functions in Flixel
I now have working tower select buttons on the side menu and the ability to pause, unpause and speed up the game working again. In order to get these abilities working, I had to override Flixel’s default update() function on PlayState (an extention of FlxState). Things can get messy real quick when changing those inherited functions, so I’ll share some of the issues I ran in to, but first, here’s a video of the game’s current status.
First of all, when I override the update() function for a FlxSprite, I’m usually trying to add behavior without removing the default behavior. To do this, I start with this basic function override:
override public function update():void { super.update(); //Add new code here }
All this does is override the inherited update() function, with a new function that calls super.update() (the inherited version of update). You can then add any additional code as needed.
My favorite addition this iteration is the ability to speed up and slow down the game. I did this by rewriting PlayState’s update() function to this:
override public function update():void { for (var i:int = 0; i < gameSpeed; i++) { super.update(); } }
This was a really simple way to add this feature. It just runs the update code as many times as needed per frame in order to reach the requested speed. Note: if gameSpeed is 0 or less, the update function won’t be run at all, effectively pausing the game.
Now, for anyone that’s played a standard Tower Defense game, you’ll know that you usually want to place some towers before the enemies start coming. To do this, the game should start in a “paused” state and then let you “unpause” to start the enemy waves. If you haven’t played a Tower Defense game, think of it like the Sims. You go into build mode to add your furniture so that the Sims aren’t getting in your way while you place things. Having this pause function lets you do that.
Unfortunately, I placed the code that enables you to press buttons and that let’s your currently selected tower follow your mouse into the TowerMenu.update() and Tower.update() functions respectively. Those functions are called from FlxState’s update function. And when gameSpeed is set to 0, FlxState’s update function is never called. To make matters worse, the code that tells the towers to fire is located in Tower.update(). So if I just call that function while the game is paused, the towers would keep firing.
My solution was to move those pieces of code directly into the PlayState class. Since the code only affect one specific object (the currently selected Tower) and not every Tower object, it’s okay to take that code out of the Tower class. So here’s my new PlayState.update() function:
override public function update():void { for (var i:int = 0; i < gameSpeed; i++) { super.update(); } this.runNonGameUpdates(); } public function runNonGameUpdates():void { var tileWidth:int = Registry.currentLevel.xWidth; var tileHeight:int = Registry.currentLevel.yWidth; if(this.activeTower !== null) { this.activeTower.x = FlxG.mouse.x - FlxG.mouse.x % tileWidth; this.activeTower.y = FlxG.mouse.y - FlxG.mouse.y % tileHeight; if (FlxG.mouse.justReleased()) { this.activeTower.placed = true; this.activeTower = null; } } Registry.towerMenu.checkMenuButtons(); }
That Registry.towerMenu.checkMenuButtons() is where the code checks if menu buttons are being pressed. Flixel doesn’t have a function to check if a FlxSprite was just clicked, so what I did was add code that would get checked when FlxG.mouse.justPressed() is true and again when FlxG.mouse.justReleased() is true. When pressing the mouse, the code checks to see if your mouse is on top of a button and then sets that as the active button. When the mouse is once again released, it checks to make sure your mouse is on top of the same button. If it is, the button’s clicked function is activated. Afterwards, regardless of whether a button was “clicked” the active button is cleared out. Here’s what the code looks like:
public function mouseButton():int { var i:int; for (i = 0; i < TowerMenu.buttonCount; i++) { if (FlxG.mouse.x >= TowerMenu.startX && FlxG.mouse.x <= (TowerMenu.startX + buttonWidth) && FlxG.mouse.y >= (TowerMenu.startY + (buttonHeight + buttonGap) * i) && FlxG.mouse.y <= (TowerMenu.startY + buttonHeight * (i + 1) + buttonGap * i)) { return i; } } return -1; } public function checkMenuButtons():void { if (FlxG.mouse.justPressed()) { TowerMenu.buttonPushed = this.mouseButton(); } if (FlxG.mouse.justReleased()) { if (TowerMenu.buttonPushed == this.mouseButton() && TowerMenu.buttonPushed >= 0) { Registry.playState.setActiveTower(TowerMenu.buttonPushed); } TowerMenu.buttonPushed = -1; } }
So now that I’ve got this code working, it’s time to add the bottom menu that tracks current funds, health, waves remaining and some victory/loss conditions. I think that will bring me back to where I was before I switched to Flixel. So far, it’s been a lot easier using Flixel. I’ll definitely be using it for any future game jams, because it makes prototyping a lot easier.
Drawing Lines in Flixel
I managed to add bullets and lasers back into the game recently. Lasers turned out to be a much tougher challenge than I thought it would be. I’m still used to drawing graphics using Flash sprites. FlxSprites offer the same functionality, but use different functions. There’s also one additional catch with FlxSprites.
Flixel has this weird habit of inserting a default sprite if you don’t add a sprite yourself. This means that if you try and jump straight into drawing a line, the default Flixel sprite jumps in and then gets partially overwritten by your line.
Here’s the code I had been using:
var tempWidth:int = this.x - this.target.x; var tempHeight:int = this.y - this.target.y; this.drawLine(0, 0, tempWidth, tempHeight, 0xff0000, 2);
Find the width and height of the line. Draw the line from the bullet’s origin to the target’s origin. Seems simple, right? Unfortunately, Flixel only draws graphics within the actual height and width of the sprite. So to fix that, we have to create a sprite big enough to encompass the entire laser beam, move the sprite to the top-left corner of the laser’s length and draw the laser from the tower to the target.
Here’s the new code:
var tempWidth:int = Math.abs(this.x - this.target.x); var tempHeight:int = Math.abs(this.y - this.target.y); this.makeGraphic(tempWidth, tempHeight, 0x00000000); this.x = Math.min(this.x, this.target.x); this.y = Math.min(this.y, this.target.y); this.drawLine(startPoint.x < this.target.x ? 0 : tempWidth, startPoint.y < this.target.y ? 0 : tempHeight, startPoint.x > this.target.x ? 0 : tempWidth, startPoint.y > this.target.y ? 0 : tempHeight, 0xff0000, 2);
Next I’ll be adding the “Wave” type tower back in and adding some UI so you can actually see the stats on the towers and enemies. Almost back to where I had been. Flixel has made the code for this game a lot simpler and taken some of the weird bugs out of it too.
Learning to use Flixel
I kept running into errors associated with objects not being deleted properly with my old tower defense engine. Part of it is because I wasn’t putting in proper destroy commands into my classes. Part of it is that I had no idea how to create a proper game framework. One that registers each object and deletes those objects when they’re no longer needed.
In comes Flixel, a flash-based game engine. Flixel breaks a game into “States” that can be created and destroyed without worrying about objects lingering. So a menu state can be opened to start the game, and when you load the game state, everything from the menu is cleared from memory. More importantly, when you exit to menu and then reopen the game, everything from the old game has been cleared out. No more invisible towers that start shooting at invisible enemies as soon as you load the game (this happened more times than I’m willing to admit).
Flixel is an open source game-making library that is completely free for personal or commercial use. Written entirely in Actionscript 3, and designed to be used with free development tools, Flixel is easy to learn, extend and customize.
-Flixel homepage
Flixel provides a large list of helper classes that takes care of some of the more basic programming needs. FlxGame is the base game class. FlxG controls global variables. FlxState can be extended to create new game states, though you only need one for your game to work. FlxSprite is the basic sprite, but with some animation automation, movement controls and automated cleanup. There’s lots of other classes that I haven’t yet dug in to.
Flixel sprites don’t have an addEventListener() function, which makes it harder to confuse yourself about which object is listening to which other object. From what I’ve read, the proper way to handle event checks is within the update function of each object. Flixel also has a class called FlxGroup which allows you to perform collision checks and tests against every object within the group. I’m currently using the FlxGroup as my enemy manager, tower manager and bullet manager.
I’ve started recoding my Weekend Tower Defense using the Flixel game engine. I’m not sure if recoding this Tower Defense game has just become a way to learn how to make games or if I’m actually planning on releasing this game eventually, but it certainly has been a learning experience. So far I’ve got a basic engine that loads up a map and spawns a series a waves to march across the map’s predefined paths.
Since finding Flixel, I’ve also found DAME a free tool for generating levels from tilemaps. You can create multiple layers in each level each using different tilemaps. It certainly makes creating new maps very easy and provides a nice visual tool for doing so. It supports several different export formats from a basic csv to an AS3 class that makes loading levels a breeze. It’s supposed to allow you to create paths, but I’ve haven’t yet been able to figure that tool out and have instead been defining paths manually.
All in all, I think learning both these tools will help with future games. Flixel especially has been teaching me a better mindset for creating games. It also seems like it’s much easier to prototype a flash game using Flixel than the old way I was doing it, so maybe it will really pay off when I move on to the next game.
Counters, Waves and Lasers
Here’s the current status on my Weekend Tower Defense game.
So the changes since the last video I posted are 2 new tower types, Wave and Laser. The Wave tower hits all enemy in range simultaneously and deals a lot of damage, but fires very slowly. The laser does very little damage per hit, but fires continuously, so it works well for lots of fast, weak enemies.
There’s also a wave counter showing all the upcoming waves so that the player can actually make plans instead of just reacting to what’s coming right now. I’ve finally balanced the amount of money the player has throughout the level with the strength of the enemies, so that you actually have to make a plan in order to finish the level without taking damage. It’s not too hard yet, but it’s a start.
I’ve been playing around this last week with Flixel. It’s an open source game-making library for flash that will hopefully make it a little bit easier to develop games in the future. I’m hoping it will also teach me the “proper” way to program a game, since it supports game states and automates a lot of collision detection, event detection and clean-up. This should at least eliminate my horrible AS3 event scripting in the future (events are a nightmare in AS3 because they can cause a lot of memory leaks).
Recent Comments