Hi, I was thinking about how to tidy up my code and give me more control (more easily) over what is drawn. I remember from programming the Net Yaroze, that when you wanted to draw an image, you added it to a list which was processed and drawn at the end of the main game loop (as opposed to numerous drawing commands throughout hte code). Does anyone do anything similar using lists in BMax?
RenderLists
BlitzMax Forums/BlitzMax Beginners Area/RenderLists Does anyone do anything similar using lists in BMax?
Yup. Cool thing is you can even do Z-ordering by overriding the compare method and just calling myList.sort() - provided you don't have too many objects in the list. Nice!
Just be aware that TList.sort() is implemented using bubble sort, which has a quadratic time complexity, which can be a significant factor with lots of elements.
I have some example code too, that's pretty messy (but working) that I can send you if you want.
I have some example code too, that's pretty messy (but working) that I can send you if you want.
I'd appreciate it if you could
I did the same for my sprite engine, only I created a new Class of my own that inherrited the TList functionality. Then I added my own properties and methods to it.
OOP IS AWESOME!!!
OOP IS AWESOME!!!
For a fairly tidy main loop, take a look at the source for GridWars (Google for it, you'll find it at yakyak.org). UpdateAll() and RenderAll() are very tidy, enabled by using Lists.
The enemy Types could have been tidier by using an Abstract Type as a parent, but other than that it's a good tutorial :)
The enemy Types could have been tidier by using an Abstract Type as a parent, but other than that it's a good tutorial :)
Here's some code I played around with when I first got BMax.
Bear in mind that I didn't really have a clue what I was doing as I'd never used an OO language before bmax.
It's pretty clunky but I quite like the idea of having layers.
Bear in mind that I didn't really have a clue what I was doing as I'd never used an OO language before bmax.
It's pretty clunky but I quite like the idea of having layers.
Strict Const CLOUD_LAYER_1 = -2 Const CLOUD_LAYER_2 = -1 Const CLOUD_LAYER_3 = 10 Global g_game_clock:Int Global game:scene = New scene Global current_player:player '=== media === 'data Incbin "gfx/smallcloud.png" Incbin "gfx/mediumcloud.png" Incbin "gfx/largecloud.png" Incbin "gfx/player.png" 'globals Global small_cloud:TImage Global medium_cloud:TImage Global large_cloud:TImage Global red_plane:TImage Type sprite Field image:TImage Field frame_total:Int = 0, current_frame:Int = 0 Field anim_start_time:Int, anim_interval:Int Field pos_x:Float, pos_y:Float Field scale_x:Float = 1, scale_y:Float = 1 Field rotation:Float = 0 Field alpha:Float = 1 Field blendmode:Int = ALPHABLEND Field visible:Int = True Field layer_no = 0 Method show() visible = True End Method Method hide() visible = False End Method Method update() Abstract End Type Type layer Field sprites:TList Field pos_x:Float = 0, pos_y:Float = 0 Field rotation:Float = 0 ' to be continued Field alpha:Float = 1 Field blendmode:Int = ALPHABLEND Field visible:Int = True Field layer_no = 0 Method add(sprite:sprite) If sprites = Null Then sprites = New TList sprites.addlast(sprite) End Method Method show() visible = True End Method Method hide() visible = False End Method End Type Type scene Field layers:layer[256] Method add_sprite(sprite:sprite, layer_no:Int=0) Local layer_index:Int layer_index = 127 + layer_no If layers[layer_index] = Null Then layers[layer_index] = New layer layers[layer_index].add(sprite) layers[layer_index].layer_no = layer_no End Method Method which_layer(sprite:sprite) Return sprite.layer_no End Method Method get_layer:layer(layer_no:Int) Return layers[layer_no + 127] End Method Method update() Local this_layer:layer, this_sprite:sprite For this_layer = EachIn layers For this_sprite = EachIn this_layer.sprites this_sprite.update Next Next End Method Method render() Local this_layer:layer, this_sprite:sprite For this_layer = EachIn layers If this_layer.visible For this_sprite = EachIn this_layer.sprites If this_sprite.visible SetBlend this_sprite.blendmode SetRotation this_sprite.rotation SetAlpha this_sprite.alpha * this_layer.alpha SetScale this_sprite.scale_x, this_sprite.scale_y DrawImage this_sprite.image, this_sprite.pos_x + this_layer.pos_x, this_sprite.pos_y + this_layer.pos_y, this_sprite.current_frame EndIf Next EndIf Next End Method End Type '=== Test === Type cloud Extends sprite Field rotation_speed:Float Function create(scene:scene, image:TImage, layer_no:Int=0, pos_x:Int=0, pos_y:Int=0, rotation:Float=0, alpha:Float=1) Local cloud:cloud = New cloud cloud.image = image cloud.pos_x = pos_x cloud.pos_y = pos_y cloud.rotation = rotation cloud.alpha = alpha cloud.rotation_speed = Rnd(180) cloud.layer_no = layer_no scene.add_sprite(cloud, layer_no) End Function Method update() rotation = Cos(rotation_speed + (g_game_clock / 40.0)) * 4 Select layer_no Case CLOUD_LAYER_1 pos_x :- (current_player.x_vel * 0.5) pos_y :- (current_player.y_vel * 0.5) Case CLOUD_LAYER_2 pos_x :- (current_player.x_vel * 0.75) pos_y :- (current_player.y_vel * 0.75) Case CLOUD_LAYER_3 pos_x :- current_player.x_vel pos_y :- current_player.y_vel End Select End Method End Type Type weapon Field image:TImage, id:Int Field pos_x:Int, pos_y:Int, angle:Float, alpha:Float Field firing_rate:Int, damage:Int End Type Type plane Extends sprite Field speed:Float, turn_rate:Float, x_vel:Float, y_vel:Float Field weapon_slot:weapon[] Method add_weapon(image:TImage, pos_x:Int, pos_y:Int, slot:Int, id:Int) weapon_slot[slot] = New weapon weapon_slot[slot].image = image weapon_slot[slot].pos_x = pos_x weapon_slot[slot].pos_y = pos_y weapon_slot[slot].id = id weapon_slot[slot].angle = rotation weapon_slot[slot].alpha = alpha End Method Method update() End Method End Type Type player Extends plane Function create:player(scene:scene, image:TImage, layer_no:Int=0, pos_x:Int=0, pos_y:Int=0, rotation:Float=0, alpha:Float=1) Local this_player:player = New player this_player.image = image this_player.pos_x = pos_x this_player.pos_y = pos_y this_player.speed = 2 this_player.x_vel = Sin(rotation) * this_player.speed this_player.y_vel = -Cos(rotation) * this_player.speed this_player.turn_rate = 2 this_player.rotation = rotation this_player.scale_x = 1 this_player.scale_y = 1 this_player.alpha = alpha this_player.frame_total = 1 this_player.current_frame = 0 this_player.anim_start_time = g_game_clock this_player.anim_interval = 50 ' this_player.weapon_slot = New weapon[2] scene.add_sprite(this_player, layer_no) Return this_player End Function Method update() If KeyDown(KEY_S) rotation :+ turn_rate x_vel = Sin(rotation) * speed y_vel = -Cos(rotation) * speed EndIf If KeyDown(KEY_A) rotation :- turn_rate x_vel = Sin(rotation) * speed y_vel = -Cos(rotation) * speed EndIf If (g_game_clock - anim_start_time) >= anim_interval current_frame :+ 1 If current_frame > frame_total Then current_frame = 0 anim_start_time = g_game_clock EndIf End Method End Type Graphics 800, 600, 32, 0 HideMouse SeedRnd MilliSecs() load_media() create_clouds(1500) Local player1:player = player.create(game, red_plane, 0, 399, 299, 0, 1) current_player = player1 SetClsColor 0, 30, 150 Repeat g_game_clock = MilliSecs() Cls game.update() game.render() ' FlushMem Flip Until KeyHit(KEY_ESCAPE) End Function create_clouds(number:Int) Local c:Int, r:Int For c=1 To number r = Rand(1, 3) Select r Case 1 cloud.create(game, small_cloud, CLOUD_LAYER_1, Rand(-3999, 3999), Rand(-2999, 2999), Rand(359), 1) Case 2 cloud.create(game, medium_cloud, CLOUD_LAYER_2, Rand(-3999, 3999), Rand(-2999, 2999), Rand(359), 0.98) Case 3 cloud.create(game, large_cloud, CLOUD_LAYER_3, Rand(-19, 19) * 200, Rand(-29, 29) * 100, Rand(359), 0.98) End Select Next End Function Function load_media() AutoMidHandle True AutoImageFlags FILTEREDIMAGE small_cloud:TImage = LoadImage("incbin::gfx/smallcloud.png") Assert small_cloud, "Couldn't load ~qgfx/smallcloud.png~q" medium_cloud:TImage = LoadImage("incbin::gfx/mediumcloud.png") Assert medium_cloud, "Couldn't load ~qgfx/mediumcloud.png~q" large_cloud:TImage = LoadImage("incbin::gfx/largecloud.png") Assert large_cloud, "Couldn't load ~qgfx/largecloud.png~q" red_plane:TImage = LoadAnimImage("incbin::gfx/player.png", 64, 64, 0, 2) Assert red_plane, "Couldn't load ~qgfx/player.png~q" End Function
I created a pretty solid z-ordering render list system in c++ for doing some iso stuff. One thing I did to get a big speed boost was to store things that changed their z/depth/layer separate from things that didn't. Things like terrain tiles, background images, HUDs don't need to be sorted if they're all static.
Of course if nothing changes their z then there's no point to even worry about it.
Of course if nothing changes their z then there's no point to even worry about it.
Sorry I've taken so long to reply, I've been in transit. Unfortunately I left my real source code in Denmark, so here is a small example I created just for you (kinda).
SuperStrict Graphics Desktop().width , Desktop().height , 32 Type TBall Field x:Double , y:Double Field x_com:Double , y_com:Double Field acc:Double , elast:Double Field r:Int, g:Int, b:Int Function Create:TBall () Local temp:TBall = New TBall temp.x = Rand ( 0 , Desktop().width-16 ) temp.y = -16 temp.acc = Rnd (.1 , .3) temp.elast = Rnd (.75 , .95) temp.x_com = Rand (1,4) If Rnd() < .5 temp.x_com = -temp.x_com EndIf temp.y_com = 1 temp.r =Rand(128,255) temp.g =Rand(128,255) temp.b =Rand(128,255) Return temp EndFunction Method Update:Int() If x_com + x <= 0 Or x_com + x => (GraphicsWidth()-16) x_com = - x_com EndIf If y_com + y < (GraphicsHeight()) y_com :+ acc Else y_com =- y_com y_com :* elast EndIf x:+x_com y:+y_com If y > GraphicsHeight() And Abs(y_com) < 16 Return True EndIf Return False EndMethod Method Draw() SetColor r , g , b DrawOval Floor(x) , Floor(y) , 16 , 16 EndMethod EndType Local renderList:TList = New TList HideMouse While Not KeyHit( KEY_ESCAPE ) Cls For Local i:TBall = EachIn renderList If i.update() renderList.remove(i) EndIf i.draw Next renderList.addlast(TBall.Create()) Flip EndWhile ShowMouse End
Fantastic stuff - you are too kind ;-)
Here's just a snippet of some code from one of my programs. I use OOP to create a new version of the TList class. I have more methods than just "Render", but I wanted to just show you the basics...
-j9t
Type BallGroup Extends TList Method Render(WithCollisions:Int = True) ForEach Local Ball:BallSprite in Self If Ball.Visible Then Ball.Render(WithCollisions) End If Next End Method End Type Local Balls:BallGroup = new BallGroup For Local i:Int = 1 to 10 Balls.AddLast(new Ball) Next While Not KeyHit(KEY_ESCAPE) Cls Balls.Render(False) Flip Wend
-j9t