Merry Christmas!

I and my girlfriend wish you all Merry Christmas!

The intro in the video is coded in BlitzMax. Some simple math and the result is what can be seen in the video. 🙂

I could not have got to the right mood to code the intro without the presence of my girlfriend. Therefore major credit for the existence of this Christmas intro belongs to my girlfriend.

I have some more or less unfinished projects on the way for the next year. Let’s hope I’ll get something finished too in the next year! 🙂

Starfield Cube

This one goes to nostalgia, old school and to my hobby corner. 🙂

Below is the video regarding this post:

The video starts with traditional old school 2D stars, then the video continues with 3D cube that has the 2D star field as texture. Each face of the cube is transparent, so you see the starfields of the cube from different faces at once… The cube fades into the background slowly…

I haven’t done much 3D programming in Blitz3D, but this kind of code is quite easy for a beginner like me to put together.

I remember, that in the good old Amiga days I saw an amazing Amiga demo, that had star field in rotating cube. Those Amiga demos were a lot more complicated to program, since they were low level assembly code using directly Amiga’s hardware. The idea to program this in Blitz3D came from one Amiga demo music remix tune, that has little speech in it: “A star field in a box. Oh my God, it’s rotating!” 🙂

Scrolling a Picture Larger Than Visible Area in Monkey X

A little tutorial on scrolling a picture that is larger than the visible area of the screen in Monkey X.

In this example we will be using a picture of 1280 x 960 pixels in “native” resolution of 640 x 480 pixels. The source is primarily meant to Android target but works for example to desktop target too.

The picture is scrolled by moving a finger on the Android device. In order to avoid the picture to “jump” after not scrolling the picture, variables related to scrolling speed must be set to zero.

Lets have a look at the source code:

Import mojo

Function Main()
	New MyApp
End

Class MyApp Extends App

	Field bg:Image
	Global touchX,touchY:Float
	Global prevTX,prevTY:Float
	Global scaleX,scaleY:Float
	Global scrollX,scrollY:Float
	Global touchXD,touchYD:Float
	Global scroll:Bool
	
	Method OnCreate()
		' Change this to picture, you want to use
		bg = LoadImage("bg1280x960.png")

		SetDeviceWindow(640,480,0)
		scaleX = DeviceWidth() / 640.0
		scaleY = DeviceHeight() / 480.0
		
	End
	
	Method OnUpdate()

		touchX = TouchX()
		touchY = TouchY()
		
		touchX = touchX / scaleX
		touchY = touchY / scaleY
										
		If TouchDown(0) > 0 Then
			If scroll = True Then
		
				' touchXD & touchYD variables prevent the picture
				' to "jump", if scrolling is stopped and user then
				' touches somewhere on the screen

				touchXD = touchX
				touchYD = touchY
			Else
				' if not scrolling, values below are set to zero,
				' because when the scrolling starts again, otherwise
				' scrollX & scrollY could change too much because of
				' values of previous scrolling
				
				touchXD = 0
				touchYD = 0
				prevTX = 0
				prevTY = 0
			Endif
			
			scroll = True
			
			scrollX = scrollX - (touchXD - prevTX)
			scrollY = scrollY - (touchYD - prevTY)
		
			prevTX = touchX
			prevTY = touchY
		Else
			scroll = False
		Endif

	End

	Method OnRender()
		PushMatrix()
		Scale(scaleX,scaleY)
				
		If scrollX > 0 Then scrollX = 0
		If scrollX < -(1280 - 640) + 1 Then scrollX = -(1280 - 640) + 1
		If scrollY > 0 Then scrollY = 0
		If scrollY < -(960 - 480) + 1 Then scrollY = -(960 - 480) + 1
		
		DrawImage bg,scrollX,scrollY
		PopMatrix()
	End
	
End Class

Examine the source code and learn. Source code license: Public Domain.

Below is a video related to this post:

 

Unique Random Integers

For example in my Memorable Ladies games it is the case that I needed a method that gives a random integer for example between 0…31 so that any integer that has once been drawn, doesn’t get drawn anymore.

One way to handle this (how I didn’t do it) could be for example to use Rand-function (depending on programming language one is using) that gives an integer between nm (m > n) and make a list of numbers that are already drawn and use Rand-function again between nm, if the integer given by Rand has already been drawn.

But in the worst case this could lead to infinite loop… In practice probably not, though. In order to avoid the infinite loop (the case where Rand function gives repeatedly a number that has already been drawn), one could for example increase the drawn number by 1 until unused number is found or go on to m and if needed start from n and increase the value by 1 until unused number is found.

In Memorable Ladies games speed isn’t critical factor, when the numbers are drawn, so in this particular case the routine doesn’t necessarily need to be fast. In addition the amount of data can be considered very small.

What I came up, was something where every random number is (in practice) necessarily unique and without possibility to get stuck on infinite loop.

The idea goes like this:

  • Let us assume that we need n integers between 0….n – 1

  • Make a list of numbers with type ”number” where type ”number” has as a member an integer between 0…n – 1 (the numbers can be in increasing order)

Loop

  • Draw an integer between 0…n – 1. Let this be i.

  • Get from the list ith ”number” element and get the integer that is in that ”number” element as a member and then delete that element from the list

  • Decrease n by 1

  • Repeat until list is empty

Even if in the original list of type ”number” the integers are ordered from 0 to n – 1, one would get this way an unique random integer each time (except if Rand gives zero (0) n times).

There are more sophisticated ways to do this, especially if the set of needed integers is large.

id-100271132

Image courtesy of Stuart Miles at FreeDigitalPhotos.net

For the sake of nostalgia… Let be mentioned what kind of random number generator I have used with Amiga with MC68040’s built-in FPU in assembly. As such this doesn’t give an unique random number, though.

(In the source code of my old Quest of Love demo you can see this in practice)

Let us assume, that we need an integer between 0…n.

The idea:

  • Get the vertical beam value from $dff006

  • ”Scale” the previous value with desired integer in order to get it big enough

  • Use FPU’s fsin instruction (sine function) to that value

  • Use FPU’s fabs instruction (absolute value) to the value we got in the previous step

    Now we have a floating point value between 0…1

  • Multiply that value by n

  • Convert the value of previous step to integer

That’s it!

If we would like to have an integer between n…m (0 < n < m) the additional steps would be

  • Let z = mn

  • Add z to the integer we got in the last step earlier

The two steps above are only the idea to get the value to the desired interval; in assembly there are not variables at all as we know them in programming languages of higher level.

This idea probably gives you a good idea how to get random integers with Monkey X’s Rnd() that gives a random value between 0…1. 🙂

Probably the most common way is to do something like following:

value = Rnd() * 100 Mod n

This gives a random integer between 0…n – 1 (0 < n <= 100 above). Notice that in the sentence above there are two possibilities to get zero: 1) Rnd() gives 0, 2) Rnd() gives 1 and n = 100.

A Bit On Strategy On Playing The Memorable Ladies

Updated: With the new release the game are more playable and easier.

At first Memorable Ladies may seem difficult to play, but with some strategy, playing the game becomes easier.

The key is patience.

If it seems to hard to find pairs, wait for a few seconds and a bonus appears. Click the bonus and you might get to see some of the numbers, mark those numbers and try to find pairs to them.

If one marks numbers, one can see those numbers until the pair for the numbers is found.

With this kind of strategy you’ll get started without making too many mistakes at the very beginning of the game.

Update: Video of the new release of the bundle:

Memorable Ladies comes as bundle with Memorable Ladies Lite.

Of course depending on how well you can memorize the numbers, you can develop your own strategy.

Memorable Ladies Lite has different concept of a memory game, it’s not about numbers. The concept  in the game is actually used for improving memory of people having difficulties with it.

Shortly about my computer gaming history (1984 – 1987)

It all started in 1984, when in Finland the prices of home computers were significantly reduced. One could buy a home computer for under 1000 Finnish marks. That’s about 167 euros. My parents bought me a Commodore Vic 20, that costed 999 Finnish marks.

I really liked the first (and at the time only) game I got with Vic 20: Radar Rat Race. See below a picture of the module:

rrr

Below is a YouTube video from the game:

I remember that the game had some bug though: When one got far enough, playing the game became somehow impossible.

Another game I liked, was Fire Galaxy. Below is a little video from the game:

A bit later my Commodore 64 times began.

At those days I saw many C64 games. The most remarkable games from me at the time were Ultima IV: Quest of the Avatar and Ultima V: Warriors of Destiny. Ah, the story of the games and all the philosophy in them… Especially the three principles (of the eight virtues): Love, Truth and Courage. These define the good in the games. Evil is defined as ”principles” of the opposite of the mentioned: hate, falsehood and cowardice.

