ZRPG (2014-01-06)

So at the end of the last year I entered the Oryx Trial with a somewhat poor entry and unsurprisingly didn’t win. I didn’t expect to but the point of entering was to push myself into writing some classes which would be used in my other game. I must say it’s also inspired me to get back into the game development side of things.

Since the new year I’ve made a big push and managed to get the random city pretty much sorted and integrated with the 3D stage. Now although this is pretty much regurgitating previous posts, a lot of work went into rewriting big ol’ chunks of code for it to work more accurately and quicker.

Now the main thing that’s up with this example is that there’s a delay when you hit the edge of the city block, this shouldn’t be a huge issue as I’ve purposely scaled the map down to down to show this. The map is really ten times the size so by the time the player reaches the edge of the map, the neighbouring block would have already loaded (in theory =P) The way the map works is on a 9×9 grid, when the player reaches half way through a block it starts loading in neighbours. The delay is present as threading in AS3 is still in beta so I’m currently daisy-chaining the processing off the main frame rending. When the player moves to a new block, the loaded blocks shift over so only new blocks have to be loaded. This now does mean that you can walk infinitely in one direction and the city will keep generating buildings around you.

There is a minor issue where some buildings don’t get loaded which I shall have to look into but I’m happy with the progress at this point. There are still a few pieces which can be optimised at this stage but I thinking I’ll work on a different piece of the game for now. I’m really hoping to have something fresh for the next post as this is yet another post which contains the same as previous entries but with minor updates.

Roguelike (2013-12-24)

Well I submitted by game last night but have mixed feelings about it.

I’m pleased with the amount I got sorted with the limited time available but I’m not happy with the quality, especially towards the end as I was having to cut a few corners in order to get core functionality in place.

For example, the inventory & UI in general was very slapdash, levelling and experience of the player and items was somewhat unfinished & untested and there were still quite a few noticeable bugs in place.

The main bug is still the shadows, they still allow defogging of tiles behind walls and there’s a weird ghosting issue where you can see ghostly enemies on the fogged tiles but they’re not there when you get to the tile.

Since the last update I’ve added in enemy AI, they’ll intercept you when they see you and go to attack. If they lose sight of you for too long they’ll lose interest and stop chasing. I quickly added in food to heal the player.

Anyway, for anyone wanting to see what was submitted, you can download it here

Roguelike Development (2013-12-08)

Progress of the roguelike has been slow but steady, not really had a huge amount of time to spend on it but happy with what I have managed to do.

I thought I’d post as a slight milestone has been hit and that’s shadows & fog of war. I remember spending many hours before trying to get a good 2D shadow algorithm created and failed due to heavy resource usage but I’ve come up with a new method which works quite well.

It still needs a little tidying up but it’s a start.

Trials of Oryx

While I was looking at sprites for my rogue like game at Oryx Design Lab, it turns out that they’re doing a competition called Trials of Oryx which started on the day I started looking into doing the game, what are the chances?

I’ve taken this chance to accept the obvious fate and join in with the challenge. Could be tricky with a minuscule amount of free time I have for such things but perhaps it’s the inspiration I need to keep at it.

I’ll try to keep updates coming but for now, I’ve got a game to make =)

RogueLike

I love playing the majority of video game genres but think out of all of them, RPG games are probably my favourite game genre and delving down, rogues would have to be my favourite sub genre.

For those not in the know, rogue like games are classic dungeon crawlers which are randomly generated and notoriously difficult.

It’d been a while since I’d played one and once I’d delved in, I became obsessed. After a week of being reminded of the unforgiving difficulty it dawned on me that a started building a dungeon based game many moons ago. I checked my project folder to make sure it was still in existence, luckily it was.

Although it’s in the very early stages, the key functionality of the seeded random dungeon generator was pretty much in place. Other pieces in place is the beginnings of the path finding and sprite loading.

As you can see it’s all rather barebones, the same one sprite is used to represent the player and the enemies, stairs are yellow tiles & doors are green. Where I left it, it was mainly the dungeon generation which was being developed with everything else to be thought about later on.

I’m quite keen to pick this project up again so hopefully I’ll have more updates in the near future while the enthusiasm is in full flow =)

Game Development (October 2013)

