Controlling a moving brush

Discuss any general programming issues here
Post Reply
pecaN
Posts: 124
Joined: Thu Jun 10, 2010 4:15 pm

Controlling a moving brush

Post by pecaN »

Hi all, I have a question for you... If I want to do something like tetris - a brush is moving from top of the screen to the bottom and while it is moving i want to have a chance to move it by 20 pixels to the left and right by pressing a key , which is the best and the fastest technique ? I use this : i am repeatedly calling AsyncDrawFrame which moves the brush by one pixel down and for keypressing test i use IsKeyDown command - and it works good and it is fast - but maybe too fast sometimes... even if i press the key slightly the brush sometimes moves by 40 pixels insteads of 20... thanx pecaN
jalih
Posts: 281
Joined: Fri Jun 18, 2010 8:08 pm
Location: Finland

Re: Controlling a moving brush

Post by jalih »

Use delta timing to control the update speed of a brush. Look at My prototype game template with game state manager thread in this forum...
User avatar
airsoftsoftwair
Posts: 5887
Joined: Fri Feb 12, 2010 2:33 pm
Location: Germany
Contact:

Re: Controlling a moving brush

Post by airsoftsoftwair »

Do not use AsyncDrawFrame() for this. It's much better to do this with sprites using DisplaySprite() or a double buffered display.
jalih
Posts: 281
Joined: Fri Jun 18, 2010 8:08 pm
Location: Finland

Re: Controlling a moving brush

Post by jalih »

Here is a simple example to get you started:

Code: Select all

;
; Simple blocks in Hollywood
;

@DISPLAY {Width = 350, Height = 360, Title = "Simple Blocks"}

Const #BLOCKSIZE = 16


; Include game framework
@INCLUDE "game.hws"



; Setup
Function game.load()
	move_delay = 0
	fall_delay = 0
	game:setUpdateRate(60) ; set update rate
	map:init()
	shapes:init()	
EndFunction


; Draw
Function game.draw()
	Cls()
	map:draw()
	piece:draw()
EndFunction


; Update
Function game.update(dt)	
	move_delay = move_delay + dt 
	If move_delay >= 0.1
		piece:move()					
		move_delay = 0
	EndIf
	
	fall_delay = fall_delay + dt
	If fall_delay >= 0.4
		piece:fall()				
		fall_delay = 0
	EndIf
	
	If IsKeyDown("ESC") Then End 	
EndFunction


;------------------------------------------------------------------------------------------

map = {}

Function map:init()
	self.map = {}
	map[-5]  =  { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }
	map[-4]  =  { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }
	map[-3]  =  { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }
	map[-2]  =  { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }
	map[-1]  =  { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }
	map[0]   =  { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }
	map[1]   =  { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }
	map[2]   =  { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }
	map[3]   =  { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }
	map[4]   =  { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }
	map[5]   =  { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }
	map[6]   =  { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }
	map[7]   =  { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }
	map[8]   =  { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }
	map[9]   =  { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }
	map[10] =  { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }
	map[11] =  { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }
	map[12] =  { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }
	map[13] =  { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }
	map[14] =  { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }
	map[15] =  { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }
	map[16] =  { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }
	map[17] =  { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }
	map[18] =  { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }
	map[19] =  { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }
	map[20] =  { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }
	map[21] =  { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }
	map[22] =  { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }

EndFunction


Function map:draw()
	SetFillStyle(#FILLCOLOR)
		
	For Local i = 1 To 21
		For Local j = 0 To 21
			 Box(j * #BLOCKSIZE, i * #BLOCKSIZE, #BLOCKSIZE, #BLOCKSIZE, map[i][j])
		Next
	Next
	
	SetFillStyle(#FILLNONE)
	Box( #BLOCKSIZE-1, 0, 20 * #BLOCKSIZE + 2, 22 * #BLOCKSIZE + 1, #WHITE)

EndFunction



shapes = {}
shapes.shapes = {}

Function shapes:init()
	Local shape = { coords = { { { x = 0, y = 1 }, { x = 1, y = 1 }, { x = 2, y = 1 }, { x = 3, y = 1 } }, 
				 { { x = 1, y = 0 }, { x = 1, y = 1 }, { x = 1, y = 2 }, { x = 1, y = 3 } } }, 
			color = #RED }

	InsertItem(self.shapes, shape)
	
	Local shape = { coords = { { { x = 0, y = 0 }, { x = 0, y = 1 }, { x = 1, y = 0 }, { x = 1, y = 1 } } }, 
			color = #OLIVE }

	InsertItem(self.shapes, shape)
	
	Local shape = { coords = { { { x = 1, y = 0 }, { x = 0, y = 1 }, { x = 1, y = 1 }, { x = 2, y = 1 } }, 
				   { { x = 1, y = 0 }, { x = 1, y = 1 }, { x = 2, y = 1 }, { x = 1, y = 2 } },
				   { { x = 0, y = 1 }, { x = 1, y = 1 }, { x = 2, y = 1 }, { x = 1, y = 2 } },
				   { { x = 1, y = 0 }, { x = 0, y = 1 }, { x = 1, y = 1 }, { x = 1, y = 2 } } }, 
			color = #TEAL }

	InsertItem(self.shapes, shape)
	
	Local shape = { coords = { { { x = 0, y = 0 }, { x = 1, y = 0 }, { x = 1, y = 1 }, { x = 2, y = 1 } }, 
				   { { x = 1, y = 0 }, { x = 0, y = 1 }, { x = 1, y = 1 }, { x = 0, y = 2 } } }, 
			color = #PURPLE }

	InsertItem(self.shapes, shape)

	Local shape = { coords = { { { x = 1, y = 0 }, { x = 2, y = 0 }, { x = 0, y = 1 }, { x = 1, y = 1 } }, 
				   { { x = 0, y = 0 }, { x = 0, y = 1 }, { x = 1, y = 1 }, { x = 1, y = 2 } } }, 
			color = #BLUE }
			
	InsertItem(self.shapes, shape)
	
	Local shape = { coords = { { { x = 2, y = 0 }, { x = 0, y = 1 }, { x = 1, y = 1 }, { x = 2, y = 1 } }, 
				   { { x = 0, y = 0 }, { x = 0, y = 1 }, { x = 0, y = 2 }, { x = 1, y = 2 } },
				   { { x = 0, y = 0 }, { x = 1, y = 0 }, { x = 2, y = 0 }, { x = 0, y = 1 } },
				   { { x = 0, y = 0 }, { x = 1, y = 0 }, { x = 1, y = 1 }, { x = 1, y = 2 } } }, 
			color = #NAVY }

	InsertItem(self.shapes, shape)

	Local shape = { coords = { { { x = 0, y = 0 }, { x = 1, y = 0 }, { x = 2, y = 0 }, { x = 2, y = 1 } }, 
				   { { x = 1, y = 0 }, { x = 1, y = 1 }, { x = 0, y = 2 }, { x = 1, y = 2 } },
				   { { x = 0, y = 0 }, { x = 0, y = 1 }, { x = 1, y = 1 }, { x = 2, y = 1 } },
				   { { x = 0, y = 0 }, { x = 1, y = 0 }, { x = 0, y = 1 }, { x = 0, y = 2 } } }, 
			color = #AQUA }

	InsertItem(self.shapes, shape)
	
EndFunction

	

piece = {}
piece.id = 0
piece.x = 1
piece.y = -4
piece.rotation = 0


Function piece:fall()
	Local ny = self.y + 1
	
	Local i, v = NextItem(shapes.shapes[self.id].coords[self.rotation])
	While GetType(i) <> #NIL
		If map[ny + shapes.shapes[self.id].coords[self.rotation][i].y][self.x + shapes.shapes[self.id].coords[self.rotation][i].x] <> 0
			Local ii, vv = NextItem(shapes.shapes[self.id].coords[self.rotation])
			While GetType(ii) <> #NIL
				map[self.y + shapes.shapes[self.id].coords[self.rotation][ii].y][self.x + shapes.shapes[self.id].coords[self.rotation][ii].x] = shapes.shapes[self.id].color
				ii, vv = NextItem(shapes.shapes[self.id].coords[self.rotation], ii)
			Wend
			
			self.id = Rnd(ListItems(shapes.shapes))
			self.rotation = Rnd(ListItems(shapes.shapes[self.id].coords))
			self.y = -4
			self.x = Rnd(20) + 1
			ny = self.y
			Break
		EndIf
		i, v = NextItem(shapes.shapes[self.id].coords[self.rotation], i)
	Wend
	
	self.y = ny
	
EndFunction


Function piece:rotate()
	Local nr = self.rotation + 1
	nr = Wrap(nr, 0, ListItems(shapes.shapes[self.id].coords))
	
	Local i, v = NextItem(shapes.shapes[self.id].coords[nr])
	While GetType(i) <> #NIL
		If map[self.y + shapes.shapes[self.id].coords[nr][i].y][self.x + shapes.shapes[self.id].coords[nr][i].x] <> 0
			nr = self.rotation
			Break
		EndIf
		i, v = NextItem(shapes.shapes[self.id].coords[nr], i)
	Wend
	
	self.rotation = nr

EndFunction


Function piece:move()
	Local nx
		
	If IsKeyDown("UP")  Then piece:rotate()
	
	If IsKeyDown("LEFT")
		nx = self.x - 1
		Local i, v = NextItem(shapes.shapes[self.id].coords[self.rotation])
		While GetType(i) <> #NIL
			If map[self.y + shapes.shapes[self.id].coords[self.rotation][i].y][nx + shapes.shapes[self.id].coords[self.rotation][i].x] <> 0
				nx = self.x
				Break
			EndIf
			i, v = NextItem(shapes.shapes[self.id].coords[self.rotation], i)
		Wend
		
		self.x = nx
			
	ElseIf IsKeyDown("RIGHT")
		nx = self.x + 1
		Local i, v = NextItem(shapes.shapes[self.id].coords[self.rotation])
		While GetType(i) <> #NIL
			If map[self.y + shapes.shapes[self.id].coords[self.rotation][i].y][nx + shapes.shapes[self.id].coords[self.rotation][i].x] <> 0
				nx = self.x
				Break
			EndIf
			i, v = NextItem(shapes.shapes[self.id].coords[self.rotation], i)
		Wend
		
		self.x = nx
	EndIf

EndFunction


Function piece:draw()
	SetFillStyle(#FILLCOLOR)

	Local i, v = NextItem(shapes.shapes[self.id].coords[self.rotation])
	While GetType(i) <> #NIL
		Box((self.x + shapes.shapes[self.id].coords[self.rotation][i].x) * #BLOCKSIZE, (self.y + shapes.shapes[self.id].coords[self.rotation][i].y) * #BLOCKSIZE, #BLOCKSIZE, #BLOCKSIZE, shapes.shapes[self.id].color)
		i, v = NextItem(shapes.shapes[self.id].coords[self.rotation], i)
	Wend
	
EndFunction	
	


; Go!
game:go()
You need my game template:

Code: Select all

;
; Game template module: game.hws
;

game = {}
game.updateRate = 20 ; Default to 20 millisec = 50 fps.
game.dt = 0
game.fps = 0


; Game setup function for the user.
Function game.load()
	; Our game must override this dummy function.
EndFunction


; Game update function for the user
Function game.update(dt)
	; Our game must override this dummy function.
EndFunction


; Game draw frame function for the user
Function game.draw()
	; Our game must override this dummy function.   
EndFunction


; Our game framework init function. Calls just user game setup function for now
; and starts double buffering.
Function game.init()
	game.load()
	BeginDoubleBuffer()   
EndFunction


; Basic game loop function for our framework.
Function game.loop(dt)
	If game.state.event$ = "init" Or game.state.event$ = "update"
		game.state.event$ = "draw"
		If RawGet(game.state.states[game.state.state], "draw") Then game.state.states[game.state.state].draw()
		Flip()	
	EndIf

	If game.state.event$ = "draw"
		game.state.event$ = "update"
		If RawGet(game.state.states[game.state.state], "update") Then game.state.states[game.state.state].update(dt)	
	EndIf
EndFunction


; Set update rate for the game.
Function game:setUpdateRate(fps)
	self.updateRate = 1000 / fps
EndFunction


; Get the current fps-rate.
Function game:getFPS()
	Return(Round(1000 / (self.dt * 1000)))
EndFunction


; Runs the game, handles calling the game loop and timing.
Function game.run()
	StartTimer(1)
	StartTimer(2)

	Repeat
		; Try to lock the frame update rate to game.updateRate
		WaitTimer(1, game.updateRate)
		; Get the actual time it took to update the frame.
		Local ftime = GetTimer(2)
		ResetTimer(2)
		; Delta time in seconds between the two last frames.
		game.dt = game.updateRate / 1000 * ftime / game.updateRate
		game.loop(game.dt)   
	Forever
EndFunction


Function game:go()
	self.init()
	self.run()
EndFunction



; Simple game state manager
game.state = {}
game.state.states = {}
game.state.states[0] = {} ; Default state
game.state.states[0].update = Function (dt) game.update(dt) EndFunction
game.state.states[0].draw = Function () game.draw() EndFunction
game.state.state = 0
game.state.event$ = "init" ; Start in init event.
game.state.ids = {}


; Registers new game state by name.
Function game.state:register(statename, state)
	self.states[statename] = state
	InsertItem(self.ids, statename)   
EndFunction


; Remove game state from manager by state name.
Function game.state:remove(statename)
	self.states[statename] = {}
	Local i, v = NextItem(self.ids)
	While GetType(i) <> #NIL
		If self.ids[i] = statename Then RemoveItem(self.ids, i)
		i, v = NextItem(self.ids, i)
	Wend
EndFunction


; Clears all states from manager except default state.
Function game.state:clear()
	Local i, v = NextItem(self.ids)
	While GetType(i) <> #NIL
		self.states[v] = {}
		i, v = NextItem(self.ids, i)
	Wend
	       
	self.ids = {}

EndFunction


; Set active game state by name
Function game.state:set(statename)
	self.state = statename
	self.event$ = "init"
	If RawGet(self.states[self.state], "init") Then self.states[self.state].init()
EndFunction


; Get the name of a currently active game state
Function game.state:get()
	Return(self.state)
EndFunction


; Get the currently active event of a game state
Function game.state:getEvent()
	Return(self.event$)
EndFunction
pecaN
Posts: 124
Joined: Thu Jun 10, 2010 4:15 pm

Re: Controlling a moving brush

Post by pecaN »

Thx for your replies guys ! As always, the solution was quite easy - just put ResetKeyStates() command after each IsKeyDown() ... i totally overlooked this command in the docs... :-( now it works good and controlling is not so sensitive...

@jalih : thx for sourcecode, will try it later

@Andreas : i know you recommend sprites for twtris like game in the docs but there is a problem - there are MANY sprites on the screen so i need to free them from memory but this is not possible because if a sprite is freed it is also deleted from screen and i don't want this... so i will probably have to use brushes...
Post Reply