my vertical scrolling is slow

BlitzMax Forums/BlitzMax Beginners Area/my vertical scrolling is slow

Hi !

i'm interested only by vertical scrolling. i use this piece of code into my project. When i increase the number of tiles, the scrolling is slow. Coz the for-next loop inside the Compute_one_row_Scrolling() and Draw_one_row (). How to avoid the for-next loop ? I'm searching a better solution.

i my game i use an editor to produce a level, next i read a file. so i must keep this structure.

Col = 0 to 32, Row 0 to 1000 or more.

Type TBrick

Field Col
Field Row

End Type

(indexed by Row, and Col)

i know scrolling examples into tutorials. i prefer some help to correct this code.

Many thanks for your help !

Strict

Global List_bricks : TList = CreateList()

Type TBrick

	Field Col
	Field Row
	
	Field r
	Field g
	Field b
	
	Field x
	Field y
	
	' ----------------------------------------------------------------------------------------------
	
	Function Create:TBrick (X,y)
	
		Local b:TBrick = New TBrick
		
		b.Col = x
		b.Row = y 
		
		b.r = Rand (100,255)
		b.g = Rand (100,255)
		b.b = Rand (100,255)

		ListAddLast List_bricks, b
		
		Return b
		
	End Function

	' ----------------------------------------------------------------------------------------------

	Function Compute_one_row_Scrolling (Row, ScrollY)
	
		For Local b:TBrick = EachIn List_bricks
		
			If b.Row = Row Then
				b.x = b.Col * 32
				b.y = ScrollY 
			End If
			
		Next
		
	End Function

	' ----------------------------------------------------------------------------------------------

	Function Draw_one_row (Row)
	
		For Local b:TBrick = EachIn List_bricks
		
			If b.Row = Row Then
			
				SetColor b.r, b.g, b.b
				DrawRect b.x, b.y, 32,32
				
			End If
			
		Next
		
	End Function
	
	' ----------------------------------------------------------------------------------------------

	Function MySort:Int(o1:Object, o2:Object)
	
        Local diff% = TBrick(o1).Row - TBrick(o2).Row
        If diff = 0 Then
             diff = TBrick(o1).Col - TBrick(o2).Col
        EndIf
        Return diff	

	End Function

	' ----------------------------------------------------------------------------------------------

	Function Sort_the_list ()
	
		SortList(List_bricks,True,MySort)
		
	End Function		

	' ----------------------------------------------------------------------------------------------

End Type

' create some tiles for this example (i use an editor for my game)

' if you increase max_row, the scrolling is slow, coz the for-next loop into the 
' Compute_one_row_Scrolling().

Local Max_row = 100

For Local y=0 To Max_Row

	For Local x=0 To 31

		TBrick.Create (x, y)

	Next

Next

' sort them by y and x (not usefull here, but the game editor don't sort them...)

TBrick.Sort_the_list()

' go

Graphics 1024,768


Local t
Local ScrollY = 0
Local Row = 0
Local TimeScroll = MilliSecs()

While Not KeyDown(KEy_ESCAPE)

	' compute scrolling
	
	If TimeScroll + 20 < MilliSecs() Then

		If ScrollY < 31 Then
		
			ScrollY = SCrollY + 1
			
		Else

			ScrollY = 0

			If Row < Max_row + 25 Then
			
				Row = Row + 1
				
			Else
			
				Row = 0
				
			End If
			
		End If
		
		t = MilliSecs()
		
		Local Decal = -32
		
		For Local r = row To (row-24) Step - 1
		
			TBrick.Compute_one_row_Scrolling(r,ScrollY + Decal)
			
			Decal = Decal + 32
			
		Next
		
		t = MilliSecs() - t		
		
		TimeScroll = MilliSecs()
		
	End If
	
	' draw
	
	Cls

	For Local r = row To (row-24) Step - 1
	
		TBrick.Draw_one_row(r)
		
	Next
	
	DrawText "time=" + String(t), 0,0
	DrawText "row=" + String(row), 0,30
	
	Flip

Wend 


32000 tile images being drawn? Not surprised it's slow.