Not too much has changed over the last couple of months, nothing at face value at least. I’d been doing some testing of the game on a tablet and wasn’t completely happy with the city transition. On the mobile device there was a slight lag when moving between the city blocks as it was generating each city block instantly which had an effect on the performance. In usual cases this would be solved by threading but unfortunately in AS3, threading is currently only available in beta for android applications and currently in development for iOS.

After some time thinking of solutions I came up with changing the city generation from using standard loops which would run through everything there & then to using the game’s frame rate as the loop instead. This would result in spreading out the processing over multiple frames allowing to maintain a good FPS. The only drawback of this method is that it will take longer for the city blocks to be generated which shouldn’t cause too much of an issue, the city will be large enough that the generation would have finished long before the player reaches the edge of the city.

There’s been two other relatively small changes, the first is random buildings which is quite significant for a random city generator. The second change was so buildings at the edge of city blocks get grouped together into one building instead of fragments.

random city buildings

 

This is a rough example just implementing the classes used for the city. The different coloured squares represent the different building types and the roads poking out the sides are the roads which link up to neighbouring blocks.

Back in the zone

For the last couple of months I’ve decided to take a step back from the game development and work on a new site. When progress seems to stand still for too long I tend to shift onto something else temporarily which usually leads to it ticking over in the back of my mind.

In the mean time I created a new site for snippets (www.snippola.com)… not really relevant but more of a shameless plug =)

Anywho, while working on the site I was also thinking about the game and have made yet another u-turn and decided to go back down the 3D route. I’ve thought of a way to turn it into more of an action RPG style game which should also help preserve the survival aspects I’m wanting to maintain.

Upon getting back into the 3D game development state of mind and starting to implement these new ideas, it dawned on me that there were a couple of underlying issues with the setup of my existing work. Although I did spend a little while trying to correct these issues, the decision was made to start afresh & gradually import the existing work into the new workflow.

Although there’s nothing much to show at this point, there should be soon =)

Game Development (June 2013)

Well it doesn’t really feel like there’s been huge leaps & bounds in anything to see over the last month but the basis of some pretty important pieces have been put into place. http://youtu.be/uHIM6X9JUHc Please note that the video is also zoomed out to what it will be, this is purely to show how the tiles are working. The first noticeable change will be the buildings but they’re merely placeholders for something better which is why they look pretty flakey. They’re currently created based on individual tiles which won’t really work as a whole as they’re going to need to know which building type they’re part of and which tile of that building. There’s also the issue where buildings at the edge of a city section have a wall through the middle. The next thing to notice would be the enemies are back, even if they are very basic in their current state. At present there’s a set limit of five which will randomly spawn on the level load and respawn when they’re knocked off the map. They move very linearly, as in a randomly picked direction and they’ll travel in that direction non-stop unless you get close enough for them to follow you. They can also be targeted and attacked by the player, though this is a very primitive process as it currently stands. What’s Next? Like I mentioned earlier, the buildings really could do with a lot of work but at this point I’m thinking that the basics of different building types would be good to get in place. Another important item on the todo list is obstacles and let the player walk through walls =)

A new perspective

After spending an excessive amount of free time trying to get 3D picking to work without any success I decided to go back and revisit the original plan.

When I first started out planning this game I was originally torn between two very different directions. Firstly there was a more old-school type RPG style using point & click on an isometric field. The other was more of an action RPG where the player would be in direct control of their character. Originally I was leaning towards the isometric as I thought it would work better on touch screen devices and there were certain aspects of gameplay could be integrated. For example, I wanted ammunition to be relatively scarce and for the player to weigh up using valuable ammo from a safe distance or going in for a melee attack. I think this idea works better in a classic RPG style due to weapon familiarity could then be added to provide better accuracy and damage.

Although I preferred isometric and thought it would work better, I chose to go with a top down approach purely out of laziness which was probably a bad step. I knew that if I used a top down setup then it would allow me to adopt a GTA style of sprites where only one direction would be needed and rotated. The isometric view would however need a different graphic for each direction.

During working on the isometric tiles I revisited the AS3 graphics API which I’ve not really looked at much for the last few years and there are a few neat feature which have been added in. One which I’m now using is the ability to use texture triangulation on the 2D canvas in the same way you would in the 3D stage. This means that I can reuse my road texture which could create all road tiles from just one square image.

