Thanks to @SpriggsySpriggs for providing the webcam code.
This makes it possible to control games by waving your hands like with Microsoft Kinect!
Enjoy
EDIT: added some better instructions
EDIT 2: Forgot to add the _FREEIMAGE lines, but now am away from PC... DOH!
EDIT 3: Fixed the _FREEIMAGE and tweaked the input detection, see that update in Reply #8 below
Click OK when you see the Video Format and Video Source popup forms...
This makes it possible to control games by waving your hands like with Microsoft Kinect!
Enjoy
EDIT: added some better instructions
EDIT 2: Forgot to add the _FREEIMAGE lines, but now am away from PC... DOH!
EDIT 3: Fixed the _FREEIMAGE and tweaked the input detection, see that update in Reply #8 below
Code: (Select All)
' Webcam Pong by Madscijr (alpha version)
' Thanks to SpriggsySpriggs for figuring out how to program the webcam
' USB Camera? Reply #26 > Help Me! > Code and Stuff > QB64 Rising > QB64 Phoenix Edition
' https://qb64phoenix.com/forum/showthread.php?tid=1163&pid=32337#pid32337
' From: SpriggsySpriggs, Professional Noticer
' Date: 2/26/2025 2:48 PM
' Now you can export straight to memory, mi amigo.
' It should be noted that it has to export to clipboard first and then load to memory.
' So your clipboard will contain the image as well. But that's the nature of the beast.
' USB Camera? Reply #28 > Help Me! > Code and Stuff > QB64 Rising > QB64 Phoenix Edition
' https://qb64phoenix.com/forum/showthread.php?tid=1163&pid=32339#pid32339
' From: SpriggsySpriggs, Professional Noticer
' Date: 2/26/2025 4:01 PM
' Q: I wonder if there's a way to change camera resolution at the API or software level?
' A: YES! If you want to change the settings first, use this code:
Option _Explicit
_Title "Webcam API Test - Parent"
' ================================================================================================================================================================
' CONSTANTS
' ================================================================================================================================================================
Const FPS = 30 ' Not recommended to set this higher than frame rate of webcam/video device
Const MOVEMENT_THRESHOLD = 160
Const CAMERA_WIDTH = 320
Const CAMERA_HEIGHT = 240
Const WS_VISIBLE = &H10000000 ' The window is initially visible.
Const WS_MAXIMIZE = &H1000000 ' The window is initially maximized.
Const WS_MINIMIZE = &H20000000 ' The window is initially minimized.
Const CF_DIB = &H0008
' Capture Driver Constants
Const WM_CAP_START = &H0400
Const WM_CAP_DRIVER_CONNECT = WM_CAP_START + 10
Const WM_CAP_DRIVER_DISCONNECT = WM_CAP_START + 11
Const WM_CAP_EDIT_COPY = WM_CAP_START + 30
Const WM_CAP_SET_SCALE = WM_CAP_START + 53
Const WM_CAP_SET_PREVIEWRATE = WM_CAP_START + 52
Const WM_CAP_SET_PREVIEW = WM_CAP_START + 50
Const WM_CAP_DLG_VIDEOSOURCE = WM_CAP_START + 42
Const WM_CAP_GRAB_FRAME_NOSTOP = WM_CAP_START + 61
Const WM_CAP_FILE_SAVEDIB = WM_CAP_START + 25
Const WM_CAP_GET_VIDEOFORMAT = WM_CAP_START + 44
Const WM_CAP_SET_VIDEOFORMAT = WM_CAP_START + 45
Const WM_CAP_DLG_VIDEOFORMAT = WM_CAP_START + 41
Const LONG_MIN = -2147483648
Const LONG_MAX = 2147483647
' ================================================================================================================================================================
' UDTs
' ================================================================================================================================================================
Type BITMAPINFOHEADER
As _Unsigned Long biSize
As Long biWidth, biHeight
As _Unsigned Integer biPlanes, biBitCount
As _Unsigned Long biCompression, biSizeImage
As Long biXPelsPerMeter, biYPelsPerMeter
As _Unsigned Long biClrUsed, biClrImportant
End Type
Type BITMAPINFO
As BITMAPINFOHEADER bmiHeader
End Type
' HOLDS DISPLAY LAYERS (ALL NEED _FREEIMAGE WHEN PROGRAM ENDS)
Type GraphicsType
imgBackground As Long ' background image
imgCamera As Long
imgWalls As Long
imgStaticText As Long '
imgDynamicText As Long
imgSprites As Long
End Type ' GraphicsType
' HOLDS VALUES FOR OPERATING WEBCAM
Type WebcamType
captureWinText As String
childID As _Offset
vidWidth As _Unsigned Long
vidHeight As _Unsigned Long
childWin As _Offset
m As _MEM
CaptureWindowStyle As _Unsigned Long
End Type ' WebcamType
' HOLDS PLAYER COORDINATES AND INFO
Type PlayerType
color As _Unsigned Long
x As Long
y As Long
width As Integer
height As Integer
score As Integer
Visible As Integer
End Type ' PlayerType
' HOLDS BALL COORDINATES AND INFO
Type BallType
color As _Unsigned Long
size As Integer
x As Long
y As Long
dx As Integer
dy As Integer
Visible As Integer
End Type ' BallType
' HOLDS RGB VALUES FOR A PIXEL
Type PixelType
r As Long
g As Long
b As Long
End Type ' PixelType
' ================================================================================================================================================================
' API DEFINITIONS
' ================================================================================================================================================================
Declare CustomType Library
Sub SendMessage Alias "SendMessageA" (ByVal hWnd As _Offset, ByVal Msg As _Unsigned Long, ByVal wParam As _Offset, ByVal lParam As _Offset)
Function SendMessage& Alias "SendMessageA" (ByVal hWnd As _Offset, ByVal Msg As _Unsigned Long, ByVal wParam As _Offset, ByVal lParam As _Offset)
Sub DestroyWindow (ByVal hWnd As _Offset)
Function GetAsyncKeyState% (ByVal vKey As Long)
Function mmioStringToFOURCC& (sz As String, ByVal uFlags As _Unsigned Long)
Function OpenClipboard& (ByVal hWndNewOwner As _Offset)
Function GetClipboardData%& (ByVal uFormat As _Unsigned Long)
Sub CloseClipboard ()
Function GlobalLock%& (ByVal hMem As _Offset)
Function GlobalUnlock& (ByVal hMem As _Offset)
End Declare
Declare Dynamic Library "Avicap32"
Function CreateCaptureWindow& Alias "capCreateCaptureWindowA" (ByVal lpszWindowName As _Offset, ByVal dwStyle As _Unsigned Long, ByVal x As Long, ByVal y As Long, ByVal nWidth As Long, ByVal nHeight As Long, ByVal hwndParent As _Offset, ByVal nId As Long)
End Declare
' ================================================================================================================================================================
' GLOBAL VARIABLES
' ================================================================================================================================================================
' BASIC PROGRAM METADATA
Dim Shared m_ProgramPath$: m_ProgramPath$ = Left$(Command$(0), _InStrRev(Command$(0), "\"))
Dim Shared m_ProgramName$: m_ProgramName$ = Mid$(Command$(0), _InStrRev(Command$(0), "\") + 1)
' GLOBAL VARIABLES TO TRACK ERROR STATE
Dim Shared m_ErrorMessage As String: m_ErrorMessage = ""
'Dim Shared m_RoutineName As String: m_RoutineName = ""
'Dim Shared m_LastStatement As String: m_LastStatement = ""
' GLOBAL VARIABLES FOR SCREEN
Dim Shared m_iScreenWidth As Long: m_iScreenWidth = 1024 ' _DesktopWidth
Dim Shared m_iScreenHeight As Long: m_iScreenHeight = 768 ' _DesktopHeight
Dim Shared m_iTextRows As Long: m_iTextRows = (m_iScreenHeight / _FontHeight)
Dim Shared m_iTextCols As Long: m_iTextCols = (m_iScreenWidth / _FontWidth)
Dim Shared PLAYER_HEIGHT As Long: PLAYER_HEIGHT = 32
Dim Shared MIN_Y As Long: MIN_Y = 44 + 1
Dim Shared MAX_Y As Long: MAX_Y = (m_iScreenHeight - 54) - (PLAYER_HEIGHT + 1)
' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
' EXECUTION STARTS HERE!
' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
' START THE MAIN ROUTINE
main
' FINISH
Print m_ProgramName$ + " finished."
'End
System ' return control to the operating system
' ================================================================================================================================================================
' ROUTINES
' ================================================================================================================================================================
' /////////////////////////////////////////////////////////////////////////////
' MAIN PROGRAM
Sub main
Dim Graph1 As GraphicsType
Dim WebCam As WebcamType
ReDim arrPlayer(1 To 2) As PlayerType ' 1 = left, 2 = right
ReDim arrComparePixels(CAMERA_WIDTH, CAMERA_HEIGHT) As PixelType
Dim Ball1 As BallType
Dim x, y As Integer
Dim iValue As Integer
Dim IsCalibrating As Integer
Dim ShowCamera As Integer
' INIT SCREEN
Screen _NewImage(m_iScreenWidth, m_iScreenHeight, 32)
_ScreenMove 0, 0
' Show initial detailed instructions
Cls
Print "Webcam Pong, a 'computer vision' experiment, v0.01"
Print "by madscijr, webcam code by spriggsyspriggs"
Print
Print "At 'Video Format' popup, values should say:"
Print " Resolution : 320x240"
Print " Pixel Depth (bits) and Compression: YUY2"
Print " Size (bytes) : 153600"
Print "Do not change these values, just click [OK]"
Print
Print "At 'Video Source' popup, values should say:"
Print " Select a Video Device: {your camera name}"
Print "Click [OK]"
Print
Print "1 window should open:"
Print " Webcam API Test - Parent"
Print
Print "And this window hidden (maybe visible in Task Manager):"
Print " Webcam API Test - Child"
Print
Print "Point webcam at an empty room or down at a blank sheet of paper,"
Print "away from you and your friends, and anything that's moving."
Print "Press Enter to 'calibrate', where program memorizes the room."
Print "Enter also toggles the camera picture on and off,"
Print "with the camera on, you can see yourself moving around."
Print "Try moving something up and down along the left half of the screen,"
Print "that controls the left paddle. Right half of screen = right paddle."
Print
Print "Per SpriggsySpriggs:"
Print "DO NOT GO CLICKING ALL WILLY-NILLY ON THE PROGRAM WINDOW,"
Print "ESPECIALLY IF YOUR CURSOR HAS THE LOADING CIRCLE/HOURGLASS."
Print "For some reason, this will cause it to freeze and crash."
Print "Why? 'cause Microsoft, that's why."
Print
Print "Press any key to start..."
Sleep
_KeyClear: '_DELAY 1 ' clear keyboard buffer
' CLEAR THE SCREEN
_Dest 0: Cls , cEmpty
_Display ' update screen with changes & wait for next update
' INITIALIZE SCREEN
InitGraphicLayers Graph1
DrawInstructionsLayer Graph1
' SET LOWER CAMERA RESOLUTION
WebCam.vidWidth = CAMERA_WIDTH
WebCam.vidHeight = CAMERA_HEIGHT
' CREATE CAPTURE WINDOW (HIDDEN)
WebCam.captureWinText = "Webcam API Test - Child" + Chr$(0)
WebCam.CaptureWindowStyle = WS_MINIMIZE ' WS_VISIBLE
WebCam.childWin = CreateCaptureWindow(_Offset(WebCam.captureWinText), WebCam.CaptureWindowStyle, 0, 0, WebCam.vidWidth, WebCam.vidHeight, _WindowHandle, WebCam.childID)
' Initialize the driver
SetupDriver WebCam.childWin, WebCam.vidWidth, WebCam.vidHeight, _FALSE ' change _FALSE to _TRUE to use default settings
' INIT PLAYERS
arrPlayer(1).color = cRed
arrPlayer(1).x = 64
arrPlayer(1).y = m_iScreenHeight / 2
arrPlayer(1).width = 8
arrPlayer(1).height = 32
arrPlayer(1).score = 0
arrPlayer(1).Visible = _FALSE
arrPlayer(2).color = cBlue
arrPlayer(2).x = m_iScreenWidth - 64
arrPlayer(2).y = m_iScreenHeight / 2
arrPlayer(2).width = 8
arrPlayer(2).height = 32
arrPlayer(2).score = 0
arrPlayer(2).Visible = _FALSE
' INIT BALL
Ball1.color = cWhite
Ball1.size = 8
Ball1.x = m_iScreenWidth / 2
Ball1.y = m_iScreenHeight / 2
Ball1.Visible = _FALSE
' RANDOM BALL DIRECTION
Randomize Timer
If RandomNumber%(1, 2) = 1 Then
Ball1.dx = -1
Else
Ball1.dx = 1
End If
iValue = RandomNumber%(1, 3)
If iValue = 1 Then
Ball1.dy = -1
Else
If iValue = 2 Then
Ball1.dy = 0
Else
Ball1.dy = 1
End If
End If
' Capture a single frame to window WebCam.childWin
IsCalibrating = _TRUE
ShowCamera = _TRUE
Do
' ----------------------------------------------------------------------------------------------------------------------------------------------------------------
' READ KEYBOARD
' GetAsyncKeyState is necessary because the capture window steals keyboard focus
' ENTER = RECALIBRATE + SHOW CAMERA FRAME
' PRESSING ENTER AGAIN HIDES CAMERA FRAME
If GetAsyncKeyState(&H0D) <> 0 Then ' user pressed Enter
If ShowCamera = _FALSE Then
ShowCamera = _TRUE
IsCalibrating = _TRUE
Else
ShowCamera = _FALSE
End If
End If
' ESC = QUIT
If GetAsyncKeyState(&H1B) <> 0 Then ' user pressed Escape
KillDriver WebCam.childWin
Exit Do
End If
' ----------------------------------------------------------------------------------------------------------------------------------------------------------------
' CAPTURE FRAME WITH WEBCAM
' Get still picture from camera to frame
GrabFrame WebCam.childWin
' Copy frame from WebCam.childWin to image in memory
GrabFrameToMemory WebCam.childWin, WebCam.m
' Remember background (to compare to detect movement)
If IsCalibrating = _TRUE Then
SaveFrame WebCam.m.IMAGE, arrComparePixels()
IsCalibrating = _FALSE
End If
' Detect movement with the camera (computer vision)
DetectMovement WebCam.m.IMAGE, arrComparePixels(), arrPlayer()
' Display the image
DrawCameraLayer Graph1, WebCam.m.IMAGE, ShowCamera
'Screen WebCam.m.IMAGE
' Draw walls layer
DrawWallsLayer Graph1
' Draw sprites layer
DrawSpritesLayer Graph1, arrPlayer(), Ball1
' Draw
DrawDynamicTextLayer Graph1, arrPlayer(), ShowCamera
' COPY LAYERS TO SCREEN AND UPDATE DISPLAY
RenderScreen Graph1
' Keep at steady frames per second
_Limit FPS
Loop
' RETURN TO AUTODISPLAY
_AutoDisplay
Cls
' Disconnect driver
Print "Disconnecting driver"
KillDriver WebCam.childWin
' Free memory
Print "Freeing memory"
_MemFree WebCam.m
'' RESUME NORMAL ERROR TRAPPING
'On Error GoTo 0
End Sub ' main
' /////////////////////////////////////////////////////////////////////////////
' COMPARE PIXELS IN CAMERA FRAME AGAINST WHAT IT SAW AT BEGINNING
Sub DetectMovement (imgCamera As Long, arrComparePixels() As PixelType, arrPlayer() As PlayerType)
Dim iPlayer As Integer
Dim x As Integer
Dim y As Integer
Dim c&
Dim dr&, dg&, db&
' LOOK FOR PLAYER 1 IN LEFT HALF OF CAMERA FRAME
iPlayer = 1
arrPlayer(iPlayer).Visible = _FALSE
_Source imgCamera
For x = (CAMERA_WIDTH - 1) To (CAMERA_WIDTH / 2) Step -1
For y = 1 To (CAMERA_HEIGHT - 1)
' COMPARE AGAINST ORIGINAL PIXEL
c& = Point(x, y)
dr& = Abs(arrComparePixels(x, y).r - _Red32(c&))
dg& = Abs(arrComparePixels(x, y).g - _Green32(c&))
db& = Abs(arrComparePixels(x, y).b - _Blue32(c&))
'_Alpha32(c&)
' DID IT CHANGE ENOUGH?
If (dr& + dg& + db&) > MOVEMENT_THRESHOLD Then
'arrPlayer(iPlayer).x = x
arrPlayer(iPlayer).y = y * (m_iScreenHeight / CAMERA_HEIGHT)
'NO:
'' Y has to be reversed (up is down & down is up)
'arrPlayer(iPlayer).y = m_iScreenHeight - (y * (m_iScreenHeight / CAMERA_HEIGHT))
' Keep within boundaries
If arrPlayer(iPlayer).y < MIN_Y Then
arrPlayer(iPlayer).y = MIN_Y
ElseIf arrPlayer(iPlayer).y > MAX_Y Then
arrPlayer(iPlayer).y = MAX_Y
End If
' Done looking
arrPlayer(iPlayer).Visible = _TRUE
Exit For
End If
Next y
If arrPlayer(iPlayer).Visible = _TRUE Then
Exit For
End If
Next x
' LOOK FOR PLAYER 2 IN RIGHT HALF OF CAMERA FRAME
iPlayer = 2
arrPlayer(iPlayer).Visible = _FALSE
_Source imgCamera
For x = 1 To (CAMERA_WIDTH / 2)
For y = 1 To (CAMERA_HEIGHT - 1)
' COMPARE AGAINST ORIGINAL PIXEL
c& = Point(x, y)
dr& = Abs(arrComparePixels(x, y).r - _Red32(c&))
dg& = Abs(arrComparePixels(x, y).g - _Green32(c&))
db& = Abs(arrComparePixels(x, y).b - _Blue32(c&))
'_Alpha32(c&)
' DID IT CHANGE ENOUGH?
If (dr& + dg& + db&) > MOVEMENT_THRESHOLD Then
'arrPlayer(iPlayer).x = x
arrPlayer(iPlayer).y = y * (m_iScreenHeight / CAMERA_HEIGHT)
'NO:
'' Y has to be reversed (up is down & down is up)
'arrPlayer(iPlayer).y = m_iScreenHeight - (y * (m_iScreenHeight / CAMERA_HEIGHT))
' Keep within boundaries
If arrPlayer(iPlayer).y < MIN_Y Then
arrPlayer(iPlayer).y = MIN_Y
ElseIf arrPlayer(iPlayer).y > MAX_Y Then
arrPlayer(iPlayer).y = MAX_Y
End If
' Done looking
arrPlayer(iPlayer).Visible = _TRUE
Exit For
End If
Next y
If arrPlayer(iPlayer).Visible = _TRUE Then
Exit For
End If
Next x
End Sub ' DetectMovement
' /////////////////////////////////////////////////////////////////////////////
' SAVE RGB OF EACH PIXEL CAMERA SEES BEFORE PLAYERS USE HANDS/LASERS
Sub SaveFrame (imgCamera As Long, arrComparePixels() As PixelType)
Dim x As Integer
Dim y As Integer
Dim c&, r&, g&, b&
'Dim a&
_Source imgCamera
For y = 1 To CAMERA_HEIGHT
For x = 1 To CAMERA_WIDTH
c& = Point(x, y)
arrComparePixels(x, y).r = _Red32(c&)
arrComparePixels(x, y).g = _Green32(c&)
arrComparePixels(x, y).b = _Blue32(c&)
'arrComparePixels(x, y).a = _Alpha32(c&)
Next x
Next y
End Sub ' SaveFrame
' /////////////////////////////////////////////////////////////////////////////
Sub InitGraphicLayers (Graph1 As GraphicsType)
' BACKGROUND = SOLID BLACK
If Graph1.imgBackground = -1 Or Graph1.imgBackground = 0 Then
Graph1.imgBackground = _NewImage(m_iScreenWidth, m_iScreenHeight, 32)
_Dest Graph1.imgBackground: Cls , cBlack
End If
' CAMERA LAYER
If Graph1.imgCamera = -1 Or Graph1.imgCamera = 0 Then
Graph1.imgCamera = _NewImage(m_iScreenWidth, m_iScreenHeight, 32)
_Dest Graph1.imgCamera: Cls , cEmpty
End If
' WALLS LAYER
If Graph1.imgWalls = -1 Or Graph1.imgWalls = 0 Then
Graph1.imgWalls = _NewImage(m_iScreenWidth, m_iScreenHeight, 32)
_Dest Graph1.imgWalls: Cls , cEmpty
End If
' TEXT + INSTRUCTIONS (STATIC TEXT)
If Graph1.imgStaticText = -1 Or Graph1.imgStaticText = 0 Then
Graph1.imgStaticText = _NewImage(m_iScreenWidth, m_iScreenHeight, 32)
_Dest Graph1.imgStaticText: Cls , cEmpty
End If
' SCORE (CONSTANTLY UPDATED TEXT)
If Graph1.imgDynamicText = -1 Or Graph1.imgDynamicText = 0 Then
Graph1.imgDynamicText = _NewImage(m_iScreenWidth, m_iScreenHeight, 32)
_Dest Graph1.imgDynamicText: Cls , cEmpty
End If
' HOLDS POINTER LASER MOVES AROUND
If Graph1.imgSprites = -1 Or Graph1.imgSprites = 0 Then
Graph1.imgSprites = _NewImage(m_iScreenWidth, m_iScreenHeight, 32)
_Dest Graph1.imgSprites: Cls , cEmpty
End If
End Sub ' InitGraphicLayers
' /////////////////////////////////////////////////////////////////////////////
Sub DrawCameraLayer (Graph1 As GraphicsType, imgCamera As Long, ShowCamera As Integer)
Dim dx1, dx2, dy1, dy2 As Integer
_Dest Graph1.imgCamera: Cls , cEmpty
If ShowCamera = _TRUE Then
' Camera image is flipped horizontally (like looking in a mirror)
'_PutImage ((m_iScreenWidth - CAMERA_WIDTH) / 2, (m_iScreenHeight - CAMERA_HEIGHT) / 2), imgCamera
' Per: https://qb64phoenix.com/qb64wiki/index.php/PUTIMAGE
' To flip the image on the x axis, swap the dx coordinate values:
' e.g., _PUTIMAGE (dx2, dy1)-(dx1, dy2), source_handle, dest_handle
dx1 = (m_iScreenWidth - CAMERA_WIDTH) / 2
dy1 = (m_iScreenHeight - CAMERA_HEIGHT) / 2
dx2 = dx1 + CAMERA_WIDTH
dy2 = dy1 + CAMERA_HEIGHT
_PutImage (dx2, dy1)-(dx1, dy2), imgCamera, Graph1.imgCamera
End If
End Sub ' DrawCameraLayer
' /////////////////////////////////////////////////////////////////////////////
' DRAW THE INSTRUCTIONS LAYER
Sub DrawInstructionsLayer (Graph1 As GraphicsType)
Dim sMessage As String
Dim iCol As Integer
_Dest Graph1.imgStaticText: Cls , cEmpty
' ROW 1: TITLE
'Color cWhite, cEmpty
sMessage = "Webcam computer vision Pong"
iCol = (m_iTextCols / 2) - (Len(sMessage) / 2) ' CENTERED TEXT
PrintTitle 1, iCol, sMessage
' ROW 47: INSTRUCTIONS
iCol = 1
sMessage = "POINT WEBCAM AT EMPTY ROOM OR WHITE SHEET OF PAPER"
'iCol = (((m_iTextCols / 3) / 2) - (Len(sMessage) / 2)) ' LEFT THIRD
Color cBlack, cWhite
Locate 47, iCol: Print sMessage;
' ROW 48: INSTRUCTIONS
iCol = 1
sMessage = "PRESS ENTER TO CALIBRATE + SHOW/HIDE CAMERA"
'iCol = (((m_iTextCols / 3) / 2) - (Len(sMessage) / 2)) ' LEFT THIRD
Color cBlack, cLime
Locate 48, iCol: Print sMessage;
iCol = iCol + Len(sMessage) + 2
sMessage = "MOVE HAND UP & DOWN ALONG LEFT AND RIGHT EDGES"
'iCol = ((m_iTextCols / 2) - (Len(sMessage) / 2)) ' MIDDLE THIRD
Color cWhite, cRed
Locate 48, iCol: Print sMessage;
iCol = iCol + Len(sMessage) + 2
sMessage = "PRESS [ESC] TO EXIT"
'iCol = (m_iTextCols - ((m_iTextCols / 3) / 2)) - (Len(sMessage) / 2) ' RIGHT THIRD
Color cWhite, cBlue
Locate 48, iCol: Print sMessage;
End Sub ' DrawInstructionsLayer
' /////////////////////////////////////////////////////////////////////////////
' DRAW NON-MOVING STUFF
Sub DrawWallsLayer (Graph1 As GraphicsType)
Dim iLoop As Integer
Dim x1, y1, x2, y2 As Integer
' CLEAR LAYER
_Dest Graph1.imgWalls: Cls , cEmpty
' DRAW WALLS
x1 = 1: y1 = 40: x2 = m_iScreenWidth: y2 = 44
Line (x1, y1)-(x2, y2), cWhite, BF
x1 = 1: y1 = m_iScreenHeight - 54: x2 = m_iScreenWidth: y2 = m_iScreenHeight - 50
Line (x1, y1)-(x2, y2), cWhite, BF
' DRAW THE NET
For iLoop = 44 To (m_iScreenHeight - 48) Step 20
Line ((m_iScreenWidth / 2) - 2, iLoop)-((m_iScreenWidth / 2) + 2, iLoop + 10), cWhite, BF
Next iLoop
End Sub ' DrawWallsLayer
' /////////////////////////////////////////////////////////////////////////////
' DRAW THE SCORE LAYER
' TEXT SCREEN AT 1024 x 768 = 128 x 48 (m_iTextCols x m_iTextRows)
Sub DrawDynamicTextLayer (Graph1 As GraphicsType, arrPlayer() As PlayerType, ShowCamera As Integer)
Dim y
_Dest Graph1.imgDynamicText: Cls , cEmpty
' SHOW SCORES
Color cCyan, cEmpty: Locate 2, 32
Print _Trim$(Str$(arrPlayer(1).score))
Color cLightPink, cEmpty: Locate 2, 96
Print _Trim$(Str$(arrPlayer(2).score))
' SHOW POSITIONS
If arrPlayer(1).Visible = _TRUE Then
Color cCyan, cEmpty: Locate 37, 32
Print "Player 1: x=" + _Trim$(Str$(arrPlayer(1).x)) + " y=" + _Trim$(Str$(arrPlayer(1).y))
End If
If arrPlayer(2).Visible = _TRUE Then
Color cLightPink, cEmpty: Locate 37, 96
Print "Player 2: x=" + _Trim$(Str$(arrPlayer(2).x)) + " y=" + _Trim$(Str$(arrPlayer(2).y))
End If
' SHOW OTHER
If ShowCamera = _TRUE Then
'y = ( ( (m_iScreenHeight - CAMERA_HEIGHT) / 2 ) / _FontHeight) - _FontHeight
'color cBlack, cYellow : Locate y, 60
Color cBlack, cYellow: Locate 16, 60
Print "ShowCamera = _TRUE"
End If
End Sub ' DrawDynamicTextLayer
' /////////////////////////////////////////////////////////////////////////////
' DRAW SPRITES (PLAYERS + BALL)
Sub DrawSpritesLayer (Graph1 As GraphicsType, arrPlayer() As PlayerType, Ball1 As BallType)
_Dest Graph1.imgSprites: Cls , cEmpty
' If players are visible draw a box at the locations
If arrPlayer(1).Visible = _TRUE Then
DrawRectSolid arrPlayer(1).x, arrPlayer(1).y, arrPlayer(1).width, arrPlayer(1).height, arrPlayer(1).color
End If
If arrPlayer(2).Visible = _TRUE Then
DrawRectSolid arrPlayer(2).x, arrPlayer(2).y, arrPlayer(2).width, arrPlayer(2).height, arrPlayer(2).color
End If
' If ball is visible, draw it
If Ball1.Visible = _TRUE Then
DrawRectSolid Ball1.x, Ball1.y, Ball1.size, Ball1.size, Ball1.color
End If
End Sub ' DrawSpritesLayer
' /////////////////////////////////////////////////////////////////////////////
' COPY LAYERS TO SCREEN AND UPDATE DISPLAY
Sub RenderScreen (Graph1 As GraphicsType)
' CLEAR THE SCREEN
_Dest 0: Cls , cEmpty
' Add the background
_PutImage , Graph1.imgBackground, 0
' Add the camera image
_PutImage , Graph1.imgCamera, 0
' Add the walls
_PutImage , Graph1.imgWalls, 0
' Add the instructions
_PutImage , Graph1.imgStaticText, 0
' Add the score
_PutImage , Graph1.imgDynamicText, 0
' Add the sprites
_PutImage , Graph1.imgSprites, 0
' update screen with changes
_Display
End Sub ' RenderScreen
' /////////////////////////////////////////////////////////////////////////////
' DRAW A 2-D RECTANGLE (SOLID)
Sub DrawRectSolid (iX As Integer, iY As Integer, iSizeW As Integer, iSizeH As Integer, fgColor As _Unsigned Long)
Line (iX, iY)-(iX + iSizeW, iY + iSizeH), fgColor, BF ' Draw a solid rectangle
End Sub ' DrawRectSolid
' /////////////////////////////////////////////////////////////////////////////
Sub GrabFrame (hwnd As _Offset)
SendMessage hwnd, WM_CAP_GRAB_FRAME_NOSTOP, 0, 0
End Sub ' GrabFrame
' /////////////////////////////////////////////////////////////////////////////
Sub GrabFrameToMemory (hwnd As _Offset, image As _MEM)
Dim As _Unsigned Long frameSize
Dim As _Offset hDIB
Dim As _Offset pDIB
Dim As _MEM p
Dim As _Unsigned Long biSize
Dim As _MEM pBI
Dim As BITMAPINFO bi
Dim As Long bytesPerPixel
Dim As Long stride
Dim As _MEM m
Dim As Long y, x, t
Dim As _Unsigned _Byte pixel24(0 To 2)
Dim As _Unsigned _Byte pixel32(0 To 3)
Dim As _Offset pScanLine
Dim As _Unsigned Long q
Dim As Long i
Dim As Long a
frameSize = SendMessage(hwnd, WM_CAP_GET_VIDEOFORMAT, 0, 0)
If frameSize = 0 Then
Print "Failed to get video format size"
KillDriver hwnd
End
End If
SendMessage hwnd, WM_CAP_EDIT_COPY, 0, 0
If OpenClipboard(0) = 0 Then
Print "Couldn't open clipboard"
KillDriver hwnd
End
End If
hDIB = GetClipboardData(CF_DIB)
If hDIB = 0 Then
Print "Failed to retrieve bitmap from clipboard"
CloseClipboard
KillDriver hwnd
End
End If
pDIB = GlobalLock(hDIB)
If pDIB = 0 Then
Print "Failed to lock the clipboard"
CloseClipboard
KillDriver hwnd
End
End If
CloseClipboard
'Print "Frame retrieved"
p = _Mem(pDIB, 4)
biSize = _MemGet(p, p.OFFSET, _Unsigned Long)
pBI = _Mem(pDIB, biSize)
'Print pBI.SIZE
_MemGet pBI, pBI.OFFSET, bi
_MemFree pBI
_MemFree p
'Print bi.bmiHeader.biWidth
'Print bi.bmiHeader.biHeight
'Print bi.bmiHeader.biBitCount
bytesPerPixel = (bi.bmiHeader.biBitCount + 7) \ 8
'Print bytesPerPixel
stride = (((bi.bmiHeader.biWidth * bi.bmiHeader.biBitCount) + 31) And Not 31) \ 8
'Print stride
'Print bi.bmiHeader.biSizeImage
m = _Mem(pDIB + Len(bi), bi.bmiHeader.biSizeImage)
i = _NewImage(bi.bmiHeader.biWidth, bi.bmiHeader.biHeight, 32)
_Dest i
If bi.bmiHeader.biHeight > 0 Then
For y = 0 To Abs(bi.bmiHeader.biHeight) - 1
pScanLine = m.OFFSET + (y * stride)
For x = 0 To bi.bmiHeader.biWidth - 1
If bi.bmiHeader.biBitCount = 24 Then
_MemGet m, pScanLine + (x * bytesPerPixel), pixel24()
q = _RGB32(pixel24(2), pixel24(1), pixel24(0))
ElseIf bi.bmiHeader.biBitCount = 32 Then
_MemGet m, pScanLine + (x * bytesPerPixel), pixel32()
q = _RGBA32(pixel32(2), pixel32(1), pixel32(0), pixel32(3))
Else
q = _RGB32(255, 255, 255)
End If
PSet (x, Abs(bi.bmiHeader.biHeight) - 1 - y), q
Next x
Next y
Else
For y = 0 To Abs(bi.bmiHeader.biHeight) - 1
pScanLine = m.OFFSET + (y * stride)
For x = 0 To bi.bmiHeader.biWidth - 1
If bi.bmiHeader.biBitCount = 24 Then
_MemGet m, pScanLine + (x * bytesPerPixel), pixel24()
q = _RGB32(pixel24(2), pixel24(1), pixel24(0))
ElseIf bi.bmiHeader.biBitCount = 32 Then
_MemGet m, pScanLine + (x * bytesPerPixel), pixel32()
q = _RGBA32(pixel32(2), pixel32(1), pixel32(0), pixel32(3))
Else
q = _RGB32(255, 255, 255)
End If
PSet (x, y), q
Next
Next
End If
_Dest 0
image = _MemImage(i)
a = GlobalUnlock(pDIB)
End Sub ' GrabFrameToMemory
' /////////////////////////////////////////////////////////////////////////////
' Returns TRUE if value OriginalString$ is numeric.
' Re: Does a Is Number function exist in QB64?
' https://www.qb64.org/forum/index.php?topic=896.15
' Version 2 by madscijr
' Returns TRUE (-1) if string is an integer, FALSE (0) if not
' Version 1 by MWheatley
' Reply #18 on: January 01, 2019, 11:24:30 AM
' returns 1 if string is an integer, 0 if not
Function IsNumber% (OriginalString$)
Dim bResult%: bResult% = _FALSE
Dim iLoop%
Dim TestString$
'Dim bNegative%
Dim iDecimalCount%
Dim sNextChar$
'THEY SHOULD TRIM OUTSIDE THE FUNCTION!
'TestString$ = _TRIM$(OriginalString$)
If Len(OriginalString$) > 0 Then
TestString$ = ""
If Left$(OriginalString$, 1) = "+" Then
TestString$ = Right$(OriginalString$, Len(OriginalString$) - 1)
'bNegative% = FALSE
ElseIf Left$(OriginalString$, 1) = "-" Then
TestString$ = Right$(OriginalString$, Len(OriginalString$) - 1)
'bNegative% = TRUE
Else
TestString$ = OriginalString$
'bNegative% = FALSE
End If
If Len(TestString$) > 0 Then
bResult% = _TRUE
iDecimalCount% = 0
For iLoop% = 1 To Len(TestString$)
sNextChar$ = Mid$(TestString$, iLoop%, 1)
If sNextChar$ = "." Then
iDecimalCount% = iDecimalCount% + 1
If iDecimalCount% > 1 Then
' TOO MANY DECIMAL POINTS, INVALID!
bResult% = _FALSE
Exit For
End If
ElseIf Asc(sNextChar$) < 48 Or Asc(sNextChar$) > 57 Then
' NOT A NUMERAL OR A DECIMAL, INVALID!
bResult% = _FALSE
Exit For
End If
Next iLoop%
End If
End If
IsNumber% = bResult%
End Function ' IsNumber%
' /////////////////////////////////////////////////////////////////////////////
Sub KillDriver (hwnd As _Offset)
SendMessage hwnd, WM_CAP_DRIVER_DISCONNECT, 0, 0
DestroyWindow hwnd
End Sub ' KillDriver
' /////////////////////////////////////////////////////////////////////////////
' Does a _PrintString at the specified row+column.
' iRow and iCol are 0-based.
' See also: PrintString1
Sub PrintString (iRow As Integer, iCol As Integer, MyString As String)
Dim iX As Integer
Dim iY As Integer
iX = _FontWidth * iCol
iY = _FontHeight * iRow ' (iRow + 1)
_PrintString (iX, iY), MyString
End Sub ' PrintString
' /////////////////////////////////////////////////////////////////////////////
' Print sMessage in fancy alternating colors
' at row RowNum, column StartCol
Sub PrintTitle (RowNum As Integer, StartCol As Integer, sMessage As String)
Dim ColNum As Integer
Dim iLoop As Integer
Dim fgColor~&
ColNum = StartCol
For iLoop = 1 To Len(sMessage)
If fgColor~& = cBlue Then fgColor~& = cRed Else fgColor~& = cBlue
Color cWhite, fgColor~&
Locate RowNum, ColNum: Print Mid$(sMessage, iLoop, 1);
'PrintString RowNum, ColNum, mid$(sMessage, iLoop, 1)
ColNum = ColNum + 1
Next iLoop
End Sub ' PrintTitle
' /////////////////////////////////////////////////////////////////////////////
' Generate random value between Min and Max inclusive.
' Note: random-number generator should be initialized with
' InitializeRandom or Randomize Timer
Function RandomNumber% (Min%, Max%)
Dim NumSpread%
'' SET RANDOM SEED
''Randomize ' Initialize random-number generator.
'Randomize Timer
NumSpread% = (Max% - Min%) + 1
RandomNumber% = Int(Rnd * NumSpread%) + Min% ' GET RANDOM # BETWEEN Max% AND Min%
End Function ' RandomNumber%
' /////////////////////////////////////////////////////////////////////////////
Sub SaveBMP (hwnd As _Offset, filename As String)
filename = filename + Chr$(0)
SendMessage hwnd, WM_CAP_FILE_SAVEDIB, 0, _Offset(filename)
'SendMessage hwnd, WM_CAP_EDIT_COPY, 0, 0 ' It is now in the clipboard, too Smile
End Sub ' SaveBMP
' /////////////////////////////////////////////////////////////////////////////
Sub SetupDriver (hwnd As _Offset, videoWidth As _Unsigned Long, videoHeight As _Unsigned Long, defaultSource As _Byte)
SendMessage hwnd, WM_CAP_DRIVER_CONNECT, 0, 0
SendMessage hwnd, WM_CAP_SET_SCALE, -1, 0
SendMessage hwnd, WM_CAP_SET_PREVIEWRATE, 1000 / FPS, 0
SendMessage hwnd, WM_CAP_SET_PREVIEW, -1, 0
Dim As _Unsigned Long formatSize: formatSize = SendMessage(hwnd, WM_CAP_GET_VIDEOFORMAT, 0, 0)
Dim As BITMAPINFO bmi
If Len(bmi) <> formatSize Then
KillDriver hwnd
Print "Wrong size"
Print formatSize, Len(bmi)
End
End If
If SendMessage(hwnd, WM_CAP_GET_VIDEOFORMAT, formatSize, _Offset(bmi)) = 0 Then
KillDriver hwnd
Print "Couldn't get format"
End
End If
bmi.bmiHeader.biWidth = videoWidth
bmi.bmiHeader.biHeight = videoHeight
bmi.bmiHeader.biBitCount = 16 'yuy2 format
bmi.bmiHeader.biCompression = mmioStringToFOURCC("YUY2" + Chr$(0), &H00000010) ' MUST BE YUY2 FORMAT
bmi.bmiHeader.biSizeImage = videoWidth * videoHeight * 2
If SendMessage(hwnd, WM_CAP_SET_VIDEOFORMAT, formatSize, _Offset(bmi)) = 0 Then
KillDriver hwnd
Print "Failed to set video format"
End
End If
If defaultSource = _FALSE Then
SendMessage hwnd, WM_CAP_DLG_VIDEOFORMAT, 0, 0 'PICK YUY2!!!
SendMessage hwnd, WM_CAP_DLG_VIDEOSOURCE, 0, 0
End If
End Sub ' SetupDriver
' /////////////////////////////////////////////////////////////////////////////
' ################################################################################################################################################################
' BEGIN COLOR FUNCTIONS #COLOR
' ################################################################################################################################################################
Function cRed~& ()
cRed = _RGB32(255, 0, 0)
End Function
Function cOrangeRed~& ()
cOrangeRed = _RGB32(255, 69, 0)
End Function ' cOrangeRed~&
Function cDarkOrange~& ()
cDarkOrange = _RGB32(255, 140, 0)
End Function ' cDarkOrange~&
Function cOrange~& ()
cOrange = _RGB32(255, 165, 0)
End Function ' cOrange~&
Function cGold~& ()
cGold = _RGB32(255, 215, 0)
End Function ' cGold~&
Function cYellow~& ()
cYellow = _RGB32(255, 255, 0)
End Function ' cYellow~&
' LONG-HAIRED FRIENDS OF JESUS OR NOT,
' THIS IS NOT YELLOW ENOUGH (TOO CLOSE TO LIME)
' TO USE FOR OUR COMPLEX RAINBOW SEQUENCE:
Function cChartreuse~& ()
cChartreuse = _RGB32(127, 255, 0)
End Function ' cChartreuse~&
' WE SUBSTITUTE THIS CSS3 COLOR FOR INBETWEEN LIME AND YELLOW:
Function cOliveDrab1~& ()
cOliveDrab1 = _RGB32(192, 255, 62)
End Function ' cOliveDrab1~&
Function cLime~& ()
cLime = _RGB32(0, 255, 0)
End Function ' cLime~&
Function cMediumSpringGreen~& ()
cMediumSpringGreen = _RGB32(0, 250, 154)
End Function ' cMediumSpringGreen~&
' ADDED THIS FOR THE GAUGE COLOR:
Function cSpringGreen~& ()
cSpringGreen = _RGB32(0, 255, 160)
End Function ' cSpringGreen~&
Function cCyan~& ()
cCyan = _RGB32(0, 255, 255)
End Function ' cCyan~&
Function cDeepSkyBlue~& ()
cDeepSkyBlue = _RGB32(0, 191, 255)
End Function ' cDeepSkyBlue~&
Function cDodgerBlue~& ()
cDodgerBlue = _RGB32(30, 144, 255)
End Function ' cDodgerBlue~&
Function cSeaBlue~& ()
cSeaBlue = _RGB32(0, 64, 255)
End Function ' cSeaBlue~&
Function cBlue~& ()
cBlue = _RGB32(0, 0, 255)
End Function ' cBlue~&
Function cBluePurple~& ()
cBluePurple = _RGB32(64, 0, 255)
End Function ' cBluePurple~&
Function cDeepPurple~& ()
cDeepPurple = _RGB32(96, 0, 255)
End Function ' cDeepPurple~&
Function cPurple~& ()
cPurple = _RGB32(128, 0, 255)
End Function ' cPurple~&
Function cPurpleRed~& ()
cPurpleRed = _RGB32(128, 0, 192)
End Function ' cPurpleRed~&
Function cDarkRed~& ()
cDarkRed = _RGB32(160, 0, 64)
End Function ' cDarkRed~&
Function cBrickRed~& ()
cBrickRed = _RGB32(192, 0, 32)
End Function ' cBrickRed~&
Function cDarkGreen~& ()
cDarkGreen = _RGB32(0, 100, 0)
End Function ' cDarkGreen~&
Function cGreen~& ()
cGreen = _RGB32(0, 128, 0)
End Function ' cGreen~&
Function cOliveDrab~& ()
cOliveDrab = _RGB32(107, 142, 35)
End Function ' cOliveDrab~&
Function cLightPink~& ()
cLightPink = _RGB32(255, 182, 193)
End Function ' cLightPink~&
Function cHotPink~& ()
cHotPink = _RGB32(255, 105, 180)
End Function ' cHotPink~&
Function cDeepPink~& ()
cDeepPink = _RGB32(255, 20, 147)
End Function ' cDeepPink~&
Function cMagenta~& ()
cMagenta = _RGB32(255, 0, 255)
End Function ' cMagenta~&
Function cBlack~& ()
cBlack = _RGB32(0, 0, 0)
End Function ' cBlack~&
Function cDimGray~& ()
cDimGray = _RGB32(105, 105, 105)
End Function ' cDimGray~&
Function cGray~& ()
cGray = _RGB32(128, 128, 128)
End Function ' cGray~&
Function cDarkGray~& ()
cDarkGray = _RGB32(169, 169, 169)
End Function ' cDarkGray~&
Function cSilver~& ()
cSilver = _RGB32(192, 192, 192)
End Function ' cSilver~&
Function cLightGray~& ()
cLightGray = _RGB32(211, 211, 211)
End Function ' cLightGray~&
Function cGainsboro~& ()
cGainsboro = _RGB32(220, 220, 220)
End Function ' cGainsboro~&
Function cWhiteSmoke~& ()
cWhiteSmoke = _RGB32(245, 245, 245)
End Function ' cWhiteSmoke~&
Function cWhite~& ()
cWhite = _RGB32(255, 255, 255)
End Function ' cWhite~&
Function cDarkBrown~& ()
cDarkBrown = _RGB32(128, 64, 0)
End Function ' cDarkBrown~&
Function cLightBrown~& ()
cLightBrown = _RGB32(196, 96, 0)
End Function ' cLightBrown~&
Function cKhaki~& ()
cKhaki = _RGB32(240, 230, 140)
End Function ' cKhaki~&
Function cEmpty~& ()
cEmpty = _RGB32(0, 0, 0, 0)
End Function ' cEmpty~&
' ################################################################################################################################################################
' END COLOR FUNCTIONS @COLOR
' ################################################################################################################################################################
Click OK when you see the Video Format and Video Source popup forms...