Help: Sprite Type: Tying graphics to instances?

BlitzMax Forums/BlitzMax Beginners Area/Help: Sprite Type: Tying graphics to instances?

WarpZone(Posted 2005) [#1]
Man, BlitzMax is a LOT harder than Blitz3D! :p



I get the compile error "identifier hero not found." I thought that the "Global t:Timage=LoadImage( t )" line in InitializeSprites() would create a global image named hero from the file sprites/hero.png, but it doesn't seem to be working. Actually, I'm not even sure if variables in Blitz are meant to be mangled in this way. I'm coming fresh out of learning Flash AS, so if this business of dynamically naming variables is insane, tell me. On the other hand, if you can think of a way to make it work, please do explain it to me.

Basically, I'd like to create a sprite type, subtype it into variaout other types of entities that have more variables, and treat the graphic tied to any one instance of the type as just another arbitrary vriable that can be set and changed on the fly during runtime. The uncompilable code above is one way of attempting it.

As you can tell, I really don't have a firm grasp of BlitzMax yet. You can link me to tutorials all day, but I won't really "get it" until I see working solutions to visual problems. Abstract examples don't usually illustrate enough to me. I need to see what works and what doesn't, and when something doesn't work, I need to be able to post my erroneous code and ask WHY it didn't work. Only after several such explainations, I'm afraid, will I start to genuinely understand Blitz's syntax and the proper way to use these commands.

Thanks, in advance, to anyone who helps me through this burn-in phase.


Dreamora(Posted 2005) [#2]
image is a TImage reference. It does not have any name. You have to assign the image you get from loadimage to Player.image.

btw: your loop is quite buggy. You already have a t:string there so the global t:TImage will throw a bug in any case if you use strict (which you should always do when programming OO)


QuietBloke(Posted 2005) [#3]
I dont think you can create variable names on the fly in the way I think you are trying to do.

I think what you might be something like

Strict

Type typeSprite 
	Field image:TImage
	Field x
	Field y
	Field xMove
	Field yMove
	
	Method Draw()
		DrawImage(image,x,y)
	End Method
	
	Method Update()
		x = x + xMove
		y = y + yMove
	End Method
End Type

Type typePlayer Extends typeSprite
	Field health
	
	Method New()
		image = imageHero
	End Method
End Type

Type typeEnemy Extends typeSprite
	Field scoreValue			' how many points this enemy is worth
	
	Method New()
		scoreValue = 20			' all enemies have a default score value
	End Method
End Type

Type typeEnemyTurtle Extends typeEnemy

	Method New()
		image = imageEnemies[ENEMY_IMAGE_TURTLE]
	End Method
End Type

Type typeEnemySnake Extends typeEnemy
	Method New()
		image = imageEnemies[ENEMY_IMAGE_SNAKE]
		scoreValue = 30
	End Method
End Type

Global imageHero:TImage
Global imageEnemies:TImage[2]

Const ENEMY_IMAGE_TURTLE	= 0
Const ENEMY_IMAGE_SNAKE	 	= 1

Graphics 640,480,0,30	' Set the refresh rate to 30fps 

loadImages()

playGame()

End

Function loadImages()
	imageHero = LoadImage("images/hero.png")
	imageEnemies[ENEMY_IMAGE_TURTLE] = LoadImage("images/turtle.png")
	imageEnemies[ENEMY_IMAGE_SNAKE] = LoadImage("images/snake.png")
End Function

Function playGame()
	Local player:typePlayer
	Local enemyList:TList

	enemyList = CreateList()
	
	player = New typePlayer
	player.x = 100
	player.y = 50

	Local enemy:typeEnemy
	
	' create 1 turtle
	enemy = New TypeEnemyTurtle
	enemy.x = 200
	enemy.y = 200
	enemy.xMove = -1		' moving left
	ListAddLast(enemyList, enemy)

	' create 1 snake
	enemy = New TypeEnemySnake
	enemy.x = 240
	enemy.y = 200
	enemy.xmove = 1			' moving right
	ListAddLast(enemyList, enemy)

	While Not KeyHit(KEY_Escape)
		Cls
		player.Update()
		For enemy = EachIn enemyList
			enemy.Update()
		Next
		
		player.Draw()
		For enemy = EachIn enemyList
			enemy.Draw()
		Next
			
		Flip
	Wend
End Function	


At the base level we have a sprite type. This has an image, position and a movement speed. A sprite type knows how to update itself and draw itself.

The a player extends the sprite and sets the image when it is created and has it's own additional field.

Then we have a generic enemy type which extends the sprite again but has a scoreValue field which has a default value set in the New method.

Then you have a turtle and snake enemy type. These set thier own images ans scores.

The game then creates the player and two enemies. The enemies are held in a list. everything is updated and drawn until the escape key is pressed.

The images are all loaded in the loadimages function. This loads up the images and stores them in global variables. If you wanted to dynamically load different images for the various sprites the loadimages function could read a text file and use that to loadup the appropriate files for each type of sprite.

Is that any help ?


WarpZone(Posted 2005) [#4]
Thank you very much, QuietBloke. :) You totally understand what I was trying to do with the types. Only one difference. I wanted the simplest kind of sprite to be a stationary background element, so there would be no need for it to have an update method. Hmmm... Or should I just use a DrawImage(image,x,y) with no associated type at all in that case?

The one thing I don't understand is, how can I dynamically load images and give them variable names by reading them from a text file? Shouldn't that theoretically have the same problems as loading the image names dynamically from an array? (I.E. the problem being that Blitz doesn't support using a string as a variable name?) Or is that what the number in [brackets] is for after imageEnemies?

Do you think I could trouble you to write the loadImages()
function the way you would if you wanted to grab those image names from a txt file? I want to understand how the variable names get set.


WarpZone(Posted 2005) [#5]
I've tried replacing imageEnemies[0] with imageTurtle in both places it appears, and it gives me an error now. That's unexpected. What's so special about the [0]? What do the numbers in brackets actually mean, here? (I assumed they were part of the variable name, or something...?)


Dreamora(Posted 2005) [#6]
[0] means element 0 in this array. [] indicates that it is an array.

This and everything else is written in the help in the language part.


WarpZone(Posted 2005) [#7]
Oh! I see... so Image Enemies is an array.

When did the array get declared? Global imageEnemies:TImage[2]? I didn't know you could declare an array without using the array keyword when strict typing is on... gah. Paradigm shift. Now my brain needs to grow new fissures. Guess my dreams will be pretty weird for a while...

Don't get me wrong, the BlitzMax help is fairly helpful, I just can't seem to digest it very easily. There is a gap between abstract programming concepts and direct, off-the-cuff implementation of ideas that, for me, can only be bridged with specific, real-world examples written in the correct syntax.

That was one thing I loved about Blitz3D... every keyword had a code example. (I know they still exist in BlitzMax, but they seem more sparse than before.)

Also some of the examples I see, both in Blitz Help and online, can seem unweildy and confusing at times. I quite like QuietBloke's variables. They are very well named! :)


QuietBloke(Posted 2005) [#8]
Hi warpZone,

Im glad the code was of some help to you. I know its a bit of struggle to get to grips with new languages sometimes ( though as you learn more languages you will find new ones get easier to get to grips with ).

The simple answer to you question above about how to write code to read a text file to get a variable name and value is that you cannot.

All the variables you use must be defined at compile time. The reason I used an array for the enemy images was so that the loadImages function could be written to use a text file to get the list of images to load into each one.

If you *really* want the text file to supply a name for each image I guess you could create a new namedImage type which will contain a TImage field and a name string. You could then have a TList of namedImage. Whenever you wanted to reference a *named* image you would seaqrch the TList to find the matching name.

Hope that makes sense. I would try to write some code for an example but Im not at my home machine at the moment.


WarpZone(Posted 2005) [#9]
Thanks, QuietBloke. :)

Your namedImage type probabaly sounds like what I want. I had simmilar issues when I was learning Flash... I wanted to make objects for whom the graphics were a variable, and in Flash it doesn't quite work that way.
Code is attached to movieClips in Flash, not the other way around. I eventually found a way to fake it, though, which I guess is what you're suggesting with a namedImage type.

No pressure or anything, but if you have time to mess with it, I would love to see your implementation of the solution you're proposing. TList and TImage are both new to me, and when I try to use them, I always seem to generate hairy compile-time errors, because I'm getting the syntax wrong or trying to use the keyword incorrectly or whatever.

I know I'll get my bearings eventually, and I should probabaly just be patient and make simpler games until then, but this kind of modularity is something I want to learn early, as soon as possible, so I can use it in all games I make.


QuietBloke(Posted 2005) [#10]
Im probably not the best person to give the best examples of how to do things in BlitzMax.. Im still feeling my way round it and my game programming skills are very limited.

Saying that though.. its all good practice for me so when I get some time Ill see what I can do. Hopefully anyone else seeing this will correct me and that way we'll both learn something :)

Im still not entirely sure why you would want to do this though. If the game itself is hardcoded then I cant see why you would want to give custom names to the images. It will just slow down the assigning of the images as you will have to scan through the TList to find a match.


QuietBloke(Posted 2005) [#11]
OK.. I extended to original code to have a new type ImageList. This implements a TList to store all the images added. 2 functions are implemented to allow you to add a new image by supplying the filename and the image name and the get function will return a TImage given a image name.

The loadimages function still hardcodes the three images to load but you just need to read a text file to get the filename, image name values. I havent ever coded any file reading code so I thought Id leave that to you.

Hope you can follow it.

Im still having a problem figuring out why you need to name the images though. As you can see when I create an instance of the player and two types of enemies I still have to had code the image name that I want to reference.

Strict

Type typeImageList
	Field image:TImage
	Field name:String
	
	Global imageList:TList

	Function Add(newFileName:String, newName:String )
		Local newImage:typeImageList
		
		newImage = New typeImageList
		
		newImage.image = LoadImage(newFileName)
		newImage.name = newName
		
		If imageList = Null Then
			imageList = CreateList()
		End If
		ListAddLast(imageList,newImage)
	End Function
	
	Function Get:TImage(searchName:String)
		Local currentImageList:typeImageList
		Local foundImage:TImage
		
		If imageList <> Null Then
			For currentImageList = EachIn imageList
				If currentImageList.name = searchName Then
					foundImage = currentImageList.image
				End If
			Next
		End If
		
		Return foundImage
	End Function				
End Type

Type typeSprite 
	Field image:TImage
	Field x
	Field y
	Field xMove
	Field yMove
	
	Method Draw()
		DrawImage(image,x,y)
	End Method
	
	Method Update()
		x = x + xMove
		y = y + yMove
	End Method
End Type

Type typePlayer Extends typeSprite
	Field health
	
	Method New()
		image = typeImageList.Get("The Hero")
	End Method
End Type

Type typeEnemy Extends typeSprite
	Field scoreValue			' how many points this enemy is worth
	
	Method New()
		scoreValue = 20			' all enemies have a default score value
	End Method
End Type

Type typeEnemyTurtle Extends typeEnemy

	Method New()
		image = typeImageList.Get("Turtle")
	End Method
End Type

Type typeEnemySnake Extends typeEnemy
	Method New()
		image = typeImageList.Get("snake")
		scoreValue = 30
	End Method
End Type

Global imageEnemies:TImage[2]

Graphics 640,480,0,30	' Set the refresh rate to 30fps 

loadImages()

playGame()

End

Function loadImages()
	' Here you could read a text file to get the 2 strings
	' needed to create the named images
	typeImageList.Add("images/hero.png","The Hero")
	typeImageList.Add("images/turtle.png","Turtle")
	typeImageList.Add("images/snake.png","snake")
End Function

Function playGame()
	Local player:typePlayer
	Local enemyList:TList

	enemyList = CreateList()
	
	player = New typePlayer
	player.x = 100
	player.y = 50

	Local enemy:typeEnemy
	
	' create 1 turtle
	enemy = New TypeEnemyTurtle
	enemy.x = 200
	enemy.y = 200
	enemy.xMove = -1		' moving left
	ListAddLast(enemyList, enemy)

	' create 1 snake
	enemy = New TypeEnemySnake
	enemy.x = 240
	enemy.y = 200
	enemy.xmove = 1			' moving right
	ListAddLast(enemyList, enemy)

	While Not KeyHit(KEY_Escape)
		Cls
		player.Update()
		For enemy = EachIn enemyList
			enemy.Update()
		Next
		
		player.Draw()
		For enemy = EachIn enemyList
			enemy.Draw()
		Next
			
		Flip
	Wend
End Function	



QuietBloke(Posted 2005) [#12]
duh !..

OK.. I was just thinking.

Maybe what you want is just an easy way to reference the array of enemy images.

I modified the original code I posted to use a couple of constants to refer to the two array positions.

Is this perhaps what you are after.
The load images routine will just need to resd a text file which will be a list of image filenames which are loaded into the array.


QuietBloke(Posted 2005) [#13]
Also.. sorry.. I was just going back through your previous posts... you were thinking of having the simplest sprite stationary and that it will not need an update function. Well.. it might be stationary but the update function can do all sorts of things that you want to do over time. For example you might want to have an animated image and the update will cycle through the images.. or it might do perhaps explode after a certain length of time. Of course if it doesnt move the xmove, ymove are not really required so you might have a typeSprite that doesnt have these. Then have a typeSpriteMoving which extends the basic sprite and then the player and enemy sprites would extend the typeSpriteMoving instead.

OK.. thats me done for tonight I think.. back to a bit of my own coding :)

Happy coding


WarpZone(Posted 2005) [#14]
Well, yes, I was hoping for a certian way to organize my code. I was thinking, if the enemy type is named enemySnake, and it looks for a graphic called snake.png, and it uses the default health and speed values specified in arraySnake, then I could do stuff like this:



That way, I could reuse the same block of code to create, initialize, and check TypeEnemyTurtle, TypeEnemyFrog, TypeEnemyChicken, or whatever other kind of enemy I decided to add to my game. But I don't think Blitz will allow you to plug a variable like [id] into a part of a variable's name the way Flash did.

Anyway, looking over your code, I think it probabaly does as good a job as the technique I had planned. Using your technique, I can subclass a different type of enemy and add new behaviors and whatnot in a method unique to that enemy! In Flash, I would have wanted everything named according to what type of enemy it was, because I would need to use one function to cycle through all enemies once and then switch/cases to define their behaviors based on whether they're a snake or a turtle.

Now that I understand how subTypes and methods work in Blitz, I realize that I don't really need to automate the process of creating new variable names.

What I really need to do is to master Blitz, rather than trying to make Blitz behave the way Flash did.

Hey, this same sort of coding style for enemies would work for 3D, too, right? Just replace sprites with models and add a z-axis, right? ;)


QuietBloke(Posted 2005) [#15]
Ive never really tried anything in 3d but I dont see why not. As you say.. it just needs an extra axis for movement.

Yes.. thats exactly what I do. Each sub type simply implements a custom update method which controls the behavior.
And because they all derive from the same base type you can store all the enemies within a TList and call the update/draw method for each one and each will do its own thing.

Happy coding
:)