Clickable image / image highlight?

Blitz3D Forums/Blitz3D Beginners Area/Clickable image / image highlight?

Guy Fawkes(Posted 2011) [#1]
Hi all. Does anyone know where I can find a clickable menu, as well as a menu that when mousing over the image, it changes the image to a highlighted image?

I need an example where I can enter and exit the options menu. :)

Thanks again! :)


Ross C(Posted 2011) [#2]
I don't think you'll find a clickable menu. You kind of need to implement that yourself, as you'll have your own options, and wyas of stylising it.

If it's 2d, you'll need to looking comparing the mouse position to the menu buttons position. If the tip of the mouse (the MouseX() and MouseY()) overlap the menu item, and the mouse is being clicked, then execute whatever code the menu item is for:


menu_item_image = loadimage("menu_item.png")
menu_item_image_x = 200
menu_item_image_y = 200

If MouseHit(1) then
   If RectsOverlap( MouseX(),MouseY(),1,1, menu_item_image_x, menu_item_image_y,ImageWidth(menu_item_image),ImageHeight(menu_item_image)) Then
      ;execute code
   End If
End If


Something like that. Typically for mouse over, you wouldn't check for the mouse hit, and change a the drawn image based on that. You need to break down what you want to do more, rather than asking for a full working menu, as not many people have that kind of code to hand. Most things are built from fairly simple pieces of code, combining to produce more complex functions.

Last edited 2011


Guy Fawkes(Posted 2011) [#3]
thanks alot, Ross! :)


Guy Fawkes(Posted 2011) [#4]
ok, something is not quite right with this code.

I got it to click ok, and to highlight, but the problem is, i need it to play a song, and i can't get it to pause the song if the song is playing and the button is clicked, or unpause if the song is not playing, and the button is clicked.

Code:

Graphics3D 800, 600, 0, 2

Global mysnd

Global vol#

Global paused

Global sndchn

vol# = 1.0

mysnd = Load3DSound("C:\Users\BLAH\Desktop\oldstuff\music\01 - burning hearts ~angel~.mp3")

menu_item_image = LoadImage("menu_item.png")
menu_item_image_2 = LoadImage("menu_item2.png")

menu_item_image_x = 200
menu_item_image_y = 200
menu_item_image2_x = 200
menu_item_image2_y = 200

While Not KeyHit(1)
Cls

UpdateWorld()
RenderWorld()

Text GraphicsWidth()/2, GraphicsHeight()/2, "MX: "+MouseX(), 1, 1
Text GraphicsWidth()/2, GraphicsHeight()/2+20, "MY: "+MouseY(), 1, 1

DrawImage menu_item_image, menu_item_image_x, menu_item_image_y


;mouse over
;If MouseHit(1) Then
   If RectsOverlap( MouseX(),MouseY(),1,1, menu_item_image_x, menu_item_image_y,ImageWidth(menu_item_image),ImageHeight(menu_item_image)) Then
      ;execute code
DrawImage menu_item_image_2, menu_item_image2_x, menu_item_image2_y
   End If

;if the channel is not playing, and the mouse has clicked the button

If MouseHit(1) Then
  	If RectsOverlap( MouseX(),MouseY(),1,1, menu_item_image_x, menu_item_image_y,ImageWidth(menu_item_image),ImageHeight(menu_item_image)) Then
		If ChannelPlaying(sndchn) = 0
			sndchn = PlaySound(mysnd)
			ChannelVolume sndchn, vol#
		EndIf
	EndIf
EndIf

;if the channel IS playing, and the mouse has clicked the button

If MouseHit(1) Then
  	If RectsOverlap( MouseX(),MouseY(),1,1, menu_item_image_x, menu_item_image_y,ImageWidth(menu_item_image),ImageHeight(menu_item_image)) Then
		If ChannelPlaying(sndchn) = 1 And paused = 0
			paused = 1
		Else
			paused = 0
		EndIf
	EndIf
EndIf

If paused = 1 And ChannelPlaying(sndchn) = 1 Then PauseChannel sndchn
If paused = 0 And ChannelPlaying(sndchn) = 0 Then ResumeChannel sndchn

DebugLog "paused:"+paused

Flip True
Wend


Thanks alot! :)

Last edited 2011


Ross C(Posted 2011) [#5]
You can only call mousehit() once per loop. Your best to do something like:

Graphics3D 800, 600, 0, 2

Global mysnd

Global vol#

Global paused

Global sndchn

vol# = 1.0

mysnd = Load3DSound("C:\Users\BLAH\Desktop\oldstuff\music\01 - burning hearts ~angel~.mp3")

menu_item_image = LoadImage("menu_item.png")
menu_item_image_2 = LoadImage("menu_item2.png")

menu_item_image_x = 200
menu_item_image_y = 200
menu_item_image2_x = 200
menu_item_image2_y = 200

While Not KeyHit(1)
	Cls
	
	UpdateWorld()
	RenderWorld()
	
	Text GraphicsWidth()/2, GraphicsHeight()/2, "MX: "+MouseX(), 1, 1
	Text GraphicsWidth()/2, GraphicsHeight()/2+20, "MY: "+MouseY(), 1, 1
	
	DrawImage menu_item_image, menu_item_image_x, menu_item_image_y
	
	
	
	;mouse over

	   If RectsOverlap( MouseX(),MouseY(),1,1, menu_item_image_x, menu_item_image_y,ImageWidth(menu_item_image),ImageHeight(menu_item_image)) Then
	      DrawImage menu_item_image_2, menu_item_image2_x, menu_item_image2_y
	   End If
	
	;if the channel is not playing, and the mouse has clicked the button
	
	If MouseHit(1) Then
	  	If RectsOverlap( MouseX(),MouseY(),1,1, menu_item_image_x, menu_item_image_y,ImageWidth(menu_item_image),ImageHeight(menu_item_image)) Then
			If ChannelPlaying(sndchn) = 0
				sndchn = PlaySound(mysnd)
				ChannelVolume sndchn, vol#
			EndIf
		EndIf
	
	;if the channel IS playing, and the mouse has clicked the button
	  	If RectsOverlap( MouseX(),MouseY(),1,1, menu_item_image_x, menu_item_image_y,ImageWidth(menu_item_image),ImageHeight(menu_item_image)) Then
			If ChannelPlaying(sndchn) = 1 And paused = 0
				paused = 1
			Else
				paused = 0
			EndIf
		EndIf
	EndIf
	
	
	
	
	
	If paused = 1 And ChannelPlaying(sndchn) = 1 Then PauseChannel sndchn
	If paused = 0 And ChannelPlaying(sndchn) = 0 Then ResumeChannel sndchn
	
	DebugLog "paused:"+paused

Flip True
Wend




Since you can only call it once, call it, then put all your other checks inside it.

Also, your best to use just the plain old loadsound, rather than the 3dsound version.

Last edited 2011


Guy Fawkes(Posted 2011) [#6]
Thanks, Ross! :D

I got it working! =D

I had to add an extra variable to check if the button was being pressed :P


Guy Fawkes(Posted 2011) [#7]
Forgot to post the fix!

If you all want to use it, feel free! :)

But I am NOT providing images

Code:



This is a basic menu that when pressed and music is NOT playing, it will play the selected track given in the mytrack$ variable. You MUST set this, otherwise the program will non-silently crash!

---------------------------------------------

NOTES:

; You will need to create your own button image, as well as your own button highlighted image :)

; You MUST have the images in the SAME folder as the program.

; If you decide to change the location of where the images are stored, you must make SURE the images are in the folder that you specified :)

;BIGGEST NOTE:

;This code can be used for more than one thing. One of it's many uses is it can be used to create a media player, radio, etc.. The other thing it can be used for, is to create a clickable, highlightable menu for a game in 2D AND / OR 3D! :)

Thanks again for the help! :)

Last edited 2011


Guy Fawkes(Posted 2011) [#8]
I'm going to leave the original above, but I have updated the entire code to work with a type system and array system, so you can create as many mouse overs and mouse click buttons as you want :)



--------------------------------

NOTES

;Each image requires there to be a brighter version of the image, which means there has to be a 2nd image. SO maxmenuimages should be 2 times what you want it to be.

;So if you want one button, you must make it = 2, if you want 4 buttons, you must make it 8 :)

;For each button with a highlight that you create, you MUST update the mouse OVER code, and the mouse CLICK code. I have included an example above.

Last edited 2011


Kryzon(Posted 2011) [#9]
Good job implementing the types and arrays; I believe this accounts for 'modularity', and can only help you increase this system and add more stuff.

There are a few changes\optimizations I would suggest for a heavy-duty GUI system:
;No need for arrays, the images can be stored in globals if you want.

;Each different gadget class for your GUI system will have its own Type.

;Button class.
Type gadgetButton
	Field msg%
	Field graphic%
	Field X#,Y#
	Field graphicWidth%
	Field graphicHeight%
	Field action%
End Type

;Checkbox class.
Type gadgetCheckbox
	;[...]
	Field action%
End Type
The reasoning behind the fields for the 'gadgetButton' class:

msg - String to overlay the button's graphic.

graphic - Integer storing the handle to the gadget's image to be drawn. I think this is more efficient.
By storing this handle to the image in the type itself (instead of using the gadget's ID to retrieve the graphic every time you draw it), multiple menu items that use the same image can all store the same handle, since this is just a value telling which loaded image in memory to draw.

X & Y - Kept as floating point to provide a pseudo sub-pixel positioning. This way you can make things move smoothly etc.

graphicWidth & graphicHeight - Compute the width and height of the gadget's graphic when you create it (or change its image), so you don't need to calculate this everytime you are drawing it (instead of calling ImageWidth() or ImageHeight(), you would just read these values directly from the fields).

action - Integer used for storing an indicator to which function this gadget calls when activated (moused-over and clicked).
Since Blitz3D doesn't support function pointers (which would enable you to store a pointer to the function directly in each gadget Type), we have to resort to a Select.

So in a function to check each gadget:
Function UpdateGadgets()
	;Poll the current state of the mouse (so as not to do this everytime you process each gadget!):
	MX = MouseX()
	MY = MouseY()
	MouseKey1 = MouseDown(1)
	MouseKey2 = MouseDown(2)

	;Process each class of gadget for your GUI.

	For g.gadgetCheckBox = Each gadgetCheckBox
		;[...] Process all "Check Box" type gadgets.
		DrawImage g\graphic,g\X,g\Y
	Next

	;[...] Update other gadget classes here; say, radio-buttons, text-fields, spinners etc.

	For g.gadgetButton = Each gadgetButton
		;If the mouse is hovering a button.
		If MX > g\X And MX < (g\X + g\graphicWidth) And MY > g\Y And MY < (g\Y + g\graphicHeight) Then
			;Change to mouse-over graphic.
			g\graphic = gadgetButton_over ;global storing the handle to the hovered button graphic.
			If MouseKey1 Then
				g\graphic = gagdetButton_active ;image handle for the pressed button graphic.
				FireEvent( g\action ) ;This button was pressed, so call the function to make some action, sending the button's action ID.
			EndIf
		EndIf

		DrawImage g\graphic,g\X,g\Y ;Draw the current graphic for this button, whichever it is (you don't need to know which image it is, you just draw the handle to whatever image the IFs above chose to show).
		Text (g\X + g\graphicWidth / 2), (g\Y + g\graphicHeight / 2), g\msg, True, True ;Draw the button's text centered on the button's graphic.
	Next
End Function

See how in the button gadget loop the UpdateGadgets() function called the FireEvent() function? this FireEvent could be something like this:
Function FireEvent( action% )
	;You're free to pick whatever number you want to associate with each function, as long as you remember which one to use when storing them in each 'action' field when creating your gadgets.
	Select action
		Case 1 ;Let's say '1' is the number I chose to associate with a certain kind of button. So when I created a button that would use this function, I would store 1 in its 'action' field.
			ShowMessage( "BLA" ) ;Calls the function I want to associate with action value '1'.
		
		;[...] Include the other kind of functions here, associating each with a number that you will store in the appropriate 'action' fields for your gadgets.
	End Select
End Function

;____________GADGET FUNCTIONS____________

Function ShowMessage( msg$ )
	;[...]
End Function
 
Function DoOtherThing( otherThing% )
	;[...]
End Function

Phew.


Kryzon(Posted 2011) [#10]
Just an addendum; I thought nobody would mind... but then I started to!

The way I showed will make the buttons fire events at the time they are pressed, instead of the common 'when released' behavior.
To implement a 'when released' behavior you need to store the state of the button (un-pressed or pressed) in an additional field. It's just an extra integer field you need to add, and can call it 'state', for instance.

Changes to the update-buttons loop:

• If the mouse hovers a button: change its graphic to hovered.
• If the mouse hovers a button AND presses it: store a value of 'active' in a state field; change its graphic to pressed.
• If the mouse hovers a button AND is released, check if the button is active. If it is: call the FireEvent() function; change the button's state to 'inactive'; change the button's graphic to unpressed.

This will give a more windows-like GUI behavior. Go ahead and try some native Windows buttons to see what I'm talking about - they only fire events when they are released and the mouse is on top of them, not when pressed.


Ross C(Posted 2011) [#11]
I have a mouse system for checking for clicks, holds and releases:

http://www.blitzbasic.com/codearcs/codearcs.php?code=2499

Super handy code if I do say so myself. I always had to redo the same thing everytime I coded something, so it's a time saver, and does away with the mousehit code. Just make sure you call the processmousehits() function everyloop.

And good job Rez.

Last edited 2011


Guy Fawkes(Posted 2011) [#12]
Thanks, Ross.

What if I want the images ONLY to disappear, and text with another menu to appear? how would I do this?

I can't delete the images, because I need the ability to go back to the main menu


Guy Fawkes(Posted 2011) [#13]
EDIT: 3RD Update!

;*Fixed problem where end of song resulted in isplaying still being "1" after the song is done :)

;*Redid the way the images were loaded, so that you can use a loop for multiple images, OR load each image using an array individually :)

;*Updated to tell whether or not the song is paused and not playing, or not playing and not paused, as well as playing and paused! :)

Thanks to Ross's AWESOME mouse code! =D



Last edited 2011


Guy Fawkes(Posted 2011) [#14]
still need help with the question above the update! ^^

Thanks! :)