“Old School” — a Little Monkey X Demonstration

Soon the year 2015 will be on the pages of history. I made today a little demonstration in Monkey X with Mojo2.

The demonstration is called “Old School” — for the good old Amiga days. See the video below:

Below is the source code:

Import mojo2
Import mojo2.glutil
Import brl.databuffer

Function Main()
	New MyApp
End

Class MyApp Extends App
	
	Const FONT_HEIGHT:Int = 149

	Global canvas:Canvas
	Global fontDat:DataBuffer
	Global gfxFont:Image
	Global gfxBG:Image
	
	
	Global angle:Float[10], lightAngle:Float, lightX:Float, lightY:Float
	
	Global x:Float, y:Float[10]
	
	Method OnCreate()

		Local angl:Float
		
		canvas = New Canvas

		gfxBG = Image.Load("bg.png",.0,.0)
		gfxFont = Image.Load("font.png")
		fontDat = New DataBuffer(95*4*2) ' 95 characters, for each character two 4 byte integers
		fontDat = DataBuffer.Load("monkey://data/font.dat")
		
		For Local i = 0 To 9
			angle[i] = angl
			angl = angl + 18
		Next

		canvas.SetAmbientLight .2,.3,.7
		
	End
	
	Method OnUpdate()
		
		For Local i = 0 To 9
			y[i] = (480 - FONT_HEIGHT) / 2 + Sin(angle[i]) * 32
			angle[i] = angle[i] + 5
		Next
		
		lightX = Cos(lightAngle) * 150 + 640 / 2
		lightY = Sin(lightAngle) * 150 + 480 / 2
		
		lightAngle = lightAngle + 1
		
	End
	
	Method OnRender()
		canvas.PushMatrix()
		canvas.Scale(DeviceWidth()/640.0,DeviceHeight()/480.0)

		canvas.Clear
		
		canvas.SetLightType 0,1
		canvas.SetLightColor 0,.7,.7,.9
		canvas.SetLightPosition 0,lightX,lightY,-100
		canvas.SetLightRange 0,200
		
		For Local sx:=0 Until DeviceWidth Step 128
			For Local sy:=0 Until DeviceHeight Step 128	
				canvas.DrawImage gfxBG,sx,sy
			Next
		Next

		canvas.SetBlendMode(BlendMode.Additive)
		
		drawString("Old School")

		canvas.Flush()
		canvas.PopMatrix()	

	End
		
	Function drawString(text:String)
		Local len = text.Length()
		
		Local chrs:Int[]
		chrs = text.ToChars()
		
		x = (640 - stringWidth("Old School")) / 2
				
		For Local i = 0 To len - 1
			canvas.DrawRect(x, y[i], fontDat.PeekInt((chrs[i]-32)*4*2 + 4), FONT_HEIGHT, gfxFont, fontDat.PeekInt((chrs[i]-32)*4*2), 0, fontDat.PeekInt((chrs[i]-32)*4*2 + 4), FONT_HEIGHT)
			x = x + fontDat.PeekInt((chrs[i]-32)*4*2 + 4)
		Next

	End

	Function stringWidth:Int(text:String)
		Local len:Int = text.Length()
		Local chrs:Int[]
		Local length:Int = 0
		
		chrs = text.ToChars()
		
		For Local i:Int = 0 To len - 1
			length = length + fontDat.PeekInt((chrs[i]-32)*4*2+4)
		Next
		
		Return length
	End

End Class

Some words about the source…

The bitmap font is made with my Font 2 PNG. On the Font 2 PNG page is explained the file structure of the font.dat file.

The new thing to me was to try the lightning effect with Mojo2. In the code to variable gfxBG is loaded one image, but in order to get the lightning effect working, one needs to make 3 versions of the image. All those three images are used while rendering the effect. The naming of the image files goes like this: name.png, name_n.png and name_s.png.

The letter “n” stands for normal, “s” for specular. These are map files for the lightning. That’s all I know from the documentation of Monkey X Pro.

