Blogus Maximus

Rubbing people the wrong way since 1970...

  Home  |   Contact  |   Syndication    |   Login
  1366 Posts | 10 Stories | 2225 Comments | 1336 Trackbacks

News


Google My Blog

Catch me at: The List!


My InstallScript Utility Belt My Amazon Wishlist
My Standard Disclaimer

Tag Cloud


Archives

Post Categories

Image Galleries

Blogs

Code Camps

CTown Geeks

Geeky Webcomics

High Geek

Magenic Blogs

Microsoft Blogs

My Articles

My Sites

PodCasts

UG

XNA

This was inspired by something I read in David Weller's VB.NET game programming book. By the way, I have to admit the more I read of it the better I like it. The whole “mismatched source code” thing still bugs me, but the concepts are pretty solid. But I digress...

I was working on towns for HA! (specifically the buildings) and I recalled how Weller creates his shapes in the .nettris game (or whatever it's called.) Basically he has a square, which he then repeats in a variety of sequences to form the shapes. So that got me thinking... I certainly don't want to manually draw out each building in HA! if I can come up with an easier way.

All of the map data is based on an array. One cell in the array contains one section of wall (or floor) or a door, etc. In order to represent these “tiles“ on the map, I insert a value into the proper spot in the array. To creat a building manually, I have to figure out where in the grid I want each tile (wall, floor, door, etc) and then plot it in the array. I also need to store stuff like the state of the door. ex: Is it open or closed, is there actually a door or just an opening? Using this method, every tile equates to one line of code. Naturally that gets pretty cumbersome, as well as hard to maintain. The opportunity for error is pretty high as well.

So instead, I wrote a routine that accepts StartX, StartY, Width, Height, DoorFacing & DoorType. Now I can crank out a simple (rectangular or square) building with a door (open, closed or just an opening) with one line of code. Too limiting, you say? Nope... More complex structures are quite simple. Multiple calls to that routine, with partially overlapping coordinates can create multi-room buildings. Need a home with more than one door? Piece o' cake! Just add a second line of code specifying the coordinates of the extra door. Two lines of code versus 10-15 (per building). Instead of spending hours coding each village, I can now knock one out in a few minutes. (I still spend hours with a pencil and graph paper, laying it out how I want it before I get started, but that's just me.)

How does the routine work? The StartX and StartY params specify where in the array your shape will start. Width and Height specify how many rows & columns to populate in your array. DoorFacing takes an enumerated value Direction.North, Direction.South, etc. DoorType is also an enumerated value Door.Open, Door.Closed, Door.None. Once you have StartX and StartY, just use a pair of nested for/next loops with Width and Height to insert the data representing a wall into your array grid. Once you build the square (or rectangle) in your array, go back and modify the cell that contains the door or opening. That's it!

If you have a special case, where you need a more exotic shape, you can still hard code it or you can drop a couple of “instant buildings” side by side and connect them however you like.

If you guys want to see the code, I can post it... but I think the concepts here are probably more useful than the code itself. Just LMK.

By request... a sample map from HA! that illustrates the buildings I'm discussing above.

