Making a Button on an Image

Meg

New Member
Joined
Mar 5, 2019
Hi Guys,
I'm struggling to make my introduction screen interactable. I have created an image and already included the buttons. Is there a way of marking the position of the buttons so that when the user hovers over that space it will show up as a button and will be able to click it?

This is my code

Code:
Import mojo
Global Game:Adventure

Function Main:Int()


    Game = New Adventure ()


End Function

'--------------------------------------------------------------------------


'Class for the GAME


Class Adventure Extends App








    Field image:Image


   


    Method OnCreate:Int()


        'This will run ONCE as soon as the instance of the class is created in here you can load all the start up images AND your INTRO screen and sound files.


           SetUpdateRate 60


   


   


   


   


    'Image- Setting dimentions so that the inage fits the screen --------


           image = LoadImage ("main.png")


          ' create an array for that image


        Local pixels:Int[image.Width * image.Height]


           ' draw the color red in the pixels array


        For Local i:Int = 0 Until image.Width * image.Height


            pixels[i] = argb(200,0,0)


        Next


           ' copy the array to the image


        image.WritePixels(pixels, 0, 0, image.Width, image.Height, 0)


   


        Return 0


   


    End Method


'--------------------------------------------------------------------------





        Method OnUpdate:Int()


'This will be called as manny times as you wish and it will handle your inputs and the movments of your sprites


   


   


            Return 0


   


        End Method


    '--------------------------------------------------------------------------


   


        Method OnRender:Int()


          'All drawing commands this is the only place that they can be called in.


           


           


      'Beckground colour - BLACK-------------------------------------------------


            Cls 0,0,0


           


           


      'Setting the image dimentions + position-----------------------------------


            DrawImage(image, 0,40)


            SetColor 255,255,255


            'DrawText "Image Width is : "+image.Width+" Image Height is : "+image.Height,0,0


           


   


           


        End Method


    '--------------------------------------------------------------------------


   


   


       Method OnLoading:Int()


           'You can also drawing commands in here however they will be called on loading insted of on render.


   


   


            Return 0


   


        End Method


    '--------------------------------------------------------------------------


   


       Method OnSuspend:Int()


           'Method you called when the app is suspended, when you go off the app but the app is still running in the background.


   


   


            Return 0


   


        End Method


     '--------------------------------------------------------------------------


   


       Method OnResume:Int()


           'Method called up when you resume playing the app


   


            Return 0


        End Method


    '--------------------------------------------------------------------------





End





'Function for the image resizing -----------------------------------------------


Function argb:Int(r:Int, g:Int, b:Int ,alpha:Int=255)


Return (alpha Shl 24) | (r Shl 16) | (g Shl 8) | b


End Function


And I have also attached the picture
 

Attachments

dawlane

Well-Known Member
CX Code Contributor
Joined
Jun 21, 2017
The first thing you need to do is break up the image buttons into separate images.
There are two ways in which you can accomplish this.
  1. Create separate images for all the buttons and their states, or combine them into a texture atlas (note a texture atlas is a very advanced way to do it).
  2. Use GrapImage to copy those parts of the image that will be the buttons and use SetColor for the different button states.
To make it easier to find out which button the mouse is over, you need to create a custom Class that represents a button and store it. Each button created should have a id code to check against.

Checking a button requires that you capture the mouse X/Y position and test to see if it's in the area where a button should be. If a mouse button is clicked while in that area, you return the id value of the button.

For highlighting a button when the mouse is over it. You set and unset a Boolean variable that represents the hover actions and then check this variable to decide how the button should be drawn in the OnRender method.

Example using your image.

Cerberus X:
Strict

Import mojo
Global Game:Adventure

Function Main:Int()
    Game = New Adventure ()
    Return 0
End Function

' Simple button class that will represent a button
Class CButton
    Global buttons:=New Stack<CButton>    ' Store any buttons to be used
    Field img:Image
    Field imgHover:Image
    Field x:Int
    Field y:Int
    Field id:Int
    Field hover:Bool

    ' Create a button by passing the image id, the x and y position, reference image x and y position, reference image width and height
    Method New(id:Int, x:Int, y:Int, imgX:Int, imgY:Int, imgW:Int, imgH:Int, img:Image)
        Self.img = img.GrabImage(imgX, imgY, imgW, imgH, 1)
        Self.x = x
        Self.y = y
        Self.id = id
    End Method

    ' Create a button by passing the image id, the x and y position and two images
    ' Just passing the one image will use colouring to for the hover action
    Method New(id:Int, x:Int, y:Int, img:Image, imgHover:Image=Null)
        Self.img = img
        Self.imgHover = imgHover
        Self.x = x
        Self.y = y
        Self.id = id
    End Method

    ' Checks each button in the list to see if the mouse is over a button
    Method Check:Int(mx:Float, my:Float)
        If Point2RectBounds(mx, my, x, y, x+img.Width(), y+img.Height())
            If MouseHit(MOUSE_LEFT) Then Return id    ' If the mouse left button is pressed then return the buton id
            hover = True
            Return -1
        Endif
        hover = False
        Return -1
    End Method

    ' Draw the button image
    Method Draw:Void()
        ' If no second image is used for the hover image, then use the main image and just change the colour.
        If imgHover=Null
            If hover Then SetColor(255,255,0) Else SetColor(255,255,255)
            DrawImage(img, x, y)
        Else
            If hover Then DrawImage(imgHover, x, y) Else DrawImage(img, x, y)
        Endif
    End Method