no : it's an example. Of course only visibles tiles being drawn to screen !!!. Please test the code to see the pb.

Hello.

Personally, I'd:
- hold the tiles in an array, not a list;
- apply a global decal offset to the values;
- create images instead of drawing rects.

The difficulty is that every inner loop you're iterating through all the tiles 24 times, regardless of if they're on screen or not, and then iterating through them once again 24 times to display them (again regardless of if they're on screen or not). That's a lot of tiles.

If you're not sure what I mean, reduce the number of rows to 1 and then increase the number of rows to 10000 to see a change in the time taken. t remains constant for each value.

Goodbye.

Hello.

Take a look at visits and tiles in the following:
Strict

Global List_bricks : TList = CreateList()
Global visits
Global tiles

Type TBrick

	Field Col
	Field Row
	
	Field r
	Field g
	Field b
	
	Field x
	Field y
	
	' ----------------------------------------------------------------------------------------------
	
	Function Create:TBrick (X,y)
	
		Local b:TBrick = New TBrick
		
		b.Col = x
		b.Row = y 
		
		b.r = Rand (100,255)
		b.g = Rand (100,255)
		b.b = Rand (100,255)

		ListAddLast List_bricks, b
		
		Return b
		
	End Function

	' ----------------------------------------------------------------------------------------------

	Function Compute_one_row_Scrolling (Row, ScrollY)
		visits:+1	
		For Local b:TBrick = EachIn List_bricks
			tiles:+1
					
			If b.Row = Row Then
				b.x = b.Col * 32
				b.y = ScrollY 
			End If
			
		Next
		
	End Function

	' ----------------------------------------------------------------------------------------------

	Function Draw_one_row (Row)
		visits:+1	
		For Local b:TBrick = EachIn List_bricks
			tiles:+1
			If b.Row = Row Then
			
				SetColor b.r, b.g, b.b
				DrawRect b.x, b.y, 32,32
				
			End If
			
		Next
		
	End Function
	
	' ----------------------------------------------------------------------------------------------

	Function MySort:Int(o1:Object, o2:Object)
	
        Local diff% = TBrick(o1).Row - TBrick(o2).Row
        If diff = 0 Then
             diff = TBrick(o1).Col - TBrick(o2).Col
        EndIf
        Return diff	

	End Function

	' ----------------------------------------------------------------------------------------------

	Function Sort_the_list ()
	
		SortList(List_bricks,True,MySort)
		
	End Function		

	' ----------------------------------------------------------------------------------------------

End Type

' create some tiles for this example (i use an editor for my game)

' if you increase max_row, the scrolling is slow, coz the for-next loop into the 
' Compute_one_row_Scrolling().

Local Max_row = 100

For Local y=0 To Max_Row

	For Local x=0 To 31

		TBrick.Create (x, y)

	Next

Next

' sort them by y and x (not usefull here, but the game editor don't sort them...)

TBrick.Sort_the_list()

' go

Graphics 1024,768


Local t
Local ScrollY = 0
Local Row = 0
Local TimeScroll = MilliSecs()

While Not KeyDown(KEy_ESCAPE)

	' compute scrolling
	
	If TimeScroll + 1 < MilliSecs() Then ' NOTE CHANGE TO 1 MILLISEC HERE TO GIVE FASTEST TRY

		If ScrollY < 31 Then
		
			ScrollY = SCrollY + 1
			
		Else

			ScrollY = 0

			If Row < Max_row + 25 Then
			
				Row = Row + 1
				
			Else
			
				Row = 0
				
			End If
			
		End If
		
		t = MilliSecs()
		
		Local Decal = -32
		
		For Local r = row To (row-24) Step - 1
		
			TBrick.Compute_one_row_Scrolling(r,ScrollY + Decal)
			
			Decal = Decal + 32
			
		Next
		
		t = MilliSecs() - t		
		
		TimeScroll = MilliSecs()
		
	End If
	
	' draw
	
	Cls

	For Local r = row To (row-24) Step - 1
	
		TBrick.Draw_one_row(r)
		
	Next
	
	DrawText "time=" + String(t), 0,0
	DrawText "row=" + String(row), 0,30
	DrawText "Visits=" + String(visits),0,48	
	DrawText "Tiles=" + String(tiles),0,64
	Flip
	visits = 0
	tiles = 0