Feel free to use the code above as you wish.

Update:

New version of “Old School” video:

 

3D Stars with Mojo2

As an example of using Mojo2 module in Monkey X, I wrote the 3D stars using Mojo2 module in Monkey X.

In earlier post the stars were made with the old Mojo. Mojo2 has lots of nice features to experiment with.

Below is the source code in full:

Import mojo2

Function Main:Int()
	New MyApp
	Return 0
End
 
Class MyApp Extends App
 
	Const BG_WIDTH:Int = 640
	Const BG_HEIGHT:Int = 480
	Const STARS:Int = 180
	
	Field scaleX:Float, scaleY:Float
	
	Field x:Float[STARS],y:Float[STARS],z:Float[STARS]
 
	Field canvas:Canvas
	
	Method OnCreate()
 
		scaleX = DeviceWidth() / BG_WIDTH
		scaleY = DeviceHeight() / BG_HEIGHT
 
		For Local i:Int = 0 To STARS - 1
			x[i] = Rnd(-50,50)
			y[i] = Rnd(-50,50)
			z[i] = Rnd(50,200)
		Next
		
		canvas = New Canvas()
		
		SetUpdateRate(60)
	End
 
	Method OnUpdate()
			
	End
	
 
	Method OnRender()
		
		Local shade:Float
		
		' Scale the graphics
		canvas.PushMatrix()
		canvas.Scale (scaleX,scaleY)
  
		canvas.Clear()
		
		For Local i:Int = 0 To STARS - 1
			z[i] = z[i] - 1
			If z[i] < 10 Then z[i] = Rnd(50,200)
					
			' shade is 0..1
			shade = 1 - z[i] / 200
			canvas.SetColor shade,shade,shade
			canvas.DrawOval 320 + x[i] / z[i] * 200, 240 + y[i] / z[i] * 200,3,3
		Next
	
		canvas.Flush()
		
		canvas.PopMatrix()
		
	End
 
End Class

Feel free to use this code.

Perhaps the Most Important Rule In Programming

This is something that I have learned the hard way. 🙂 Don’t code when you are tired, night time really isn’t the best time to code. It may be the case that you’ll have to do everything at least twice.

Also when posting or publishing something, it’s better first sleep over night.

ID-1002617

Image courtesy of Peter Haken at FreeDigitalPhotos.net

 

Scaling Bitmap Font Made With Font 2 PNG in Monkey X

This time a little tutorial on how to scale bitmap font made with Font 2 PNG in Monkey X.

The code uses DrawImageRect command to draw the font. The font.dat file has two 4 byte integers for each character. First is the starting position in the PNG-file in pixels, the second is the width of the character, in pixels. These are consecutively for each character.

The text is centered with the current scale. In order to get the text appear correctly, in drawString function also the width of the character must be multiplied with the scale.

As for the Font 2 PNG, the font is saved with black background color and in the code the blend mode is AdditiveBlend and the font looks smooth.

Here’s the code:

Import mojo
Import brl.stream
Import brl.filestream

Function Main()
	New MyApp
End

Class MyApp Extends App
	
	Const FONT_HEIGHT:Float = 37

	Field gfxBG:Image
	Global angle:Float	
	Global fontDat:Int[][]
	Global gfxFont:Image
	
	Global textX:Int, textY:Int
	Global textScale:Float
	
	Method OnCreate()

		gfxFont = LoadImage("font.png")
		
		Local File:=FileStream.Open("monkey://data/font.dat","r")

		fontDat = allocateArray(95,2)
		
		For Local i:Int = 0 To 95 - 1
			fontDat[i][0] = File.ReadInt()
			fontDat[i][1] = File.ReadInt()
		Next
		
		File.Close()
		
		gfxBG = LoadImage("bg3.png")
		
		SetDeviceWindow(1024,768,0)
		
		angle = 180
		SetUpdateRate(60)
	End
	
	Method OnUpdate()
		textScale = (2 - Cos(angle)) * 1.5
		angle = angle + 4
	End
	
	Method OnRender()
		SetBlend AlphaBlend
		SetAlpha 1
		DrawImage gfxBG,0,0
		SetBlend AdditiveBlend
		drawString("Sample Text", (1024 - (stringWidth("Sample Text") * textScale)) / 2, (768 - (FONT_HEIGHT * textScale)) / 2, textScale)
		
		
	End
	
	Function drawString(text:String, x:Float, y:Float,scale:Float)
		Local len = text.Length()
		
		Local chrs:Int[]
		chrs = text.ToChars()
		
		For Local i = 0 To len - 1
			'                            pos. in font.png          width in pixels in font.png
			DrawImageRect gfxFont, x, y, fontDat[chrs[i]-32][0], 0, fontDat[chrs[i]-32][1], FONT_HEIGHT,0,scale,scale,0
			x = x + fontDat[chrs[i]-32][1] * scale
		Next

	End
	
	Function stringWidth:Int(text:String)
		Local len:Int = text.Length()
		Local chrs:Int[]
		Local length:Int = 0
		
		chrs = text.ToChars()
		
		For Local i:Int = 0 To len - 1
			length = length + fontDat[chrs[i]-32][1]
		Next
		
		Return length
	End
	
	Function allocateArray:Int[][](i:Int, j:Int)
    	Local arr:Int[][] = New Int[i][]
    	
    	For Local index = 0 Until i
    	    arr[index] = New Int[j]
    	Next
    	
    	Return arr		
	End

End Class

You may use freely this code.

Below is a YouTube video of this tutorial:

Another Game That I Dream to Create

This goes again to the nostalgia category. I really would like to make my own version of Commodore 64’s Stix.

This is a classic game from the year 1983. Below is a screenshot from C64 emulator:

stix-capture

The idea is to fill 75% of the playing area by turning 90 degrees from the direction the player is going. Kind of addictive game!

This idea could be refined to something more interesting…

Nostalgia: Missile Attack

Again, some nostalgia. I remember, that it was late 80s, when I had a game called “Missile Attack” for Amiga. It was shareware game priced at $5.

Many years ago I made just for fun my own implementation of this classic game in Blitz3D. For curiosity, if my memory serves me right, in Terminator II young John Connor is playing this kind of game in one part of the movie.

In this blog post is shortened version of my Missile Attack game. The code is quite messy. I hope there are not any horrible bugs in this old game. This is the first time the game sees the daylight.

Copy paste the code to Blitz3D and start playing, it’s really quite fun, though quite pointless. 🙂 No external files are needed.

ma_ingame

AppTitle "Missile Attack"
HidePointer

Graphics 640,480,32,2

SeedRnd MilliSecs()


fntArial20 = LoadFont("Arial",20)
fntCourier20 = LoadFont("courier",20,True)


SeedRnd MilliSecs()

Type missile
	Field x#,y#
	Field dx#,dy#
	Field origX#, origY#
	Field r,g,b
	Field ovalM
End Type

Type ammoCircle
	Field frame
	Field x,y
	Field hit
End Type


Type energyBar
	Field r#
	Field dr#
	Field g#
	Field dg#
	Field b#
	Field hit	
End Type

Type yellowFlash
	Field hit
End Type

gfxAim = CreateImage(25,25)
SetBuffer ImageBuffer(gfxAim)
Line 0,13,25,13
Line 13,0,13,25


gfxAmmo = CreateImage(72,72,72)


For q = 0 To 71							
	SetBuffer ImageBuffer (gfxAmmo,q)

	Origin ox#,oy#
	Color 0,0,255
	Oval 72/2,72/2,r#,r#,0

	ox# = ox# - 0.5
	oy# = oy# - 0.5
	r# = r# + 1


Next

gfxOval = CreateImage(15,15)
SetBuffer ImageBuffer(gfxOval)
Origin 0,0
Oval 0,0,15,15,0

SetBuffer BackBuffer()

.start
FlushKeys


For mssl.missile = Each missile
	Delete mssl
Next
		
For ammo.ammoCircle = Each ammoCircle
	Delete ammo
Next

For yc.yellowFlash = Each yellowFlash
	Delete yc
Next
		
For eb.energyBar = Each energyBar
	Delete eb
Next

eb.energyBar = New energyBar

divisor = 48

yellowFlashes = 0	; nr of yellow flashes

energyBarLen = 72
yellow = 0

maxMissiles = 0

r# = 1

orig# = 50
ox# = 0
oy# = 0


nr_of_missiles = 0
gameover = False
printInstr = False
printStr = False

extraEarned = False
print_fullDestr = True
big = False

SetBuffer BackBuffer()

Repeat
	Cls

	If KeyHit(1) Then	
		End
	EndIf	

			
	Color 255,0,0
	SetFont fntCourier20
	Text (640 - StringWidth("Missile Attack")) / 2,40,"Missile Attack"
			
	SetFont fntArial20
	Color 200,80,180
	Text (640 - StringWidth("Use mouse to shoot the missiles")) / 2, 100, "Use mouse to shoot the missiles"
	Text (640 - StringWidth("Space for full destruction")) / 2, 140, "Space for full destruction"
	
	Color 0,255,0
	Text (640 - StringWidth("Press Return to begin"))/2,380,"Press Return To begin"
	
	Color 255,0,255
	Text (640 - StringWidth("Public Domain"))/2,420,"Public Domain"

	Flip
		
Until KeyHit(28)	; Return starts the game



FlushKeys
Cls
destr = 0	; full destructions


SetBuffer BackBuffer()

blueFrame = False
bigBangs = 3
amountMissiles = Rand(5,8)
nr_of_missiles = 0

print_bigShot = False

divisor = 48

yellowFlashes = 0
energyBarLen = 72
yellow = 0

maxMissiles = 0

r# = 1

orig# = 50
ox# = 0
oy# = 0

gameover = False
extraEarned = False
print_fullDestr = True
big = False

firstBigShot = True
yellowFlashesInfo = False

While Not gameover = True

	Cls
	
	If KeyHit(1) Then Goto start
	
	mx = MouseX()
	my = MouseY()
	
	DrawImage gfxAim,mx,my

	While nr_of_missiles < amountMissiles
	
		mssl.missile = New missile
		mssl\origX# = Rand(100,580)
		mssl\x# = Rand(50,100)
		mssl\origY# = 31
		mssl\x# = mssl\origX# + Rand(-5,5)
		mssl\y# = 32
		mssl\dx# = Rnd(-2,2)
		mssl\dy# = Rnd(0,2)
	
		mssl\r = Rand(50,255)
		mssl\g = Rand(50,255)
		mssl\b = Rand(50,255)

		mssl\ovalM = Rand(5,15)

		nr_of_missiles = nr_of_missiles + 1
		
	Wend

	If MouseHit(1) Then
		ammo.ammoCircle = New ammoCircle
		ammo\x = mx - 24
		ammo\y = my - 24
		ammo\frame = 1
		ammo\hit = 0 
	EndIf

	Origin 0,0

	Color 0,0,255
	If bigBangs > 0 And bigBangs < 4 Then
		For i = 1 To bigBangs
			Oval 565 + i * 15,460,10,10,1
		Next
	EndIf

	For ammo.ammoCircle = Each ammoCircle
		ammo\frame = ammo\frame + 2
		
		If ammo\frame >= divisor Then
			Delete ammo
		Else
			If nr_of_missiles > 0 Then
			
				DrawImage gfxAmmo,ammo\x,ammo\y,ammo\frame

				For mssl.missile = Each missile
					If ImageRectCollide (gfxAmmo,ammo\x,ammo\y,ammo\frame, mssl\x ,mssl\y ,5,5)					
						Delete mssl
						ammo\hit = ammo\hit + 1
						nr_of_missiles = nr_of_missiles - 1
					EndIf
				Next
			EndIf	
		EndIf
	Next


	If print_bigShot = True Then

		Color 0,0,255

		If MilliSecs() - 2500 < timer_bigShot Then
			Text 270,200,"Big shot earned!"
		Else
			print_bigShot = False
		EndIf
				
		
	EndIf
		

	
	For ammo.ammoCircle = Each ammoCircle 
		maxMissiles = ammo\hit

		If ammo\hit >= 2 Then
			divisor = 72
			
			If firstBigShot = True Then
			
				firstBigShot = False
				print_bigShot = True
				timer_bigShot = MilliSecs()
				
				yellowFlashes = 0 
			EndIf
						
			big = True
		EndIf
			
		
		; If nr of missiles = 0, player get all 3 full destruction balls
		If nr_of_missiles = 0 Then

			timer_all = MilliSecs()
			print_fullDestr = True
	
			first_bigShot = True
			timer_bigShot = MilliSecs()
			
			blueTimer = MilliSecs()
			blueFrame = True
			
			bigBangs = 3
			
			destr = destr + 1
			
			For mssl.missile = Each missile
				Delete mssl
			Next
			
			For ammo.ammoCircle = Each ammoCircle
				Delete ammo
			Next


			For yc.yellowFlash = Each yellowFlash
				Delete yc
			Next
		
			For eb.energyBar = Each energyBar
				Delete eb
			Next
			nr_of_missiles = 0

			divisor = 72
			big = True
			Exit
			
		EndIf
						
	Next
	

	If print_fullDestr = True Then
		If MilliSecs() - timer_all < 2500 Then
			Text 270,220,"Full destruction!"
		EndIf
		If MilliSecs() - timer_all > 2500 Then print_fullDestr = False
	EndIf
	
	
	If gameover = True Then
			
		For mssl.missile = Each missile
			Delete mssl
		Next
		
		For ammo.ammoCircle = Each ammoCircle
			Delete ammo
		Next

		For yc.yellowFlash = Each yellowFlash
			Delete yc
		Next
		
		For eb.energyBar = Each energyBar
			Delete eb
		Next

		Goto gameEnd
	EndIf

	; BigBang
	If KeyHit(57) Then
		
		If bigBangs > 0 Then
			blueTimer = MilliSecs()
			blueFrame = True
			bigBangs = bigBangs - 1
			amountMissiles = 1
			nr_of_missiles = 0
			
		Else
			bigBangs = 0
			blueFrame = False
		EndIf
	EndIf

	Color 0,255,0
	Line 0,450,639,450
	Line 0,30,639,30
	

	For mssl.missile = Each missile
		If mssl\x# <= 0 Or mssl\x# >= 639 Then
			Delete mssl
			nr_of_missiles = nr_of_missiles - 1
			
			ec.yellowFlash = New yellowFlash
			ec\hit = True
			
			yellowFrame = True
			yellowTimer = MilliSecs()
						
			yellowFlashes = yellowFlashes + 1
						
		EndIf
	Next


	; If 20 missiles have gone to side of the screen.
	; return the normal size shot
	If yellowFlashesInfo = False And yellowFlashes > 20 And big = True Then	
		divisor = 48
		yellowFlashesDelay = MilliSecs()
		yellowFlashesInfo = True
		yellowFlashes = 0
	EndIf
	
	If yellowFlashesInfo = True Then
		If big = True Then print_bigShot = True	;
		If MilliSecs() - 2500 < yellowFlashesDelay Then
			firstBigShot = True
			Color 255,0,0
			Text 270,185,"Big shot lost!"
		Else
			yellowFlashesInfo = False
			big = False
			firstBigShot = True
		EndIf
	EndIf


	For mssl.missile = Each missile
		mssl\x# = mssl\x# + mssl\dx#
		mssl\y# = mssl\y# + mssl\dy#

		Color 255,255,255
		Line mssl\origX#, 31, mssl\x#, mssl\y#

		Color mssl\r,mssl\g,mssl\b	
		Oval mssl\x#-mssl\ovalM/2,mssl\y#-mssl\ovalM/2,mssl\OvalM,mssl\ovalM,1		
				
		If mssl\y# >=450 Then
			gameover = True
		EndIf
	Next
			
	
	If gameover = False Then
	
		eb.energyBar = New energyBar
		eb\r# = 255
		eb\dr# = 255 / 72
		eb\g# = 0
		eb\dg# = 255 / 72
		eb\b# = 0
		eb\hit = 0
		
		yellow = 0
		For ec.yellowFlash = Each yellowFlash
			If ec\hit = True Then
				yellow = yellow + 1
				Delete ec
			EndIf
		Next
		
		energyBarLen = energyBarLen - yellow
		
		If energyBarLen <= 0 Then
			gameOver = True
			Goto gameEnd
		EndIf
		
		For i = 1 To energyBarLen
		
			eb\r# = eb\r# - eb\dr#
			eb\g# = eb\g# + eb\dg#

			Color eb\r#,eb\g#,eb\b#
	
			Line i + 263 ,460,i + 263,470

		Next
		yellow = 0
	EndIf
	
	If gameover = True Then
	
		For mssl.missile = Each missile
			Delete mssl
		Next
		
		For ammo.ammoCircle = Each ammoCircle
			Delete ammo
		Next

		For yc.yellowFlash = Each yellowFlash
			Delete yc
		Next
		
		For eb.energyBar = Each energyBar
			Delete eb
		Next

	EndIf		

	If yellowFrame = True Then
		Color 255,255,0
		Rect 0,31,640,419,1
		If MilliSecs() < yellowTimer + 2000 Then yellowFrame = False		
	EndIf

	If blueFrame = True Then
		Color 0,0,255
		Rect 0,31,640,420,1
		
		For mssl.missile = Each missile
			nr_of_missiles = nr_of_missiles - 1
			
			Delete mssl
		Next

		For ammo.ammoCircle = Each ammoCircle
			Delete ammo
		Next

		nr_of_missiles = 0
		amountMissiles = Rand(5,8)
				
		If MilliSecs() < blueTimer + 2000 Then
			blueFrame = False
			nr_of_missiles = 0
		EndIf
	
	EndIf

	Flip	

Wend

.gameEnd
endTimer = MilliSecs()

Repeat
	If MilliSecs() > endTimer + 10000 Then Exit
	Color 200,100,100
	Text (640 - StringWidth("Game Over")) / 2, 200, "Game Over"
	Flip
Until KeyHit(1) Or KeyHit(28) Or KeyHit(57)

Goto start
End

Instructions for playing:

  • Use mouse to shoot the missiles
  • Space bar for full destruction
  • When at least 2 missiles are shot with one shot. the player is rewarded by bigger shot
  • If a missile goes to the side of the screen, player’s energy decreases
  • If missile touches the line at the bottom of the window, the game ends
  • If the player clears the screen by firing, the player is rewarded by giving 3 full destruction balls

The code isn’t really commented, sorry!

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:

How to capture a screenshot from BlitzMax 2D fullscreen game

It’s of course possible to capture a screenshot from BlitzMax fullscreen game with video capture programs such Fraps. It can be also done with the in the BlitzMax code of the game. Here’s my little code to do it:

SuperStrict

HideMouse

Const JPEG_QUALITY:Int = 90

Graphics 640,480,32,1

Local screenpixmap:TPixmap = CreatePixmap(640,480,PF_RGBA8888)

While Not KeyHit(KEY_ESCAPE)
   Cls

   DrawOval (640 - 32) / 2, (480 - 32) / 2, 32, 32

   If KeyHit(KEY_C) Then
      screenpixmap = GrabPixmap(0,0,640,480)
      SavePixmapJPeg (screenpixamp,"filename.jpg",JPEG_QUALITY)	
   EndIf
 
   Flip
	
Wend
End

One must define a Pixmap to which the screen’s content is grabbed. When the contents are grabbed the pixmap is saved as JPEG picture by name “filename.jpg”.

In this example program the screenshot is taken when key c is pressed and saved as jpeg-file.

How to make a worm game

Let’s have some nostalgia here, we’ll take a look, how to make a simple worm game. The example codes have been coded in Monkey X Pro, but can be compiled with free Monkey X to desktop and HTML5. Perhaps someone will develop a fun worm game with Monkey X Pro to Android. 🙂

 

First, simple code where the player can just control the worm, that moves the very old traditional way:

Import mojo

Function Main()
    New MyApp
End

Class MyApp Extends App

    Const BG_WIDTH:Int = 640
    Const BG_HEIGHT:Int = 480

    Global devWidth:Float, devHeight:Float, scaleX:Float, scaleY:Float
    Global wormLength:Int = 8
    Global wormX:Int[8], wormY:Int[8]
    Global dx:Int, dy:Int

    Method OnCreate()

        devWidth = DeviceWidth()
        devHeight = DeviceHeight()
        
        scaleX = devWidth / BG_WIDTH
        scaleY = devHeight / BG_HEIGHT

        ' Init worm
        For Local w:Int = 0 To 7
            wormX[w] = 16*7 - w * 16
            wormY[w] = 0
        Next
        
        dx = 1
        dy = 0

        SetUpdateRate(10)
    End

    Method OnUpdate()

        If KeyHit(KEY_DOWN) Then
            dx = 0
            dy = 1
        Endif
        
        If KeyHit(KEY_UP) Then
            dx = 0
            dy = -1
        Endif
        
        If KeyHit(KEY_LEFT) Then
            dx = -1
            dy = 0
        Endif
        
        If KeyHit(KEY_RIGHT) Then
            dx = 1
            dy = 0
        Endif

        For Local w:Int = wormLength - 1 To 1 Step -1
            wormX[w] = wormX[w-1]
            wormY[w] = wormY[w-1]
        Next

        wormX[0] = wormX[0] + dx * 16
        wormY[0] = wormY[0] + dy * 16
    End
    

    Method OnRender()
        
        ' Scale the graphics
        PushMatrix()
        Scale (scaleX,scaleY)

        SetColor 180,0,180        
        DrawRect 0,0,BG_WIDTH,BG_HEIGHT

        SetColor 0,255,0

        For Local w:Int = 1 To wormLength - 1
            DrawOval wormX[w],wormY[w],16,16
        Next
                
        SetColor 255,0,0
        DrawOval wormX[0],wormY[0],16,16

        PopMatrix()
    End

End Class

Then a bit more advanced code, where the worm moves with step of one pixel, but one part of the worm is an oval that’s width and height are 16 pixels. This version is also a little game where eating the food lengthens the worm.

Import mojo

Function Main:Int()
    New MyApp
    Return 0
End

Class MyApp Extends App

    Const STARTSCREEN = 0
    Const PLAY = 1

    Const BG_WIDTH:Int = 640
    Const BG_HEIGHT:Int = 480

    Global devWidth:Float, devHeight:Float, scaleX:Float, scaleY:Float
    Global wormLength:Int = 8
    Global wormX:Int[wormLength * 16], wormY:Int[wormLength * 16]
    Global dx, dy:Int
    Global foodX:Int, foodY:Int
    Global drawFood:Bool
    Global gameState:Int
    
    Method OnCreate()

        devWidth = DeviceWidth()
        devHeight = DeviceHeight()
        
        scaleX = devWidth / BG_WIDTH
        scaleY = devHeight / BG_HEIGHT

        wormX[0] = 200
        ' Init worm
        For Local w:Int = 0 To 7 * 16
            wormX[w] = 200-w
            wormY[w] = 10*16
        Next
        
        dx = 1
        dy = 0
        
        drawFood = True
        SetUpdateRate(60)
    End

    Method OnUpdate()
        
        ' Left mouse button starts the "game"
        If MouseHit(0) > 0 Then gameState = PLAY
        
        If gameState = STARTSCREEN Then Return
        
        If drawFood = True Then
            foodX = Rnd()*(639-16)
            foodY = Rnd()*(479-16)
            drawFood = False
        Endif
        
        If KeyHit(KEY_DOWN) Then
            dx = 0
            dy = 1
        Endif
        
        If KeyHit(KEY_UP) Then
            dx = 0
            dy = -1
        Endif
        
        If KeyHit(KEY_LEFT) Then
            dx = -1
            dy = 0
        Endif
        
        If KeyHit(KEY_RIGHT) Then
            dx = 1
            dy = 0
        Endif
        
        If Abs(wormX[0] - foodX) < 16 And Abs(wormY[0] - foodY) < 16 Then
            wormLength = wormLength + 1
            ' Dynamically resize the arrays
            wormX = wormX.Resize(wormLength * 16)
            wormY = wormY.Resize(wormLength * 16)

            For Local w:Int = 0 To 15
                wormX[(wormLength-1)*16-w] = wormX[(wormLength-2)*16-w]
                wormY[(wormLength-1)*16-w] = wormY[(wormLength-2)*16-w]
            Next
            drawFood = True
        Endif
        
        For Local w:Int = (wormLength - 1) * 16 To 1 Step -1
            wormX[w] = wormX[w-1]
            wormY[w] = wormY[w-1]
        Next    

        wormX[0] = wormX[0] + dx
        wormY[0] = wormY[0] + dy
    End
    

    Method OnRender()
        
        Local alpha:Float
        
        ' Scale the graphics
        PushMatrix()
        Scale (scaleX,scaleY)

        SetColor 180,0,180        
        DrawRect 0,0,BG_WIDTH,BG_HEIGHT

        Select gameState
            Case STARTSCREEN
                SetColor 255,255,255
                SetBlend AdditiveBlend
                
                DrawText "Simple worm game",(640 - TextWidth("Simple worm game")) / 2,10
                DrawText "Use cursor keys to control",(640 - TextWidth("Use cursor keys to control")) / 2,40
                DrawText "Press left mousebutton to start",(640 - TextWidth("Press left mousebutton to start")) / 2,70
    
            Case PLAY
                SetColor 255,255,255
                SetBlend AdditiveBlend
                DrawText "Worm Length: " + wormLength,0,0
                SetColor 0,255,0
                
                SetBlend AlphaBlend
                SetAlpha 1
                SetColor 0,255,0

                For Local w:Int = 0 To wormLength - 1
                    DrawOval wormX[w * 16],wormY[w * 16],16,16
                Next
                
                SetColor 255,0,0
    
                DrawOval wormX[0],wormY[0],16,16

                SetColor 255,255,0
                DrawOval foodX,foodY,16,16
        End Select
        
        PopMatrix()
    End

End Class

If you wan’t to see how above code works in practice, click here to play (HTML5 game). Feel free to use these codes.

Where to get sound effects and music

Updated 18.6.2018

In my early times I’ve used FreeSFX for example in Ball Shooter. The sound effects and musics from FreeSFX can also be used in commercial software products. In the credits for the sound effects and musics FreeSFX must be mentioned as follows: http://www.freesfx.co.uk, when using their library.

One of the probably finest sites for free music and sound effects is Soundimage.org. There are simply amazing compositions for free, you only need to put attribution information to your product.

There are also many sites, that sell royalty free music and sound effects. Personally I like very much the music collection of Shockwave-Sound.com. There is also fine collection of sound effects at Shockwave-Sound. The customer service of Shockwave-Sound is also excellent. Remember to choose the right kind of a license when buying.

Also great site for music to YouTube videos!

Link: Shockwave-Sound