source code:

    Public Sub GenericBuilding(ByVal startX As Int16, ByVal startY As Int16, _
                               ByVal width As Int16, ByVal height As Int16, _
                               ByVal door As Heading, ByVal doorstate As DoorState, _
                               ByVal town As Town)

        '   creates a square or rectangular building
        '   starting at the top left (startX & startY)
        '   and going to (width - 1) and (height - 1) 
        '   door placement is passed in as heading (NSEW)
        '
        '   town indicates the Z axis the map is stored on

        Dim intCtr As Int16

        ' N wall
        For intCtr = 0 To width - 1
            m_TownMap(startX + intCtr, startY, Town).CellType = CellType.wall
        Next
        ' W wall
        For intCtr = 1 To height - 2
            m_TownMap(startX, startY + intCtr, Town).CellType = CellType.wall
        Next
        ' S wall
        For intCtr = 0 To width - 1
            m_TownMap(startX + intCtr, startY + (height - 1), town).CellType = CellType.wall
        Next
        ' E wall
        For intCtr = 1 To height - 2
            m_TownMap(startX + (width - 1), startY + intCtr, Town).CellType = CellType.wall
        Next
        ' door
        Select Case door
            Case Heading.North
                Select Case doorstate
                    Case Towns.DoorState.none
                        m_TownMap(startX + (width \ 2), startY, town).CellType = CellType.grass
                    Case Towns.DoorState.closed, Towns.DoorState.locked
                        m_TownMap(startX + (width \ 2), startY, town).CellType = CellType.door
                        m_TownMap(startX + (width \ 2), startY, town).DoorState = doorstate
                    Case Towns.DoorState.open
                        m_TownMap(startX + (width \ 2), startY, town).CellType = CellType.opendoor
                        m_TownMap(startX + (width \ 2), startY, town).DoorState = doorstate
                End Select

            Case Heading.West
                Select Case doorstate
                    Case Towns.DoorState.none
                        m_TownMap(startX, startY + (height \ 2), town).CellType = CellType.grass
                    Case Towns.DoorState.closed, Towns.DoorState.locked
                        m_TownMap(startX, startY + (height \ 2), town).CellType = CellType.door
                        m_TownMap(startX, startY + (height \ 2), town).DoorState = doorstate
                    Case Towns.DoorState.open
                        m_TownMap(startX, startY + (height \ 2), town).CellType = CellType.opendoor
                        m_TownMap(startX, startY + (height \ 2), town).DoorState = doorstate
                End Select

            Case Heading.South
                Select Case doorstate
                    Case Towns.DoorState.none
                        m_TownMap(startX + (width \ 2), startY + height - 1, town).CellType = CellType.grass
                    Case Towns.DoorState.closed, Towns.DoorState.locked
                        m_TownMap(startX + (width \ 2), startY + height - 1, town).CellType = CellType.door
                        m_TownMap(startX + (width \ 2), startY + height - 1, town).DoorState = doorstate
                    Case Towns.DoorState.open
                        m_TownMap(startX + (width \ 2), startY + height - 1, town).CellType = CellType.opendoor
                        m_TownMap(startX + (width \ 2), startY + height - 1, town).DoorState = doorstate
                End Select

            Case Heading.East
                Select Case doorstate
                    Case Towns.DoorState.none
                        m_TownMap(startX + width - 1, startY + (height \ 2), town).CellType = CellType.grass
                    Case Towns.DoorState.closed, Towns.DoorState.locked
                        m_TownMap(startX + width - 1, startY + (height \ 2), town).CellType = CellType.door
                        m_TownMap(startX + width - 1, startY + (height \ 2), town).DoorState = doorstate
                    Case Towns.DoorState.open
                        m_TownMap(startX + width - 1, startY + (height \ 2), town).CellType = CellType.opendoor
                        m_TownMap(startX + width - 1, startY + (height \ 2), town).DoorState = doorstate
                End Select
        End Select

    End Sub

' code to create a couple buildings (#1 and #2 on the map above)
        GenericBuilding(4, 4, 8, 5, Heading.East, DoorState.none, Town.Fincastle)
        GenericBuilding(9, 11, 5, 5, Heading.North, DoorState.none, Town.Fincastle)

' code to create a composite building
        GenericBuilding(27, 10, 5, 5, Heading.North, DoorState.none, Town.Fincastle)
        GenericBuilding(31, 10, 5, 5, Heading.West, DoorState.none, Town.Fincastle)

There's a verticle offset of 3 not explained above, just trust me. (4,4) is (4,7) on the map in notepad. :)

7/10/05 - This code has been refactored since the original posting. New code appears here now.

posted on Wednesday, July 6, 2005 10:23 AM

Feedback

# More RPG goodness from Chris Williams 7/18/2005 2:14 AM Brian Keller - Product Manager f
In his latest article about Heroic Adventure, Chris Williams describes his approach for building maps...

# re: Easy building generation on a tile-based map (less code, more fun) 6/4/2011 8:49 AM shoes coupons
Thanks for sharing but this kind of stuff is too difficult for my tiny brain.

Post A Comment
Title:
Name:
Email:
Comment:
Verification: