Recent posts

#1
Tutorials / Re: Learning Basic For Total B...
Last post by Midimaster - Yesterday at 13:39:31
Radio Group Buttons

I expanded the last lesson to demonstrate RADIO BUTTONS. Please have again a look on the previous lesson:
https://www.syntaxbomb.com/index.php?msg=347060951



The Button Neighbour Problem


If more than one button can be pressed at the same time, you need different images for the case, when two neighbours are pressed:

RadioButtonProblem.gif


This need 2 more images and a complex search algorithm:

    Method FindBest:Int()
        If Selected=0 Return 0
        If Search(ID-1)
            If Search(ID-1).Selected=True
                If Search(ID+1).Selected=True
                    Return 4
                EndIf
                Return 2           
            EndIf
        EndIf
        If Search(ID+1)
            If Search(ID+1).Selected=True
                Return 1
            EndIf
        EndIf
        Return 3
    End Method
   
    Method Search:TButton(SearchId:Int)
        For Local loc:TButton=EachIn All
            If loc.ID=SearchID
                Return loc
            EndIf
        Next
        Return Null
    End Method


Now it looks better:

RadioButtonSolved.gif


This is the whole code:

SuperStrict
Graphics 500,200
For Local i:Int= 0 To 5
    New TButton(i+1,30+i*66,30,66,122, 0)
Next

SetClsColor 0,155,155

Global MouseState:Int
Repeat
    Cls
    MouseState = MouseHit(1)
    TButton.DrawAll
    Local Which:TButton = TButton.CheckAll()
    If Which
        If Which.Selected=1
            Print "Button " + Which.ID + " selected"      
        Else
            Print "Button " + Which.ID + " un-selected"
        EndIf
    EndIf
    Flip
Until AppTerminate()


Type TButton
    Global All:TList = New TList
  
    Field ID:Int, X:Int, Y:Int, W:Int, H:Int
    Field Selected:Int, RadioGroup:Int
    Global Images:TImage
  
    Method New (id:Int, x:Int, y:Int, w:Int, h:Int, group:Int)
        If Images=Null
            Images=LoadAnimImage("VintageButtonBetter.png",66,122,0,5)
        EndIf
        Self.ID = id
        Self.X  = x
        Self.Y  = y
        Self.W  = w
        Self.H  = h
        Self.RadioGroup = group
        All.Addlast Self
    End Method
  
    Function DrawAll()
        For Local loc:TButton = EachIn All
            loc.Draw
        Next
        SetColor 255,255,255
    End Function
  
    Method Draw()
        SetColor 255,255,255
        Local ImageNr:Int = FindBest()
        DrawImage Images,X,Y,ImageNr
     End Method
  
    Method FindBest:Int()
        If Selected=0 Return 0
        If Search(ID-1)
            If Search(ID-1).Selected=True
                If Search(ID+1).Selected=True
                    Return 4
                EndIf
                Return 2           
            EndIf
        EndIf
        If Search(ID+1)
            If Search(ID+1).Selected=True
                Return 1
            EndIf
        EndIf
        Return 3
    End Method
   
    Method Search:TButton(SearchId:Int)
        For Local loc:TButton=EachIn All
            If loc.ID=SearchID
                Return loc
            EndIf
        Next
        Return Null
    End Method
   
    Function CheckAll:TButton ()
        If MouseState=0 Return Null
        For Local loc:TButton = EachIn All
            Local result:Int = loc.Check()
            If result>0 Return loc
        Next
        Return Null  
    End Function
  
    Method Check:Int()
        Local mX:Int = MouseX() - X
        Local mY:Int = MouseY() - Y
        If mX<0 Or mX>W Return 0
        If mY<0 Or mY>H Return 0
        Selected=1-Selected
        If Selected=1
            Deselect RadioGroup
        EndIf
        Return ID
    End Method

    Method DeSelect(Group:Int)
        If RadioGroup=0 Return
        For Local loc:TButton=EachIn All
            If loc=Self Continue
            If loc.RadioGroup=Group
                loc.Selected=0
            EndIf
        Next
    End Method
End Type


And you need this better PNG to run the app:

VintageButtonBetter.png


#2
@Derron Thank you for your bug report. I immediately changed the source code and added an advice for pointing to this problematic.
#3
BlitzMax / BlitzMax NG / Re: Critics and Advises to Bli...
Last post by Derron - Yesterday at 09:56:04
Feedback for the button tutorial part: https://www.syntaxbomb.com/index.php?msg=347060950


There is a _big_ issue with your button code.
"MouseHit()" does reset the hit state! It returns the "count of hits" since the last call to "MouseHit()".
This means as soon as "button 1" checked "MouseHit(1)", all other buttons will receive "hit = 0" when they do their checks.

In your specific example/case this is not an issue.  But think of widgets with "overlap" (eg moveable windows etc). For (even simple) GUI systems you need to tackle multiple things:
- "eating events" (mouse input event -> SetHandled(true) in case of having taken care of the mouse input)
- order of widgets (z-index so to say), who gets chance first to handle an event (a widget might have a "hole in it" in which you can "click through", so it might get the event first, but skip handling it))

Regarding mouse input I have my own "MouseHandler" which gets updated and persists mouse click states until told to "Reset" (which is then done by the GUI system).

I understand that the tutorial is for now "for total beginners" but the fact that "MouseHit()" (and other similar commands) return a number which resets afterwards, is very important. As people might use it in "clicked on an item in the inventory" and wonder why clicks only randomly work.
It should not be "item checks for user input" but a "got user input, check what to do with it".

Something like this (crude approach)
Local mouseGotHit:Int = MouseHit(1)
Local mouseHitHandled:Int = False
If mouseGotHit and not mouseHitHandled then CheckButtons(mousex(), mousey(), mouseHitHandled (as var) )
If mouseGotHit and not mouseHitHandled then CheckOtherStuff_MouseHitStillNotHandled(mouseHitHandled)

The idea is to store the information "mouse was clicked" and ask all elements if they care for it. If they cared, they should tell the system (eg setting a variable or by resetting the "hit state" (here mouseGotHit).

This is also why your objects never should react to specific "input" but receive "events" (or a "signal" or so). Because then you can replace the "signal emitter" to your likes (the click can come from a network client, from a finger touch, ...). As said, this is not for "beginners" but still something you need to explain (that in this case MouseHit() is not a "state" persisted until the next tick).



bye
Ron
#4
Unity / Re: Minimalist platformer
Last post by playniax - Yesterday at 02:06:41

QuoteThats a cool little game
Tnx, this little game gave me a new "big" idea...
#5
Unity / Re: Just getting ....
Last post by playniax - Yesterday at 02:04:54

QuoteI must confess that Unity is really starting to grow on me
Took me a while too.

Nice stuff.
#6
AppGameKit ( AGK ) / Re: My devious plan.
Last post by Pfaber11 - April 16, 2024, 21:08:23
That little scene of yours is excellent. I'm taking a look at Lovr as it is free however what you are using does look good. I figure I can keep using AGK for as long as I can as I am really enjoying my programming right now. I am way faster at AGK studio now and yes some things might  be held up for an hour or two when trying to solve a problem but that is nothing compared to when I first started, sometimes I would be stuck on something for days but there we are. VR is gonna be great in the near future imagine a 3D operating system etc. Probably already out there. 
Anyway it is time for a cup of tea. 

#7
General Discussion / Re: Mark Sibly is returning to...
Last post by Amon - April 16, 2024, 17:24:30
This project will fail if it goes down the Patreon route. My word.

I'm going to leave a comment to suggest instead a one time payment for 12 months access to updates. 

Mark even stated it himself when he said Patreon was only an earner for him because of one guy.

A yearly subscription would bring in a healthy wage. Patreon will have him on edge because of fluctuations in payments from month to month. Nothing kills a project harder than when the developer starts to feel that it's just not worth it.
#8
Tutorials / Re: Learning Basic For Total B...
Last post by Midimaster - April 16, 2024, 13:32:44
Button II: More Design and Functionality

After we switched now to TYPE buttons it is easy to add more features to the buttons. Because we have to code it only once and all buttons will react immediately.


Example: Selected Buttons

To enable the buttons to SELECTED/UNSELECTED we only have to add a new Property SELECTED and write reactions in DRAW() and CHECK(). Additionally we now return back not longer the pressed state as an INTEGER, but the whole button itself (WHICH). So we can now handle this "last action" button also outside the TYPE.


Advice:
User Derron points me to the problematic, that a MouseHit() call should not be inside a function, but at a central place. That is necessary, because other part of the app may also need to know the MouseState. So I define a GLOBAL variable MOUSESTATE, which keeps the state until the next FLIP


SuperStrict
Graphics 800,300
New TButton(1,100,200,120,80)
New TButton(2,234, 45,222,33)
New TButton(3,600, 100,50,180)
SetClsColor 0,155,155

Global MouseState:Int
Repeat
    Cls
    MouseState = MouseHit(1)
    TButton.DrawAll
    Local Which:TButton = TButton.CheckAll()
    If Which
        If Which.Selected=1
            Print "Button " + Which.ID + " selected"       
        Else
            Print "Button " + Which.ID + " un-selected"
        EndIf
    EndIf
    Flip
Until AppTerminate()


Type TButton
    Global All:TList = New TList
   
    Field ID:Int, X:Int, Y:Int, W:Int, H:Int
    Field Selected:Int
   
    Method New (id:Int, x:Int, y:Int, w:Int, h:Int)
        Self.ID = id
        Self.X  = x
        Self.Y  = y
        Self.W  = w
        Self.H  = h
        All.Addlast Self
    End Method 
   
    Function DrawAll()
        For Local loc:TButton = EachIn All
            loc.Draw
        Next
    End Function
   
    Method Draw()
        SetColor 1,1,1
        DrawRect X, Y, W, H
        SetColor 111,111,111
        DrawRect X+1, Y+1, W-2, H-2
        SetColor 155,155,155
        If Selected=1
            SetColor 155,255,155       
        EndIf
        DrawRect X+4, Y+4, W-7, H-7
        SetColor 1,1,1
        DrawText id, x + (w-TextWidth(id))/2,y + (h-TextHeight("T"))/2+2
    End Method
   
   
    Function CheckAll:TButton ()
        If MouseState=0 Return Null
        For Local loc:TButton = EachIn All
            Local result:Int = loc.Check()
            If result>0 Return loc
        Next
        Return Null   
    End Function
   
    Method Check:Int()
        Local mX:Int = MouseX() - X
        Local mY:Int = MouseY() - Y
        If mX<0 Or mX>W Return 0
        If mY<0 Or mY>H Return 0
        Selected=1-Selected
       
        Return ID
    End Method
End Type



Example Radio Group Buttons

If you need to de-select the other buttons when pressing a new button we talk about RADIO-BUTTONS. Here is a solution, that cares about this.

RadioButton.gif
(the GIF animation does not show the real shading quality. Check the app!)

Every time, when a Check() is successful, it calls the method DeSelect() to reset the other buttons. Therefore, we need a variable RADIO-GROUP to know which buttons belong to the same group.

    Method Check:Int()
        Local mX:Int = MouseX() - X
        Local mY:Int = MouseY() - Y
        If mX<0 Or mX>W Return 0
        If mY<0 Or mY>H Return 0
        Selected=1-Selected
        If Selected=1
Deselect RadioGroup
EndIf
        Return ID
    End Method

Method DeSelect(Group:Int)
If Group=0 Return
For Local loc:TButton=EachIn All
If loc=Self Continue
If loc.RadioGroup=Group
loc.Selected=0
EndIf
Next
End Method

We scan all buttons and select each, that belong to the group. To prevent switching off the newly pressed button we have to exclude SELF.


Here is the complete code:
SuperStrict
Graphics 800,400
For Local i:Int= 0 To 5
New TButton(i+1,100+i*66,200,66,122, 1)
Next

SetClsColor 0,155,155

Global MouseState:Int
Repeat
    Cls
    MouseState = MouseHit(1)
    TButton.DrawAll
    Local Which:TButton = TButton.CheckAll()
    If Which
        If Which.Selected=1
            Print "Button " + Which.ID + " selected"       
        Else
            Print "Button " + Which.ID + " un-selected"
        EndIf
    EndIf
    Flip
Until AppTerminate()


Type TButton
    Global All:TList = New TList
   
    Field ID:Int, X:Int, Y:Int, W:Int, H:Int
    Field Selected:Int, RadioGroup:Int
Global Images:TImage
   
    Method New (id:Int, x:Int, y:Int, w:Int, h:Int, group:Int)
If Images=Null
Images=LoadAnimImage("VintageButton.png",66,122,0,4)
EndIf
        Self.ID = id
        Self.X  = x
        Self.Y  = y
        Self.W  = w
        Self.H  = h
Self.RadioGroup = group
        All.Addlast Self
    End Method
   
    Function DrawAll()
        For Local loc:TButton = EachIn All
            loc.Draw
        Next
SetColor 255,255,255
DrawImage  Images,0,0,0
    End Function
   
    Method Draw()
        SetColor 255,255,255
Local ImageNr:Int = FindBest()
        DrawImage Images,X,Y,ImageNr
     End Method
   
Method FindBest:Int()
If Selected=0 Return 0
Return 3
End Method

    Function CheckAll:TButton ()
        If MouseState=0 Return Null
        For Local loc:TButton = EachIn All
            Local result:Int = loc.Check()
            If result>0 Return loc
        Next
        Return Null   
    End Function
   
    Method Check:Int()
        Local mX:Int = MouseX() - X
        Local mY:Int = MouseY() - Y
        If mX<0 Or mX>W Return 0
        If mY<0 Or mY>H Return 0
        Selected=1-Selected
        If Selected=1
Deselect RadioGroup
EndIf
        Return ID
    End Method

Method DeSelect(Group:Int)
If RadioGroup=0 Return
For Local loc:TButton=EachIn All
If loc=Self Continue
If loc.RadioGroup=Group
loc.Selected=0
EndIf
Next
End Method
End Type



You need this image to run the example:
VintageButton.png
 


Advise:
Critics to this tutorial are welcome, but please do not post here, but there:

https://www.syntaxbomb.com/blitzmax-blitzmax-ng/critics-and-advises-to-blitzmax-tutorial/
#9
Tutorials / Buttons in Games for Total Beg...
Last post by Midimaster - April 16, 2024, 13:07:53
Button I: Simple Buttons in Games for Total Beginners

This Tutorial show how to handle some self made buttons in your game. In the first example you will see a very easy to understand solution. The we will expand this to a TYPE-solution which can handle several buttons with one code.

Advice:
User Derron points me to the problematic, that a MouseHit() call should not be inside a function, but at a central place. That is necessary, because other part of the app may also need to know the MouseState. So I define a GLOBAL variable MOUSESTATE, which keeps the state until the next FLIP

One Button

A button is a rectangle, that interact with the mouse. The image can be a PNG-file or a code painted area.
The Checkbutton() function excludes all cases, where nothing happened and returns 1 if the Mouse was inside the rectangle:

SuperStrict
Graphics 800,300
Global MouseState:Int

Repeat
    Cls
    MouseState = MouseHit(1)
    DrawRect 100,200,120,80
     If CheckButton()>0 Then
        Print "Button pressed"
    EndIf
    Flip
Until AppTerminate()

Function CheckButton:Int()
    If MouseState=0 Return 0
    Local mX:Int = MouseX()-100
    Local mY:Int = MouseY()-200
    If mX<0 Or mX>120 Return 0
    If mY<0 Or mY>80  Return 0
    Return 1
End Function

Before checking we substract the start coordinate (100/200) of the rectangle to simplify the code.


Five Buttons In A Row

Nearly the same code. We only have to divide the relative MouseX by the width of the Rectangle. This returns float values between 0.00 and 5.00. When we take the INTEGER of them we get the values 0,1,2,3,4. To receive 1,2,3,4,5 we add +1.

SuperStrict
Graphics 800,300
Global MouseState:Int

Repeat
    Cls
    MouseState = MouseHit(1)
    For Local I:Int=0 Until 5
        DrawRect 100+i*120+1, 200,118,80
    Next
    Local Pressed:Int = CheckButton()
    If Pressed>0 Then
        Print "Button " + Pressed + " pressed"
    EndIf
    Flip
Until AppTerminate()

Function CheckButton:Int()
    If MouseState=0 Return 0
    Local mX:Int = MouseX() - 100
    Local mY:Int = MouseY() - 200
    If mX<0 Or mX>5*120 Return 0
    If mY<0 Or mY>80 Return 0
    Return Int(mX/120) + 1
End Function

The same would be possible with vertical buttons. Here we calculate from MouseY and the height(80).


A Simple Button Type

To be more flexible and to handle a dozend individual buttons we define a button TYPE. This enables us to give away the control of painting, checking, etc to an automated process. Via a personal ID each button will report back, if it was pressed:

SuperStrict
Graphics 800,300
Global MyButton:TButton= New TButton(1,100,200,120,80)
Global MouseState:Int

Repeat
    Cls
    MouseState = MouseHit(1)
    MyButton.Draw
    Local Pressed:Int = MyButton.Check()
    If Pressed>0 Then
        Print "Button " + Pressed + " pressed"
    EndIf
    Flip
Until AppTerminate()

Type TButton
    Field ID:Int, X:Int, Y:Int, W:Int, H:Int
   
    Method New (id:Int, x:Int, y:Int, w:Int, h:Int)
        Self.ID = id
        Self.X  = x
        Self.Y  = y
        Self.W  = w
        Self.H  = h
    End Method 
   
    Method Draw()
        DrawRect X, Y, W, H
    End Method
   
    Method Check:Int()
        If MouseState=0 Return 0
        Local mX:Int = MouseX() - X
        Local mY:Int = MouseY() - Y
        If mX<0 Or mX>W Return 0
        If mY<0 Or mY>H Return 0
        Return ID
    End Method
End Type

A Bundle of Type Buttons

With adding a Global TList we are able to self contain all buttons inside the TYPE. Now the function DrawAll() care about the painting of all buttons and the function CheckAll:Int() checks the mouse for all buttons:

SuperStrict
Graphics 800,300
New TButton(1,100,200,120,80)
New TButton(2,234, 45,222,33)
New TButton(3,600, 100,50,180)
Global MouseState:Int

Repeat
    Cls
    MouseState = MouseHit(1)
    TButton.DrawAll
    Local Pressed:Int = TButton.CheckAll()
    If Pressed>0 Then
        Print "Button " + Pressed + " pressed"
    EndIf
    Flip
Until AppTerminate()


Type TButton
    Global All:TList = New TList
   
    Field ID:Int, X:Int, Y:Int, W:Int, H:Int
   
    Method New (id:Int, x:Int, y:Int, w:Int, h:Int)
        Self.ID = id
        Self.X  = x
        Self.Y  = y
        Self.W  = w
        Self.H  = h
        All.Addlast Self
    End Method 
   
    Function DrawAll()
        For Local loc:TButton = EachIn All
            loc.Draw
        Next
    End Function
   
    Method Draw()
        DrawRect X, Y, W, H
        DrawText id, x,y-30
    End Method
   
   
    Function CheckAll:Int ()
        If MouseState=0 Return 0
        For Local loc:TButton = EachIn All
            Local result:Int = loc.Check()
            If result>0 Return result
        Next
        Return 0   
    End Function
   
    Method Check:Int()
        Local mX:Int = MouseX() - X
        Local mY:Int = MouseY() - Y
        If mX<0 Or mX>W Return 0
        If mY<0 Or mY>H Return 0
        Return ID
    End Method
End Type




#10
Blitz2D, BlitzPlus, Blitz3D / Re: Blitz3D has been updated
Last post by Derron - April 15, 2024, 12:50:13
you might prefer to download 1.112 instead (aka another update to fix some regression)


bye
Ron