MissJoJo,
I took your functions and expanded them a bit (and also corrected the bugs).
Here's the new set of of expanded MissJoJo MakeButton functions, V1.00, Build 0004.
//=============================================================================
//### FILEHEADER
//-----------------------------------------------------------------------------
//
// Filename: main.agc
// Title: AGK EXAMPLE - Makebutton by MissJoJo
//
// Version: 1.00, Build 0004
// Created: 2013-05-30 by MissJoJo
// Last modified: 2013-05-30 by AgentSam
//
// Project state: Published on the AGK forums
// Release type: Public domain
//
// Compatibility: Tested with AGK 107.06, AGK 108.11, AGK 108.12
//
// Programmer's notes
// ==================
//
// o This AGK snippet shows how to make a simple button handler.
//
// o The original code snippet by MissJoJo was published here:
// http://forum.thegamecreators.com/?m=forum_view&t=205779&b=41
//
// Revision History
// ================
//
// o 2013-05-30, AgentSam
// - NEW BUILD: 1.00, Build 0002
// - NOTES: Made a few modifications and corrections to
// MissJoJo's original code snippet.
// - NOTES Created a set of original button graphics for
// this example.
// - NOTES: Grabbed a metallic background from somewhere
// via google. Hopefully within fair use.
// - NOTES: Created a small set of sounds using SoundForge.
// The original sounds were taken from FreeSounds.org
// The sounds included with this example are slightly
// modified.
//
// o 2013-05-30, AgentSam
// - NEW BUILD: 1.00, Build 0003
// - CHANGE: Commented out the "SetImageSavePixels" function,
// which is not supported on AGK 1076.
// - CHANGE: Button_Create function was accessing two
// undefined variables when settings button position.
// Removed the line. Button position is set by the
// Button_PlaceAt function, instead.
// - CHANGE: Added a couple of new comments to the source.
// - CHANGE: Made sure that the type of all function arguments
// is defined explicitly. (This goes better with my
// general coding convention, and is always required
// in TIER2 anyway.)
//
// o 2013-05-30, AgentSam
// - NEW BUILD: 1.00, Build 0004
// - NOTES: Dev release. Just plan for future improvements.
// - NOTES: Added various todo notes by importance.
// When will I have time to implement them?
// Hopefully I don't have to, if someone else
// picks up from here.
//
// To Do
// =====
//
// o 2013-05-30, AgentSam
// - HIGH: Allow making labels over the graphics, using
// text objects.
// - HIGH: Improve modularization by moving all button
// handler functions into a stand-alone include
// file that can be dropped on any project.
// - MEDIUM: Allow fading of the highlight state with
// various timing parameters.
// - MEDIUM: Allow creating buttons without premade button
// graphics.
// - MEDIUM: Add fully commented function headers to the
// source.
// - MEDIUM: Allow animated button graphics.
// - MEDIUM: More built-in button parameters for each state;
// color, transparency, fading, rotation, position
// offsets, label font, text size/position, label
// alignment, text padding, text clipping.
// - MEDIUM: Make sure code runs equally well in world-
// coordinate space (view offsets and zoom), as
// it does in native virtual resolution.
// - LOW: Tweak the "Button_State" function so that,
// when a large number of buttons has been created,
// pointer position and sprite hit does not have to
// be checked on a button-by-button basis.
// - LOW: Use GetSpriteHitGroup to check for a button hit
// in one call. (Require some redesign.)
// - LOW: See how the code copes with the percentage based
// system.
// - LOW: Convert to a TIER2 class.
// - LOW: 3D-buttons?
//
// Author contact information
// ==========================
//
// o MissJoJO
// - Email: ---
// - Notes: Original author of the MissJoJo GUI buttons snippet.
//
// o AgentSam
// - Email: ---
// - Notes: Modified the original code by MissJoJo.
//
//---------------------------------------------------------------------------
//=============================================================================
//### SECTION - DISPLAY SETUP (VIRTUAL RESOLUTION)
//-----------------------------------------------------------------------------
// Landscape orientation using pixel based coordinates @60 FPS
SetVirtualResolution (1024, 768)
SetSyncRate (60, 0)
SetResolutionMode (1)
SetSortTextures (0)
// SetImageSavePixels (0) // <-- not supported in AGK 107
// Initialize physics system properties
SetPhysicsMaxPolygonPoints (12)
// Set mipmaps and filters
SetGenerateMipmaps (0)
SetDefaultMinFilter (1)
SetDefaultMagFilter (1)
// Set text output properties
SetPrintSize (16)
SetPrintColor (255, 255, 0)
// Set backbuffer properties
SetClearColor (0, 0, 0)
SetBorderColor (32, 32, 255)
// Error reporting mode
SetErrorMode (2)
//=============================================================================
//### SECTION - CONSTANTS
//-----------------------------------------------------------------------------
// Button state constants
#constant C_BTN_INACTIVE 0
#constant C_BTN_HIGHLIGHT 1
#constant C_BTN_DOWN 2
#constant C_BTN_CLICKED 3
//=============================================================================
//### SECTION - DATA TYPES
//-----------------------------------------------------------------------------
// Button type
type TJoJoButton
btnID as integer // Button ID (self)
sprID as integer // Sprite IDs
imgInactive as integer // Image IDs
imgHighlight as integer
imgDown as integer
sndHighlight as integer // Sound IDs
sndDown as integer
sndClick as integer
iState as integer // Current button state
iLastState as integer // Last button state
endtype
// Button placer type
type TJoJoPlacer
fLastX as float
fLastY as float
fLastHeight as float
endtype
//=============================================================================
//### SECTION - DECLARE GLOBAL VARIABLES
//-----------------------------------------------------------------------------
// Image IDs
global g_imgBackground as integer
global g_imgAllBtnStates as integer
global g_imgBtnInactive as integer
global g_imgBtnHighlight as integer
global g_imgBtnDown as integer
// Sprite IDs
global g_sprBackground as integer
// Sound IDs
global g_sndHighlight as integer
global g_sndDown as integer
global g_sndClick as integer
// Buttons
global g_btnButton1 as TJoJoButton
global g_btnButton2 as TJoJoButton
global g_btnButton3 as TJoJoButton
global g_btnButton4 as TJoJoButton
global g_btnButton5 as TJoJoButton
global g_btnButton6 as TJoJoButton
// Other variables
global g_iBtnCounter as integer
global g_iLastActiveButton as integer
// Helper variable for the "Button_Place..." routines
global g_JoJoPlacer as TJoJoPlacer
//=============================================================================
//### SECTION - INITIALIZE GLOBAL VARIABLES
//-----------------------------------------------------------------------------
// Reset some internal values used by the functions
g_iBtnCounter = 0
g_iLastActiveButton = 0
//=============================================================================
//### SECTION - LOAD MEDIA RESOURCES
//-----------------------------------------------------------------------------
// Show background image
g_imgBackground = LoadImage("Background.png")
g_sprBackground = CreateSprite(g_imgBackground)
SetSpriteSize(g_sprBackground, GetVirtualWidth(), GetVirtualHeight())
// Load the atlas image containing all button states
g_imgAllBtnStates = LoadImage("BtnStates.png")
// Load subimages into these variables (just as a way of shorthand notation)
g_imgBtnInactive = LoadSubImage(g_imgAllBtnStates, "BtnInactive")
g_imgBtnHighlight = LoadSubImage(g_imgAllBtnStates, "BtnHighlight")
g_imgBtnDown = LoadSubImage(g_imgAllBtnStates, "BtnDown")
// Create buttons
g_btnButton1 = Button_Create(-1, -1, g_imgBtnInactive, g_imgBtnHighlight, g_imgBtnDown)
g_btnButton2 = Button_Create(-1, -1, g_imgBtnInactive, g_imgBtnHighlight, g_imgBtnDown)
g_btnButton3 = Button_Create(-1, -1, g_imgBtnInactive, g_imgBtnHighlight, g_imgBtnDown)
g_btnButton4 = Button_Create(-1, -1, g_imgBtnInactive, g_imgBtnHighlight, g_imgBtnDown)
g_btnButton5 = Button_Create(-1, -1, g_imgBtnInactive, g_imgBtnHighlight, g_imgBtnDown)
g_btnButton6 = Button_Create(700, -1, g_imgBtnInactive, g_imgBtnHighlight, g_imgBtnDown)
// Place buttons
Button_PlaceAt (g_btnButton1, GetVirtualWidth() / 2.0, 100)
Button_PlaceBelow(g_btnButton2, -20) // <-- I'm using a negative margin here
Button_PlaceBelow(g_btnButton3, -20) // because this set of button images
Button_PlaceBelow(g_btnButton4, -20) // contains a lot of alpha-transparency
Button_PlaceBelow(g_btnButton5, -20) // around the images.
Button_PlaceBelow(g_btnButton6, -20)
// Load button sounds
g_sndHighlight = LoadSound("BtnHighlight.wav")
g_sndDown = LoadSound("BtnDown.wav")
g_sndClick = LoadSound("BtnClick.wav")
// Setup button sounds
g_btnButton1 = Button_Sounds(g_btnButton1, g_sndHighlight, g_sndDown, g_sndClick) // <-- all sounds
g_btnButton2 = Button_Sounds(g_btnButton2, 0 , 0 , g_sndClick) // <-- click sound only
g_btnButton3 = Button_Sounds(g_btnButton3, 0 , g_sndDown, g_sndClick) // <-- no highlight sound
g_btnButton4 = Button_Sounds(g_btnButton4, g_sndHighlight, 0 , g_sndClick) // <-- no down sound
g_btnButton5 = Button_Sounds(g_btnButton5, g_sndHighlight, g_sndDown, 0 ) // <-- no click sound
g_btnButton6 = Button_Sounds(g_btnButton6, 0 , 0 , 0 ) // <-- no sounds at all!
//=============================================================================
//### SECTION - MAIN LOOP
//-----------------------------------------------------------------------------
// Our main loop
do
// Update screen
Sync()
// Show usage info
Print("")
Print("Usage:")
Print(" To highlight a button - move the pointer over it.")
Print(" To press a button down - hold the pointer down over it.")
Print(" To cancel a click - move the pointer away from the button before releasing.")
Print(" To activate the button - hold the pointer down, then release (click).")
Print("")
Print("Help")
Print(" What, we have no labels! That's right - feel free to")
Print(" create some labeled graphics yourself. Now, here's")
Print(" Some information on what the buttons are programmed to do.!")
Print("")
Print(" Click first button to set physics debug ON.")
Print(" Click second button to set physics debug OFF.")
Print(" Click the last button to QUIT!")
Print("")
Print(" Use the source, Luke!")
Print("")
// Handle buttons
g_btnButton1 = Button_State(g_btnButton1)
g_btnButton2 = Button_State(g_btnButton2)
g_btnButton3 = Button_State(g_btnButton3)
g_btnButton4 = Button_State(g_btnButton4)
g_btnButton5 = Button_State(g_btnButton5)
g_btnButton6 = Button_State(g_btnButton6)
// Show button states
Print("g_btnButton1.iState = " + str(g_btnButton1.iState))
Print("g_btnButton2.iState = " + str(g_btnButton2.iState))
Print("g_btnButton3.iState = " + str(g_btnButton3.iState))
Print("g_btnButton4.iState = " + str(g_btnButton4.iState))
Print("g_btnButton5.iState = " + str(g_btnButton5.iState))
Print("g_btnButton6.iState = " + str(g_btnButton6.iState))
// Button 1 clicked?
if (g_btnButton1.iState = C_BTN_CLICKED)
// Set physics debug ON
SetPhysicsDebugOn()
endif
// Button 2 clicked?
if (g_BtnButton2.iState = C_BTN_CLICKED)
// Set physics debug OFF
SetPhysicsDebugOff()
endif
// Implement handlers for other buttons here!
// When button 6 is clicked, exit loop (and terminate)
if (g_btnButton6.iState = C_BTN_CLICKED) then exit
loop
// Terminate application
End
//=============================================================================
//### SECTION - SUPPORT ROUTINES
//-----------------------------------------------------------------------------
//=============================================================================
//# FUNCTION - Button_Create
//-----------------------------------------------------------------------------
function Button_Create(a_iWidth as integer, a_iHeight as integer, a_imgInactive as integer, a_imgHighlight as integer, a_imgDown as integer)
// Declare local variables
v_btnNew as TJoJoButton
// Increment the button ID counter
inc g_iBtnCounter
// Create a sprite for this button
v_btnNew.sprID = CreateSprite(a_imgInactive)
// Save button ID
v_btnNew.btnID = g_iBtnCounter
// Set sprite size
SetSpriteSize(v_btnNew.sprID, a_iWidth, a_iHeight)
// Set button shape for hit detection
SetSpriteShape(v_btnNew.sprID, 3) // Set shape to polygon
// Set state images
v_btnNew.imgInactive = a_imgInactive
v_btnNew.imgHighlight = a_imgHighlight
v_btnNew.imgDown = a_imgDown
// Return new button to caller
endfunction v_btnNew
//=============================================================================
//# FUNCTION - Button_Sounds
//-----------------------------------------------------------------------------
function Button_Sounds(a_btnButton as TJoJoButton, a_sndHighlight as integer, a_sndDown as integer, a_sndClick as integer)
// Save sound IDs
a_btnButton.sndHighlight = a_sndHighlight
a_btnButton.sndDown = a_sndDown
a_btnButton.sndClick = a_sndClick
// Return modified button data to caller
endfunction a_btnButton
//=============================================================================
//# FUNCTION - Button_State
//-----------------------------------------------------------------------------
function Button_State(a_btnButton as TJoJoButton)
// If pointer is within this button, then ...
if (GetSpriteHitTest(a_btnButton.sprID, ScreenToWorldX(GetPointerX()), ScreenToWorldY(GetPointerY())) = 1)
// If pointer state is DOWN, then ....
if (GetPointerState() = 1)
// .. button must be down!
a_btnButton.iState = C_BTN_DOWN
SetSpriteImage(a_btnButton.sprID, a_btnButton.imgDown)
else
// If pointer was clicked, then ...
if (GetPointerReleased() = 1)
// .. button was clicked!
a_btnButton.iState = C_BTN_CLICKED
SetSpriteImage(a_btnButton.sprID, a_btnButton.imgHighlight)
else
// .. button must be highlighted!
a_btnButton.iState = C_BTN_HIGHLIGHT
SetSpriteImage(a_btnButton.sprID, a_btnButton.imgHighlight)
endif
endif
// If this was NOT the last "active" button, OR state has changed, then ...
if ((g_iLastActiveButton <> a_btnButton.btnID) OR (a_btnButton.iLastState <> a_btnButton.iState))
// Play sound based on new state!
select a_btnButton.iState
case C_BTN_DOWN:
if (a_btnButton.sndDown <> 0) then PlaySound(a_btnButton.sndDown)
endcase
case C_BTN_CLICKED:
if (a_btnButton.sndClick <> 0) then PlaySound(a_btnButton.sndClick)
endcase
case C_BTN_HIGHLIGHT:
if ((a_btnButton.sndHighlight <> 0) and (a_btnButton.iLastState <> C_BTN_CLICKED)) then PlaySound(a_btnButton.sndHighlight)
endcase
endselect
endif
// Save last active button
g_iLastActiveButton = a_btnButton.btnID
else
// .. button is inactive!
a_btnButton.iState = C_BTN_INACTIVE
SetSpriteImage(a_btnButton.sprID, a_btnButton.imgInactive)
endif
// Save current state
a_btnButton.iLastState = a_btnButton.iState
// Return modified button data to caller
endfunction a_btnButton
//=============================================================================
//# FUNCTION - Button_PlaceAt
//-----------------------------------------------------------------------------
function Button_PlaceAt(a_btnThis as TJoJoButton, a_fX as float, a_fY as float)
// Save params for later
g_JoJoPlacer.fLastX = a_fX
g_JoJoPlacer.fLastY = a_fY
g_JoJoPlacer.fLastHeight = GetSpriteHeight(a_btnThis.sprID)
// Set button position
SetSpritePositionByOffset(a_btnThis.sprID, a_fX, a_fY)
endfunction
//=============================================================================
//# FUNCTION - Button_PlaceBelow
//-----------------------------------------------------------------------------
function Button_PlaceBelow(a_btnThis as TJoJoButton, a_fMargin as float)
// Compute new location
g_JoJoPlacer.fLastY = g_JoJoPlacer.fLastY + (g_JoJoPlacer.fLastHeight / 2.0)
g_JoJoPlacer.fLastY = g_JoJoPlacer.fLastY + (GetSpriteHeight(a_btnThis.sprID) / 2.0) + a_fMargin
// Set button position
SetSpritePositionByOffset(a_btnThis.sprID, g_JoJoPlacer.fLastX, g_JoJoPlacer.fLastY)
// Save current button height
g_JoJoPlacer.fLastHeight = GetSpriteHeight(a_btnThis.sprID)
endfunction
The attached ZIP contains the source and media files. Screenshot to follow.
@Seed:
These are fairly simple to convert to TIER 2, so I'll leave that as an exercise for readers.
Cheers,
AgentSam