How to draw directly onto images

BlitzMax Forums/BlitzMax Beginners Area/How to draw directly onto images

PowerPC603(Posted 2010) [#1]
Hi guys,

I've been looking for ways to draw directly onto an image.

I'm designing a game where the player has an inventory which can hold up to 60 items.

In Blitz3D, it's easy to do, using Setbuffer ImageBuffer(Inventory).
Then I draw all the icons of the items onto this empty inventory, put some text over the icons (the quantity of each item in the inventory) and finally use this final image to draw onto the screen.
The icons of the items are of size 32x32.

When something in the inventory changes, the entire inventory is redrawn once (not every frame).

This way, I don't need my game to redraw the entire inventory every frame. It only needs to draw the complete inventory image.

I've been searching through the forums, but didn't find any solution yet.
I've read that most people would draw the empty inventory to the backbuffer, draw all the icons to the backbuffer too, same as the overlaying text (which represents the quantity of every item) and finally grab the image. But this is sloooooowwww.

As it's an inventory which will change frequently during the game (everytime the player picks up an item), I think I cannot afford to grab the image, as it's too slow.

How can I solve this, as BMax doesn't use buffers, like Blitz3D.

Or is BMax fast enough to draw the empty inventory image, 60 icons (size 32x32), 60 lines of text, and a few rects every frame?
I still need to draw other stuff on the screen, so it needs to be fast.


plash(Posted 2010) [#2]
The only way you could do it with Max2D is with GrabImage.

Draw your stuff, grab the image, clear the screen; or something like that.
I think you can draw and grab off the screen's size to get around the clearing part (e.g. start drawing at 800, 600 with an 800, 600-sized window, the user should never see the temporary draw), but I do not recall if there was graphics card issues with that method or not.


PowerPC603(Posted 2010) [#3]
I didn't test it myself if GrabImage is slow, as I don't have BMax installed yet.

I bought BMax years ago and tried it on my previous computer, but went back to B3D while waiting for BMax3D.
I just checked my accounts-tab and can download BMax when I get home.
Then I'll try this, to see if it's fast enough.

If I were to use Xors3D (which has render-to-texture), would it be possible to render the inventory to a texture (as images are actually textures AFAIK)? Would it be faster?


matibee(Posted 2010) [#4]
Or is BMax fast enough to draw the empty inventory image, 60 icons (size 32x32), 60 lines of text, and a few rects every frame?



Yes it is. Even so, as this is the simplest route, you'd do well to only resort to complex solutions if and when they're proved necessary.


PowerPC603(Posted 2010) [#5]
I've installed BlitzMax and tried some of the samples.
Especially the fireworks demo was fast.
I'll try to convert my current code to BMax code and see how it performs.

This can take a while though, as I still need to learn BMax.
I even need time to convert my entire type-structures.

Using BMax, it would perhaps be simpler to use all my different itemtypes: potions, weapons, armor, loot, ...

Right now, I need to have one "TItem" type structure, which holds all parameters of any itemtype.
An HP potion only has a few parameters, while an upgraded weapon uses alot more. Right now, every item holds all parameters, while most parameters aren't used by the item in question (an HP potion doesn't need attack-speed, defense, accuracy, evasion, and alot more).

But in Blitz3D, I used one array to hold all kinds of items in the bag, so I could only use one kind of type-structure (which holds all parameters).

With BMax, this could become simpler i guess.


Dreamora(Posted 2010) [#6]
not could, definitely will if you really unleash the object oriented nature and make a TItem type and then expanded types like TWeapon, ...

you can still have a global list in TItem to handle all items in there but you can also have one on each item type on its own etc


PowerPC603(Posted 2010) [#7]
I've tried converting my code to BMax and it works for now.

This is what the code currently draws:
- Clear the screen using Cls
- The empty player infoscreen
- The player's level and HP/FP/SP-bars (drawn over the player infoscreen)
- The empty inventory image
- The icons of 60 items in the inventory
- The quantity of each item is displayed on top of the icons

The time it takes to draw all this is only 1.201 (1 and 1/5) milliseconds (I let BMax draw all this 1000 times each frame and time this, so the results was more accurate).

So I guess it isn't neccessary to hold a copy of the original inventory (which is empty) and draw all the icons onto this image and finally draw the updated inventory in one go.

I've ran into some strange stuff by coding this.

Type TVehicle
	Field x, y

	Method SetName(n:String)
	End Method
	Method GetName:String()
	End Method
End Type

Type TCar Extends TVehicle
	Field Name:String

	Method SetName(n:String)
		Name = n
	End Method
	Method GetName:String()
		Return Name
	End Method
End Type


car1:TVehicle = New TCar
car2:TCar = New TCar

'car1.Name = "car1"
'car2.Name = "car2"

car1.SetName("car1")
car2.SetName("car2")

Print car1.GetName()
Print car2.GetName()


Take this example.
Car1 will hold a TVehicle type, while Car2 holds a TCar type.
I find it strange that I can use "car2.Name = blabla" but not car1.Name = blabla" (see the commented lines).
For car1, I get the error: Identifier Name not found.

But when I use the methods, it works fine.

Doesn't car1 see that it's actually a TCar object, which includes the Name-field, even when car1 is of the type TVehicle?
When using methods, it sees them correctly (it uses the correct method, the one in TCar).

I'm using this structure for my items in my game.
It uses TItemBase as the basetype.
In the menu for the inventory, there is an array: Items:TItemBase[60].

All other itemtypes are derived from this TItemBase type: TItemWeapon, TItemPotion, TItemArmor, ...

When I add a TItemPotion to the array, I cannot access the fields that are specifically for the potions. I only have access to the fields in the TItemBase type.

Do I really need to created methods for setting each field, like in the above example?
I also tried creating a temporary variable "Item:TPotion", then fill in all fields and then link the item to the array using Items[Slot] = Item.

Why doesn't it work directly?


Jesse(Posted 2010) [#8]
when you created the car1 you crated it as Tvehicle Tvehicle doesn't know about the field "name" but it does know about setName and getName because you created abstracts for it. when you created the car2 you created it as Tcar which included the field "name" and therefore accessible directly.
to solve your problem just move field "name" to TVehicle.


matibee(Posted 2010) [#9]
Doesn't car1 see that it's actually a TCar object, which includes the Name-field, even when car1 is of the type TVehicle?


No. It can't look forwards in the hierarchy. It can only know about itself and all that it's built of. Where would the world be if every kid had access to dads bank account? :)

Type child
method Drink3LitresOfColaAndEatAKiloOfHaribos:string ()
   return "go hyper"
end method
end type 
Type adult Extends child
method Drink3LitresOfColaAndEatAKiloOfHaribos:string ()
   return "vomit"
end method
method BuyBeer:String ()
  return "oooh!"
end method
end type 


Permissions automatically imply the adult can do everything the child can (but not always with the same results) but not vice versa.

Your inventory isn't a simple hierarchical problem. Show me some parameters for armor, potion, shield etc and I'll try and help you further.


PowerPC603(Posted 2010) [#10]
I cannot post those parameters yet, because I don't have them myself yet.

I've only started with some potions for now.
The fields for every itemtype will be expanded while the game grows larger.
I don't even know all the parameters for every item yet.


to solve your problem just move field "name" to TVehicle.



I can't do this, because then I would have to create one big type-structure with all possible fields in it.
Then there is no point to using derived types.

I have a TItemBase type-structure that will get extended into TItemWeapon, TItemArmor, TItemShield, TItemPotion, ...

The TItemWeapon will have an Attack field, while a potion doesn't have any use for an Attack value.

Same as a TItemShield object has a Defense value, while a weapon or potion doesn't have a benefit for these parameters.

But the inventory should be able to store ALL types of items, so it uses an array: "Items:TItemBase[60]".
Since all items in the game will be derived from TItemBase, they can all be stored in the inventory's array.

Some items also have a Quantity parameter, but not all.
Since a weapons cannot be stacked, they don't have a use for the Quantity parameter. But potions and loot have the Quantity parameter.

So I guess I'll need to do one of these:
- create the item first as the proper itemtype, set the fields and then add it to the inventory's array
- create methods to Get/Set every parameter for every itemtype, except the basetype parameters


matibee(Posted 2010) [#11]
Since a weapons cannot be stacked, they don't have a use for the Quantity parameter.


How would you deal with bows and arrows?


PowerPC603(Posted 2010) [#12]
The bow uses the TItemWeapon type-structure, the ammo uses TItemAmmo.

The bow is just the weapon. It only defines which type of ammo to use.
The weapon itself doesn't hold the ammo.
The ammo is something different (is a different TItem).
In the equipment window, you will be able to equip the bow in one slot and the ammo in another slot.

There are 2 slots present to equip ammo.
This way, the player can equip arrows in one slot and buttets in the other.
Then the player can swap both types of weapons and the weapon will use the correct ammo-type.

When you use a weapon that needs ammo, the game checks both ammo-slots for the correct type.
If it finds ammo of the correct type in either slot, 1 is reduced from the correct slot.
If the player has equipped arrows and bullets, but the weapon needs rockets, the weapon won't do anything, as the correct type of ammo isn't equipped in either slot.

There is a reason why weapons/armors/shields cannot be stacked.
If you have a sword without any upgrades, it has a different value for the attack power then the same sword with 2 upgrades.

Most MMORPG's won't allow you to stack weapons, it will be the same for my game.


Jesse(Posted 2010) [#13]

I can't do this, because then I would have to create one big type-structure with all possible fields in it.
Then there is no point to using derived types.



that was just to show you the principle behind derived Types.
A way to solve it is to cast it back to what the item really is:
like I said move the name field to the base let it be a int constant not a string so that it can be tested more efficiently. once you access it through the base. check to see what type it is by reading the name. then cast it to what ever you need it to be:

function (obj:Tbase)
       select obj.name   'name is an integer
            case CGUN     'CGUN is value 1
                 local g:Tgun = Tgun(obj) ' casted back to the original type
                 g.Life = 35 'life is a member of Tgun only
                  .
                  .
                  .
            case CBOW    'CBOW is value 2
                 local b:Tbow = Tbow(obj)
                  .
                  .
                  .
        end select
end function


the thing is you are trying to mix different types with nothing in common but if you store only common items in a base type you wont have that problem.
like say, for example, bullets and arrows will both have speed and maybe life and cause damage so those three variables can be used as fields in the base type. and wont need to be casted back to the original one. but if you are trying to put bullets and potions and shields and create a common base type for all of them, you will have to deal with stuff like what I posted above. in a way it sort of defeat the purpose of derived types or polymorphing which is what you are trying to do.


PowerPC603(Posted 2010) [#14]
The basetype has some common parameters.
It has the most important one: a reference to a TItemType instance.

I use one array which holds the data about every sort of item in the game.
There are HP potions which restore 100, 250, 500, 1000, 2000, 3000, 4000, 5000 hp when they are used.
Then there are FP potions with values of 50, 125, 250, 500, 1000, 1500, 2000. And also SP potions of 100, 200, 400, 800.

All these potions are represented by TItemTypePotion.
And they exist only once.

When the player buys 10 HP potions of value 500 for example, a new TItemPotion is created inside the inventory of the player (if he hasn't any yet). And a reference towards the TItemTypePotion of "HP potion 500" is stored in the TItemPotion instance. Then this TItemPotion instantly knows it's name, description, what the potion does (restore 500 hp), and many more.

The basetype of TItemPotion holds the reference to the TItemTypePotion, which holds the name of the item, the icon image, what type the item really is, the HP this potion restores when used, the description of the potion and alot more information.

So, there is only one instance (TItemTypePotion) which describes an HP potion of 500, but many TItemPotions can reference this instance.
This is done to save memory.

There is no need to copy the name, description, ... to every HP potion of 500 in existance. It only needs a reference to it.

I'll try to create an image to clarify all this, as it confuses myself too.
Right now, I only have the potions, but there will be alot more itemtypes.
Then it will be very complex to work with, if I don't have anything on paper as a reference.


PowerPC603(Posted 2010) [#15]
I've constructed an example of how my type-structure is implemented now.

As I said before, I use one array called AItemTypes:TItemType[1000].
So it can store all types of itemtypes in my game: weapons, armor, ammo, potions, ...

One file is loaded with all the data you see at the bottom.
Every itemtype has an ID. The index of the array where an itemtype is stored is equal to the ID of the item. There can never be 2 different itemtypes with the same ID.

Now, when the user buys 10 "HP potion 250", a TItemPotion is created in the player's inventory (which is derived from TItem).
The TItem typestructure has one base-field: ItemType:TItemType.

So, the TItemPotion holds the quantity (10 in this case), while the ItemType field in the baseclass holds the reference to the TItemTypePotion with ID 2 (the reference to the object AItemTypes[2] is stored).

So, the item in the player's inventory holds all information regarding the potion through the reference.





Jesse(Posted 2010) [#16]
the simplest way "I think" that this can be resolve is to create 5 Arrays, one for each item type base. other than that, I don't see any easy solution to your problem. the item type slot itself will control what kind of array to access.
I can't see any other simple way of doing that.


PowerPC603(Posted 2010) [#17]
Then I'll waste too many slots in each array, as each type of item has it's unique ID.
If I have 1000 itemtypes, I'll need 5 arrays with 1000 indexes each.

In the potions array, there will only be about 50 types of potion I think.
This will waste 950 array-indexes.
The same will happen for each array.

Then I will also need more arrays than 5, as I didn't include amulets and rings, and alot of other stuff in the example above.

I need only one array that holds ALL itemtypes.
Then it's easier to find the correct itemtype.

I'll find a solution to my problem, it will only take some time.


Jesse(Posted 2010) [#18]
another idea would be to store them all as "object"s in the array and access them as what ever type you need them as long as the program knows where are whatever type of objects in the array.

just ideas. Good luck with your project.


PowerPC603(Posted 2010) [#19]
I can store all kinds of items into the AItemTypes array just fine.

It's just that, when the array holds all these items as their base-type, that I cannot access the fields in the derived section directly.
I'll need to use methods to retreive them.

I've just modified them to load differently.
I was using separate functions for each ItemType.

Now I only need to create them and the New() method automatically loads the required fields from the opened file (which holds the data for each itemtype).

I'll do some more optimising and report back when I have some more difficulties.

I use this bmx-file to load all itemtypes (only AutoPotions for now):
' An ItemTypes holds all information about a specific type of item
' During the game, there will be TItem items everywhere.
' These all link to a specific ItemType, so the TItem knows it's name, ID, icon, price, ...



' This variable is used as a reference to the "ItemTypes.txt" file, so every ItemType can access the opened file
Global ItemTypesFile

' Predefine the array where all itemtypes are stored (all items will have the reference to one instance in this array to define what the item is and it's properties)
Global AItemTypes:TItemTypeBase[1000]

' This declares the base-type for all itemtypes
Type TItemTypeBase
	Field ID:Int ' The ItemID of the itemtype
	Field Name:String ' The name of the itemtype
	Field Image:TImage ' The image used To represent the itemtype (32x32 bitmap)

	Field Stackable:Int ' Defines If this item can be stacked
	Field Price:Int ' The money that the players gets when he sells the item To an NPC
End Type

' This declares the structure for AutoPotion ItemTypes
Type TItemTypeAutoPotion Extends TItemTypeBase
	Field Restore:Int ' The amount of HP/FP/SP that gets restored when used
	Field PotionType:Int ' Defines the type of autopotion: 1 = HP, 2 = FP, 3 = SP
	Field UseDelay:Int ' The time in milliseconds before the item can be used again

	Method New()
		Local LineFromFile:String, ItemImage:String

		' Keep reading
		While Not Eof(ItemTypesFile)
			' Read one line from the file "ItemTypes.txt"
			LineFromFile:String = ReadLine$(ItemTypesFile)

			Select True
				Case Mid$(LineFromFile:String, 1, 10) = "ItemTypeID"
					ID:Int = Int(Mid$(LineFromFile:String, 14))
				Case Mid$(LineFromFile:String, 1, 8) = "ItemName"
					Name:String = Mid$(LineFromFile:String, 12)
				Case Mid$(LineFromFile:String, 1, 9) = "ItemImage"
					Image:TImage = LoadImage(GameDir$ + "Images/Potions/" + Mid$(LineFromFile:String, 13) + ".bmp")
					If Image:TImage = Null Then RuntimeError "Failed to load autopotion image: " + Mid$(LineFromFile:String, 13)
				Case Mid$(LineFromFile:String, 1, 9) = "Stackable"
					If Mid$(LineFromFile:String, 13) = "True"
						Stackable:Int = True
					Else
						Stackable:Int = False
					EndIf
				Case Mid$(LineFromFile:String, 1, 5) = "Price"
					Price:Int = Int(Mid$(LineFromFile:String, 9))
				Case Mid$(LineFromFile:String, 1, 7) = "Restore"
					Restore:Int = Int(Mid$(LineFromFile:String, 11))
				Case Mid$(LineFromFile:String, 1, 10) = "PotionType"
					Select True
						Case Mid$(LineFromFile:String, 14) = "HP"
							PotionType:Int = 1
						Case Mid$(LineFromFile:String, 14) = "FP"
							PotionType:Int = 2
						Case Mid$(LineFromFile:String, 14) = "SP"
							PotionType:Int = 3
					End Select
				Case Mid$(LineFromFile:String, 1, 8) = "UseDelay"
					UseDelay:Int = Int(Mid$(LineFromFile:String, 12))
				Case LineFromFile:String = "[/ItemType]"
					AItemTypes[ID] = Self ' Store this instance in the AItemTypes array at the proper index (the itemtype's ID)
					Exit ' The end of the ItemType has been found, stop reading
			End Select
		Wend
	End Method
End Type



Function LoadItemTypes()
	Local LineFromFile:String

	' Open the file "ItemTypes.txt"
	ItemTypesFile = ReadFile(GameDir$ + "Data\ItemTypes.txt")

	' Check If the file has been opened
	If ItemTypesFile = 0 Then
		RuntimeError "Unable to read file: ItemTypes.txt"
	Else
		' Read until the und of the file
		While Not Eof(ItemTypesFile)
			' Read one line
			LineFromFile:String = ReadLine$(ItemTypesFile)

			' Check if a new ItemType has been found
			If Mid$(LineFromFile:String, 1, 9) = "[ItemType"
				' Read the MainType, create a new devired instance of the TItemTypeBase depending on the itemtype, and let the new instance use it's New()
				' method to load the data about the itemtype automatically
				Select True
					' In case the itemtype is an AutoPotion
					Case Mid$(LineFromFile:String, 13) = "AutoPotion]"
						' Create a new TItemTypeAutoPotion instance, this triggers the New() method in the instance (which loads the rest of the data
						' until "[/ItemType]" has been found)
						NewItemType = New TItemTypeAutoPotion
				End Select
			EndIf
		Wend

		CloseFile ItemTypesFile
	EndIf
End Function


And this is the ItemTypes.txt file:
[ItemType = AutoPotion]
ItemTypeID = 1
ItemName = HP potion 100
ItemImage = HPPot100
Stackable = True
Price = 0
Restore = 100
PotionType = HP
UseDelay = 2500
[/ItemType]

[ItemType = AutoPotion]
ItemTypeID = 2
ItemName = HP potion 250
ItemImage = HPPot250
Stackable = True
Price = 0
Restore = 250
PotionType = HP
UseDelay = 2500
[/ItemType]

[ItemType = AutoPotion]
ItemTypeID = 3
ItemName = HP potion 500
ItemImage = HPPot500
Stackable = True
Price = 0
Restore = 500
PotionType = HP
UseDelay = 2500
[/ItemType]

[ItemType = AutoPotion]
ItemTypeID = 4
ItemName = HP potion 1000
ItemImage = HPPot1000
Stackable = True
Price = 0
Restore = 1000
PotionType = HP
UseDelay = 2500
[/ItemType]

[ItemType = AutoPotion]
ItemTypeID = 5
ItemName = HP potion 2000
ItemImage = HPPot2000
Stackable = True
Price = 0
Restore = 2000
PotionType = HP
UseDelay = 2500
[/ItemType]

[ItemType = AutoPotion]
ItemTypeID = 6
ItemName = HP potion 3000
ItemImage = HPPot3000
Stackable = True
Price = 0
Restore = 3000
PotionType = HP
UseDelay = 2500
[/ItemType]

[ItemType = AutoPotion]
ItemTypeID = 7
ItemName = HP potion 4000
ItemImage = HPPot4000
Stackable = True
Price = 0
Restore = 4000
PotionType = HP
UseDelay = 2500
[/ItemType]

[ItemType = AutoPotion]
ItemTypeID = 8
ItemName = HP potion 5000
ItemImage = HPPot5000
Stackable = True
Price = 0
Restore = 5000
PotionType = HP
UseDelay = 2500
[/ItemType]



[ItemType = AutoPotion]
ItemTypeID = 21
ItemName = FP potion 50
ItemImage = FPPot50
Stackable = True
Price = 0
Restore = 50
PotionType = FP
UseDelay = 2500
[/ItemType]

[ItemType = AutoPotion]
ItemTypeID = 22
ItemName = FP potion 125
ItemImage = FPPot125
Stackable = True
Price = 0
Restore = 125
PotionType = FP
UseDelay = 2500
[/ItemType]

[ItemType = AutoPotion]
ItemTypeID = 23
ItemName = FP potion 250
ItemImage = FPPot250
Stackable = True
Price = 0
Restore = 250
PotionType = FP
UseDelay = 2500
[/ItemType]

[ItemType = AutoPotion]
ItemTypeID = 24
ItemName = FP potion 500
ItemImage = FPPot500
Stackable = True
Price = 0
Restore = 500
PotionType = FP
UseDelay = 2500
[/ItemType]

[ItemType = AutoPotion]
ItemTypeID = 25
ItemName = FP potion 1000
ItemImage = FPPot1000
Stackable = True
Price = 0
Restore = 1000
PotionType = FP
UseDelay = 2500
[/ItemType]

[ItemType = AutoPotion]
ItemTypeID = 26
ItemName = FP potion 1500
ItemImage = FPPot1500
Stackable = True
Price = 0
Restore = 1500
PotionType = FP
UseDelay = 2500
[/ItemType]

[ItemType = AutoPotion]
ItemTypeID = 27
ItemName = FP potion 2000
ItemImage = FPPot2000
Stackable = True
Price = 0
Restore = 2000
PotionType = FP
UseDelay = 2500
[/ItemType]



[ItemType = AutoPotion]
ItemTypeID = 41
ItemName = SP potion 100
ItemImage = SPPot100
Stackable = True
Price = 0
Restore = 100
PotionType = SP
UseDelay = 2500
[/ItemType]

[ItemType = AutoPotion]
ItemTypeID = 42
ItemName = SP potion 200
ItemImage = SPPot200
Stackable = True
Price = 0
Restore = 200
PotionType = SP
UseDelay = 2500
[/ItemType]

[ItemType = AutoPotion]
ItemTypeID = 43
ItemName = SP potion 400
ItemImage = SPPot400
Stackable = True
Price = 0
Restore = 400
PotionType = SP
UseDelay = 2500
[/ItemType]

[ItemType = AutoPotion]
ItemTypeID = 44
ItemName = SP potion 800
ItemImage = SPPot800
Stackable = True
Price = 0
Restore = 800
PotionType = SP
UseDelay = 2500
[/ItemType]



PowerPC603(Posted 2010) [#20]
I think I won't get many problems using the inventory items.

I just added a feature that enables the player to right-click any item in the inventory to equip it.

Some potions (HP, FP and SP potions) need to be equipped in the Auto-potion menu before they can be used.
It's like RF Online's macro function.

In the Auto-potion menu, you'll be able to equip those 3 types of potions and a potion is used automatically when the player's HP/FP/SP drop below a preset value (which the player can choose himself).

Right-clicking an item in the inventory equips those 3 types of potions automatically in the AutoPotion menu.
If there isn't any potion equipped yet, the item is moved from the inventory to the AutoPotion menu.
If there is a potion equipped already, the code determines if the ID is the same.
If it's the same, the quantity of the potions in the inventory is added to the quantity of the potions in the autopotion menu.
If they aren't the same, they switch places.

I use the method Equip() for every itemtype and it works for autopotions already.
The Equip method currently isn't present for other itemtypes, because the potions are all i have for now.


PowerPC603(Posted 2010) [#21]
I've created a worklog for my 3D RPG, so I don't have to report any progress here in the forum.
It also has screenshots of the current menu's and some explanation how they work and what they do.
http://blitzbasic.com/logs/userlog.php?user=6662&log=1756