End Class

' Global function to create a button using an image for reference
Function CreateButton:Void(id:Int, x:Int, y:Int, imgX:Int, imgY:Int, imgW:Int, imgH:Int, img:Image)
    CButton.buttons.Push(New CButton(id, x, y, imgX, imgY, imgW, imgH, img))
End Function

' Global function to create a button using two image - the first image for a normal button and the second for when the mouse is over the button.
 ' Just passing the one image will use colouring to for the hover action
Function CreateButton:Void(id:Int, x:Int, y:Int, img:Image, imgHover:Image=Null)
    CButton.buttons.Push(New CButton(id, x, y, img, imgHover))
End Function

' Global function to check the buttions
Function CheckButtons:Int(mx:Float, my:Float)
    Local id:=-1
    For Local i:=Eachin CButton.buttons
        id = i.Check(mx, my)
        If id>=0 Then Exit
    Next
    Return id
End Function

'Global function to draw the buttons
Function DrawButtons:Void()
    For Local i:=Eachin CButton.buttons
        i.Draw()
    Next
End Function

Function ClearButtons:Void()
    CButton.buttons.Clear()
End Function

' Constants used for setting the game state
Const GSTATE_TITLE:=0
Const GSTATE_PLAYING:=1
Const GSTATE_TUTORIAL:=2
Const GSTATE_LEVELS:=3
Const GSTATE_SCOREBOARD:=4
Const GSTATE_GAMEEND:=5

'--------------------------------------------------------------------------
'Class for the GAME
Class Adventure Extends App
    Const BTN_PLAY:=0
    Const BTN_TUTORIAL:=1
    Const BTN_LEVELS:=2
    Const BTN_SCOREBOARD:=3

    Field image:Image
       Field gstate:Int

    Method OnCreate:Int()

        'This will run ONCE as soon as the instance of the class is created in here you can load all the start up images AND your INTRO screen and sound files.
        SetUpdateRate 60
           image = LoadImage ("main.png")
     
           ' Get the buttons in to separate images and store them in to a list for easy processing.
           TitleButtons()
           gstate = GSTATE_TITLE
        Return 0
    End Method

'--------------------------------------------------------------------------
    Method OnUpdate:Int()
        Local mx:= MouseX(), my:= MouseY()
        Local button:Int
   
        'This will be called as manny times as you wish and it will handle your inputs and the movments of your sprites
        ' Select the code to use depending on game state
        Select gstate
            Case GSTATE_TITLE
                ' Check the buttons and get the button id if the left mouse button was pressed
                button=CheckButtons(mx, my)
                Select button
                    Case BTN_PLAY
                        gstate = GSTATE_PLAYING
                   
                        ' Clear the button list ready for any new buttons to be used.
                        ClearButtons()
                   
                    Case BTN_TUTORIAL
                        gstate = GSTATE_TUTORIAL
                    Case BTN_LEVELS
                        gstate = GSTATE_LEVELS
                    Case BTN_SCOREBOARD
                        gstate = GSTATE_SCOREBOARD
                End Select
            Case GSTATE_PLAYING
                Print "GSTATE_PLAYING"
           
            Case GSTATE_TUTORIAL
                Print "GSTATE_TUTORIAL"
           
            Case GSTATE_LEVELS
                Print "GSTATE_LEVELS"
           
            Case GSTATE_SCOREBOARD
                Print "GSTATE_SCOREBOARD"

            Case GSTATE_GAMEEND
                gstate = GSTATE_TITLE
                Print "GSTATE_GAMEEND"
           
                ' Reset the buttons back into the button list for the title
                TitleButtons()
        End Select
   
        Return 0
    End Method
    '--------------------------------------------------------------------------
    Method OnRender:Int()
      'All drawing commands this is the only place that they can be called in.

      'Beckground colour - BLACK-------------------------------------------------
        Cls 0,0,0
      'Setting the image dimentions + position-----------------------------------
        Select gstate
            Case GSTATE_TITLE
                SetColor(255,255,255)
                DrawImage(image, 0, 40)
                DrawButtons()
            Case GSTATE_PLAYING
                Print "DRAW GSTATE_PLAYING"
           
            Case GSTATE_TUTORIAL
                Print "DRAW GSTATE_TUTORIAL"
           
            Case GSTATE_LEVELS
                Print "DRAW GSTATE_LEVELS"
           
            Case GSTATE_SCOREBOARD
                Print "DRAW GSTATE_SCOREBOARD"
        End Select
        Return 0
    End Method

    ' Set buttons for title screen
    Method TitleButtons:Void()
        ' CreateButton(button id, x position, y position, reference image x position, reference image y position, reference image width, reference image height)
        CreateButton(BTN_PLAY,442, 192+40, 442, 192, 180, 55, image)
        CreateButton(BTN_TUTORIAL, 442, 266+40, 442, 266, 180, 26, image)
        CreateButton(BTN_LEVELS, 442, 292+40, 442, 292, 182, 26, image)
        CreateButton(BTN_SCOREBOARD, 442, 320+40, 442, 320, 180, 26, image)
    End Method
End

#Rem    This is a general purpose function to check if a point is within a rectangle.
        x and y is the position of the point in 2D space
        x1 and y1 is the top left corner of the rectangle.
#End    x2 and y2 is the bottom right of the rectangle and would be the rectangles x positon + width and the rectangles y position + height
Function Point2RectBounds:Bool(x:Float, y:Float, x1:Float, y1:Float, x2:Float, y2:Float)
    If x > x1 And x < x2 And y > y1 And y < y2 Then Return True
    Return False
End Function
Have a look here. It's pretty advance code, but emulates a GUI button to a certain point.
 
Last edited:

Dubbsta

Active Member
Joined
Jul 13, 2017
im interested in dawlanes version , this is how i make my buttons would like to know the difference.

just create new button ex. start = new Button ()
then use the coll method to check if there is a collision AND if mouse clicked the do action
use a image with 2 frame pressed and unpressed

edit: dont know why i used but instead of btn haha
edit2: added set frame method

Cerberus X:
Class Button
Field btn_snd:Sound
Field x:Int
Field y:Int
Field img:Image
Field frame:Int = 0
Field alphaset:Float = 1.0

         Method  New(name:String = "",x:Int,y:Int,numframes:Int)
             btn_snd =  LoadSound("sfx/select.wav")
             img = LoadImage(name,numframes,Image.MidHandle)
             Self.x = x
             Self.y = y
         End
      
         Method draw:Void()
             SetAlpha(alphaset)
             DrawImage(img,x,y,frame)
         End
      
        Method snd:Void()
            PlaySound(btn_snd,1)
        End
        
        method setframe:void(framenum:int )
              frame  =  framenum
        end 


        Method coll:Bool(but:Button)                             
            If MouseX() < but.x - but.img.Width()/2 Or
                MouseX() > but.x + but.img.Width()/2  Return False
                  
            If MouseY() < but.y - but.img.Height()/2 Or
                MouseY() > but.y + but.img.Height()/2 Return False
                          
            Return True                     
        End
End
 
Last edited:

dawlane

Well-Known Member
CX Code Contributor
Joined
Jun 21, 2017
im interested in dawlanes version , this is how i make my buttons would like to know the difference.
The first thing is my code is one of the ways to do it. I can possibly think of another dozen other ways to do it. But as the opening poster has combined the buttons into the main image I showed a way to do it without needing additional images, but left the option to use to separate images as I don't know the opening posters programming knowledge, I tried to keep it reasonably simple and the use of combined images can get complicated for a real beginner to understand.

There are a number of differences in the code I posted, but the main difference is that the button class is self contained, ready for use for each button object created to be stored into container for ease of reading in a loop. Each button gets a unique id code so you can tell which button has been activated just by calling the one global function. There is nothing to stop you from creating an instance of CButton and using the methods directly to save on functions call, but you would have to recreate the For/Next loops in you own code.
The other differences are:
Reading mouse input just the once in the OnUpdate. You use the variables of checking, or pass them onto other functions.​
Over loaded methods and functions, so there is more that one way to created a button object.​
The point/rectangle collision if a separate stand-alone function. You never know if you are going to need to use it elsewhere. If it's only going to be used for just the one purpose, then include it where it's needed.​
Note: You could change that code to:​
Cerberus X:
Function Point2RectBounds:Bool(x:Float, y:Float, x1:Float, y1:Float, x2:Float, y2:Float)
    Return (x > x1 And x < x2 And y > y1 And y < y2)
End Function
I would be using a more advanced version of this where all button images are combined into a single texture atlas and use an array of stored rectangle coordinates for each button in the source image. It's just a case then using the correct array to use DrawImageRect with the correct offset. The advanced version uses a IntMap to store the buttons. Doing it this way opens it up to get a button just by it's id code so you can do fancy stuff with it when the extra code is added, like flashing a button to press in a game tutorial.
 
