Touching an object in Monkey X

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):

ball

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):

ball2

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]

%d bloggers like this: