Tutorial on Dungeon Generation in Objective-C

So I am getting a Cellular Automator (CA) generator ready for this years 7DRL and hoping to create the same game in iOS and HTML5 – I am sure I am biting too much off. However, I have a basic implementation of a Dungeon generator and detailed how to create it below in X-Code using Objective-C. It’s a very simple implementation – so feel free to comment if you think we can clean the code up!!!!

In terms of CA there are some great references to get you going:

http://pcg.wikidot.com/pcg-algorithm:cellular-automata

http://www.futuredatalab.com/proceduraldungeon/

http://csharpcodewhisperer.blogspot.com.au/2013/07/Rouge-like-dungeon-generation.html

For this tutorial the dungeon is represented by 0’s as Rock and 1’s as  Path (or moveable space).

This is the output so far:

Screen Shot 2014-02-16 at 1.19.12 AM

So its a pretty simple set of steps to get us going.

Firstly create a OSX->Application->Command LineTool->Foundation project in Xcode

Then add a class called Dungeon

The header for Dungeon should look like this

#import <Foundation/Foundation.h>
 @interface Dungeon : NSObject
{
    int map[100][100];  //max size of dungeon
    int maxRows;        //size of this dungeon
    int maxCols;        //size of this dungeon
}
- (id)initWithRows:(int)rows AndCol:(int)cols;   
-(void)printMap;
@end

The implementation for this should look like

#import "Dungeon.h"

@implementation Dungeon
- (id)initWithRows:(int)rows AndCol:(int)cols
{
    self = [super init];
    if (self) {
        [self initMap];
        maxRows = rows;
        maxCols = cols;
        [self createCellularAutomatorDungeon:maxRows ForCols:maxCols];
    }

    return self;
}

//set all the map to rock
-(void)initMap
{
    for(int i=0;i<maxRows;i++)
        for(int j=0;j<maxCols;j++)
            map[i][j] = 0;
}
 //Loop through the 2 dim array and add space (not rock) if the wall has a specific minimum neighbourCount
- (void)createCellularAutomatorCellsForNeighbourCount:(int)neighbourCount
{
    //first pass CA
    for(int i=0;i<maxRows;i++)
    {
        for(int j=0;j<maxCols;j++)
        {
            if (map[i][j] == 0 && ([self getNeighboursForRow:i ForCol:j]>=neighbourCount))
                map[i][j] = 1;
        }
    }
}

//use a classic CA algorithm 
-(void)createCellularAutomatorDungeon:(int)rows ForCols:(int)cols
{
    //randomly initialize wih a few random spaces in the rock
    for(int i=1;i<maxRows;i++)
        for(int j=1;j<maxCols;j++)
            if (arc4random()%4==1)
                map[i][j] = 1;

    [self createCellularAutomatorCellsForNeighbourCount:4];//first pass
    [self createCellularAutomatorCellsForNeighbourCount:5];//second pass
}

//Count of the number of neighbours for this point
-(int)getNeighboursForRow:(int)row ForCol:(int)col
{
    int n = 0;

    //top neighbours
    if (row>0 && col>0)
    {
        for(int i=col-1;i<col+2;i++)
        {
            if (map[row-1][i]==1)
                n++;
        }
    }

    //middle neighbour left
    if (col>0 && col<maxCols)
        if (map[row][col-1]==1)
            n++;

    //middle neighbour right
    if (col>0 && col<maxCols)
        if (map[row][col+1]==1)
            n++;

    //bottom neighbours
    if (row<maxRows && col<maxCols)
    {
        for(int i=col-1;i<col+2;i++)
        {
            if (map[row+1][i]==1)
            {
                n++;
            }
        }
    }
    return n;
}

-(BOOL)ifValidCoordinateForRow:(int)row ForCol:(int)col
{
    return (map[col][row]==0 && row>0 && col > 0 && row<maxRows && col<maxCols);
}

-(void)printMap
{
    for(int i=0;i<maxRows;i++)
    {
        for(int j=0;j<maxCols;j++)
            printf("%u", map[i][j] );
        printf("\n");
    }
}

And make sure that main.m looks like

#import <Foundation/Foundation.h>
#import "Dungeon.h"
 int main(int argc, const char * argv[])
{
    @autoreleasepool {
        // insert code here...
        NSLog(@"Basic Dungeon CA Algorithm");
        Dungeon *dungeon  =[[Dungeon alloc]initWithRows:30 AndCol:50];
        [dungeon printMap];
            }
    return 0;

}

Ok so if you look at the code above in dungeon.m you should get a pretty good idea of how we are doing the generation of the dungeon using a CA algorithm. Read the comments as I have tried to explain anything a little unusual. If anything doesn’t make sense – leave a comment and I’ll get back to you.

Happy coding.

Advertisements

iPhone roguelike design doco for my University assignment

OK slightly off topic – but this is my first assignment (presentation) for my iOS rogue like/RPG game

http://dungeonbard.com/2012/09/14/powerpoint-presentation-on-dungeonbard-i-gave-a-few-days-ago-at-university/
Dom

 

CannonAttack iPhone v1.0 – An iPhoneTutorial

Which way to the battle? –  The Introduction

This tutorial is a short introduction to iPhone development. Like most of my tutorials it’s based on a game.

This is the second in a series of tutorials. The first tutorial was a TDD tutorial in C# 4.0.

This version of the tutorial is similar in functionality to the C# version with obvious changes to the UI as needed. I am not using a TDD approach, mainly because I am not really up to TDD on the iPhone yet.

By completing this tutorial you will:

  • Get a taste of the iPhone development experience; and
  • Design a simple UI in Interface Builder.

CannonAttack iPhone is a simple game where a player enters an angle and velocity of a cannonball to hit a target at a given distance. The game uses a basic formula for calculating the trajectory of the cannonball and the player keeps taking turns at shooting at the target until it has been hit

Where are the cannonballs – What you need:

I built this with the following tools:

  • iPhone SDK (3.1); and
  • MAC OS X 10.6 Snow Leopard

Whilst this is a pretty basic introduction to iPhone development, it assumes a very basic knowledge of iPhone development and while it doesn’t spell out every single step, you should have more than enough detail to complete it.

The battleplan – The CannonAttack Requirements/Specs:

The following is a combination of Requirements and Specifications that will give us some guide in terms of the application we are trying to build:

  • iPhone Application;
  • Allow player to set Angle and Speed of the Cannon Ball to Shoot at  a Target;
  • Target Distance is simply the distance of the Cannon to Target, and is created randomly by default but can be overridden;
  • Max distance for target is 20000 meters;
  • Base the algorithm for the calculation of the cannons trajectory upon the following code (distance and height is meters and velocity is meters per second):

distance = velocity * Math.Cos(angleInRadians) * time;

height = (velocity * Math.Sin(angleInRadians) * time) – (GRAVITY * Math.Pow(time, 2)) / 2;

  • A hit occurs if the cannon is within 50m of the target;
  • Display number of shots for a hit

Building the Cannon – Creating the Cannon UI

  • Start XCode and Create a View based application – call it CannonAttack
  • Now add a class to the Project and call it Cannon.m (generate the .h file as well).
  • Double click on the file CannonAttackViewController.xib:  (this will open up Interface Builder).
  • Create the following UI using the design tools in IB (base this on this screen):


  • Also, Add a button that covers the whole screen in the iPhone UI in Interface Builder. Click on this button and from the LAYOUT Menu select SEND TO BACK. This is important as we will use this hidden button to remove the keyboard for the textfields when we click in the background or one of the buttons.
  • Also make sure you have set the keyboard type for the textboxes to number pad.
  • You need to then go back to XCode and add the following Code to the .h file.
  • Now add the following to the .m file

#import <UIKit/UIKit.h>
@interface CannonLauncher1ViewController : UIViewController {
IBOutlet UILabel *descriptionLabel;
IBOutlet UILabel *resultLabel;
IBOutlet UITextField *speedTextField;
IBOutlet UITextField *angleTextField;
IBOutlet UILabel *shotCountLabel;
int targetDistance;
int shotCount;
}
//Properties
@property (nonatomic,retain) UILabel *descriptionLabel;
@property (nonatomic,retain) UILabel *resultLabel;
@property (nonatomic,retain) UITextField *speedTextField;
@property (nonatomic,retain) UITextField *angleTextField;
@property (nonatomic,retain) UILabel *shotCountLabel;
//Actions
-(IBAction)fireButton:(id)sender;
-(IBAction)resetButton:(id)sender;
-(IBAction)backgroundClick:(id)sender;
//Methods
-(void)hideNumberKeyboard;
-(void)resetTextBoxes;
@end

So now add the following code to the .m file

#import "CannonLauncher1ViewController.h"
#import "Cannon.h"
@implementation CannonLauncher1ViewController
@synthesize descriptionLabel;
@synthesize resultLabel;
@synthesize speedTextField;
@synthesize angleTextField;
@synthesize shotCountLabel;
- (void)viewDidLoad {
[super viewDidLoad];
[self resetTextBoxes];
}
//Button handler for the fire button
-(IBAction) fireButton:(id) sender
{
NSInteger velocity = [[speedTextField text]intValue];
NSInteger angle = [[angleTextField text]intValue];
Cannon *cannon = [[Cannon alloc]init];
cannon.targetDistance = targetDistance;
[cannon Shoot:angle Speed:velocity];
shotCount ++;
resultLabel.text = cannon.message;
shotCountLabel.text =
[NSString stringWithFormat: @"Shot Count %d", shotCount];
[self hideNumberKeyboard];
[cannon release];
}
//Button handler for the reset button
-(IBAction) resetButton:(id) sender
{
[self resetTextBoxes];
}
//Button Handler for the main backgtound button
-(IBAction)backgroundClick:(id)sender
{
[self hideNumberKeyboard];
}
//Hides the number keyboard
-(void)hideNumberKeyboard
{
[speedTextField resignFirstResponder];
[angleTextField resignFirstResponder];
}
//Reset the distance and set it as rge text of the description label
-(void)resetTextBoxes
{
targetDistance = rand() %10000;
NSString *distanceText = [[NSString alloc] initWithFormat: @"Distance %d meters (50m",targetDistance];
descriptionLabel.text = distanceText;
speedTextField.text = @"";
angleTextField.text = @"";
shotCount = 0;
[distanceText release];
}
//Built in Method I didn't change this
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
}
//Built in Method to clean up any objects
- (void)dealloc {
[descriptionLabel release];
[resultLabel release];
[speedTextField release];
[angleTextField release];
[shotCountLabel  release];
[super dealloc];
}
@end

OK so all we have to do now is implement the cannon class.

Now edit the cannon.h file and replace the code with this header code:

//
//  Cannon.h
//  CannonLauncher1
//
//  Created by Dom Millar on 16/06/10.
//  Copyright 2010 __MyCompanyName__. All rights reserved.
#import <Foundation/Foundation.h>
@interface Cannon : NSObject {
@private int targetDistance;
NSString *message;
}
@property  (readwrite,assign)int targetDistance;
@property  (nonatomic,retain) NSString *message
-(IBAction) Shoot: (int) angle Speed: (int) speed;
@end

Now all we have to do is add the code for cannon.m

//
//  Cannon.m
//  CannonLauncher1
//
//  Created by Dom Millar on 16/06/10.
//  Copyright 2010 __MyCompanyName__. All rights reserved.
//
#import "Cannon.h"
@implementation Cannon
@synthesize targetDistance;
@synthesize message;
-(IBAction) Shoot: (NSInteger) angle Speed: (NSInteger) velocity
{
//set up the local variables for the shoot method
float time = 0;
float height = 0;
float distance = 0;
float gravity = 9.80665;
float variation = 50;
float pi = 3.1415926536;
//Keep calculating height and distance untill height < 0
while(height >= 0)
{
float angleInRadians = ( pi / 180 ) * angle;
distance = velocity * cos(angleInRadians) * time;
height = (velocity * sin(angleInRadians)) - (gravity * pow(time,2));
time++;
}
//Complete calculation
if ((targetDistance >= distance-variation) && (targetDistance <= distance+variation))
{
//Display the hit
message = @"Hit";
}
else
{
//Display the Miss
NSString *description = [NSString stringWithFormat:@"Missed target landed at %f meters" , distance];
message = description;
//[description release];
}
}
@end

Now make sure everything builds. Go back interface builder and you need to do setup the following outlets and actions:

This shows you all the hooks we need:

File owner dialog screen shot

OK now You should be ready to run, go back to XCode and run the app and you should be able to play the game like:

You can see the Keyboard is visible here – once we click on a button or the main screen the keyboard will disappear:

Victory Condition – In summary

So that’s it – our latest version of the Cannon Attack game is complete. There are lots of changes you can make to make the game more interesting so play around with the project as much as you like.

Happy Coding – Dom.