Need help debugging

BlitzMax Forums/BlitzMax Beginners Area/Need help debugging

abelian_grape(Posted 2010) [#1]
So here's a puzzler. Currently I am building a map editor that allows you to create a new map, left click on a blank tile to place the current texture/RGB tile there, and if you right-click, you are prompted with some Editing options (which calls the function below).



I tried to set up an elementary GUI so that when you click within a rectangle that has been designated a button, it's like clicking a button.

There is a portion of code that just doesn't seem to be executing, and I don't know why. In this block:
					If MouseHit(1) And MouseX() >= 442 And MouseX() <= 482 And MouseY() <= 296 And MouseY() >= 244
						RGBTile = False
						Exit
					ElseIf MouseHit(1) And MouseX() >= 492 And MouseX() <= 532 And MouseY() <= 296 And MouseY() >= 244
						RGBTile = True


There are bounds on where the mouse can be when left clicking to determine if a button has been pressed. The two buttons are Yes and No (Yes is the top one, No is the bottom one). If you click Yes, the program actually works and you can create a texture tile. If you click in the No box, nothing happens. This is so annoying because I can't see any difference between the two conditions, other than the No box is right-shifted further than the Yes box. So apparently, what isn't working is this:

				ElseIf MouseHit(1) And MouseX() >= 492 And MouseX() <= 532 And MouseY() <= 296 And MouseY() >= 244
						RGBTile = True


Because if it were, the rest of the code would be executed which looks like:
ElseIf MouseHit(1) And MouseX() >= 492 And MouseX() <= 532 And MouseY() <= 296 And MouseY() >= 244
						RGBTile = True
				
						'IF THE USER WANTS AN RGB TILE, PROMPT THEM FOR THE RGB VALUES
						'THIS LOOP TAKES THE R VALUE
						While Not KeyDown(KEY_ENTER) And RGBTile = True
							Cls
							SetColor(120, 120, 120)	'Dark gray
							DrawRect(262, 234, 500, 56)
							SetColor(255, 255, 255)
							DrawText("Enter R value for RGB tile: " + inputNum, 381, 234)
							DrawText("Press ENTER when done, BACKSPACE to clear input.", 381, 250)
							
							Char = GetChar()
							If Char >= 48 And Char <= 57	'As long as it's a number 0 - 9, accept input into string
								inputNum :+ Chr(Char)
							ElseIf Char = 8					'If user hits BACKSPACE, clear the input
								inputNum = Null
							EndIf
						
							Flip
						Wend
						
						'Store input, reset input
						rValue = Int(inputNum)
						FlushKeys()
						inputNum = Null
						
						
						'THIS LOOP TAKES THE G VALUE
						While Not KeyDown(KEY_ENTER) And RGBTile = True
							Cls
							SetColor(120, 120, 120)	'Dark gray
							DrawRect(262, 234, 500, 56)
							SetColor(255, 255, 255)
							DrawText("Enter G value for RGB tile: " + inputNum, 381, 234)
							DrawText("Press ENTER when done, BACKSPACE to clear input.", 381, 250)
							Char = GetChar()
							If Char >= 48 And Char <= 57	'As long as it's a number 0 - 9, accept input into string
								inputNum :+ Chr(Char)
							ElseIf Char = 8					'If user hits BACKSPACE, clear the input
								inputNum = Null
							EndIf
						
							Flip
						Wend
						
						'Store input, reset input
						gValue = Int(inputNum)
						FlushKeys()
						inputNum = Null
						
						'THIS LOOP TAKES THE B VALUE
						While Not KeyDown(KEY_ENTER) And RGBTile = True
							Cls
							SetColor(120, 120, 120)	'Dark gray
							DrawRect(262, 234, 500, 56)
							SetColor(255, 255, 255)
							DrawText("Enter B value for RGB tile: " + inputNum, 381, 234)
							DrawText("Press ENTER when done, BACKSPACE to clear input.", 381, 250)
							Char = GetChar()
							If Char >= 48 And Char <= 57	'As long as it's a number 0 - 9, accept input into string
								inputNum :+ Chr(Char)
							ElseIf Char = 8					'If user hits BACKSPACE, clear the input
								inputNum = Null
							EndIf
						
							Flip
						Wend
						
						
						'Store input, reset input, create tile
						bValue = Int(inputNum)
						FlushKeys()
						inputNum = Null
						
						
					EndIf
					
					Flip
					
				Wend
				
				If RGBTile = True
					'CREATE THE TILE HERE
					floors[rgb_id] = TFloor.Create(rValue, gValue, bValue, "RGB")
					map[x, y] = TTile.Create(x, y, rgb_id, passableToggle)
					curr_id = rgb_id		'Set the current floor_id
				EndIf
				
				'Reset input
				FlushKeys()
				inputNum = Null


This is seriously driving me up the wall.

-muffins


Jesse(Posted 2010) [#2]
what seems to be happening and I am just guessing but I am 99.9% shure, when you check the MouseHit in the first if statement, the mouseHit automatically is cleared out of the buffer so if you check it again it is no longer present and therefore that condition will never be true for the second,third... if statement. I suggest you store the mouseHit in a variable and do the tests from the variable.
I have never liked the way the mouseHit or keyhit works.
that is one of the reasons I created my own mouse handling object


abelian_grape(Posted 2010) [#3]
That is exactly it, thank you Jesse! I hadn't even considered that.

If the admins choose, please feel free to delete this thread.


TaskMaster(Posted 2010) [#4]
Why delete a thread? It is a good learning experience for others. That is the point of a forum like this.

This is something a lot of people don't realize, when you call mousehit or keyhit, it clears it. So, you should save its results in a variable before testing it multiple times.


Czar Flavius(Posted 2010) [#5]
In my main game loop I have an input gathering function, which stores in global variables things such as left button pressed/right button pressed etc, so the rest of my game loop knows if the button has been hit or not.


abelian_grape(Posted 2010) [#6]
I guess while we're on it, there is another issue that I cannot reconcile, and I believe it is a product of the whole "MouseHit" debacle. Here is the deal. When a user right-clicks on a tile in my tile editor, an "Edit" function is called which is a Type function. The function within this Tile Type looks like this:



Now, when the Edit function is called, essentially a message box pops up prompting the user to choose between a pre-defined texture tile, or a custom RGB tile whereby one can define their own RGB values for a solid-colored tile. Depending on whether the user answers yes (create texture) or no (create RGB), another function is called (this time the function does not belong to a type). Setup_Texture(x,y) to create the texture tile, and Setup_RGB(x,y) for creating the RGB tile, where the parameters to both functions are the coordinates of the particular tile chosen within the map array.

Here is the Setup_RGB function:


Therefore, once the person right clicks a tile, the position of that tile within the 2D map array of tiles is recorded and once the user sets up the texture or RGB tile, the tile will be placed at that location. My problem is this:

When the user chooses to create an RGB tile, they are presented with a message box telling them to enter the explicit values for R, G, B, and the user can cycle between the 3 using left and right arrow keys. There is also a preview window for the overall color, and depending on whether the user is editing R, G, or B, the up/down arrows will adjust the value of R, G, or B between 0 - 255. At any time between editing R, G, or B, the user has the option of clicking a button labeled "Create Tile" where the colors chosen are locked in and then the tile is created. ISSUE: Not only does the editor create the tile of specified RGB values at the tile that was originally right clicked, it creates a duplicate tile at the location of the Create Tile button. So by having that button, and having the user click it with a mouse, for some reason the code is creating a duplicate tile at the location of the Create Tile button. All the Setup_RGB function was passed was the x, y coordinates of the tile in the 2D map array, and it was only used for the TTile.Create() function call. I would guess that because the Edit function is a Type function, and the Setup_RGB function is not a type function, then the Edit function seems to still be registering left mouse clicks (which creates the most recently created tile at the location left-clicked) even though the program control is in the Setup_RGB function.


abelian_grape(Posted 2010) [#7]
I should also note that clicking anywhere other than the "Create Tile" button doesn't create a new tile at that spot, nor does the act of clicking "No" in the first menu to create an RGB tile cause a tile to be created there either. Very strange.


Jesse(Posted 2010) [#8]
try "FlushMouse" after selecting the tile. If that doesn't work, you might have to post a sample executable or executable source code.


abelian_grape(Posted 2010) [#9]
haha, It needed a FlushMouse. I was using FlushKeys and wondering why it didn't do anything. Thanks Jesse!


abelian_grape(Posted 2010) [#10]
Any help would be AWESOME on debugging the following code. If no one wants to, I understand, because it is a big heap of code. It's big for me, at least...still under 1000 lines, many of which are blank or comments.

NOTE: In order to get this code to 'work', create a "Textures" directory in the same directory as the .bmx file. Put TWO 32x32 pixel images that can be drawn by BlitzMax using the DrawImage function in the "Textures" directory.

When I run the program, create a new map, right-click on a tile, choose "Yes" for when I get prompted if I want a texture tile, the program crashes with a "Unhandled Exception:Attempt to access field or method of Null object" error. I know what the error relates to (it is fairly self-explanatory) but I can't figure out where my code is failing. I get re-directed to the DrawImage function which leads me to believe that the texture floors that are being created in this block of code:

			'Load the textures from the Textures directory
			For Local i:Int = 0 To (texture_num - 1)
				texture_file_name = NextFile(dir)
				Local texture_img:TImage = LoadImage(texture_file_name)
				textures[i] = TFloor.Create(255, 255, 255, texture_file_name, texture_img)
				texture_file_name = Null
			Next


are somehow falling out of scope for when I go to draw them in this block of code:

							'Draw the textures in the texture array
							For Local j:Int = 0 To (texture_num - 1)
								textures[j].Draw(texture_x, texture_y)
								texture_x:+tileSize	'Move the next texture over to prevent overlap
								'Wrap the texture images to remain on the screen
								If texture_x > (1024 - (tileSize / 2))
									texture_x = (tileSize / 2)
									texture_y:+tileSize
								EndIf
							Next


In any event, I think the areas to focus on are the Setup_Texture() function, as well as the TFloor.Draw() method. Here is the code in its entirety:



Controls for the program:

CTRL + N will create a new map. Do this first.
Right click any blank (white) tile to edit it. (Clicking "Yes" will go on to crash the program as it stands. Clicking "no" will allow you to define an RGB tile.)
Once you've created a tile (at this point you can only create RGB tiles), you will be returned to the map, and if you left click, you will create a tile that is the same as the last one you created.

Even if you can't or don't want to track down this problem, I am open to suggestions/comments about my code. This is my second BlitzMax program, and my first attempt at creating a tool to be used in a later game, so please, don't be too harsh :)

-muffins


Jesse(Posted 2010) [#11]
your main problem is with reading the directory. when you get a chance, make a program that display the contents of a directory. you will see that you get a file name: "." and another one: "..". Those are trace back directories to the parent directory. you can use them to change directories with the ChangeDir command. it will take you to the parent directory. figure out a way to ignore them and your problem will be solved.

[edit]

I forgot to mention that you need to move the directory pointer using"ChangeDir" to the "Textures" dir before you can actually read the files in it.


abelian_grape(Posted 2010) [#12]
Isn't the directory pointer already at the "Textures" dir?
Local dir:Int = ReadDir(AppDir + "/Textures")	'Textures directory


I think I figured out how to bypass the "." and "..":
For Local i:Int = 0 To (texture_num - 1)
				texture_file_name = NextFile(dir)
				If texture_file_name = "" Exit
				If texture_file_name = "." Or ".."
					texture_file_name = NextFile(dir)
				Else
					Local texture_img:TImage = LoadImage(texture_file_name)
					textures[i] = TFloor.Create(255, 255, 255, texture_file_name, texture_img)
				EndIf
				
			Next

I still get the same error message though. My program is definitely reading in the correct names for the texture_file_name, so I'm not sure why it's still a Null object.


Jesse(Posted 2010) [#13]

Isn't the directory pointer already at the "Textures" dir?



No. you are just telling it to read the names from the specific directory.
and all you get are a bunch of name not pointing to anything and sense the directory pointer is currently pointing to the "appDir" because that is where it point when the application starts, it will never find any images. you can access them directly like this:
  
LoadImage(AppDir+"\Textures\"+texture_file_name) 

or change directory:
ChangeDir(AppDir+"\Textures")

then do all of the ReadDir and load files with out having to do as in the previous example.
if you change directory to the "Textures" directory you won't need to add "AppDir" or "\Textures\" for the "LoadImage" Command.


abelian_grape(Posted 2010) [#14]
Okay, I definitely understand the error I was making between using ReadDir and needing ChangeDir unless I wanted to modify the directory LoadImage was loading from. I really didn't understand how the filesystem was working. I initially thought that ReadDir was performing the same task that ChangeDir actually does.

As a check for debugging purposes, I made the console print out the names of the files that its reading. For some reason, it skips over the ".." but not the "." as the "." gets printed first every time, and then my first texture gets printed. The second texture file name doesn't get printed, and I assume the program is crashing because it can't create a tile with the "." because that is not an image, and therefore it can't load it.

ChangeDir(AppDir + "/Textures")
Local texture_file_name:String = Null	'Name of the current texture image file under consideration
Local dir:Int = ReadDir(AppDir + "/Textures")	'Textures directory
		
'Load the textures from the Textures directory
For Local i:Int = 0 To (texture_num - 1)
        texture_file_name = NextFile(dir)
	Print texture_file_name
	If (texture_file_name = ".") Or (texture_file_name = "..")
		texture_file_name = NextFile(dir)
	Else
		Local texture_img:TImage = LoadImage(texture_file_name)
		textures[i] = TFloor.Create(255, 255, 255, texture_file_name, texture_img)
	EndIf
Next
			
CloseDir(dir)



ima747(Posted 2010) [#15]
late to this conversation, but just a side note, if you're doing dynamic loading it's usually a good idea to confirm that the load worked before doing anything with the result. e.g.

local anImage:TImage = LoadImage(a_var_path)
if(anImage) ' ensure that the image loaded
	'do stuff with the image
end if


LoadImage should return null if the load fails unless I'm miss-remembering the docs...


Czar Flavius(Posted 2010) [#16]
I just load all images with a function that checks for me so I never forget.

Function safe_image_load:TImage(path:String, animation, width = 0, height = 0, first = 0, count = 0, flags = -1)
	Local image:TImage
	If animation
		image = LoadAnimImage(path, width, height, first, count, flags)
	Else
		image = LoadImage(path, flags)
	End If
	If Not image
		RuntimeError "Cannot find " + path
	End If
	Return image
End Function



Jesse(Posted 2010) [#17]

For some reason, it skips over the ".." but not the "." as the "." gets printed first every time, and then my first texture gets printed. The second texture file name doesn't get printed, and I assume the program is crashing because it can't create a tile with the "."


Really simple. The first nextFile() you print but the seconde one inside the if statement you are not printing it so when you get back to the next loop you nextFile() it again and is why you never get to see the second one.


abelian_grape(Posted 2010) [#18]
Alright, I made the correction, so now it prints "." and "..". I think the problem still remains that it's not actually skipping over the "." and ".." like I had hoped. But if that were true, I'm not sure why it's even making it to printing out "grass.bmp", since I defined texture_num = 2 as a Const, and the loop is going from 0 to (texture_num - 1). It's not printing out the actual second texture which I'm calling "water.bmp".

I am so frustrated with this I might just hard code everything into the program, because at this point, I think the map editor has taken up more time than it should have since it was supposed to expedite the game-making process :[

Edit:
Well let me ask this. I have my textures array as a global array of TFloor objects. TFloor is a type I have defined. My program is claiming that it is attempting to access a Null method or object, and pointing to the line
DrawImage(textures[0].texture_img, texture_x, texture_y).  


Now, what is living at index 0 of textures is a TFloor object that was initialized in this line:
textures[i] = TFloor.Create(255, 255, 255, texture_file_name, texture_img)


In which it was given r, g, b values for a base color, a name, and an image loaded into the texture_img field. Could it be that since my textures array is an array of TFloors, that it is trying to access a field of the textures array and not the field of the TFloor object at that index in the array? Is it treating the textures array as a type with its own fields?


Czar Flavius(Posted 2010) [#19]
It might be easier to define your floors (and a list of their paths) in a text file which you load rather than trying to "find" them in the folder. It gives you the flexibility without the headache.

	If (texture_file_name = ".") Or (texture_file_name = "..")
		texture_file_name = NextFile(dir)
	Else
		Local texture_img:TImage = LoadImage(texture_file_name)
		textures[i] = TFloor.Create(255, 255, 255, texture_file_name, texture_img)
	EndIf
If the file is . or .., it won't do the else and will skip to the next i. Try something like While texture_file_name = "." Or texture_file_name = ".." texture_file_name = NextFile(dir)


abelian_grape(Posted 2010) [#20]
Well the way I figure, if I have to create a separate text file in which I define the floors and reference their paths, then I am just as well off hard-coding them into the .bmx file, and then modifying the .bmx file with the appropriate file names each time I modify the contents of the Textures folder. Because if I made the text file with this information, all the text file is doing is loading everything, where I could do the same thing with some copying and pasting. The dynamic file "finding" and loading seemed to be more of a time saver because then I didn't have to record any changes I made anywhere, but it's looking like this is beyond me.


Jesse(Posted 2010) [#21]
i did a lazy test like this:
			For Local i:Int = 0 To (texture_num - 1)
				Repeat
					texture_file_name = NextFile(dir)
				Until texture_file_name <> "." And texture_file_name <> ".."
				Local texture_img:TImage = LoadImage(AppDir + "\Textures\"+texture_file_name)
				If texture_img = Null Print "texture image = Null " End
				textures[i] = TFloor.Create(255, 255, 255, texture_file_name, texture_img)
				texture_file_name = Null
			Next


and it works fine.


Czar Flavius(Posted 2010) [#22]
The only reason you softcode anything is to allow the user to mod your program or it's the biggest time saver just to hardcore it :D