Wend 


And try changing the number of rows from 100 to 1000 to 10000.

Goodbye.

The difficulty is that every inner loop you're iterating through all the tiles 24 times, regardless of if they're on screen or not, and then iterating through them once again 24 times to display them (again regardless of if they're on screen or not). That's a lot of tiles.
agree with this it's my pb !


create images instead of drawing rects.


i use images into my game




The difficulty is that every inner loop you're iterating through all the tiles 24 times, regardless of if they're on screen or not, and then iterating through them once again 24 times to display them

note that i separate logic and drawing into the game to apply delta time.


- hold the tiles in an array, not a list;
- apply a global decal offset to the values;

good idea.

Many thanks for your idea, is somebody know how to avoid this for-next loops ??? Finally i'm searching a better code structure ! My first idea produce bad code !!!

Hello.

Ok, what is the sort function there for?

Goodbye.

Ok, what is the sort function there for?
oups... for nothing in this case. i use it into my game as the tiles are not yet sorted when you create them into the editor.



Put the Tbricks in an array with dimensions the width/height of your 'wall'. Then keep track of screen_offset and draw from that index to the number of bricks that can fit on a screen.

Put the Tbricks in an array with dimensions the width/height of your 'wall'. Then keep track of screen_offset and draw from that index to the number of bricks that can fit on a screen.

no sure to understand your idea. Is it this :
use an one dimension array, determine an index for each tile ? i've thinking about this idea to direct access to a tile. The problem is that produce a huge array...
32 x 24 x 1000. with a lot of null values (have not a tile for each array cell).

Not that the previous editor screen is a test, there are no 'walls' into the game. See the ingame screenshoot picture.

I was responding to the code you posted but I would still consider a two dimension array even with the null values.
I might not be thinking straight by why would it be 32*24*1000? If there are 1000 rows and each row contains 32 bricks isn't that array[32,1000]?
Somebody else might be able to say whether a tmap might help with your tilelist indexed against it's x position (can you have duplicate indexes in a tmap?) converted to a string.
You can then use your screen_pos (x value for topleft of your world map) and compare it against the indexes drawing anything that fits between screen_pos and screen_pos+max_number_per_screen.
I haven't really used tmaps but it might be possible.
This might be a bit too much for the Beginner's forum and I'm sure many people have written games which resemble you screenshots so there could be a simple answer.

If there are 1000 rows and each row contains 32 bricks isn't that array[32,1000]?

oups, yes you're right. Sorry for the mystake.

This might be a bit too much for the Beginner's forum and I'm sure many people have written games which resemble you screenshots so there could be a simple answer

hope they also read this forum !

Thanks, time to me to code something with your ideas... i'll post my results here.

First attempt to code with arrays :

Strict

Const Max_TILEMAP_HEIGHT = 1000

Type TScreen

	Field array_cells   : TCell[32 * MAX_TILEMAP_HEIGHT]
	Field array_tiles   : TTile[]
	Field array_cannons : TCannon[]
		
	Field NbTiles
	Field NbCannons
	
	Field ScrollY
	Field EllapsedTime
	
	Field t
		
	Function Init:TScreen ()
	
		Local s:TScreen = New TScreen
		
		' fill the array with empy values
		
		For Local y = 0 To MAX_TILEMAP_HEIGHT - 1
		
			For Local x=0 To 32 - 1
			
				Local i = y * 32 + x
				
				s.array_cells[i] = TCell.Create (x,y)
					
			Next
			
		Next
		
		s.NbTiles = 0
		s.NbCannons = 0
		
		s.EllapsedTime = MilliSecs()
		s.ScrollY = 0
	
		Return s
	
	End Function
	
	Method Add_tile (pPosX, pPosY)
	
		' check index if not out of bound
		
		Local index = pPosY * 32 + pPosX
		
		If index > 32 * MAX_TILEMAP_HEIGHT Then
			RuntimeError "Bad Index"
		End If
		
		array_cells[Index].Id_tile = NbTiles + 1
		
		NbTiles = NbTiles + 1		
		
		array_tiles = array_tiles [..NbTiles+1]
		
		array_tiles [NbTiles] = TTile.Create(pPosX, pPosY)
		
	End Method

	Method Add_cannon (pPosX, pPosY)
	
		' check index if not out of bound
		
		Local index = pPosY * 32 + pPosX
		
		If index > 32 * MAX_TILEMAP_HEIGHT Then
			RuntimeError "Bad Index"
		End If
		
		array_cells[index].Id_cannon = NbCannons + 1
		
		NbCannons = NbCannons + 1		
		
		array_cannons = array_cannons [..NbCannons+1]
		
		array_cannons [NbCannons] = TCannon.Create(pPosX, pPosY)		
	
	End Method
	
	Method Compute_logic()
	
		If EllapsedTime + 20 < MilliSecs() Then
			
			t = MilliSecs()
			
			For Local y=0 To MAX_TILEMAP_HEIGHT - 1
			
				For Local x=0 To 31
				
					Local Index = y * 32 + x
					 
		
					If array_cells[Index].Id_tile <> - 1 Then
					
						Local ix
						ix = array_cells[Index].Id_tile
						array_tiles [ix].PosY = array_tiles [ix].PosY - 1
						
					End If
		
					If array_cells[Index].Id_cannon <> - 1 Then
					
						Local ix
						ix = array_cells[Index].Id_cannon
						array_cannons [ix].PosY = array_cannons [ix].PosY - 1
					
					End If
		
				
				Next
				
			Next
			
			t = MilliSecs() - t
			
		
			EllapsedTime = MilliSecs()

		End If
	
	End Method
	
	Method Draw()
	
		For Local y=0 To MAX_TILEMAP_HEIGHT - 1
		
			For Local x=0 To 31
			
				Local Index = y * 32 + x
				 
	
				If array_cells[Index].Id_tile <> - 1 Then
				
					Local px,py, ix, r,g,b
					ix = array_cells[Index].Id_tile 
					px = array_tiles [ix].PosX
					py = array_tiles [ix].PosY
					r = array_tiles [ix].r
					g = array_tiles [ix].g
					b = array_tiles [ix].b
					
					SetColor r,g,b
					DrawRect px,py, 32,32
					
				End If
	
				If array_cells[Index].Id_cannon <> - 1 Then
				
					Local px,py, angle, ix
					ix = array_cells[Index].Id_cannon
					px = array_cannons [ix].PosX
					py = array_cannons [ix].PosY
					angle = array_cannons [ix].Angle
					
					SetColor 255,0,0
					DrawOval px,py, 32,32
					
					SetColor 0,0,255
					SetRotation angle
					SetLineWidth 3
					DrawLine px+16,py+16,px+16+20,py+16
					SetLineWidth 2
					SetRotation 0
				
				End If
	
			
			Next
			
		Next
		
		SetColor 255,255,255
		DrawText "Time=" + String(t),10,10
		
	End Method
	
	
End Type

Type TCell

	Field PosX
	Field PosY
	
	Field Id_tile
	Field Id_Cannon
	
	Function Create : TCell (PosX, PosY)
	
		Local c:TCell = New TCell
		
		c.PosX = PosX 
		c.PosY = PosY 
		
		c.Id_Tile = -1
		c.Id_Cannon = -1
		
		Return c
	
	End Function
	
End Type

Type TTile

	Field PosX
	Field PosY
	
	Field r
	Field g
	Field b
	
	Function Create:TTile (PosX, PosY)
	
		Local t:TTile = New TTile
		
		t.PosX = PosX * 32
		t.PosY = PosY * 32
		
		t.r = Rand (100,255)
		t.g = Rand (100,255)
		t.b = Rand (100,255)
		
		Return t
	
	End Function
	
End Type

Type TCannon

	'Field Id_cannon : Int

	Field PosX
	Field PosY
	
	Field angle
	
	Function Create:TCannon (PosX, PosY)
	
		Local c:TCannon = New TCannon
		
		c.PosX = PosX * 32
		c.PosY = PosY * 32
		
		c.angle = Rand (0,360)
		
		Return c
	
	End Function	
	
End Type


' main program

Graphics 1024, 768

Global Screen : TScreen = TScreen.init()

DrawText "please wait, computing random tiles and canon positions...",0,0
Flip

For Local i=1 To 10000

	Local x = Rand (0,31)
	Local y = Rand (0,MAX_TILEMAP_HEIGHT-1)
	Screen.Add_tile (x,y)

	x = Rand (0,31)
	y = Rand (0,MAX_TILEMAP_HEIGHT-1)
	Screen.Add_cannon (x,y)

Next


While Not KeyDown (KEY_ESCAPE)

	Screen.Compute_logic()
	Cls
	Screen.Draw()
	Flip
	
Wend


Is this
		For Local y=0 To MAX_TILEMAP_HEIGHT - 1

still looping through everything rather than what's on-screen?

yes, this is a very basic scrolling code !!!

update !
Strict

Const Max_TILEMAP_HEIGHT = 1000

Type TScreen

	Field array_cells   : TCell[32 * MAX_TILEMAP_HEIGHT]
	Field array_tiles   : TTile[]
	Field array_cannons : TCannon[]
		
	Field NbTiles
	Field NbCannons
	
	Field ScrollY
	Field LigneMap
	
	Field EllapsedTime
	
	Field t
		
	Function Init:TScreen ()
	
		Local s:TScreen = New TScreen
		
		' fill the array with empy values
		
		For Local y = 0 To MAX_TILEMAP_HEIGHT - 1
		
			For Local x=0 To 32 - 1
			
				Local i = y * 32 + x
				
				s.array_cells[i] = TCell.Create (x,y)
					
			Next
			
		Next
		
		s.NbTiles = 0
		s.NbCannons = 0
		
		s.EllapsedTime = MilliSecs()
		s.ScrollY = 0
		s.LigneMap = 0
	
		Return s
	
	End Function
	
	Method Add_tile (pPosX, pPosY)
	
		' check index if not out of bound
		
		Local index = pPosY * 32 + pPosX
		
		If index > 32 * MAX_TILEMAP_HEIGHT Then
			RuntimeError "Bad Index"
		End If
		
		array_cells[Index].Id_tile = NbTiles + 1
		
		NbTiles = NbTiles + 1		
		
		array_tiles = array_tiles [..NbTiles+1]
		
		array_tiles [NbTiles] = TTile.Create(pPosX, pPosY)
		
	End Method

	Method Add_cannon (pPosX, pPosY)
	
		' check index if not out of bound
		
		Local index = pPosY * 32 + pPosX
		
		If index > 32 * MAX_TILEMAP_HEIGHT Then
			RuntimeError "Bad Index"
		End If
		
		array_cells[index].Id_cannon = NbCannons + 1
		
		NbCannons = NbCannons + 1		
		
		array_cannons = array_cannons [..NbCannons+1]
		
		array_cannons [NbCannons] = TCannon.Create(pPosX, pPosY)		
	
	End Method
	
	Method Compute_logic()
	
	
		If EllapsedTime + 20 < MilliSecs() Then
		
			If ScrollY < 31 Then
			
				ScrollY = ScrollY + 1
				
			Else
			
				ScrollY = 0
				
				If LigneMap < MAX_TILEMAP_HEIGHT Then
					LigneMap = LigneMap + 1
				Else
					LigneMap = 0
				End If
				
			End If
			
			t = MilliSecs()
			
			Local Decal = -32
			
			For Local y= Lignemap+24 To LigneMap Step -1
			
				For Local x=0 To 31
				
					Local Index = y * 32 + x
					
		
					If array_cells[Index].Id_tile <> - 1 Then
					
						Local ix
						ix = array_cells[Index].Id_tile
						array_tiles [ix].PosY = ScrollY + Decal
						
					End If
		
					If array_cells[Index].Id_cannon <> - 1 Then
					
						Local ix
						ix = array_cells[Index].Id_cannon
						array_cannons [ix].PosY = ScrollY + Decal
					
					End If
		
				
				Next
				
				Decal = Decal + 32
				
			Next
			
			t = MilliSecs() - t
			
		
			EllapsedTime = MilliSecs()

		End If
	
	End Method
	
	Method Draw()
	
		For Local y= Lignemap To LigneMap + 24 Step 1
		
			For Local x=0 To 31
			
				Local Index = y * 32 + x
				 
	
				If array_cells[Index].Id_tile <> - 1 Then
				
					Local px,py, ix, r,g,b
					ix = array_cells[Index].Id_tile 
					px = array_tiles [ix].PosX
					py = array_tiles [ix].PosY
					r = array_tiles [ix].r
					g = array_tiles [ix].g
					b = array_tiles [ix].b
					
					SetColor r,g,b
					DrawRect px,py, 32,32
					
				End If
	
				If array_cells[Index].Id_cannon <> - 1 Then
				
					Local px,py, angle, ix
					ix = array_cells[Index].Id_cannon
					px = array_cannons [ix].PosX
					py = array_cannons [ix].PosY
					angle = array_cannons [ix].Angle
					
					SetColor 255,0,0
					DrawOval px,py, 32,32
					
					SetColor 0,0,255
					SetRotation angle
					SetLineWidth 3
					DrawLine px+16,py+16,px+16+20,py+16
					SetLineWidth 2
					SetRotation 0
				
				End If
	
			
			Next
			
		Next
		
		SetColor 255,255,255
		DrawText "Time=" + String(t),10,10
		DrawText "Lignemap=" + String(LigneMap),10,20
		
	End Method
	
	
End Type

Type TCell

	Field PosX
	Field PosY
	
	Field Id_tile
	Field Id_Cannon
	
	Function Create : TCell (PosX, PosY)
	
		Local c:TCell = New TCell
		
		c.PosX = PosX 
		c.PosY = PosY 
		
		c.Id_Tile = -1
		c.Id_Cannon = -1
		
		Return c
	
	End Function
	
End Type

Type TTile

	Field PosX
	Field PosY
	
	Field r
	Field g
	Field b
	
	Function Create:TTile (PosX, PosY)
	
		Local t:TTile = New TTile
		
		t.PosX = PosX * 32
		t.PosY = PosY * 32
		
		t.r = Rand (100,255)
		t.g = Rand (100,255)
		t.b = Rand (100,255)
		
		Return t
	
	End Function
	
End Type

Type TCannon

	'Field Id_cannon : Int

	Field PosX
	Field PosY
	
	Field angle
	
	Function Create:TCannon (PosX, PosY)
	
		Local c:TCannon = New TCannon
		
		c.PosX = PosX * 32
		c.PosY = PosY * 32
		
		c.angle = Rand (0,360)
		
		Return c
	
	End Function	
	
End Type


' main program

Graphics 1024 , 768 , 32 , 75

Global Screen : TScreen = TScreen.init()

Global MaxTiles = 10000

For Local i=1 To MaxTiles

	Local x = Rand (0,31)
	Local y = Rand (0,MAX_TILEMAP_HEIGHT-1)
	Screen.Add_tile (x,y)

	x = Rand (0,31)
	y = Rand (0,MAX_TILEMAP_HEIGHT-1)
	Screen.Add_cannon (x,y)
	
	If KeyDown (KEY_ESCAPE) Then End
	
	Cls
	DrawText "please wait, computing " + String(MaxTiles) + " random tiles And canons positions... i=" + String(i),0,0
	Flip

Next


While Not KeyDown (KEY_ESCAPE) Or MouseHit(1)

	Screen.Compute_logic()
	Cls
	Screen.Draw()
	Flip
	
Wend


I suggest you do a flip(0) with the first flip to speed up the calculations and map creation.

I am working on a game using tile scrolling of size 32 x 32 is a random map generator if you want to try it out is here:
http://www.gamedev.net/community/gds/viewentry.asp?projectid=427815

good suggestion jesse. Thanks. Good game too.