You may be wondering what’s going on with the tiles, the lime green & white set is the old tiles which was loading in each city block as it was in the 3D version. The dark green tiles is using a new technique which consists on only 100 tiles which dynamically update their positions and tile types based on the player’s position. Doing it in this manner should be more memory friendly as there will be less creating / removing and fewer tiles overall.

Random City Generator Code

I’ve been meaning to release the code for my random city generator for a little while now but just hadn’t got round to it…. UNTIL NOW!

Now I’m not saying this is the best or most efficient method for doing this, this is just the method I’ve created. Below is an interactive version of the generator, click on it to create a new city.

[swf:https://defenestrate.me/wp-content/uploads/2013/04/City.swf 201 201]

As you can see, far from perfect but it does the job. There’s still the issue of two lanes of road running next to each other but this is something I can live with

City.as

package
{

	import city.Tile;

	import flash.display.Sprite;

	public class City extends Sprite
	{
		// Array of all the tiles
		private var _tiles:Array = new Array();
		// Visual display of the map
		private var _map:Sprite = new Sprite;

		// Width of the map to create
		private var _width:int = 50;
		// Height of the map
		private var _height:int = 50;

		// minimum length of a building
		private var _buildingMin:int = 5;
		// maximum length of a building
		private var _buildingMax:int = 10;

		// Exit points, can be predefined or randomly generated
		private var _exitPoints:Object = {
			//'north':new Array(10,20,30,40,50,60,70,80,90,100,110,120,130,140,150,160,170,180,190),
			'north':new Array(),
			'south':new Array(),
			'east':new Array(),
			'west':new Array()
		};

		private const SIDE_NORTH:int = 1;
		private const SIDE_SOUTH:int = 2;
		private const SIDE_EAST:int = 3;
		private const SIDE_WEST:int = 4;

		// The current side we're building up
		private var _activeSide:int;

		// The level away from the boundary
		private var _activeLevel:int = 0;

		// Array containing the buildings details
		private var _buildings:Array = new Array();

		public function City()
		{
			// Let's start from the top
			_activeSide = this.SIDE_NORTH

			addChild(_map);
			this.createMap();
			this.checkExits();
			this.process();

			// Just for cosmetics, move away from the window
			_map.x = 20;
			_map.y = 20;
		}

		private function createMap():void
		{
			// Creates a map of empty tiles based on the width / height
			for (var i:int = 0; i < this._height; i++) {
				this._tiles.push(new Array());
				for (var j:int = 0; j < this._width; j++) {
					var tile:Tile = new Tile();
					_map.addChild(tile);
					tile.x = j*4;
					tile.y = i*4;
					this._tiles&#91;i&#93;.push(tile);
				}
			}
		}

		// Checks to see if exits exist and randomly generates any missing
		private function checkExits():void
		{
			// Randomly generate the exits if they weren't predefined
			if (!this._exitPoints&#91;'north'&#93;) {
				this._exitPoints&#91;'north'&#93; = this.generateExits(this._width);
			}
			if (!this._exitPoints&#91;'south'&#93;) {
				this._exitPoints&#91;'south'&#93; = this.generateExits(this._width);
			}
			if (!this._exitPoints&#91;'east'&#93;) {
				this._exitPoints&#91;'east'&#93; = this.generateExits(this._height);
			}
			if (!this._exitPoints&#91;'west'&#93;) {
				this._exitPoints&#91;'west'&#93; = this.generateExits(this._height);
			}

			// Draw the exits on the map
			var i:int;
			for(i=0;i<this._exitPoints&#91;'north'&#93;.length;i++) {
				this._tiles&#91;0&#93;&#91;this._exitPoints&#91;'north'&#93;&#91;i&#93;&#93;.type = Tile.TYPE_ROAD;
			}
			for(i=0;i<this._exitPoints&#91;'south'&#93;.length;i++) {
				this._tiles&#91;this._height-1&#93;&#91;this._exitPoints&#91;'south'&#93;&#91;i&#93;&#93;.type = Tile.TYPE_ROAD;
			}
			for(i=0;i<this._exitPoints&#91;'east'&#93;.length;i++) {
				this._tiles&#91;this._exitPoints&#91;'east'&#93;&#91;i&#93;&#93;&#91;this._width-1&#93;.type = Tile.TYPE_ROAD;
			}
			for(i=0;i<this._exitPoints&#91;'west'&#93;.length;i++) {
				this._tiles&#91;this._exitPoints&#91;'west'&#93;&#91;i&#93;&#93;&#91;0&#93;.type = Tile.TYPE_ROAD;
			}

		}

		// Function to randomly generate the exits
		private function generateExits(length:int):Array
		{
			var arrReturn:Array = new Array();
			var tmpLoc:int = 0;
			while (tmpLoc < length) {
				tmpLoc += Math.floor( Math.random()*this._buildingMin+(this._buildingMax-this._buildingMin));
				if (tmpLoc < length-3) { 					arrReturn.push(tmpLoc); 				} 			} 			return arrReturn; 		} 		 		 		 		// Function to rotate around the boundary on an inwards spiral 		private function process():void 		{ 			var count:int = 4; 			while (count > 0) {
				// Scan the row and create buildings if needed
				this.scanSide();

				// Switch the active side once we reach the edge
				switch (this._activeSide) {
					case SIDE_NORTH:
						this._activeSide = SIDE_EAST;
						break;
					case SIDE_EAST:
						this._activeSide = SIDE_SOUTH;
						break;
					case SIDE_SOUTH:
						this._activeSide = SIDE_WEST;
						break;
					case SIDE_WEST:
						this._activeSide = SIDE_NORTH;
						break;
				}
				count--;

				// If we've covered all four sides, let's move in a level
				if (
					count == 0 &&
					this._activeLevel < (this._height/2)-2 &&
					this._activeLevel < (this._width/2)-2
				) {
					this._activeLevel++;
					count = 4;
				}

			}
		}

		// Finds empty tiles & populates with a building
		private function scanSide():void
		{
			var xLoc:int;
			var yLoc:int;
			switch (this._activeSide) {
				case SIDE_NORTH:
					for (xLoc=0;xLoc<this._width;xLoc++) {
						if (this._tiles&#91;this._activeLevel&#93;&#91;xLoc&#93;.type == Tile.TYPE_EMPTY ) {
							this.createBuilding(xLoc,this._activeLevel);
						}
					}
					break;

				case SIDE_EAST:
					for (yLoc=0;yLoc<this._height;yLoc++) { 						if (this._tiles&#91;yLoc&#93;&#91;this._width-1-this._activeLevel&#93;.type == Tile.TYPE_EMPTY ) { 							this.createBuilding(this._width-1-this._activeLevel,yLoc); 						} 					} 					break; 				 				case SIDE_SOUTH: 					for (xLoc=this._width-1;xLoc>=0;xLoc--) {
						if (this._tiles[this._height-1-this._activeLevel][xLoc].type == Tile.TYPE_EMPTY ) {
							this.createBuilding(xLoc,this._height-1-this._activeLevel);
						}
					}
					break;

				case SIDE_WEST:
					for (yLoc=(this._height-1);yLoc>=0;yLoc--) {
						if (this._tiles[yLoc][this._activeLevel].type == Tile.TYPE_EMPTY ) {
							this.createBuilding(this._activeLevel,yLoc);
						}
					}
					break;

			}
		}

		// This tile is empty, let's create a building
		private function createBuilding(xLoc:int,yLoc:int):void
		{
			var xPos:int = xLoc;
			var yPos:int = yLoc;

			// Let's check the first dimention

			while (this.isTileEmpty(xPos,yPos)) {
				if (this._activeSide == SIDE_NORTH) {
					xPos++;
				}else if (this._activeSide == SIDE_SOUTH) {
					xPos--;
				}else if (this._activeSide == SIDE_EAST) {
					yPos++;
				}else if (this._activeSide == SIDE_WEST) {
					yPos--;
				}
			}

			if (
				this._activeSide == SIDE_NORTH &&
				(xPos - xLoc) > 10
			) {
				xPos = xLoc + Math.floor( Math.random()*this._buildingMin+(this._buildingMax-this._buildingMin));

			}else if (
				this._activeSide == SIDE_SOUTH &&
				(xLoc - xPos) > 10
			) {
				xPos = xLoc - Math.floor( Math.random()*this._buildingMin+(this._buildingMax-this._buildingMin));

			}else if (
				this._activeSide == SIDE_EAST &&
				(yPos - yLoc) > 10
			) {
				yPos = yLoc + Math.floor( Math.random()*this._buildingMin+(this._buildingMax-this._buildingMin));

			}else if (
				this._activeSide == SIDE_WEST &&
				(yLoc - yPos) > 10

			) {
				yPos = yLoc - Math.floor( Math.random()*this._buildingMin+(this._buildingMax-this._buildingMin));
			}

			var tmpDimention:int;
			var direction:String;
			var length:int;

			if (xLoc != xPos) {
				tmpDimention = xPos;
				if(xLoc < xPos){
					direction = 'right';
					length = xPos-xLoc;
				}else{
					direction = 'left';
					length = xLoc-xPos;
				}
			}else{
				tmpDimention = yPos;
				if(yLoc < yPos){ 					direction = 'down'; 					length = yPos-yLoc; 				}else{ 					direction = 'up'; 					length = yLoc-yPos; 				} 			} 			 			xPos = xLoc; 			yPos = yLoc; 			 			 			// Let's check the second dimention 			 			while (this.isLineEmpty(xPos, yPos, direction, length)) { 				if (this._activeSide == SIDE_NORTH) { 					yPos++; 				}else if (this._activeSide == SIDE_SOUTH) { 					yPos--; 				}else if (this._activeSide == SIDE_EAST) { 					xPos--; 				}else if (this._activeSide == SIDE_WEST) { 					xPos++; 				} 			} 			 			if ( 				this._activeSide == SIDE_NORTH && 				(yPos - yLoc) > 10
			) {
				yPos = yLoc + Math.floor( Math.random()*this._buildingMin+(this._buildingMax-this._buildingMin));

			}else if (
				this._activeSide == SIDE_SOUTH &&
				(yLoc - yPos) > 10
			) {
				yPos = yLoc - Math.floor( Math.random()*this._buildingMin+(this._buildingMax-this._buildingMin));

			}else if (
				this._activeSide == SIDE_EAST &&
				(xLoc - xPos) > 10
			) {
				xPos = xLoc - Math.floor( Math.random()*this._buildingMin+(this._buildingMax-this._buildingMin));

			}else if (
				this._activeSide == SIDE_WEST &&
				(xPos - xLoc) > 10

			) {
				xPos = xLoc + Math.floor( Math.random()*this._buildingMin+(this._buildingMax-this._buildingMin));
			}

			if (xLoc != xPos) {
				yPos = tmpDimention;
			}else{
				xPos = tmpDimention;
			}

			var left:int,right:int,top:int,bottom:int;

			// let's get the dimentions
			// Left / Right
			if (xPos > xLoc) {
				left = xLoc;
				right = xPos-1;
			}else{
				left = xPos+1;
				right = xLoc;
			}

			// Top / Bottom
			if (yPos > yLoc) {
				top = yLoc;
				bottom = yPos-1;
			}else{
				top = yPos+1;
				bottom = yLoc;
			}

			// check to see if all tiles within this area are empty
			var allEmpty:Boolean = true;
			for(var $y:int=top;$y<=bottom;$y++){
				for(var $x:int=left;$x<=right;$x++){
					if (!this.isTileEmpty($x,$y)) {
						allEmpty = false;
					}
				}
			}

			// if all the tiles aren't empty, let's quit out
			if (!allEmpty) {
				return void;
			}

			//var building:Object = {'top':top,'right':right,'bottom':bottom,'left':left};
			this._buildings.push({'top':top,'right':right,'bottom':bottom,'left':left});

			// Let's change to building
			for($y=top;$y<=bottom;$y++){
				for($x=left;$x<=right;$x++){ 					this._tiles&#91;$y&#93;&#91;$x&#93;.type = Tile.TYPE_BUILDING; 					 					// make the roads 					//LEFT 					if ($x==left && (this._tiles&#91;$y&#93;&#91;$x-1&#93;) != undefined) { 						this._tiles&#91;$y&#93;&#91;$x-1&#93;.type = Tile.TYPE_ROAD; 					} 					//RIGHT 					if ($x==right && (this._tiles&#91;$y&#93;&#91;$x+1&#93;) != undefined) { 						this._tiles&#91;$y&#93;&#91;$x+1&#93;.type = Tile.TYPE_ROAD; 					} 					//TOP 					if ($y==top && (this._tiles&#91;$y-1&#93;) != undefined) { 						this._tiles&#91;$y-1&#93;&#91;$x&#93;.type = Tile.TYPE_ROAD; 					} 					//BOTTOM 					if ($y==bottom && (this._tiles&#91;$y+1&#93;) != undefined) { 						this._tiles&#91;$y+1&#93;&#91;$x&#93;.type = Tile.TYPE_ROAD; 					} 					//TOP LEFT 					if ( 						$x==left && (this._tiles&#91;$y&#93;&#91;$x-1&#93;) != undefined && 						$y==top && (this._tiles&#91;$y-1&#93;)!= undefined 					) { 						this._tiles&#91;$y-1&#93;&#91;$x-1&#93;.type = Tile.TYPE_ROAD; 					} 					//TOP RIGHT 					if ( 						$x==right && (this._tiles&#91;$y&#93;&#91;$x+1&#93;) != undefined && 						$y==top && (this._tiles&#91;$y-1&#93;) != undefined 					) { 						this._tiles&#91;$y-1&#93;&#91;$x+1&#93;.type = Tile.TYPE_ROAD; 					} 					//BOTTOM LEFT 					if ( 						$x==left && (this._tiles&#91;$y&#93;&#91;$x-1&#93;) != undefined && 						$y==bottom && (this._tiles&#91;$y+1&#93;) != undefined 					) { 						this._tiles&#91;$y+1&#93;&#91;$x-1&#93;.type = Tile.TYPE_ROAD; 					} 					//BOTTOM RIGHT 					if ( 						$x==right && (this._tiles&#91;$y&#93;&#91;$x+1&#93;) != undefined && 						$y==bottom && (this._tiles&#91;$y+1&#93;) != undefined 					) { 						this._tiles&#91;$y+1&#93;&#91;$x+1&#93;.type = Tile.TYPE_ROAD; 					} 				} 			} 		} 		 		private function isTileEmpty(xLoc:int,yLoc:int):Boolean 		{ 			if ( 				(this._tiles&#91;yLoc&#93;) != undefined && 				(this._tiles&#91;yLoc&#93;&#91;xLoc&#93;) != undefined && 				this._tiles&#91;yLoc&#93;&#91;xLoc&#93;.type == Tile.TYPE_EMPTY 			) { 				return true; 			} 			return false; 		} 		 		private function isLineEmpty(xLoc:int,yLoc:int,direction:String,length:int):Boolean 		{ 			var xPos:int = xLoc; 			var yPos:int = yLoc; 			 			while (length > 0) {
				if(!this.isTileEmpty(xPos,yPos)){
					return false;
				}
				switch (direction) {
					case 'left':
						xPos--;
						break;
					case 'right':
						xPos++;
						break;
					case 'up':
						yPos--;
						break;
					case 'down':
						yPos++;
						break;
				}
				length--;
			}
			return true;

		}

		public function get buildings():Array
		{
			return _buildings;
		}
	}
}

Tile.as

The Tile class is simply a graphical element to show different colours for the different types of square, white being buildings & black being roads

package city
{
	import flash.display.Sprite;
	public class Tile extends Sprite
	{

		public static const TYPE_EMPTY:int = 0;
		public static const TYPE_BUILDING:int = 1;
		public static const TYPE_ROAD:int = 2;

		private var _type:int;

		public function Tile() {
			// constructor code
			_type = TYPE_EMPTY;
			colour = 0xffffff;
		}

		public function set colour(val:uint):void
		{
			graphics.clear();
			graphics.lineStyle(1,0x000000);
			graphics.beginFill(val);
			graphics.drawRect(0,0,4,4);
			graphics.endFill();
		}

		public function get type():int
		{
			return this._type;
		}

		public function set type(value:int):void
		{
			this._type = value;
			if (value==TYPE_EMPTY) {
				colour = 0xffffff;
			}else if (value==TYPE_BUILDING) {
				colour = 0xefefef;
			}else if (value==TYPE_ROAD) {
				colour = 0x333333;
			}else {
				colour = 0x000000;
			}
		}

	}

}