Last edited:

Meg

New Member
Joined
Mar 5, 2019
The first thing you need to do is break up the image buttons into separate images.
There are two ways in which you can accomplish this.
  1. Create separate images for all the buttons and their states, or combine them into a texture atlas (note a texture atlas is a very advanced way to do it).
  2. Use GrapImage to copy those parts of the image that will be the buttons and use SetColor for the different button states.
To make it easier to find out which button the mouse is over, you need to create a custom Class that represents a button and store it. Each button created should have a id code to check against.

Checking a button requires that you capture the mouse X/Y position and test to see if it's in the area where a button should be. If a mouse button is clicked while in that area, you return the id value of the button.

For highlighting a button when the mouse is over it. You set and unset a Boolean variable that represents the hover actions and then check this variable to decide how the button should be drawn in the OnRender method.

Example using your image.

Cerberus X:
Strict

Import mojo
Global Game:Adventure

Function Main:Int()
    Game = New Adventure ()
    Return 0
End Function

' Simple button class that will represent a button
Class CButton
    Global buttons:=New Stack<CButton>    ' Store any buttons to be used
    Field img:Image
    Field imgHover:Image
    Field x:Int
    Field y:Int
    Field id:Int
    Field hover:Bool

    ' Create a button by passing the image id, the x and y position, reference image x and y position, reference image width and height
    Method New(id:Int, x:Int, y:Int, imgX:Int, imgY:Int, imgW:Int, imgH:Int, img:Image)
        Self.img = img.GrabImage(imgX, imgY, imgW, imgH, 1)
        Self.x = x
        Self.y = y
        Self.id = id
    End Method

    ' Create a button by passing the image id, the x and y position and two images
    ' Just passing the one image will use colouring to for the hover action
    Method New(id:Int, x:Int, y:Int, img:Image, imgHover:Image=Null)
        Self.img = img
        Self.imgHover = imgHover
        Self.x = x
        Self.y = y
        Self.id = id
    End Method

    ' Checks each button in the list to see if the mouse is over a button
    Method Check:Int(mx:Float, my:Float)
        If Point2RectBounds(mx, my, x, y, x+img.Width(), y+img.Height())
            If MouseHit(MOUSE_LEFT) Then Return id    ' If the mouse left button is pressed then return the buton id
            hover = True
            Return -1
        Endif
        hover = False
        Return -1
    End Method

    ' Draw the button image
    Method Draw:Void()
        ' If no second image is used for the hover image, then use the main image and just change the colour.
        If imgHover=Null
            If hover Then SetColor(255,255,0) Else SetColor(255,255,255)
            DrawImage(img, x, y)
        Else
            If hover Then DrawImage(imgHover, x, y) Else DrawImage(img, x, y)
        Endif
    End Method

End Class

' Global function to create a button using an image for reference
Function CreateButton:Void(id:Int, x:Int, y:Int, imgX:Int, imgY:Int, imgW:Int, imgH:Int, img:Image)
    CButton.buttons.Push(New CButton(id, x, y, imgX, imgY, imgW, imgH, img))
End Function

' Global function to create a button using two image - the first image for a normal button and the second for when the mouse is over the button.
' Just passing the one image will use colouring to for the hover action
Function CreateButton:Void(id:Int, x:Int, y:Int, img:Image, imgHover:Image=Null)
    CButton.buttons.Push(New CButton(id, x, y, img, imgHover))
End Function

' Global function to check the buttions
Function CheckButtons:Int(mx:Float, my:Float)
    Local id:=-1
    For Local i:=Eachin CButton.buttons
        id = i.Check(mx, my)
        If id>=0 Then Exit
    Next
    Return id
End Function

'Global function to draw the buttons
Function DrawButtons:Void()
    For Local i:=Eachin CButton.buttons
        i.Draw()
    Next
End Function

Function ClearButtons:Void()
    CButton.buttons.Clear()
End Function

' Constants used for setting the game state
Const GSTATE_TITLE:=0
Const GSTATE_PLAYING:=1
Const GSTATE_TUTORIAL:=2
Const GSTATE_LEVELS:=3
Const GSTATE_SCOREBOARD:=4
Const GSTATE_GAMEEND:=5

'--------------------------------------------------------------------------
'Class for the GAME
Class Adventure Extends App
    Const BTN_PLAY:=0
    Const BTN_TUTORIAL:=1
    Const BTN_LEVELS:=2
    Const BTN_SCOREBOARD:=3

    Field image:Image
       Field gstate:Int

    Method OnCreate:Int()

        'This will run ONCE as soon as the instance of the class is created in here you can load all the start up images AND your INTRO screen and sound files.
        SetUpdateRate 60
           image = LoadImage ("main.png")
    
           ' Get the buttons in to separate images and store them in to a list for easy processing.
           TitleButtons()
           gstate = GSTATE_TITLE
        Return 0
    End Method