As to choosing the character in these games, I always  answered the philosophical questions as myself, not by trying to get my character certain quality. As a result of this, my character became to be of the weakest profession in the game: The shepherd.

Intro from C64’s Ultima IV:

Intro from Ultima V (C64/C128):

When running the game on the C64, there isn’t background music, the sound effects only, because the game is so huge, that C64 hasn’t enough memory for the music.

Boulder Dash and Boulder Dash II were my favourites too.

Below is a video from both games:

I played a lot also Delta, a game by a Finnish programmer Stavros Fasoulas (music by famous Rob Hubbard), see the video below:

One good game for the C64 that teaches also some strategy is Paradroid. There are many remakes of this classic game also to PC.

Commodore 64’s Tetris has incredible music!

There are many games in the world of Commodore 64 that I wasn’t really interested in, but the music is incredible. As an example I must mention Cybernoid II:

Some games were simple and fun to play with good graphics and music, but let the player down with one thing: There was no ending of anykind! I remember playing ”endlessly” both International Karate and IK+ with black belt, but the games just went on and on…

As an youngster I decided, that if I was ever to program a computer game, it would always have an ending.

One incredible C64 game I remember, is Wizball:

Some C64 games I liked, were kind of games that often other people didn’t like. As an example let me mention Armourdillo:

the music of Armourdillo:

If my memory serves my right, I played Armourdillo with music.

Another game, that I almost forget as an example of a game that other people often didn’t seem to like was Master of Lamps:

For the third example of game that the other people didn’t seem to like, but was fun to me as a kid is Poster Paster:

As to Defender of the Crown, I liked C64’s version more than Amiga’s, altough the Amiga version has better graphics and music.

One very good game I almost forgot to the C64 is Thrust:

With Spy vs. Spy II we both, I and one of my friends had lots of fun. We made lots of traps to each other in order to get to that submarine and win the game. There might be one “room” filled with so many traps while we were playing the game, that in practice there was only one special way to get through the room.

One special game from the year 1984: Ghostbusters. Why this game was special? At the time I was Vic 20 user and to me (and to many others) it was simply amazing to hear so authentic speech from a movie in a computer game! What a miracle computer C64 was: With it one can even listen little parts of authentic speech from a movie in a game. How we were amazed!

A game that made many of us laugh, when we were kids, is Super Pipeline II. A fun quality game with nice music and funny graphics and not too hard to finish.

One funny detail from the past in the 80s in our C64 times was C64’s Commando; first the other boys told how hard game Commando is. Eventually one of my friends tried to play Commando for the first time in his life (and this was the first time he saw the game too) and this one friend managed to finish the game from the very beginning to the end at his first try — how the other boys were confused. “Yeah, really hard game!”, he said with little sarcasm.

A game that required fast thinking and good reflexes and gave us visually something new compared to what we had seen before was Cosmic Causeway:

For the end let be mentioned some early gems in the world of Commodore 64 games: M.U.L.E., Archon and Archon II.

Videos below:

Perhaps I come back later with my Amiga gaming history…

Image Buttons with MaxGUI

This time a little tutorial on how to use images as buttons in BlitzMax with MaxGUI module.

For beginners the most important thing is to understand the event loop.

The image buttons are made with panels that have background graphics. The source below clarifies the rest.

In order to try the code, download the following images:

demobg

Button1

Button2

Button1sel

Button2sel

 

 

Save the images to the same folder where you save the code below. In order to get the code working with images, name the violet background picture as “demobg.png” and the blue “Button” labeled images as “button1.png” and “button2.png” and the rest “Button” labeled images as “button1sel.png” and “button2sel.png”.

The code uses gadget sensitivity to change the button images when touched with the mouse.

Import MaxGui.Drivers
Import MaxGUI.XPManifest

Strict

Global background:TPixmap=LoadPixmap("demobg.png")

Global gfxButton1:TPixmap = LoadPixmap("button1.png")
Global gfxButton2:TPixmap = LoadPixmap("button2.png")

Global gfxButton1Sel:TPixmap = LoadPixmap("button1sel.png")
Global gfxButton2Sel:TPixmap = LoadPixmap("button2sel.png")

Global window:TGadget = CreateWindow("Image Buttons Demo",40,40,304,228,Null,WINDOW_TITLEBAR|WINDOW_CLIENTCOORDS|WINDOW_CENTER)
Global panel:TGadget

panel=CreatePanel(0,0,ClientWidth(window),ClientHeight(window),window)
SetPanelPixmap(panel,background)


Local pnlButton1:TGadget = CreatePanel(98,40,107,47,panel,PANEL_ACTIVE,"PanelButton1")
Local pnlButton2:TGadget = CreatePanel(98,100,107,47,panel,PANEL_ACTIVE,"PanelButton2")

Local lblButtonPressed:TGadget = CreateLabel("",20,20,100,34,panel)

SetPanelPixmap(pnlButton1, gfxButton1)
SetPanelPixmap(pnlButton2, gfxButton2)

SetGadgetSensitivity(pnlButton1,EVENT_MOUSEENTER|EVENT_MOUSELEAVE)
SetGadgetSensitivity(pnlButton2,EVENT_MOUSEENTER|EVENT_MOUSELEAVE)

Repeat

	WaitEvent()

	Select EventID()
	
		Case EVENT_WINDOWCLOSE, EVENT_APPTERMINATE
			End
		
		Case EVENT_MOUSEENTER
			Select EventSource()
				Case pnlButton1
					SetPanelPixmap(pnlButton1, gfxButton1Sel)
					SetGadgetText lblButtonPressed,""

				Case pnlButton2
					SetPanelPixmap(pnlButton2, gfxButton2Sel)
					SetGadgetText lblButtonPressed,""

			End Select
		
		Case EVENT_MOUSELEAVE
			Select EventSource()
				Case pnlButton1
					SetPanelPixmap(pnlButton1,gfxButton1)
				Case pnlButton2
					SetPanelPixmap(pnlButton2,gfxButton2)
			End Select
			
		Case EVENT_MOUSEUP              'checking for a mouseup event
			Select EventSource()    'where does it come from
				Case pnlButton1
					SetGadgetText lblButtonPressed,"Button 1 pressed."
					
				Case pnlButton2
					SetGadgetText lblButtonPressed,"Button 2 pressed."
					
			End Select
			
	EndSelect

Forever

Feel free to use this code.

Missile Attack in Monkey X

Again, some nostalgia. In older blog post I presented a shortened version of my old implementation of Amiga’s “Missile Attack”. This night I made the game in Monkey X and the source can be directly compiled to Android target.

The game is quite simple one: Just shoot the missiles before they get to the bottom of the screen.

If a missile goes to the left or right side of the screen, you see the colors of the background changing — and also when you fire a shot. This gives the game more life. 🙂

Below is the source code:

Import mojo2

Function Main()
	New MyApp
End

Class Missile
	Field x:Float		' x-coordinate of a missile
	Field y:Float		' y-coordinate of a missile
	Field startx:Float
	Field addx:Float
	' the color of the missile
	Field red:Float
	Field green:Float
	Field blue:Float
End Class

