Code archives/Graphics/Deformable terrain

This code has been declared by its author to be Public Domain code.

Download source code

Deformable terrain by matibee2010
The terrain is made up of horizontal line segments while a second, smaller shape is used as an eraser and some falling particles are added for effect.

Subtracting one line segment from another is really simple.. Depending on how they intersect; the start could be changed, the end could be changed, it could be deleted altogether or split into two.

It's completely unoptimised but still runs quite well.

Edit: Now updated to sort the line segments by height, allowing the subtract and collision tests to exit once they've reached lines higher than the test height.

Edit 2: I've uploaded a textured and slightly updated version here.

Cheers
Matt
SuperStrict 

' Horizontal line type
Type hline
	Field _start:Int
	Field _end:Int
	Field y:Int
	
	Method draw()
		DrawLine( _start, y, _end, y )
	End Method 
	
	Function Create:hline ( s:Int, e:Int, y:Int )
		Local hl:hline = New hline
		hl._start = s
		hl._end = e
		hl.y = y
		Return hl
	End Function 
	
	' Subtract another hline from this one, if this line is
	' deleted completely this function returns true.
	'
	' If this line is split, hlDest contains the newly created
	' line.
	Method Split:Int ( hl:hline, hlDest:hline Var )
		If ( hl.y <> y ) Return False 
		If ( _start < hl._start )
			If ( _end > hl._end )
				hlDest = hline.Create( hl._end, _end, y )
				_end = hl._start
			Else If ( _end > hl._start )
				_end = hl._start
			End If 
		Else If ( _start < hl._end )
			If ( _end < hl._end )
				Return True
			Else 
				_start = hl._end
			End If 
		End If 
		Return False 
	End Method 
	
	Method Move( _x:Int, _y:Int )
		_start :+ _x
		_end :+ _x
		y :+ _y
	End Method 
	
	Method Collide:Int ( _x:Int, _y:Int )
		If ( _y = y )
			If ( _x >= _start And _x <= _end )
				Return True 
			End If 
		End If 
		Return False 
	End Method 
	
End Type 

' A shape created from the horizontal lines..
Type hshape
	Field lines:TList
	Field x:Int, y:Int
	
	Function Create:hshape ()
		Local hs:hshape = New hshape
		hs.lines = New TList
		Return hs
	End Function 
	
	Method Draw()
		For Local hl:hline = EachIn lines
			hl.Draw()
		Next 
	End Method 
	
	Method AddLine( s:Float, e:Float, y:Float )
		lines.AddLast( hline.Create( s, e, y ) )
		SortList( lines, True, hshape.SortLinesByHeight )
	End Method 
	
	Function SortLinesByHeight:Int ( h0:Object, h1:Object )
		Return hline(h1).y < hline(h0).y
	End Function 
	
	Method Position( _x:Int, _y:Int )
		Local xmove:Int = _x - x
		Local ymove:Int = _y - y
		x :+ xmove
		y :+ ymove
		If ( xmove Or ymove )
			For Local hlThis:hline = EachIn lines
				hlThis.Move( xmove, ymove )
			Next
		End If 
	End Method 
	
	Method Subtract( hs:hshape )
		For Local hlOther:hline = EachIn hs.lines
			For Local hlThis:hline = EachIn lines
				Local hlNew:hline
				If ( hlOther.y < hlThis.y ) Exit 
				If ( hlThis.Split( hlOther, hlNew ) )
					lines.Remove( hlThis )
				End If 
				If ( hlNew ) 
					lines.AddLast( hlNew )
					SortList( lines, True, hshape.SortLinesByHeight )
				End If 
			Next
		Next 
	End Method 
	
	Method CollidePixel:Int ( x:Int, y:Int )
		For Local hlThis:hline = EachIn lines
			If ( hlThis.y > y ) 
				Return False 
			Else If ( hlThis.y = y And hlThis.Collide( x, y ) )
				Return True 
			End If 
		Next 
		Return False 
	End Method 
	
End Type 

Type phyxel ' simple physics pixel
	Field x:Float
	Field y:Float 
	Field vel:Float
	
	Function Create:phyxel ( x:Float, y:Float )
		Local p:phyxel = New phyxel
		p.x = x
		p.y = y
		Return p
	End Function 
	
	Method Draw()
		DrawRect( x, y, 1, 1 )
	End Method 
	
	Method Update( terrain:hshape )
		If ( Not terrain.CollidePixel( x, y + vel + 0.5 ) )
			vel :+ 0.1
			If ( vel > 0.98 ) vel = 0.98
		Else 
			vel :* -( 0.4 + RndFloat() * 0.5 )
		End If 
		y :+ vel
		If ( y > 600 ) y = 600
	End Method 
	
End Type 



' Simple test
Graphics 800, 600

' The first shape is the terrain
Local hs1:hshape = hshape.Create()
For Local t:Int = 300 To 599
	hs1.AddLine( 0, 799, t )
Next 

' The second shape is the eraser
Local hs2:hshape = hshape.Create()
For Local t:Int = 0 To 30
	hs2.AddLine( 0, 40, t )
Next 
hs2.AddLine( 1, 39, 31 )
hs2.AddLine( 2, 38, 32 )
hs2.AddLine( 4, 36, 33 )
hs2.AddLine( 6, 34, 34 )
hs2.AddLine( 8, 32, 35 )
hs2.AddLine( 11, 29, 36 )
hs2.AddLine( 15, 25, 37 )

' a list of falling pixels.
Local phyxelList:TList = New TList
For Local t:Int = 0 To 200
	phyxelList.AddLast( phyxel.Create( Rand( 5, 795 ), Rand( 20, 80 ) ) )
Next 


While Not AppTerminate()
	hs1.Subtract( hs2 )
	Cls
	SetColor( 0,0,255 )
	hs1.draw()
	SetColor( 255, 0, 0 )
	hs2.Position( MouseX(), MouseY() )
	hs2.draw()
	DrawText( "Line segments: " + hs1.lines.Count() , 0, 0 )
	SetColor( 0, 255, 0 )
	For Local p:phyxel = EachIn phyxelList
		p.Update( hs1 )
		p.Draw()
	Next 
	Flip	
Wend

Comments

AndrewT2010
Awesome! I'm very tempted to make a Worms clone now :P.


Ethan2012
Which program did you do this in. In my blitz plus it does not work?


Yasha2012
This is BlitzMax code. It's not compatible with BlitzPlus.

BlitzPlus can use those Archive entries that are marked as ".bb" format in the index, as opposed to ".bmx" like this one (at least, when people remember to tag them correctly).


matibee2012
As Yasha says it is Blitzmax but the description of how it works is here: http://www.matibee.co.uk/wpsite/2010/04/04/2d-deformable-terrain-in-blitzmax/

(Not sure why the original link above isn't working)


Code Archives Forum