'--------------------------------------------------------------------------
    Method OnUpdate:Int()
        Local mx:= MouseX(), my:= MouseY()
        Local button:Int
  
        'This will be called as manny times as you wish and it will handle your inputs and the movments of your sprites
        ' Select the code to use depending on game state
        Select gstate
            Case GSTATE_TITLE
                ' Check the buttons and get the button id if the left mouse button was pressed
                button=CheckButtons(mx, my)
                Select button
                    Case BTN_PLAY
                        gstate = GSTATE_PLAYING
                  
                        ' Clear the button list ready for any new buttons to be used.
                        ClearButtons()
                  
                    Case BTN_TUTORIAL
                        gstate = GSTATE_TUTORIAL
                    Case BTN_LEVELS
                        gstate = GSTATE_LEVELS
                    Case BTN_SCOREBOARD
                        gstate = GSTATE_SCOREBOARD
                End Select
            Case GSTATE_PLAYING
                Print "GSTATE_PLAYING"
          
            Case GSTATE_TUTORIAL
                Print "GSTATE_TUTORIAL"
          
            Case GSTATE_LEVELS
                Print "GSTATE_LEVELS"
          
            Case GSTATE_SCOREBOARD
                Print "GSTATE_SCOREBOARD"

            Case GSTATE_GAMEEND
                gstate = GSTATE_TITLE
                Print "GSTATE_GAMEEND"
          
                ' Reset the buttons back into the button list for the title
                TitleButtons()
        End Select
  
        Return 0
    End Method
    '--------------------------------------------------------------------------
    Method OnRender:Int()
      'All drawing commands this is the only place that they can be called in.

      'Beckground colour - BLACK-------------------------------------------------
        Cls 0,0,0
      'Setting the image dimentions + position-----------------------------------
        Select gstate
            Case GSTATE_TITLE
                SetColor(255,255,255)
                DrawImage(image, 0, 40)
                DrawButtons()
            Case GSTATE_PLAYING
                Print "DRAW GSTATE_PLAYING"
          
            Case GSTATE_TUTORIAL
                Print "DRAW GSTATE_TUTORIAL"
          
            Case GSTATE_LEVELS
                Print "DRAW GSTATE_LEVELS"
          
            Case GSTATE_SCOREBOARD
                Print "DRAW GSTATE_SCOREBOARD"
        End Select
        Return 0
    End Method

    ' Set buttons for title screen
    Method TitleButtons:Void()
        ' CreateButton(button id, x position, y position, reference image x position, reference image y position, reference image width, reference image height)
        CreateButton(BTN_PLAY,442, 192+40, 442, 192, 180, 55, image)
        CreateButton(BTN_TUTORIAL, 442, 266+40, 442, 266, 180, 26, image)
        CreateButton(BTN_LEVELS, 442, 292+40, 442, 292, 182, 26, image)
        CreateButton(BTN_SCOREBOARD, 442, 320+40, 442, 320, 180, 26, image)
    End Method
End

#Rem    This is a general purpose function to check if a point is within a rectangle.
        x and y is the position of the point in 2D space
        x1 and y1 is the top left corner of the rectangle.
#End    x2 and y2 is the bottom right of the rectangle and would be the rectangles x positon + width and the rectangles y position + height
Function Point2RectBounds:Bool(x:Float, y:Float, x1:Float, y1:Float, x2:Float, y2:Float)
    If x > x1 And x < x2 And y > y1 And y < y2 Then Return True
    Return False
End Function
Have a look here. It's pretty advance code, but emulates a GUI button to a certain point.

Thank you!!! I also looked at the post that you have suggested, it was really helpful! You know Cerberus X really well by the looks of things;), I wish I knew it that well!
 

Dubbsta

Active Member
Joined
Jul 13, 2017
Cool will def look into it later, wanna learn every trick possible. My code comes from noob experience but works for me, but i want to learn the more advanced stuff myself
 

dawlane

Well-Known Member
CX Code Contributor
Joined
Jun 21, 2017
I should point out that the reason why you should learn to start to use texture atlases has to do with how graphics cards work. See Render Hell for the potential problems.

In my advanced button version. I've used each bit in a integer data type as a Boolean value by setting or clearing with bit-wise operators. Doing it this way gives you 32 Boolean variable in one that I have used for storing the input state of a button. By using this method you can read multiple device controls, set the corresponding bit for that controllers input action and then in the code where you check for movement, you can just trap (bit-wise AND(&)) for that action to perform. You can also use this method for other things such a event triggers etc.
 

Dubbsta

Active Member
Joined
Jul 13, 2017
Not familiar with bit wise operations but will look into it thx dawlane. Btw glad your problem solved meg.
 
  • Like
Reactions: Meg

Meg

New Member
Joined
Mar 5, 2019
The first thing you need to do is break up the image buttons into separate images.
There are two ways in which you can accomplish this.
  1. Create separate images for all the buttons and their states, or combine them into a texture atlas (note a texture atlas is a very advanced way to do it).
  2. Use GrapImage to copy those parts of the image that will be the buttons and use SetColor for the different button states.
To make it easier to find out which button the mouse is over, you need to create a custom Class that represents a button and store it. Each button created should have a id code to check against.

Checking a button requires that you capture the mouse X/Y position and test to see if it's in the area where a button should be. If a mouse button is clicked while in that area, you return the id value of the button.

For highlighting a button when the mouse is over it. You set and unset a Boolean variable that represents the hover actions and then check this variable to decide how the button should be drawn in the OnRender method.

Example using your image.

Cerberus X:
Strict

Import mojo
Global Game:Adventure

Function Main:Int()
    Game = New Adventure ()
    Return 0
End Function

' Simple button class that will represent a button
Class CButton
    Global buttons:=New Stack<CButton>    ' Store any buttons to be used
    Field img:Image
    Field imgHover:Image
    Field x:Int
    Field y:Int
    Field id:Int
    Field hover:Bool

    ' Create a button by passing the image id, the x and y position, reference image x and y position, reference image width and height
    Method New(id:Int, x:Int, y:Int, imgX:Int, imgY:Int, imgW:Int, imgH:Int, img:Image)
        Self.img = img.GrabImage(imgX, imgY, imgW, imgH, 1)
        Self.x = x
        Self.y = y
        Self.id = id
    End Method

    ' Create a button by passing the image id, the x and y position and two images
    ' Just passing the one image will use colouring to for the hover action
    Method New(id:Int, x:Int, y:Int, img:Image, imgHover:Image=Null)
        Self.img = img
        Self.imgHover = imgHover
        Self.x = x
        Self.y = y
        Self.id = id
    End Method

    ' Checks each button in the list to see if the mouse is over a button
    Method Check:Int(mx:Float, my:Float)
        If Point2RectBounds(mx, my, x, y, x+img.Width(), y+img.Height())
            If MouseHit(MOUSE_LEFT) Then Return id    ' If the mouse left button is pressed then return the buton id
            hover = True
            Return -1
        Endif
        hover = False
        Return -1
    End Method

    ' Draw the button image
    Method Draw:Void()
        ' If no second image is used for the hover image, then use the main image and just change the colour.
        If imgHover=Null
            If hover Then SetColor(255,255,0) Else SetColor(255,255,255)
            DrawImage(img, x, y)
        Else
            If hover Then DrawImage(imgHover, x, y) Else DrawImage(img, x, y)
        Endif
    End Method

End Class

' Global function to create a button using an image for reference
Function CreateButton:Void(id:Int, x:Int, y:Int, imgX:Int, imgY:Int, imgW:Int, imgH:Int, img:Image)
    CButton.buttons.Push(New CButton(id, x, y, imgX, imgY, imgW, imgH, img))
End Function

' Global function to create a button using two image - the first image for a normal button and the second for when the mouse is over the button.
' Just passing the one image will use colouring to for the hover action
Function CreateButton:Void(id:Int, x:Int, y:Int, img:Image, imgHover:Image=Null)
    CButton.buttons.Push(New CButton(id, x, y, img, imgHover))
End Function

' Global function to check the buttions
Function CheckButtons:Int(mx:Float, my:Float)
    Local id:=-1
    For Local i:=Eachin CButton.buttons
        id = i.Check(mx, my)
        If id>=0 Then Exit
    Next
    Return id
End Function

'Global function to draw the buttons
Function DrawButtons:Void()
    For Local i:=Eachin CButton.buttons
        i.Draw()
    Next
End Function

Function ClearButtons:Void()
    CButton.buttons.Clear()
End Function

' Constants used for setting the game state
Const GSTATE_TITLE:=0
Const GSTATE_PLAYING:=1
Const GSTATE_TUTORIAL:=2
Const GSTATE_LEVELS:=3
Const GSTATE_SCOREBOARD:=4
Const GSTATE_GAMEEND:=5

'--------------------------------------------------------------------------
'Class for the GAME
Class Adventure Extends App
    Const BTN_PLAY:=0
    Const BTN_TUTORIAL:=1
    Const BTN_LEVELS:=2
    Const BTN_SCOREBOARD:=3

    Field image:Image
       Field gstate:Int

    Method OnCreate:Int()

        'This will run ONCE as soon as the instance of the class is created in here you can load all the start up images AND your INTRO screen and sound files.
        SetUpdateRate 60
           image = LoadImage ("main.png")
    
           ' Get the buttons in to separate images and store them in to a list for easy processing.
           TitleButtons()
           gstate = GSTATE_TITLE
        Return 0
    End Method

'--------------------------------------------------------------------------
    Method OnUpdate:Int()
        Local mx:= MouseX(), my:= MouseY()
        Local button:Int
  
        'This will be called as manny times as you wish and it will handle your inputs and the movments of your sprites
        ' Select the code to use depending on game state
        Select gstate
            Case GSTATE_TITLE
                ' Check the buttons and get the button id if the left mouse button was pressed
                button=CheckButtons(mx, my)
                Select button
                    Case BTN_PLAY
                        gstate = GSTATE_PLAYING
                  
                        ' Clear the button list ready for any new buttons to be used.
                        ClearButtons()
                  
                    Case BTN_TUTORIAL
                        gstate = GSTATE_TUTORIAL
                    Case BTN_LEVELS
                        gstate = GSTATE_LEVELS
                    Case BTN_SCOREBOARD
                        gstate = GSTATE_SCOREBOARD
                End Select
            Case GSTATE_PLAYING
                Print "GSTATE_PLAYING"
          
            Case GSTATE_TUTORIAL
                Print "GSTATE_TUTORIAL"
          
            Case GSTATE_LEVELS
                Print "GSTATE_LEVELS"
          
            Case GSTATE_SCOREBOARD
                Print "GSTATE_SCOREBOARD"

            Case GSTATE_GAMEEND
                gstate = GSTATE_TITLE
                Print "GSTATE_GAMEEND"
          
                ' Reset the buttons back into the button list for the title
                TitleButtons()
        End Select
  
        Return 0
    End Method
    '--------------------------------------------------------------------------
    Method OnRender:Int()
      'All drawing commands this is the only place that they can be called in.

      'Beckground colour - BLACK-------------------------------------------------
        Cls 0,0,0
      'Setting the image dimentions + position-----------------------------------
        Select gstate
            Case GSTATE_TITLE
                SetColor(255,255,255)
                DrawImage(image, 0, 40)
                DrawButtons()
            Case GSTATE_PLAYING
                Print "DRAW GSTATE_PLAYING"
          
            Case GSTATE_TUTORIAL
                Print "DRAW GSTATE_TUTORIAL"
          
            Case GSTATE_LEVELS
                Print "DRAW GSTATE_LEVELS"
          
            Case GSTATE_SCOREBOARD
                Print "DRAW GSTATE_SCOREBOARD"
        End Select
        Return 0
    End Method

    ' Set buttons for title screen
    Method TitleButtons:Void()
        ' CreateButton(button id, x position, y position, reference image x position, reference image y position, reference image width, reference image height)
        CreateButton(BTN_PLAY,442, 192+40, 442, 192, 180, 55, image)
        CreateButton(BTN_TUTORIAL, 442, 266+40, 442, 266, 180, 26, image)
        CreateButton(BTN_LEVELS, 442, 292+40, 442, 292, 182, 26, image)
        CreateButton(BTN_SCOREBOARD, 442, 320+40, 442, 320, 180, 26, image)
    End Method
End

#Rem    This is a general purpose function to check if a point is within a rectangle.
        x and y is the position of the point in 2D space
        x1 and y1 is the top left corner of the rectangle.
#End    x2 and y2 is the bottom right of the rectangle and would be the rectangles x positon + width and the rectangles y position + height
Function Point2RectBounds:Bool(x:Float, y:Float, x1:Float, y1:Float, x2:Float, y2:Float)
    If x > x1 And x < x2 And y > y1 And y < y2 Then Return True
    Return False
End Function
Have a look here. It's pretty advance code, but emulates a GUI button to a certain point.
I have a quick question you have used "Select" multiple times, what else would you use it for apart from creating the buttons? And what does it actually do ?
 

Dubbsta

Active Member
Joined
Jul 13, 2017
Ive never seen it used like that cool. Looks like its nested like nested if statements so the second select will run if the first select condition is true . dawlane can give a better answer but that should be the case
 

dawlane

Well-Known Member
CX Code Contributor
Joined
Jun 21, 2017
I have a quick question you have used "Select" multiple times, what else would you use it for apart from creating the buttons? And what does it actually do ?
From the documentation:
The Select statement
The Select statement allows you to execute a block of statements depending on a series of comparisons.

The first comparison to produce a match will cause the associated block of statements to be executed.

If no comparisons produce a match, then the final Default block will be executed if present.

The syntax for the Select statement is:

Select Expression
Case Expression [ , Expression... ]
Statements...
Default
Statements...
End [ Select ]

There may be any number of Case blocks, or none. The final Default block is optional. If the Default block is present, it must appear after all Case blocks.
The example I've shown, shows two uses for the select statement:
  1. It's used for selecting the game state. Meaning is the game showing the title screen, is it in the playing mode etc.
  2. It selects the action to perform based of the result of what's been stored in the 'button variable' from the function that reads the buttons.
In the example the first Select Select gstate will determine the Case code block to run for a 'game state'. So if the variable gstate was set to the value defined in one of the constant variables, it would execute that Case block which matches.

As the first game state is GSTATE_TITLE, it will run that Case code block, until an action defined by the result of the variable 'button' in the second select (Select button) that is within the GSTATE_TITLE Case, changes the variable gstate to one of the other 'game states'.

The one thing that Select is not, is another 'IF' statement. You cannot do something like this:
Cerberus X:
Function Main:Int()
    Local value:Int=70
    Select value
        Case value<20
            Print value+"<20"
        Case value>50
            Print value+">50"
        Default
            Print "Cannot work it out"
            Print value+"<20:is "+Int(value<20)
            Print value+">50:is "+Int(value>50)
    End Select
    Return 0
End Function
This is because the expressions val<20 and val>50 will return a Boolean value (equates to either 0 or 1), which will not match the number in value, which is 70.

But you can do something like this:
Cerberus X:
Strict

Const BIT_0:=$01
Const BIT_1:=$02
Const BIT_2:=$04
Const BIT_3:=$08
Const BIT_4:=$10
Const BIT_5:=$20
Const BIT_6:=$40
Const BIT_7:=$80

Function Main:Int()
    ' Change the last byte of the flag variable.
    ' 0-255 in hexadecimal.
    Local flag:=$FFFFFF01
    
    Select flag & $0000FF    ' Mask out the three most significate bytes and trap the least significate byte
        
        ' If all bits are set
        Case BIT_0|BIT_1|BIT_2|BIT_3|BIT_4|BIT_5|BIT_6|BIT_7
            Print "All bits set"
        Case BIT_0
            Print "Bit 0 set"
        Case BIT_1
            Print "Bit 1 set"
        Case BIT_2
            Print "Bit 2 set"
        Case BIT_3
            Print "Bit 3 set"
        Case BIT_4
            Print "Bit 4 set"
        Case BIT_5
            Print "Bit 5 set"
        Case BIT_6
            Print "Bit 6 set"
        Case BIT_7
            Print "Bit 7 set"
        Default
            Print "At least two or more bits have been set"
    End Select
    
    Return 0
End Function
 

Meg

New Member
Joined
Mar 5, 2019
From the documentation:
The example I've shown, shows two uses for the select statement:
  1. It's used for selecting the game state. Meaning is the game showing the title screen, is it in the playing mode etc.
  2. It selects the action to perform based of the result of what's been stored in the 'button variable' from the function that reads the buttons.
In the example the first Select Select gstate will determine the Case code block to run for a 'game state'. So if the variable gstate was set to the value defined in one of the constant variables, it would execute that Case block which matches.

As the first game state is GSTATE_TITLE, it will run that Case code block, until an action defined by the result of the variable 'button' in the second select (Select button) that is within the GSTATE_TITLE Case, changes the variable gstate to one of the other 'game states'.

The one thing that Select is not, is another 'IF' statement. You cannot do something like this:
Cerberus X:
Function Main:Int()
    Local value:Int=70
    Select value
        Case value<20
            Print value+"<20"
        Case value>50
            Print value+">50"
        Default
            Print "Cannot work it out"
            Print value+"<20:is "+Int(value<20)
            Print value+">50:is "+Int(value>50)
    End Select
    Return 0
End Function
This is because the expressions val<20 and val>50 will return a Boolean value (equates to either 0 or 1), which will not match the number in value, which is 70.

But you can do something like this:
Cerberus X:
Strict

Const BIT_0:=$01
Const BIT_1:=$02
Const BIT_2:=$04
Const BIT_3:=$08
Const BIT_4:=$10
Const BIT_5:=$20
Const BIT_6:=$40
Const BIT_7:=$80

Function Main:Int()
    ' Change the last byte of the flag variable.
    ' 0-255 in hexadecimal.
    Local flag:=$FFFFFF01
   
    Select flag & $0000FF    ' Mask out the three most significate bytes and trap the least significate byte
       
        ' If all bits are set
        Case BIT_0|BIT_1|BIT_2|BIT_3|BIT_4|BIT_5|BIT_6|BIT_7
            Print "All bits set"
        Case BIT_0
            Print "Bit 0 set"
        Case BIT_1
            Print "Bit 1 set"
        Case BIT_2
            Print "Bit 2 set"
        Case BIT_3
            Print "Bit 3 set"
        Case BIT_4
            Print "Bit 4 set"
        Case BIT_5
            Print "Bit 5 set"
        Case BIT_6
            Print "Bit 6 set"
        Case BIT_7
            Print "Bit 7 set"
        Default
            Print "At least two or more bits have been set"
    End Select
   
    Return 0
End Function
Thank you so much! This is really helpful :)
 
Top Bottom