Class MyApp Extends App

	Field bg:Image
	Global canvas:Canvas
	Global nrOfMissiles:Int
	Global mx:Float, my:Float
	Global shot:Bool
	Global gameOver:Bool = False	
	Global px:Float, py:Float, pr:Float
	Global scalex:Float, scaley:Float
	
	Global missileList := New List<Missile>	' build a missile list
	
	Method OnCreate()
		SetDeviceWindow(640,480,0)
		canvas = New Canvas
		shot = False
		nrOfMissiles = 0
		bg = Image.Load("bg.png",0,0)
		scalex = DeviceWidth() / 640.0
		scaley = DeviceHeight() / 480.0
		SetUpdateRate(60)
	End
	
	Method OnUpdate()

		If gameOver = False Then
			
			If nrOfMissiles < 8 Then
			
				Local mssl:Missile = New Missile
				mssl.x = Rnd(100,539)
				mssl.startx = mssl.x
				mssl.y = 0
				mssl.addx = Rnd(-2,2)
				mssl.red = Rnd(0.2,1)
				mssl.green = Rnd(0.2,1)
				mssl.blue = Rnd(0.2,1)
				missileList.AddLast(mssl)
			
				nrOfMissiles = nrOfMissiles + 1
			Endif

			If TouchHit(0) > 0 Then
				mx = TouchX() / scalex
				my = TouchY() / scaley
				pr = 4
				px = mx
				py = my
				shot = True
			Endif
		
		Endif
		
	End

	Method OnRender()

		canvas.PushMatrix()
		canvas.Scale(scalex, scaley)
		
		canvas.DrawImage bg,0,0
		
		If gameOver = False Then
			
			If nrOfMissiles > 0 Then
								
				For Local mssl:Missile = Eachin missileList
				
					If mssl <> Null Then
						canvas.SetColor mssl.red,mssl.green,mssl.blue	
						canvas.DrawLine(mssl.startx,0,mssl.x,mssl.y)
						mssl.y = mssl.y + 1
				
						If mssl.y >= 479 Then gameOver = True
						
						mssl.x = mssl.x + mssl.addx
					
						If mssl.x < 0 Or mssl.x > 639 Then
							missileList.Remove(mssl)
							nrOfMissiles = nrOfMissiles - 1
						Endif
					
					Endif
				Next
			Endif
		
			If shot = True Then
				canvas.SetColor 0,0,1
				canvas.DrawCircle(px,py,pr)
			Endif

			If shot = True And pr < 50 Then pr = pr + 1 Else shot = False
			
			For Local mssl:Missile = Eachin missileList
				Local removeMissile:Bool = False
				
				If mssl <> Null And shot = True Then
				
					' Mathematical way to determine if player's shot has hit the missile...
					For Local angle:Int = 0 To 360
						If Abs((px + Cos(angle) * pr) - mssl.x) <= 1 And Abs((py + Sin(angle) * pr) - mssl.y) <= 1 Then
							removeMissile = True
							score = score + 1
							Exit
						Endif
					Next
					
					If removeMissile = True Then
						missileList.Remove(mssl)
						nrOfMissiles = nrOfMissiles - 1
					Endif
					
				Endif

			Next

		Else
			canvas.SetColor 1,1,1
			canvas.DrawText "Game Over",(640 - 9 * 8) / 2, (480 - 8) / 2
		Endif
		
		canvas.Flush()
		canvas.PopMatrix()	
	End
	
End Class

Source code license: GNU General Public License 3.0.

Below is the video of the game on Android tablet and on computer screen:

I may make a better version of the game later on this summer…

How to Make a Worm Game Part 2

(Updated 03/05/2017 with improved source code and new video)

A little update to older post. As the title of the post says we’re making a worm game (in Monkey X). In this version the worm is controlled by touching the screen keeping in mind that the game is really aimed to Android.

I’ll explain here how the worm is controlled.

If you move your finger ”up” from the worm’s head, the worm goes to that direction and respectively to other directions.

See the video below:

The direction is determined by comparing two subsequent update rounds’ TouchX() and TouchY() coordinates.

The test can’t be straightforward TouchX() or TouchY() test, because the player probably won’t move his/her finger absolutely to one of the four directions the worm is to be controlled.

This is why there is another test in controlling the worm: The absolute values of difference of the two subsequent update round’s TouchX() and TouchY() coordinates. If the player wants to control the worm ”up”, the player probably has moved his/her finger more vertically than horizontally.

See the source code:

Import mojo

Function Main:Int()
	New MyApp
	Return 0
End

