|
-
Nov 18th, 2012, 12:55 PM
#8
Thread Starter
Fanatic Member
Re: Short list of examples (exclusively 2D) as an introduction to XNA
Example 5: Using Perlin simplex-noise to generate an infinite tiled map.
vb.net Code:
Public Class Game1 Inherits Microsoft.Xna.Framework.Game Private WithEvents graphics As GraphicsDeviceManager Private WithEvents spriteBatch As SpriteBatch Private mymap As TiledMap Public Sub New() graphics = New GraphicsDeviceManager(Me) Content.RootDirectory = "Content" End Sub Protected Overrides Sub Initialize() mymap = New TiledMap(Me, 0.04F) Components.Add(mymap) IsMouseVisible = True Window.AllowUserResizing = True Window.Title = "Example 5 (A tiled map using Perlin simplex-noise)" 'Modified title. MyBase.Initialize() End Sub Protected Overrides Sub LoadContent() spriteBatch = New SpriteBatch(GraphicsDevice) MyBase.LoadContent() End Sub Protected Overrides Sub UnloadContent() MyBase.UnloadContent() End Sub Protected Overrides Sub Update(ByVal gameTime As GameTime) If Keyboard.GetState.IsKeyDown(Keys.Escape) Then Me.Exit() MyBase.Update(gameTime) End Sub Protected Overrides Sub Draw(ByVal gameTime As GameTime) GraphicsDevice.Clear(Color.Black) MyBase.Draw(gameTime) End Sub End Class
A new class called TiledMap (create this class in the main project and copy/paste the code):
vb.net Code:
Public Class TiledMap Inherits DrawableGameComponent Private sprites As SpriteBatch Private tile_texture As Texture2D Private mappos_x, mappos_y, pixel_x, pixel_y As Integer Private _rect As Rectangle = New Rectangle(0, 0, 64, 64) Private _scale As Single Private _oldmstate As MouseState Public Sub New(myproject As Game, scale As Single) MyBase.New(myproject) _scale = scale End Sub Public Overrides Sub Initialize() MyBase.Initialize() End Sub Protected Overrides Sub LoadContent() MyBase.LoadContent() sprites = New SpriteBatch(GraphicsDevice) tile_texture = Game.Content.Load(Of Texture2D)("SmallTextures") End Sub Protected Overrides Sub UnloadContent() MyBase.UnloadContent() End Sub Public Overrides Sub Update(ByVal gameTime As GameTime) Dim mstate As MouseState = Mouse.GetState If mstate.LeftButton = ButtonState.Pressed AndAlso Game.IsActive Then If _oldmstate.LeftButton = ButtonState.Pressed Then Dim deltax As Integer = _oldmstate.X - mstate.X Dim deltay As Integer = _oldmstate.Y - mstate.Y mappos_x += deltax \ 64 mappos_y += deltay \ 64 pixel_x -= deltax Mod 64 pixel_y -= deltay Mod 64 While pixel_x < 0 pixel_x += 64 mappos_x += 1 End While While pixel_x >= 64 pixel_x -= 64 mappos_x -= 1 End While While pixel_y < 0 pixel_y += 64 mappos_y += 1 End While While pixel_y >= 64 pixel_y -= 64 mappos_y -= 1 End While End If End If _oldmstate = mstate MyBase.Update(gameTime) End Sub Public Overrides Sub Draw(ByVal gameTime As GameTime) Dim w As Integer = GraphicsDevice.Viewport.Width Dim h As Integer = GraphicsDevice.Viewport.Height Dim wtile As Integer = w \ 64 + 1 Dim htile As Integer = h \ 64 + 1 Dim loc, mappos As Vector2 Dim perlin As Single sprites.Begin() For i As Integer = 0 To wtile loc.X = pixel_x + 64.0F * (i - 1) mappos.X = (mappos_x + i) * _scale For j As Integer = 0 To htile loc.Y = pixel_y + 64.0F * (j - 1) mappos.Y = (mappos_y + j) * _scale perlin = PerlinNoise.PerlinSimplex2D(mappos) If perlin < 0.0F Then 'water _rect.X = 0 ElseIf perlin < 0.6F Then 'grass _rect.X = 64 ElseIf perlin < 0.8F Then 'forest _rect.X = 128 ElseIf perlin < 0.9F Then 'desert _rect.X = 192 Else 'mountain _rect.X = 256 End If sprites.Draw(tile_texture, loc, _rect, Color.White) Next Next sprites.End() MyBase.Draw(gameTime) End Sub End Class
A new class called PerlinNoise (create this class in the main project and copy/paste the code):
vb.net Code:
Public Class PerlinNoise 'Created on the basis on this excellent article explaining simplex noise: Private Shared gradients() As Vector2 = _ {New Vector2(1.0F, 1.0F), New Vector2(-1.0F, 1.0F), New Vector2(1.0F, -1.0F), New Vector2(-1.0F, -1.0F), _ New Vector2(1.0F, 0.0F), New Vector2(-1.0F, 0.0F), New Vector2(1.0F, 0.0F), New Vector2(-1.0F, 0.0F), _ New Vector2(0.0F, 1.0F), New Vector2(0.0F, 1.0F), New Vector2(0.0F, -1.0F), New Vector2(0.0F, -1.0F)} Private Shared bytehash() As Byte = _ {&H97, &HA0, &H89, &H5B, &H5A, &HF, &H83, &HD, &HC9, &H5F, &H60, &H35, &HC2, &HE9, &H7, &HE1, _ &H8C, &H24, &H67, &H1E, &H45, &H8E, &H8, &H63, &H25, &HF0, &H15, &HA, &H17, &HBE, &H6, &H94, _ &HF7, &H78, &HEA, &H4B, &H0, &H1A, &HC5, &H3E, &H5E, &HFC, &HDB, &HCB, &H75, &H23, &HB, &H20, _ &H39, &HB1, &H21, &H58, &HED, &H95, &H38, &H57, &HAE, &H14, &H7D, &H88, &HAB, &HA8, &H44, &HAF, _ &H4A, &HA5, &H47, &H86, &H8B, &H30, &H1B, &HA6, &H4D, &H92, &H9E, &HE7, &H53, &H6F, &HE5, &H7A, _ &H3C, &HD3, &H85, &HE6, &HDC, &H69, &H5C, &H29, &H37, &H2E, &HF5, &H28, &HF4, &H66, &H8F, &H36, _ &H41, &H19, &H3F, &HA1, &H1, &HD8, &H50, &H49, &HD1, &H4C, &H84, &HBB, &HD0, &H59, &H12, &HA9, _ &HC8, &HC4, &H87, &H82, &H74, &HBC, &H9F, &H56, &HA4, &H64, &H6D, &HC6, &HAD, &HBA, &H3, &H40, _ &H34, &HD9, &HE2, &HFA, &H7C, &H7B, &H5, &HCA, &H26, &H93, &H76, &H7E, &HFF, &H52, &H55, &HD4, _ &HCF, &HCE, &H3B, &HE3, &H2F, &H10, &H3A, &H11, &HB6, &HBD, &H1C, &H2A, &HDF, &HB7, &HAA, &HD5, _ &H77, &HF8, &H98, &H2, &H2C, &H9A, &HA3, &H46, &HDD, &H99, &H65, &H9B, &HA7, &H2B, &HAC, &H9, _ &H81, &H16, &H27, &HFD, &H13, &H62, &H6C, &H6E, &H4F, &H71, &HE0, &HE8, &HB2, &HB9, &H70, &H68, _ &HDA, &HF6, &H61, &HE4, &HFB, &H22, &HF2, &HC1, &HEE, &HD2, &H90, &HC, &HBF, &HB3, &HA2, &HF1, _ &H51, &H33, &H91, &HEB, &HF9, &HE, &HEF, &H6B, &H31, &HC0, &HD6, &H1F, &HB5, &HC7, &H6A, &H9D, _ &HB8, &H54, &HCC, &HB0, &H73, &H79, &H32, &H2D, &H7F, &H4, &H96, &HFE, &H8A, &HEC, &HCD, &H5D, _ &HDE, &H72, &H43, &H1D, &H18, &H48, &HF3, &H8D, &H80, &HC3, &H4E, &H42, &HD7, &H3D, &H9C, &HB4, _ &H97, &HA0, &H89, &H5B, &H5A, &HF, &H83, &HD, &HC9, &H5F, &H60, &H35, &HC2, &HE9, &H7, &HE1, _ &H8C, &H24, &H67, &H1E, &H45, &H8E, &H8, &H63, &H25, &HF0, &H15, &HA, &H17, &HBE, &H6, &H94, _ &HF7, &H78, &HEA, &H4B, &H0, &H1A, &HC5, &H3E, &H5E, &HFC, &HDB, &HCB, &H75, &H23, &HB, &H20, _ &H39, &HB1, &H21, &H58, &HED, &H95, &H38, &H57, &HAE, &H14, &H7D, &H88, &HAB, &HA8, &H44, &HAF, _ &H4A, &HA5, &H47, &H86, &H8B, &H30, &H1B, &HA6, &H4D, &H92, &H9E, &HE7, &H53, &H6F, &HE5, &H7A, _ &H3C, &HD3, &H85, &HE6, &HDC, &H69, &H5C, &H29, &H37, &H2E, &HF5, &H28, &HF4, &H66, &H8F, &H36, _ &H41, &H19, &H3F, &HA1, &H1, &HD8, &H50, &H49, &HD1, &H4C, &H84, &HBB, &HD0, &H59, &H12, &HA9, _ &HC8, &HC4, &H87, &H82, &H74, &HBC, &H9F, &H56, &HA4, &H64, &H6D, &HC6, &HAD, &HBA, &H3, &H40, _ &H34, &HD9, &HE2, &HFA, &H7C, &H7B, &H5, &HCA, &H26, &H93, &H76, &H7E, &HFF, &H52, &H55, &HD4, _ &HCF, &HCE, &H3B, &HE3, &H2F, &H10, &H3A, &H11, &HB6, &HBD, &H1C, &H2A, &HDF, &HB7, &HAA, &HD5, _ &H77, &HF8, &H98, &H2, &H2C, &H9A, &HA3, &H46, &HDD, &H99, &H65, &H9B, &HA7, &H2B, &HAC, &H9, _ &H81, &H16, &H27, &HFD, &H13, &H62, &H6C, &H6E, &H4F, &H71, &HE0, &HE8, &HB2, &HB9, &H70, &H68, _ &HDA, &HF6, &H61, &HE4, &HFB, &H22, &HF2, &HC1, &HEE, &HD2, &H90, &HC, &HBF, &HB3, &HA2, &HF1, _ &H51, &H33, &H91, &HEB, &HF9, &HE, &HEF, &H6B, &H31, &HC0, &HD6, &H1F, &HB5, &HC7, &H6A, &H9D, _ &HB8, &H54, &HCC, &HB0, &H73, &H79, &H32, &H2D, &H7F, &H4, &H96, &HFE, &H8A, &HEC, &HCD, &H5D, _ &HDE, &H72, &H43, &H1D, &H18, &H48, &HF3, &H8D, &H80, &HC3, &H4E, &H42, &HD7, &H3D, &H9C, &HB4} Public Shared Function PerlinSimplex2D(loc As Vector2) As Single Static F As Single = Convert.ToSingle(Math.Sqrt(0.75)) - 0.5F Static G As Single = 0.5F - Convert.ToSingle(Math.Sqrt(1.0 / 12.0)) Dim s As Single = (loc.X + loc.Y) * F Dim i As Integer = fastfloor(loc.X + s) Dim j As Integer = fastfloor(loc.Y + s) Dim t As Single = (i + j) * G Dim v0 As New Vector2(loc.X - i + t, loc.Y - j + t) Dim v1 As New Vector2(v0.X + G, v0.Y + G) Dim v2 As New Vector2(v1.X + G - 1.0F, v1.Y + G - 1.0F) Dim ii As Integer = i And &HFF Dim jj As Integer = j And &HFF Dim gi0 As Integer = bytehash(ii + bytehash(jj)) Mod 12 Dim gi1 As Integer Dim gi2 As Integer = bytehash(ii + 1 + bytehash(jj + 1)) Mod 12 If v0.X > v0.Y Then v1.X -= 1.0F gi1 = bytehash(ii + 1 + bytehash(jj)) Mod 12 Else v1.Y -= 1.0F gi1 = bytehash(ii + bytehash(jj + 1)) Mod 12 End If Dim t0 As Single = 0.5F - v0.LengthSquared Dim t1 As Single = 0.5F - v1.LengthSquared Dim t2 As Single = 0.5F - v2.LengthSquared Dim d0, d1, d2 As Single If t0 < 0.0F Then d0 = 0.0F Else t0 *= t0 d0 = t0 * t0 * Vector2.Dot(gradients(gi0), v0) End If If t1 < 0.0F Then d1 = 0.0F Else t1 *= t1 d1 = t1 * t1 * Vector2.Dot(gradients(gi1), v1) End If If t2 < 0.0F Then d2 = 0.0F Else t2 *= t2 d2 = t2 * t2 * Vector2.Dot(gradients(gi2), v2) End If Return 70.0F * (d0 + d1 + d2) End Function Public Shared Function Generate(dimension As Integer, scale As Single, offset As Vector2, c1 As Color, c2 As Color) As Color() Dim v As New Vector2 Dim k As Integer = 0 Dim rval(dimension * dimension - 1) As Color For i As Integer = 0 To dimension - 1 v.X = i * scale + offset.X v.Y = offset.Y For j As Integer = 0 To dimension - 1 rval(k) = Color.Lerp(c1, c2, (PerlinSimplex2D(v) + 1.0F) / 2.0F) v.Y += scale k += 1 Next Next Return rval End Function Private Shared Function fastfloor(val As Single) As Integer Return Convert.ToInt32(If(val > 0, Int(val), Int(val) - 1)) End Function End Class
I will not explain how Perlin noise is implemented or how it works. There are plenty of pages on that already - for interested readers, the article referred at the top of the PerlinNoise class is an excellent place to start. For others simply accept that for any given 2d-vector Perlin noise will give a floating point number, so that the function is continuous everywhere yet produces semi-random values. And Perlin spent a long time working on his version(s) trying to get it as fast as possible; using the additional shared generate method to construct entire textures of Perlin-noise is indeed doable. Even with multiple calls per pixel (as is costumary to generate the most spectacular of effects). In our little example however, we will just call the plain simplex function since this example is not about generating beautiful and accurate landscapes but rather illustrating a method. To run the example, you will need to supply a tiled texture of size 320 x 64 pixels consisting of 5 tiles of water, grass, forest, desert and mountains respectively. The one I used is linked here , which is merely colored squares representing the areas (I'm not much of an artist).
Aside from the fact that Perlin-noise is used, nothing much is new in this example. The tiled texture is loaded and 64 by 64 bits of it is displayed at every location on the screen based on the Perlin-simplex of the location (or rather a scaled down version of the location). The update method in the TiledMap class illustrates how an 'old' state of the mouse is saved to determine if the left button has just been pressed. Then the location of the mousepointer is used to generate a small offset of pixels and an offset into the map. Feel free to adjust the levels in TiledMap.Draw to accomodate more forest or whatever - Perlin-noise gives values in the range -1 to 1, and it should be easy to allow for more area-types or various other features.
In truth, a mature man who uses hair-oil, unless medicinally , that man has probably got a quoggy spot in him somewhere. As a general rule, he can't amount to much in his totality. (Melville: Moby Dick)
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|