2D UI System - Pre-Rendered Gadget Images
Putting all your drawn gadgets into images will speed up your program immensly, even with Cloggys D3D plugin that increasing 2D drawing insanly (mainly text, text in DBP is really demanding), it's still quicker to use pre-rendered images. The simplest way to create a pre-render is the Get Image Command:
Get Image <Image Num>, <left>,<top>,<right>,<bottom>,<texture flag>
Left/Top/Right/Bottom are screen coordinates.
The value of the texture flag to use (0-use stretching, filter, colorkey, or 1-no stretching, no filter, colorkey, or 2-use stretching, no filter, no colorkey, or 3-no stretching, no filter, no colorkey.
The Get Image command is slow so I suggest only using it in the initialization stage of your games. Get Image doesn't retrieve alpha, my rule about alpha with this command is: if you require an alpha channel then create this image in a drawing program and save it as a PNG or DDS. If you only need rgb(0,0,0) for alpha and your game is small then you can use this command, otherwise don't use it.
You don't need to create a bitmap for this command to work, just insure the screen is clear CLS (rgb). But if you don't want to disturb the screen I suggest creating a bitmap. From the help files:
CREATE BITMAP Bitmap Number,Width,Height
CREATE BITMAP Bitmap Number,Width,Height,SystemMemoryMode
The size of the bitmap is only limited by the amount of system memory available. When you create a bitmap, it becomes the current bitmap. All drawing operations will be re-directed to the current bitmap and away from the screen. You can use the SET CURRENT BITMAP command to restore drawing operations to the screen. The parameters should be specified using integer values.
Note: I believe deleting the bitmap restores drawing operations aswell.
Here's an example:
Without Bitmap:
cls
ink rgb(255,0,0),0
box 0,0,128,64
ink rgb(0,0,255),0
center text 64,24,"Hello World"
get image 1,0,0,128,64,1
cls
do
paste image 1,0,0
loop
With Bitmap:
create bitmap 1,screen width(),screen height()
ink rgb(255,0,0),0
box 0,0,128,64
ink rgb(0,0,255),0
center text 64,24,"Hello World"
get image 1,0,0,128,64,1
delete bitmap 1
do
paste image 1,0,0
loop
Pre-Rendered Button
Take are example of are base button so far seen above, we'd need to render each state for are button and store them, then draw them to the screen. There are 2 methods we can use, Images and Sprites (there's also 3d Plains, but I won't go into that yet). So:
Images Drawing
This involves drawing images directly to the screen:
type Point2d
x as integer
y as integer
endtype
type Rect
x as integer
y as integer
w as integer
h as integer
endtype
global _rect as Rect
#constant UI_BUTTON_STATE_UP 0
#constant UI_BUTTON_STATE_OVER 1
#constant UI_BUTTON_STATE_DOWN 2
Type uiButtonGadget
bounds as Rect
alpha as float
caption as string
state as dword
img0 as integer // up
img1 as integer // over
img2 as integer // down
endtype
button as uiButtonGadget
button.bounds.x = 128
button.bounds.y = 128
button.bounds.w = 128
button.bounds.h = 36
button.alpha = 100
button.state = UI_BUTTON_STATE_UP
button.caption = "Hello World"
button.img0 = 1 // replace this with a free image function
button.img1 = 2
button.img2 = 3
_rect = button.bounds
_rect.x = 0
_rect.y = 0
// pre-render are buttons
create bitmap 1,_rect.w,_rect.h
// up image
ink rgb(98, 204, 255)
box2D( _rect, 0, 0, 0, 0 )
ink rgb(0, 89, 132)
box2D( _rect, 3,3, -3, -3 )
ink rgb(255, 255, 255)
center text (_rect.w*.5),((_rect.h-text size())*.5)-1,button.caption
get image button.img0,0,0,_rect.w,_rect.h,1
cls
// over image
ink rgb(148, 255, 40)
box2D( _rect, 0, 0, 0, 0 )
ink rgb(49, 98, 0)
box2D( _rect, 3,3, -3, -3 )
ink rgb(255, 255, 255)
center text (_rect.w*.5),((_rect.h-text size())*.5)-1,button.caption
get image button.img1,0,0,_rect.w,_rect.h,1
cls
ink rgb(255, 102, 179)
box2D( _rect, 0, 0, 0, 0 )
ink rgb(157, 0, 79)
box2D( _rect, 3,3, -3, -3 )
ink rgb(255, 255, 255)
center text (_rect.w*.5),((_rect.h-text size())*.5)-1,button.caption
get image button.img2,0,0,_rect.w,_rect.h,1
delete bitmap 1
global _mousePos as Point2d
global _mouseClick as integer
global _mouseOldClick as integer
sync on : sync rate 60
do : cls
// update mouse variables
_mouseOldClick = _mouseclick
_mouseclick = mouseclick()
_mousePos.x = mousex()
_mousePos.y = mousey()
// check to see if are bounds check is valid
valid = inBounds2D( button.bounds, _mousePos )
ink 0,0
if valid
select button.state
case UI_BUTTON_STATE_UP
// <do stuff>
button.state = UI_BUTTON_STATE_OVER
endcase
case UI_BUTTON_STATE_OVER
if _mouseClick > 0 and _mouseOldClick <> _mouseClick
button.state = UI_BUTTON_STATE_DOWN
endif
endcase
case UI_BUTTON_STATE_DOWN
if _mouseClick = 0 and _mouseOldClick <> _mouseclick
// so FIRE EVENT
button.state = UI_BUTTON_STATE_UP
endif
endcase
endselect
else
button.state = UI_BUTTON_STATE_UP
endif
// draw button
select button.state
case UI_BUTTON_STATE_UP
paste image button.img0,button.bounds.x,button.bounds.y
endcase
case UI_BUTTON_STATE_OVER
paste image button.img1,button.bounds.x,button.bounds.y
endcase
case UI_BUTTON_STATE_DOWN
paste image button.img2,button.bounds.x,button.bounds.y
endcase
endselect
sync
loop
// 2d in bounds check
function inBounds2D( r as Rect, p as Point2d )
if p.x > r.x and p.y > r.y
if p.x < (r.x+r.w) and p.y < (r.y+r.h)
exitfunction 1
endif
endif
endfunction 0
// wrapper for box to use the RECT Class plus offsets
function box2D( r as Rect, ox, oy, ow, oh )
box r.x+ox, r.y+oy, (r.x+r.w)+ow, (r.y+r.h)+oh
endfunction
Sprite Drawing
The advantages of Sprites is there fast, control over alpha/colour and we only update them when we need to. For this I'm adding an last state variable to are button so we don't update the sprite every frame:
type Point2d
x as integer
y as integer
endtype
type Rect
x as integer
y as integer
w as integer
h as integer
endtype
global _rect as Rect
#constant UI_BUTTON_STATE_UP 0
#constant UI_BUTTON_STATE_OVER 1
#constant UI_BUTTON_STATE_DOWN 2
Type uiButtonGadget
bounds as Rect
alpha as float
caption as string
lastState as dword
state as dword
sprite0 as integer
img0 as integer // up
img1 as integer // over
img2 as integer // down
endtype
button as uiButtonGadget
button.bounds.x = 128
button.bounds.y = 128
button.bounds.w = 128
button.bounds.h = 36
button.alpha = 100
button.state = UI_BUTTON_STATE_UP
button.caption = "Hello World"
button.sprite0 = 1 // replace this with a free xxx functions
button.img0 = 1
button.img1 = 2
button.img2 = 3
_rect = button.bounds
_rect.x = 0
_rect.y = 0
create bitmap 1,_rect.w,_rect.h
// up image
ink rgb(98, 204, 255)
box2D( _rect, 0, 0, 0, 0 )
ink rgb(0, 89, 132)
box2D( _rect, 3,3, -3, -3 )
ink rgb(255, 255, 255)
center text (_rect.w*.5),((_rect.h-text size())*.5)-1,button.caption
get image button.img0,0,0,_rect.w,_rect.h,1
cls
// over image
ink rgb(148, 255, 40)
box2D( _rect, 0, 0, 0, 0 )
ink rgb(49, 98, 0)
box2D( _rect, 3,3, -3, -3 )
ink rgb(255, 255, 255)
center text (_rect.w*.5),((_rect.h-text size())*.5)-1,button.caption
get image button.img1,0,0,_rect.w,_rect.h,1
cls
ink rgb(255, 102, 179)
box2D( _rect, 0, 0, 0, 0 )
ink rgb(157, 0, 79)
box2D( _rect, 3,3, -3, -3 )
ink rgb(255, 255, 255)
center text (_rect.w*.5),((_rect.h-text size())*.5)-1,button.caption
get image button.img2,0,0,_rect.w,_rect.h,1
delete bitmap 1
sprite button.sprite0,button.bounds.x,button.bounds.y,button.img0
global _mousePos as Point2d
global _mouseClick as integer
global _mouseOldClick as integer
sync on : sync rate 60
do : cls
// update mouse variables
_mouseOldClick = _mouseclick
_mouseclick = mouseclick()
_mousePos.x = mousex()
_mousePos.y = mousey()
// check to see if are bounds check is valid
valid = inBounds2D( button.bounds, _mousePos )
button.lastState = button.state
ink 0,0
if valid
select button.state
case UI_BUTTON_STATE_UP
// <do stuff>
button.state = UI_BUTTON_STATE_OVER
endcase
case UI_BUTTON_STATE_OVER
if _mouseClick > 0 and _mouseOldClick <> _mouseClick
button.state = UI_BUTTON_STATE_DOWN
endif
endcase
case UI_BUTTON_STATE_DOWN
if _mouseClick = 0 and _mouseOldClick <> _mouseclick
// so FIRE EVENT
button.state = UI_BUTTON_STATE_UP
endif
endcase
endselect
else
button.state = UI_BUTTON_STATE_UP
endif
if button.state <> button.lastState
// render button
select button.state
case UI_BUTTON_STATE_UP
set sprite image button.sprite0,button.img0
endcase
case UI_BUTTON_STATE_OVER
set sprite image button.sprite0,button.img1
endcase
case UI_BUTTON_STATE_DOWN
set sprite image button.sprite0,button.img2
endcase
endselect
endif
sync
loop
// 2d in bounds check
function inBounds2D( r as Rect, p as Point2d )
if p.x > r.x and p.y > r.y
if p.x < (r.x+r.w) and p.y < (r.y+r.h)
exitfunction 1
endif
endif
endfunction 0
// wrapper for box to use the RECT Class plus offsets
function box2D( r as Rect, ox, oy, ow, oh )
box r.x+ox, r.y+oy, (r.x+r.w)+ow, (r.y+r.h)+oh
endfunction
A dream is a fantasy, if you achieve that fantasy it was never a dream to begin with.