Class MyApp Extends App

	Const STARTSCREEN = 0
	Const PLAY = 1

	Field gfxBG:Image
	
	Const BG_WIDTH:Int = 640
	Const BG_HEIGHT:Int = 480

	Field devWidth:Float, devHeight:Float, scaleX:Float, scaleY:Float
	Global wormLength:Int = 8
	Global wormX:Int[wormLength * 17], wormY:Int[wormLength * 17]
	Field dx, dy:Int
	Field foodX:Int, foodY:Int
	Field drawFood:Bool
	Field gameState:Int
	Field touchX:Float, touchY:Float
	Field prevTX:Float, prevTY:Float
	Field prevHorChange:Float, prevVerChange:Float

	Method OnCreate()
	
		gfxBG = LoadImage("bg.png")

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

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

	Method OnUpdate()
		
		If TouchHit(0) > 0 Then gameState = PLAY
		
		If gameState = STARTSCREEN Then Return
		
		
		If TouchDown() > 0 Then
			touchX = TouchX() / scaleX
			touchY = TouchY() / scaleY

			' Calculate the differences between current and previous update rounds' touch values		
			If prevTX < touchX Then prevHorChange = Abs(prevTX - touchX) Else prevHorChange = Abs(touchX - prevTX)
			If prevTY < touchY Then prevVerChange = Abs(prevTY - touchY) Else prevVerChange = Abs(touchY - prevTY)
			
			' Up
			If touchY - prevTY < 0 And Abs(touchY - prevTY) > prevHorChange Then
				dx = 0
				dy = -1
			Endif
		
			' Down
			If touchY - prevTY > 0 And Abs(prevTY - touchY) > prevHorChange Then
				dx = 0
				dy = 1
			Endif
			
			' Left
			If touchX - prevTX < 0 And Abs(touchX - prevTX) > prevVerChange Then
				dx = -1
				dy = 0
			Endif
		
			' Right
			If touchX - prevTX > 0 And Abs(prevTX - touchX) > prevVerChange Then
				dx = 1
				dy = 0
			Endif

			prevTX = touchX
			prevTY = touchY
			
		Endif
		
		If drawFood = True Then
			foodX = Rnd()*(639-17)
			foodY = Rnd()*(479-17)
			drawFood = False
		Endif

		If Abs(wormX[0] - foodX) < 17 And Abs(wormY[0] - foodY) < 17 Then
			wormLength = wormLength + 1
			' Dynamically resize the arrays
			wormX = wormX.Resize(wormLength * 17)
			wormY = wormY.Resize(wormLength * 17)

			wormX[(wormLength-1)*17] = wormX[(wormLength-2)*17]
			wormY[(wormLength-1)*17] = wormY[(wormLength-2)*17]

			drawFood = True
		Endif
		
		For Local w:Int = (wormLength - 1) * 17 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()
				
		' Scale the graphics
		PushMatrix()
		Scale (scaleX,scaleY)

		DrawImage gfxBG,0,0

		Select gameState
			Case STARTSCREEN
				SetColor 180,120,180
				SetBlend AdditiveBlend
				
				DrawText "Simple worm game",(640 - TextWidth("Simple worm game")) / 2,10
				DrawText "Move your finger on the screen to control the worm",(640 - TextWidth("Move your finger on the screen to control the worm")) / 2,40
				DrawText "Touch anywhere to start",(640 - TextWidth("Touch anywhere to start")) / 2,70
	
			Case PLAY
				SetColor 180,120,180
				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 * 17],wormY[w * 17],17,17
				Next
				
				SetColor 255,0,0
	
				DrawOval wormX[0],wormY[0],17,17

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

End Class

Source code license: Public domain.

Notice, that in this code one part of the worm’s “body” is 17 x 17 pixels, but the worm moves with step of one pixel and can be controlled with accuracy of one pixel. The example code above is simple implementation of this kind of worm game. The down side of the code is, that the sizes of the arrays for x– and y-coordinates depend of the length of the worm in pixels.

I may come back later with some implementation with different concept of moving the worm of which “body” is built with “blocks” of different size than one pixel, but the worm is controlled  with accuracy of one pixel, without using arrays of which lengths depend on the size of the worm in pixels.

Scaling Bitmap Font Made with Font 2 PNG in Monkey X part 2

Just a quick update to old code, this one can be compiled to Android target.

Source code copyright: public domain.

Import mojo
Import brl.databuffer


Function Main()
	New MyApp
End

Class MyApp Extends App
	
	Const FONT_HEIGHT:Float = 49

	Field gfxBG:Image
	Global angle:Float	
	Global fontDat:DataBuffer
	Global gfxFont:Image

	Global textScale:Float
	
	Method OnCreate()

		gfxFont = LoadImage("font.png")
		
		fontDat = New DataBuffer(95*4*2) ' 95 characters, for each character two 4 byte integers
		fontDat = DataBuffer.Load("monkey://data/font.dat")
				
		gfxBG = LoadImage("bg.png")
		
		SetDeviceWindow(1024,768,0)
		
		angle = 180
		SetUpdateRate(60)
	End
	
	Method OnUpdate()
		textScale = (2 - Cos(angle))
		angle = angle + 4
	End
	
	Method OnRender()
		SetBlend AlphaBlend
		SetAlpha 1
		DrawImage gfxBG,0,0
		SetBlend AdditiveBlend
		'                             x-coordinate                                         y-coordinate                           scale
		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.PeekInt((chrs[i]-32)*4*2), 0, fontDat.PeekInt((chrs[i]-32)*4*2 + 4), FONT_HEIGHT,0,scale,scale,0
			x = x + fontDat.PeekInt((chrs[i]-32)*4*2 + 4) * 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.PeekInt((chrs[i]-32)*4*2 + 4)
		Next
		
		Return length
	End

End Class

Below is a YouTube video of this post: