Shape Invaderz!

BlitzMax Forums/BlitzMax Beginners Area/Shape Invaderz!

Matt McFarland(Posted 2009) [#1]
EDIT:
This is an OLDER VERSION
Please review the LATEST VERSION Here

Just finished a Space Invaders clone.

My primary focus is memorizing blitzmax code, especially OOP handling so I figured this would be a good exercise.

I programmed this in 3 hours. It could have been done a lot faster but I had to reference code and had to tweak collisions, etc

You can just copy/paste the code and play, no other downloads are required, please give me feedback!

Highlights:
The code uses Superstrict!
The code uses different gamestates to handle titlescreen / game play / fail or win/ pause screen
The code uses TLists
The code uses polygon graphics thanks to Indiepath

Wishes:
I need to learn how to update the screen based on hertz instead of pass-thru's
I need to learn how to make a highscore list

Feedback welcome!




_Skully(Posted 2009) [#2]
Nice :)


Czar Flavius(Posted 2009) [#3]
Very nice!!! I can only think of a few nitpicks ;)

1. Don't bother with bytes (or shorts) unless you have a reason to. You might think you're saving memory, but it'll be less than 1kb which is nothing, and they are actually slower than ints! Don't become paranoid though, it's very slight ;) But bytes and shorts are 8 and 16 bit respectively, which causes some minor inconvenience to a 32 bit processor, whereas 32 bit ints do not. (What happens on a 64 bit system, I'm not sure)

2. If you're going for OOP, don't use countlist(list), use list.count() :P

3. The colours for SetColor are integers, not floats/doubles. You are being needlessly rational :P (sorry maths joke) Make them ints.. or you could put them as bytes if you want to, as colours are in the range 0-255.

4. You are not being consistant with your capitalisation. Some type names have capitals, others are lower case.

5. Personally, I'd put the list declarations within the appropriate types themselves. I find it makes things better organised.

6. You only need to declare the type of a variable once. objectlist.AddLast obj:tplayer - the :tplayer is unnecessary, unwise even, as it already knows what kind obj is. If you've done it to remember that obj is a player in this case, it's better to rename obj to player.

7. Try using the Blide editor, you can download a free version. I find it easier to code in, than the editor that comes with BlitzMax.


If you're up for it, here is some advanced code to show a better way of removing items from lists! The problem with list.remove(o), is that it needs to scan through the list, until it finds the object in question. If it's at the end of the list, it will potentially have to traverse the entire list looking for it! This, needless to say, is not very efficient. Although, in practice, the performance impact probably isn't that much.

A better way nonetheless, is to store in each object a "link" to that object's position in the list. Then, when we need to remove it, we can pull it directly from the list. Here is the code.



One thing you may notice, is the unusal way I've made the types, by using a create method instead of a create/make function. I'm just showing off and it isn't relevent to the problem :D It isn't inherently better or worse than the way you have done it, it's just an alternative. Which you may or may not find more natural :)

The key thing is the me:TLink - this lets objects know where they are in the list. The New() method is a special built-in method, that you can add to. It is automatically ran when any object springs forth into existance, so I've put the code there to put it in the list.

When removing the object, it's important to make the distinction between truely deleting the object, or just removing it from the list. An object is only deleted, when no more variables "reference" it. If the object exists only in the list, removing it will delete it. In my example, they also exist as local variables, so I have to remember to Null alice after killing it(her).

I welcome comments from anybody about that method.

I hope I haven't confused you but given you some useful tips!


Matt McFarland(Posted 2009) [#4]
@ Skully! Thanks :) I enjoyed making it

@Czar Flavius:

Thanks a million for nitpicking my code! This is the only way I'm going to improve, so I really appreciate your time.

1. I didn't know that bytes would actually be slower! My whole reasoning for using them was to speed things up. I always thought Integers used more memory and I figured if I was dealing with numbers that were smaller than 1000 to store them as doubles. And true/falsies as Bytes! With that knowledge I'll have to rethink my coding. Too bad this bytes/double/int thing is a habit. So I guess there's no reason to mess with bytes or doubles at all then?

2. Why is list.count better? I'm assuming it's faster?

3. Didnt know Setcolor were integers, thanks a million :)

4. Yeah I got tired of hitting the shift key while coding. That was laziness, I'll try to be more consistent though as it DOES make it easier to read.

5. I think you're on to something with putting list decelerations in the types themselves. But if I wanted to declare them wouldn't I have to put them outside of the Type? Maybe one line above the type declaration? I found myself scrolling to the top of the file to reference the list names a few times so I think this would be helpful in speeding the coding process up.

6. objectlist.addlast obj:tplayer could be player.addlast player instead, more readable!

7. Oh yeah, I already am using BLIDE ;)


Finally, I your code snippet looks unfamiliar to me, I'm seeing some new codez!! I'll have to study it for a bit (After I'm done updating my code to your suggestions!!!) tonight and apply that method. I'll touch more on it once I'm ready.

Thanks again, I really appreciate the criticism as I want to improve my coding skills.


Brucey(Posted 2009) [#5]
2. Why is list.count better? I'm assuming it's faster?

It'll be slightly faster (not that you will ever notice), but mainly because it is a call to the method on the Type, rather than a call to a procedural "wrapper" function.
Since you mentioned you are interested in OOP, The Czar simply points out that using list.count is an OOP style call ;-)


Czar Flavius(Posted 2009) [#6]
1. The effect is not huge, so don't panic about it :) You use floats and doubles when you are dealing with decimal numbers (your height for example). If the values are always going to be integer, there is no point using them. Bytes are used mainly when interfacing with outside code. For example, the programming language C models strings using arrays of bytes, so if you want to include C code in your program, you might need bytes then. But forget about that for ages and ages.

5. It's as easy as this
Type tplayer Extends tGameobject
	Global playerlist:TList = CreateList()
	Field shotdelay:Float
;)

6. I'm not too sure what you mean by this. Is there a reason you have an object list for everything, and seperate lists for the player etc which you use together?


Brucey(Posted 2009) [#7]
*cough*
Global playerlist:TList = New TList

*cough*

:-p


Matt McFarland(Posted 2009) [#8]
New TList or Createlist() ? that is the question!

5. thanks :)

6. The reason I had an object list for everything is so I could sweep through the entire list once on method.update() abstract

for instance:
for local obj:tgameobject = eachin objectlist
       obj.update()
Next

instead of
for local obj:tobject1 = eachin object1list
    obj.update()
Next
for local obj:tobject2 = eachin object2list
    obj.update()
Next
And for object3 and 4 and 5 and 6

Thus reducing redundancy and lines of code.


Czar Flavius(Posted 2009) [#9]
If you wanted to be really clever, you could have a list of lists ;)


Matt McFarland(Posted 2009) [#10]
objectlist IS a list of lists.. The only thing I dont like about doing it my way is that I'm putting an instance in two lists, thus having to remove it from both lists. Reducing lines of code is my goal


Beaker(Posted 2009) [#11]
I agree that the lists should be inside the type itself. You can even name it _list or similar so you can cut and paste the handling code without worrying about what you called the TList. Seems silly to reference "tPlayer.playerlist" when you can reference "tPlayer._list".

Also, you can then change your clearEverything function to:
Function ClearEverything()
	tPlayer._list = Null
	tGameObject._list = Null
	''etc
End Function

Or even better have a type function to clear the list and call it like so:
tPlayer.Clear()


Matt McFarland(Posted 2009) [#12]
Hi, thanks a lot for your help!

Question:
Using tPlayer._list = Null clears the list without having to For Next loop the list and clear them each one by one? If so that's fast! :D

If I used it that way then how would I declare the list?

I'm not really sure how I could apply the latest suggestions on lists and such. Not to be a burden, but care to elaborate?


Beaker(Posted 2009) [#13]
	' Global list which holds all instances
	Global _list:TList

	'
	' This adds a new instance to the global list
	Method New()
		If _list = Null
			_list = New TList
		EndIf
		_list.addlast(Self)
	EndMethod

Taken from here:
http://blitzmax.com/Community/posts.php?topic=50285#573109
(see my adjustment of Fredborgs code)


Matt McFarland(Posted 2009) [#14]
Amazing! Not sure why I should do it that way though?


Czar Flavius(Posted 2009) [#15]
I think it's more trouble than its worth to delete the lists themselves, because you have to worry about making them again. If you just want to clear the list and start afresh, use list.Clear().

Function ClearEverything()
	blahblahlist.Clear()
	.......
End Function


The method you used originally, using the loops and the Remove list method, is VERY inefficient. From my previous post, can you deduce why? :)

objectlist IS a list of lists.
I don't think you follow what I mean, here is an example: (I've renamed objectlist to superlist)

Global superlist:TList = CreateList()

Global enemylist:TList = CreateList()
superlist.AddLast(enemylist)
Global playerlist:TList = CreateList()
superlist.AddLast(playerlist)
Global bulletlist:TList = CreateList()
superlist.AddLast(bulletlist)
Global playershots:TList = CreateList()
superlist.AddLast(playershots)
Global particlelist:TList = CreateList()
superlist.AddLast(particlelist)


Lists can contain any object, and lists themselves are objects! So superlist doesn't contain any game objects directly, but now contains all the above lists.

When making, say a player, you only need to add it to the playerlist, NOT to superlist.

'---- do not need anymore - superlist.AddLast obj
playershots.AddLast obj


To update all the objects in your game, use this:

For Local a_list:TList = EachIn superlist
	For Local an_object:tGameobject = EachIn a_list
		an_object.update()
	Next
Next


The game will go through all lists in super list, and then go through all of them, and update the objects they contain. When you restart the game, you don't want to Clear superlist (as you'd have to add all the lists into it again) but should only clear its sublists.


Function ClearEverything()
	For Local a_list:TList = EachIn superlist
		a_list.Clear()
	Next
End Function



Matt McFarland(Posted 2009) [#16]
THANK YOU! That makes PERFECT Sense! You're awesome! :) I can't wait to redo the codez. I'll post a more efficient version soon.

I have learned that TLists are actually objects and that you can put them inside other lists, I had no idea this was possible until today.. again, thank you.


Matt McFarland(Posted 2009) [#17]
Here's Version 2:

UPDATE INFO:
1. Removed Bytes and Doubles and replaced them with Integers
2. Changed CountList() to List.Count()
3. Fixed inconsistencies with Code Capitalization
4. Added more Code Comments
5. Added a SuperList and put all Lists in there, thus removing lots of code redundancy! I wanted to declare the lists in the types themselves, but doing it this way brought me syntax errors!
6. Changed "objlist.addlast obj:tobj" to "objlist.addlast obj"
7. If the shapes reach the bottom you FAIL!

Thanks a lot guys! Feedback is welcome!

SuperStrict
'DrawGeom function is used to draw all the shapes in the game.
Function DrawGeom(x:Float, y:Float, sides:Float, length:Float, angle:Float, r:Int = 255, g:Int = 255, b:Int = 255, glow:Int = True)

	Local aStep# = 360 / sides
	Length:Float = (length / 2) / Sin(aStep:Float / 2) 'Calculate the correct length of a side 			
	For Local a:Byte = 0 To sides - 1
		Local x1:Float = x:Float - (Sin(angle + (aStep * a)) * length)
		Local y1:Float = y:Float - (Cos(angle + (aStep * a)) * length)
		Local x2:Float = x:Float - (Sin(angle + (aStep * (a + 1))) * length)
		Local y2:Float = y:Float - (Cos(angle + (aStep * (a + 1))) * length)
		If glow = True
			SetColor r / 4, g / 4, b / 4
			DrawOval x1 - 5, y1 - 5, 11, 11							' Draw Circle at Vertex
		End If
		SetColor r, g, b
		DrawLine x1, y1, x2, y2									' Draw Connecting Lines
	Next
	
End Function

'Initialize the Game *****************************************
	'Set the playfield
	Graphics 320, 260
	
	'Declare Global Variables
	Global Direction:Int
	Global GameState:Int
	Global Level:Int
	Global PS:Int
	Global Score:Int
	
	'Declare Constants
	Const MOVELEFT:Int = 0
	Const MOVERIGHT:Int = 1
	Const STATE_TITLE:Int = 0
	Const STATE_PLAY:Int = 1
	Const STATE_PAUSE:Int = 2
	Const STATE_WIN:Int = 3
	Const STATE_FAIL:Int = 4
	
	'Create Object Lists
	Global SuperList:TList = New TList
	
	Global EnemyList:TList = New TList
	SuperList.AddLast(EnemyList)
	Global BulletList:TList = New TList
	SuperList.AddLast(BulletList)
	Global PlayerShots:TList = New TList
	SuperList.AddLast(PlayerShots)
	Global PlayerList:TList = New TList
	SuperList.AddLast(PlayerList)
'Game Initialized *****************************************

'All objects contain x,y,xv,and yv variables
Type tGameObject
	Field x:Float, y:Float, xv:Float, yv:Float
	Method update() Abstract
End Type

Type tEnemy Extends tGameObject
	
	'Create additional fields that are used for the Enemy Type
	Field r:Int, g:Int, b:Int 'Colorsettings 
	Field Sides:Float 'Sides settings
	Field Level:Int 'Level the Enemy is 
	Field EnemyType:Int 'Type the Enemy is
	'The Make function is used to create and confiugre a new Instance of the Enemy
	Function Make(x:Float, y:Float, EnemyType:Int = 1, Level:Int)
		Local NewEnemy:tEnemy = New tEnemy
		NewEnemy.x = x
		NewEnemy.y = y
		NewEnemy.EnemyType = EnemyType
		NewEnemy.Level = Level
		NewEnemy.xv = Level *.1 'The higher the level the faster the enemy will be
		If NewEnemy.xv > 2 NewEnemy.xv = 2 'No need to make it TOO fast though, cap it at 2
		'Depending on the type, we confugre it's shape and color
		Select EnemyType
			Case 1
				NewEnemy.r = 255
				NewEnemy.g = 0
				NewEnemy.b = 0
				NewEnemy.Sides = 4
				
			Case 2
				NewEnemy.r = 0
				NewEnemy.g = 255
				NewEnemy.b = 0
				NewEnemy.Sides = 3
			
			Case 3
				NewEnemy.r = 0
				NewEnemy.g = 0
				NewEnemy.b = 255
				NewEnemy.Sides = 5
		End Select
		'Let's add it to the List
		EnemyList.AddLast(NewEnemy)
	End Function
	
	'The Update Method updates the instance every step
	Method Update()
		'Determine it's direction and move accordingly
		If Direction = MOVELEFT x:-xv
		If Direction = MOVERIGHT x:+xv
		'If any Enemy it's the edge of the screen, 
		'move all enemies downward and reverse the direction
		If x < 0
			Direction = MOVERIGHT
			For Local Enemy:tEnemy = EachIn EnemyList
				Enemy.y:+4
			Next
		End If
		If x > 320
			Direction = MOVELEFT
			For Local Enemy:tEnemy = EachIn EnemyList
				Enemy.y:+4
			Next
		End If
		'If the Enemy reaches the bottom, the game ends
		If y > 240 Then GameState = STATE_FAIL
		'Draw the Enemy
		DrawGeom(x, y, Sides, 10, 0, r, g, b, False)
		' make it shoot
		Local R:Int = Rnd(10000)
		'The Higher the level the more frequent they shoot
		If R < Level + 1 tBullet.make(x, y)
	End Method
End Type

Type tPlayer Extends tGameObject
	Field ShotDelay:Float
	
	Function Make()
		Local NewPlayer:tPlayer = New tPlayer
		NewPlayer.x = 320 / 2
		NewPlayer.y = 232
		PlayerList.AddLast(NewPlayer)
	End Function
	
	Method Update()
		'Increment the ShotDelay
		ShotDelay:+1
		'Playership is controlled with arrow keys and Z
		If KeyDown(KEY_RIGHT) x:+3
		If KeyDown(KEY_LEFT) x:-3
		'Shoot if the ShotDelay permits
		If KeyHit(KEY_Z) And ShotDelay > 5
			If PlayerShots.Count() < 2 tPlayerShot.Make(x, y) ;ShotDelay = 0
		End If
		'Bound the player in the playfield
		If x < 0 x = 0
		If x > 304 x = 304
		'Draw the player
		DrawGeom(x, y, 3, 16, 0,,, , False)
	End Method
End Type

Type tBullet Extends tGameObject
	
	'Make Function Called when instance is created
	Function Make(x:Float, y:Float)
		Local Bullet:tBullet = New tBullet
		Bullet.x = x
		Bullet.y = y
		Bullet.yv = 3
		Bulletlist.AddLast(Bullet)
	End Function
	
	'Update Function called with each step
	Method Update()
		y:+yv 'Move the bulled downard, remove if out of bounds
		If y > 244 Then BulletList.Remove(Self)
		SetColor 255, 255, 0
		DrawOval x, y, 4, 4
		
		'Check for a collision with the Player
		For Local Player:tPlayer = EachIn PlayerList
			If Abs(Player.x - x - 2) < 10 And Abs(Player.y - y) < 8
				BulletList.Remove(Self)
				GameState = STATE_FAIL
				
			End If
		Next
	End Method
End Type

Type tPlayerShot Extends tGameObject
	
	'Make Function called when the Instance is created
	Function Make(x:Float, y:Float)
		Local PlayerShot:tPlayerShot = New tPlayerShot
		PlayerShot.x = x
		PlayerShot.y = y
		PlayerShot.yv = -8 'sets the speed to go upward 8 pixels per step
		playershots.AddLast(PlayerShot)
	End Function
	
	Method Update()
		y:+yv 'Move the shot
		'Destroy the instance if out of bounds
		If y < - 4 Then PlayerShots.Remove(Self)
		'Check for a collision with enemies
		For Local Enemy:tEnemy = EachIn EnemyList
			If Abs(Enemy.x - x - 2) < 8 And Abs(Enemy.y - y) < 10
				If Enemy.EnemyType = 2 Score:+1
				If Enemy.EnemyType = 1 Score:+15
				If Enemy.EnemyType = 3 Score:+70
				EnemyList.Remove(Enemy)
				PlayerShots.Remove(Self)
			End If
		Next
		'Draw the Instance
		SetColor 0, 255, 255
		DrawOval x, y, 2, 4
	End Method
End Type

Function MakeGame(Level:Int)
	tPlayer.Make()
	For Local N:Int = 2 To 14
		tEnemy.Make(N * 20, 16 * 2, 3, Level)
	Next
	For Local N:Int = 2 To 14
		tEnemy.Make(N * 20, 16 * 4, 1, Level)
	Next
	For Local N:Int = 2 To 14
		tEnemy.Make(N * 20, 16 * 6, 2, Level)
	Next
End Function

Function ClearEverything()
	For Local a_list:TList = EachIn SuperList
		a_list.Clear()
	Next
End Function


'MAIN LOOP
Repeat
	Cls
	'Determine the GameState and act accordingly
	Select GameState
		Case STATE_TITLE
			DrawText "Shape Invaderz", 105, 20
			DrawText "ARROW KEYS", 30, 90
			DrawText "Z KEY", 30, 110
			DrawText "P KEY", 30, 130
			DrawText ". . .", 140, 90
			DrawText ". . .", 140, 110
			DrawText ". . .", 140, 130
			DrawText "Move Ship", 200, 90
			DrawText "Shoot", 200, 110
			DrawText "Pause", 200, 130
			DrawText "Press SPACE to play or ESC to Exit", 25, 220
			If KeyHit(KEY_SPACE)
				Level = 1
				SCORE = 0
				MakeGame(Level)
				GameState = STATE_PLAY
			End If
			
		Case STATE_PLAY
			'Update All Objects
			For Local a_list:TList = EachIn SuperList
				For Local an_object:tGameObject = EachIn a_list
					an_object.update()
				Next
			Next
			'Pause the screen if player pushes the P Key			
			If KeyHit (KEY_P) GameState = STATE_PAUSE
			
			'Draw HUD
			SetColor 0, 0, 200
			DrawLine 0, 240, 320, 240
			SetColor 255, 255, 255
			DrawText "Level:" + String(Level), 10, 244
			DrawText "Score:" + String(Score), 200, 244
			If CountList(EnemyList:TList) < 1 GameState = STATE_WIN			
			
		Case STATE_PAUSE
			'PS is used to Scroll the "Game paused" text
			PS:+1
			If PS > 320 PS = -320
			DrawText "Game PAUSED, Press SPACE to Continue", PS, 210
			If KeyHit(KEY_SPACE) GameState = STATE_PLAY

		Case STATE_WIN
			DrawText "WIN!", 145, 20
			DrawText "You Have owned Level " + String(Level) + "!!!!!!!!!!!!", 30, 60
			DrawText "Press SPACE to CONTINUE or ESC to Exit", 06, 210

			If KeyHit(KEY_SPACE)
				ClearEverything()
				Level:+1
				MakeGame(Level)
				GameState = STATE_PLAY			
			End If
			
			If KeyHit(KEY_ESCAPE)
				ClearEverything()
				GameState = STATE_TITLE
			End If
			
		Case STATE_FAIL
			DrawText "FAIL!", 145, 20
			DrawText "You DIED on Level " + String(Level) + "!!!!!!!!!!!!", 30, 60
			DrawText "You Scored " + String(Score) + " Points FTW", 10, 90
			DrawText "Press SPACE to CONTINUE or ESC to Exit", 06, 210
			
			If KeyHit(KEY_SPACE)
				ClearEverything()
				Level = 1
				GameState = STATE_TITLE
				SetColor 255, 255, 255
			End If
		
	End Select
	Flip
Until KeyHit (KEY_ESCAPE)



amonite(Posted 2009) [#18]
Cool little game !
I have learnt new things thanks to this thread :)
I like your DrawGeom function.


Matt McFarland(Posted 2009) [#19]
Thanks! The DrawGeom function was borrowed from Indiepath :)


matibee(Posted 2009) [#20]
I read you wanted to make your lists part of the type they belonged to. In a (for a lack of a better word) 'real' OOP language that would be a 'static' member or field. I didn't think static members were supported but I had to google it anyway;

http://www.blitzbasic.com/Community/posts.php?topic=59233

You can put the global inside the type which makes things a bit tidier.



See the second Global? This is a bit subjective but I made the type self register with the global list so you don't have to worry about that anymore either.

Function Make(x:Float, y:Float, EnemyType:Int = 1, Level:Int)
		If ( Not RegisteredToSuperList ) Then 
			SuperList.AddLast(EnemyList)
			RegisteredToSuperList = 1
		End If 


It's subjective because tEnemy is now tied to a global variable called SuperList which I don't like. If we we're taking tEnemy into it's own source file or module we could add a tEnemy.Init( SuperList:TList ) function or add the SuperList to the Make() function arguments.

I personally wouldn't want tEnemy.EnemyList accessed anywhere apart from within the tEnemy code but I initially had to add the GetList() function for your bullet/collision updates. That was avoided by passing a bullet position to a tEnemy function and asking it to check for collisions...

	Function CheckCollision:tEnemy( x:Int, y:Int )
		For Local Enemy:tEnemy = EachIn EnemyList
			If Abs(Enemy.x - x - 2) < 8 And Abs(Enemy.y - y) < 10
				Return Enemy
			End If 
		Next 
		Return NULL
	End Function 


If it returns an enemy object you can act on it.

All in all though, it's a very, very good job :)


Czar Flavius(Posted 2009) [#21]
I think that's making it a bit more complicated than it needs to be.


Matt McFarland(Posted 2009) [#22]
I wanted to keep the global list declaration inside the object code but I received syntax errors when combining with "superlist.addlast(tlist)" so I refrained from doing so. In the end I really REALLY like that I can parse through the superlist and update each object that way with a few lines of code.

I have spent officially 12 hours on this project, I'm considering making it a 24 hour project then ending it there. I am thinking I'll make 24 hour game projects just to see where I'll go. I should do them from scratch, borrowing maths codes but remaining true to memorizing blitzmax coding.

I have a newer version but I'm not sure if I should post it or not for a couple of reason, if you guys want me to I'll post it but it's got a huge amount of code!

1. I programmed a neat "mod" (per se) that allows me to type one line of code and get a fancy vector text font. For instance:
VecDrawText("Hello World",x,y,size)

draws the text in a neat vector style font. I painstakingly created the function(s) that draw each letter in the alphabet and all the numbers. However no other symbols so far(except chevrons but that was so I could draw a K)

I'll post a separate topic on the matter, I think you guys might find it useful and I'd be happy to share it.


Czar Flavius(Posted 2009) [#23]
but I received syntax errors
If you post the errors, we can help you with them. When using a list outside of a type, you need to prefix it with the type name. So if you put Global playerlist:TList inside Type TPlayer, any code inside TPlayer can just say playerlist, but outside you need to write TPlayer.playerlist.


Arabia(Posted 2009) [#24]
I got 21910, died on level 20 :)

Nice game. I think I'll try and figure out your source code a bit and have a play around. Might be a good starting point for others to do something with this.


Matt McFarland(Posted 2009) [#25]
Hi guys, I have a download available for version .5 (the 12th hour of development) and would like some feedback.

The latest version includes:

VectorText - A font upgrade that is inefficient(I'm thankful for the guys who reviewed the VT code and pointing this out to me), I plan on changing this to a bitmap font by the time of completion.

Shields - You now have destroyable shields that help protect you from enemy bullets, but also block yours (just like in space invaders)

Fixed fail boundary - When the enemies reach the bottom you fail the game, before they went past the bottom.

Added CTRL fire key - For those who have a different playstyle you may use the CTRL keys for firing. My primary reason is for accessibility, as one-handed people cannot play a game with the fire key being too far from the controls.

Game Difficulty - I have re-programmed the way the enemies behave for a more balanced approach.

I plan on releasing the source code once I finish the final 12 hours of this game project. Once I hit 24 hours it'll be completed, and after scrutiny I'd like to release a comprehensive tutorial for beginners on how to make this game.

You can download it (500kb, zip file with 1 exe file) here:
http://www.gamedev.net/community/gds/viewentry.asp?projectid=548722


Matt McFarland(Posted 2009) [#26]
OH one last thing:

@ Czar:

Here's my syntax error problem:
Type tEnemy Extends tGameObject
	Global EnemyList:TList = New TList
	SuperList.AddLast(EnemyList)

on the line "Superlist"... I get:
Compile Error: Syntax error in user defined type declaration
Build Error: failed to compile C:/BlitzMax/projects/ShapeInvaderz.bmx



Jesse(Posted 2009) [#27]
you have to put use of superlist.addlast(enemylist) in a method or function.
you can do it like this:
type tEnemy Extends TGameObject
	Global EnemyList:TList = New TList

        method new()
           SuperList.AddLast(EnemyList)
        End Method
        

when you do:
game = new TGameObject

it will automatically call the "new" method
you don't have to do this:
game.new()
the method new is used by the constructor to initialize any variables or do anything at the time of creating the current object.


Czar Flavius(Posted 2009) [#28]
Jesse's code would try to add the enemylist to superlist everytime you created a new enemy, which isn't our intention. You should leave the superlist.adds outside of type declarations, and leave them where they were in the original code (in the initialize the game area), but move the Global declarations.

Remember to call the lists outside the type block like this - TEnemy.EnemyList. You could perhaps remain the lists to just "list", to make it less wordy. So SuperList.AddLast(TEnemy.list).. SuperList.AddLast(TPlayer.list) etc


Matt McFarland(Posted 2009) [#29]
I get the same syntax error if I move the global declarations but keep the superlist.addlast in the initialization area. I'm not exactly sure how to avoid them unless I just keep the list declaration in the top with the superlist.addlast(list)


Matt McFarland(Posted 2009) [#30]
wrong post


Jesse(Posted 2009) [#31]
If you post the complete code we can help you solve the problem. That code doesn't tell us much other than, you are not following syntax rules somewhere.


Matt McFarland(Posted 2009) [#32]
Ok here's the problem:

SuperStrict

Global SuperList:TList = New TList

Type tEnemy
Global EnemyList:TList = New TList
	SuperList.AddLast(EnemyList)
End Type


Creates same problem. It clearly highlights at "SuperList.AddLast(EnemyList)" as a syntax error. (Type Declaration)

So, now try moving SuperList.AddLast(EnemyList) to the top as suggested:

SuperStrict

Global SuperList:TList = New TList
SuperList.AddLast(EnemyList)

Type tEnemy
Global EnemyList:TList = New TList
End Type

Syntax Error @ SuperList.addlast(EnemyList) is now (missing EnemyList)

NOW: The only way I can get it to work:


Global SuperList:TList = New TList
Global EnemyList:TList = New TList
SuperList.AddLast(EnemyList)

Type tEnemy
End Type


Thus I have to keep the enemylist creation outside of the type.


Czar Flavius(Posted 2009) [#33]
If you're using the list outside of tEnemy, you need to call it tEnemy.EnemyList
SuperStrict

Global SuperList:TList = New TList
SuperList.AddLast(tEnemy.EnemyList)

Type tEnemy
Global EnemyList:TList = New TList
Method something_inside_the_type()
Print Enemylist.Count() + " no need for tEnemy. inside here!"
End Method
End Type



Matt McFarland(Posted 2009) [#34]
I wanted to use the list inside the tenemy to "tidy up the code"


REDi(Posted 2009) [#35]
As Jesse said in post #27, you cant have code inside a type like that, it must be in a method or function...

SuperStrict

Global SuperList:TList = New TList

Type tEnemy
	Global EnemyList:TList = New TList
	
	Method New()
		SuperList.AddLast(EnemyList)
	EndMethod
End Type



Matt McFarland(Posted 2009) [#36]
Thanks! Also Sorry Jesse I see you answered it but I didnt understand at the time :S I appreciate your help!


REDi(Posted 2009) [#37]
OOPS, I just read post #28 :D

I Didn't even notice its a global list in the type (its been a long night), anyway this is what you're after...

SuperStrict

Global SuperList:TList = New TList
SuperList.AddLast(tEnemy.EnemyList)

Type tEnemy
	Global EnemyList:TList = New TList
End Type



Matt McFarland(Posted 2009) [#38]
Yeah it was a late night for me too, not to mention how frustrating coding has been in the last day or so. Anyway, you can't put superlist.addlast(tenemy.enemylist) at the top there unless you put the enemylist at the top with it, just tyry it; doesn't work.

EDIT:
The only way you can put the superlist.addlast command inside the tenemy is if you put it into a method, but you don't want to keep adding the list into the superlist.

From what I gather, you'd have to call the method only one time as some sort of init method.

EDIT 2:
It's too complicated to just get the list declaration in the enemy type. It's more practical to put the list declaration at the top of the file along with the superlist stuff. If you need to remember what the lists were called, it's easy to simply use BLIDE and use a split screen where one side shows the declarations and the other side shows where you're coding.


Jesse(Posted 2009) [#39]
I don't understand why you would want many lists inside a List if the objects are similar. The compiler is going to take longer to process if it has to determine what type of object the list contain. personally I would have an array of lists as the super list. That is not how I code but if I did that is how I would do it.
in my games what I do is, I crate a list of baddies and two list of bullets(one for the baddies and one for the player). Each of the baddies extend from the base type. I use the base to process everything. like this:

for bullets:Tbase = eachin bulletlist
'process bullet
next

there is a tutorial on OOP: Polymorphism for beginners It helped me out a bit, it Might help you also.


Matt McFarland(Posted 2009) [#40]
The reason why I like the superlist is that you only have to iterate through the objects one time and update them all, so if you add new objects and add them to the superlist you do not have to add any additional code to update them.

For instance:

If you have 5 lists without a superlist you must make 5 for..next loops. If you have 5 lists within a superlist then you only need 1 for..next loop. (^_^)=b!!

So instead of having several blocks of code to update all the objects in the game you only need one. This is why I believe I'll be using superlists from here on out. If you look at the top and read my first post I was not using a superlist at all. Someone showed me an interesting method (using superlists) and I think they are better than not using them.

Lastly I'm not sure what the big deal is about these arrays or why you would want to put lists in array[]. It's far easier to read the code when things arent [0] or [4] .. just my humble opinion on the matter.

I suppose if using brackets with numbers in them makes it easier to read for you then go for it. But one of the reasons why I like blitzmax so much is that you can put things in types instead of brackets and numbers. My only concern about using arrays and their funky brackets with numbers is that it could increase speed. But if the speed increase is negligable then they seem rather archaic. The whole purpose of BASIC is that its an easy language to read. using "dim" and [] brackets with numbers in them just isn't that easy for reading and it doesn't seem to be OOP but more like the times of QBasic and GWBasic etc. If I was going to program games with arrays all day then I'd probably be using DarkBASIC.


REDi(Posted 2009) [#41]

Anyway, you can't put superlist.addlast(tenemy.enemylist) at the top there unless you put the enemylist at the top with it, just tyry it; doesn't work.



I beg to differ, have you tried that code snippet?


REDi(Posted 2009) [#42]
Quick tester...
SuperStrict

Global SuperList:TList = New TList
SuperList.AddLast(tEnemy.EnemyList)

New TEnemy
New TEnemy
New TEnemy

Local list:TList, enemy:TEnemy
For list=EachIn SuperList
	For enemy=EachIn list
		enemy.test()
	Next 
Next

Type tEnemy
	Global EnemyList:TList = New TList
	Field Link:TLink = EnemyList.AddLast(Self)
	Field index%=EnemyList.Count()
	Method test()
		Print "Enemy "+index
	EndMethod
End Type



Matt McFarland(Posted 2009) [#43]
Awesome! I stand corrected! :D I had to put tenemy.enemylist instead of enemylist...

I think this was mentioned earlier but it when right over my head, I really apologize for that. It finally makes sense! Yes it sunk in:) Thanks a million.

For some reason I thought that if you put "Global" inside a Type it would be the same as putting a "Global" OUTSIDE of a type.

So basically tenemy.list would be if I used a GLOBAL declaration of a list inside the type. Therefore I could have tbullet.list,tplayer.list,tenemy.list and I love the idea!

Thanks a lot!


Jesse(Posted 2009) [#44]
Matt, just so you know, I am not picking on you. I don't know if I sound to rough. I was just trying to say that a group of lists in a list is not such a great idea,that arrays are faster and a better choice. at least in my opinion and mind I am no expert. I don't even code with arrays generally. The only time I have used arrays is with my tile map engine I have in the code archives. I do use lists a lot and now with direct link removal but that's for another topic. if you would like to take a look at how I code. you can take a look at my Community Framework contest entry here:
http://www.filefront.com/13724392/bmfc_c.zip
you can take a look at the source code.
it's not too well optimized or complete as it was made in a hurry for the contest. but it works.


Matt McFarland(Posted 2009) [#45]
Ok guys: I have the 21st hour source code for you. To play you need two blitzmax files..

UPDATES:

MAJOR SPEED INCREASES!
***** 1 - Created a new VectorDraw_img_Text Function that saves all the letters into image files, and the font loads faster than DrawText() - The letters are still drawn with lines, but saved into uiamges using GrabImage()
***** 2 - All shapes are no longer drawn in realtime, but drawn int he beginning and then saved into images using GrabImage.
***** 3 - Using a more modern way to iterate through a string.

It's amazing that these optimizations took me 9 hours to do, partially because I had a lot of learning to do and I appreciate the blitzmax community in helping me along the way!

Filename "VectorText.bmx"

Filename "ShapeInvaderz.bmx"


Download the .8 version on gamedev here:
http://www.gamedev.net/community/gds/viewentry.asp?projectid=548722&mode=gameinfo

Enjoy!!!

PS: @ Jesse, thanks :) No worries. I plan on using arrays more too. Sorry about the rant about arrays, kinda irrelevent. I'll check out your source, sounds interesting.


Czar Flavius(Posted 2009) [#46]
I was just trying to say that a group of lists in a list is not such a great idea,that arrays are faster and a better choice
I don't think it will make that much difference, at the end of the day.

It's possible you could consider having only one list that stores all your objects, rather than multiple lists tied together.


Jesse(Posted 2009) [#47]

I don't think it will make that much difference, at the end of the day.


I think it does, although I haven't really tested it. but I bet when you have hundreds of bullets and hundreds particles per say, and an instruction set have to determine if to assign it to a specific type of object or not, logic tells me it will definitely take a speed hit.
Have you tried Grid Wars, from Mark Incitti if you haven't you can download the game including source code from my signature link. One of the reasons this game is so fast is his use of recycling of objects and use of arrays. nothing to figure out, no garbage collector taking over your program, no memory fragmentation, and no guessing. just smart coding. :)


Czar Flavius(Posted 2009) [#48]
and an instruction set have to determine if to assign it to a specific type of object or not, logic tells me it will definitely take a speed hit.
What does this mean?


Jesse(Posted 2009) [#49]
I know I am really bad at explaining stuff. Sorry.
an "instruction set" is a set of code instructions assigned to do a specific task. it can be a function/call or even an simple if statement. Does that help? maybe not. Sorry, best I can do.

There have to be code written to determine whether to assign the object type to a specified variable or not. When you have all different type of objects in a list, how is it determined to put one type of object into a variable or not when doing a For/EachIn loop. There is obviously some hidden code added to the executable by the compiler at the time of creating to deal with this type of situation. But then again, maybe I am wrong. I don't really know what's inside the Processor or the Bios. :)

Just ignore me. I am in a different, and weird mentality.


Czar Flavius(Posted 2009) [#50]
If all the objects in the list are of the same type, does that remove the problem? As each list only contains one type of object (tenemy, tplayer..)


Jesse(Posted 2009) [#51]
don't really know but I would guess the more it varies the more the work. consider it still have to go through all of the objects even if it only have to test one kind of object for collision or anything similar.


Czar Flavius(Posted 2009) [#52]
That is going to happen anyway, to draw the objects and move the aliens and bullets, is it not?


Jesse(Posted 2009) [#53]
if you say so. Personally, I wouldn't do it that way. maybe it's my inexperience but I do think my way is faster/better. who knows, I might end up figuring out in the long run that your way is better.


Czar Flavius(Posted 2009) [#54]
It's not that I'm saying my way is faster, per se, but rather on today's computers, it doesn't make enough difference to lose any sleep over ;)


Matt McFarland(Posted 2009) [#55]
The game is fully finished and when compiled its only 64kb. Check the showcase for details!