I’ll share here something that is related to a project I’m working on.
How to test if the user/player has touched an object on the screen. In our example program the object is the following (you may download the object):
My little code tests if object is touched while it moves around the screen. The test is pixel accurate.
The idea is to use a databuffer where the image’s pixel data is stored as integers, which is done by using the LoadImageData command. Each pixel takes 4 bytes of space in the buffer.
The alpha component is set to zero; the remaining bits represent the RGB values. Each integer is stored in variable data. If data <> 0 then the player has touched the object. Only the pixel of the image that has been touched, will be tested.
The code below clarifies this:
Import brl.databuffer Import opengl.gles11 Import mojo Function Main() New MyApp End Class MyApp Extends App Global BallData:DataBuffer Field gfxBall:Image Global hit:Bool Global info:Int[2] Global touchX:Float Global touchY:Float Global angle:Float Method OnCreate() SetDeviceWindow(800,600,0) gfxBall = LoadImage("ball.png") BallData = LoadImageData("monkey://data/ball.png",info) SetUpdateRate(60) End Method OnUpdate() Local data:Int Local coordX:Int Local coordY:Int Local pointX:Int Local pointY:Int touchX = TouchX() touchY = TouchY() angle = angle + 0.7 hit = False coordX = (800 - 72) / 2 + Cos(angle)*200 coordY = (600 - 72) / 2 + Sin(angle)*200 If touchX - coordX < 72.0 And touchX - coordX >= 0 And touchY - coordY < 72.0 And touchY - coordY >= 0 Then pointX = touchX - coordX pointY = touchY - coordY data = BallData.PeekInt(pointX*4 + pointY*72*4) data = data & $00ffffff If data <> 0 Then hit = True Endif End Method OnRender() Cls SetBlend AdditiveBlend DrawImage gfxBall,(800 - 72) / 2 + Cos(angle)*200, (600 - 72) / 2 + Sin(angle)*200 If hit = True Then DrawText "HIT!",(800 - TextWidth("HIT!")) / 2, (600 - 8) / 2 End End Class
This code can’t be compiled to HTML5 target, so you’ll have to try this code in Monkey yourself to see how it works. In free Monkey X this can be compiled to desktop, in Monkey X Pro the code can have also Android target.
Feel free to use this code!
Updated 3/8/2016
Below is a bit more advanced code, where you can use an image where the background color isn’t transparent.
You may download the ball2.png file used in the code (the image has white background color):
The code masks the image with given mask color in function MaskImage, in the example mask color is 255,255,255.
Updated to handle scaled graphics.
Import brl.databuffer Import opengl.gles11 Import mojo Function Main() New MyApp End Class MyApp Extends App Field gfxBall:Image Field gfxBG:Image Global BallData:DataBuffer Global imWidth:Int, imHeight:Int Global hit:Bool Global touchX:Float Global touchY:Float Global scaleX:Float Global scaleY:Float Global maskR:Int, maskG:Int, maskB:Int Global angle:Float Method OnCreate() scaleX = DeviceWidth() / 800.0 scaleY = DeviceHeight() / 600.0 gfxBall = LoadImage("ball2.png") gfxBG = LoadImage("bg.png") ' BallData holds the color information of the image BallData = LoadImageData("monkey://data/ball2.png") imWidth = gfxBall.Width imHeight = gfxBall.Height maskR = 255 maskG = 255 maskB = 255 ' gfxBall will be replaced with new image with mask color as transparent color gfxBall = MaskImage(gfxBall,BallData) SetUpdateRate(60) End Method OnUpdate() Local data:Int Local coordX:Float Local coordY:Float Local pointX:Float Local pointY:Float touchX = TouchX() / scaleX touchY = TouchY() / scaleY angle = angle + 0.4 hit = False coordX = ((800 - imWidth) / 2 + Cos(angle)*200) coordY = ((600 - imHeight) / 2 + Sin(angle)*200) ' If touched in the image rectangle, test if pixel was touched If touchX - coordX < imWidth And touchX - coordX >= 0 And touchY - coordY < imHeight And touchY - coordY >= 0 Then pointX = Int(touchX - coordX) ' pointX gets values 0..image width - 1 pointY = Int(touchY - coordY) ' pointY gets values 0..image height - 1 ' BallData holds the original color information of the image data = BallData.PeekInt(pointX * 4 + pointY * imWidth*4) data = data & $00ffffff ' Is mask color touched? If data <> ((maskR Shl 16) | (maskG Shl 8) | maskB) Then hit = True Endif End ' Draw image Method OnRender() PushMatrix() Scale(scaleX,scaleY) DrawImage gfxBG,0,0 DrawImage gfxBall,(800 - imWidth) / 2 + Cos(angle)*200, (600 - imHeight) / 2 + Sin(angle)*200 If hit = True Then SetBlend AdditiveBlend SetColor 255,0,0 DrawRect 0,0,800,600 Endif PopMatrix() End Function MaskImage:Image(gfxImage:Image, databuffer:DataBuffer) Local pixels:Int[imWidth * imHeight] ' pixel array Local color:Int For Local j = 0 To imHeight - 1 For Local i = 0 To imWidth - 1 Local r:Int Local g:Int Local b:Int Local a:Int ' databuffer holds the original color information of the image color = databuffer.PeekInt(i*4+j*4*imWidth) ' each integer is 4 bytes ' Alpha, Red, Green, Blue a = (color & $ff000000) Shr 24 r = (color & $00ff0000) Shr 16 g = (color & $0000ff00) Shr 8 b = color & $000000ff ' if mask color is found, make it transparent If r = maskR And g = maskG And b = maskB Then pixels[i+j*imWidth] = color & $00ffffff Else ' Alpha, Blue, Green, Red pixels[i+j*imWidth] = (a Shl 24) | r | (g Shl 8) | (b Shl 16) Endif Next Next gfxImage.WritePixels(pixels,0,0,imWidth,imHeight) ' Replace original image Return gfxImage End End Class
Feel free to improve and use the codes above!
Below is a video demonstration of the code above:
[wpdevart_youtube]aSCQ4TVl_Bg[/wpdevart_youtube]