QB64 has been successfully ported to Google Androidhttp://www.qb64.net/forum/index.php?topic=10919.msg91805#msg91805
DECLARE SUB AINPC (currentNPC%) ' A sub that controls the movement of NPCsDECLARE SUB Engine.DrawNPCs () ' A sub that draws NPCsDECLARE SUB Engine.DrawPlayer (Player AS ANY) ' A sub that draws the player spriteDECLARE SUB Engine.UpdatePlayer (Player AS ANY, Direction%) ' A sub that moves the playerDECLARE SUB Engine.UpdateCamera (Level AS ANY, Player AS ANY) ' A sub that moves the camera according to player positionDECLARE SUB Engine.DrawScreen () ' A sub that calls all other drawing subsDECLARE SUB Engine.DrawMap () ' A sub that draws the map tilesDECLARE SUB InitVariables () ' A sub that initiates game variablesDECLARE SUB InitMap (mapcon AS STRING) ' A sub that loads a map you specifyDECLARE SUB MainLoop () ' The main loopDECLARE SUB MainScreen () ' Extra sub for intro screen (simple and boring)DECLARE FUNCTION Engine.TileCollide% (Player AS ANY)'============================================================================DECLARE SUB Engine.DoCollision (currentNPC%)' Sub to detect collison between' Hero and any moving/static sprite'============================================================================DEFINT A-Z'$DYNAMIC'========Declare types hereTYPE SpriteType Typ AS INTEGER ' Used to flag the type of sprite (like for different NPCs) X AS SINGLE ' Used to flag the X position of the sprite Y AS SINGLE ' Used to flag the Y position of the sprite XV AS SINGLE ' Used to flag the X velocity (speed) of the sprite YV AS SINGLE ' Used to flag the Y velocity (speed) of the sprite Frame AS INTEGER ' Used to flag the sprite frame to draw Move AS INTEGER ' Used to flag the movement of a sprite (moving or not) AI AS INTEGER ' Used to flag AI mode of the sprite (like different NPCs) Active AS INTEGER ' Used to flag is a sprite is active or not Money AS INTEGER ' Various gameplay related variables not used HP AS INTEGER ' in this engine. Item AS INTEGER Direction AS INTEGER ' used to flag the direction of the sprite TileX AS INTEGER ' Use to flag the tile on which the sprite is TileY AS INTEGEREND TYPETYPE LevelType ' Our level (map) type Xmax AS INTEGER ' Maximum number of tiles a map has in x direction Ymax AS INTEGER ' Maximum number of tiles a map has in y direction CamX AS INTEGER ' Pixel*Pixel camera X position CamY AS INTEGER ' Pixel*Pixel camera Y position TileX AS INTEGER ' Tile postion of Camera TileY AS INTEGER ' Calculated by CamX\TileSize Xpos AS INTEGER ' Pixel position inside the tile Ypos AS INTEGER ' (used for Scrolling)END TYPETYPE MapLayerType ' Map layers BaseL AS INTEGER ' Base Layer Implemented FringeL AS INTEGER ' Fringe Not Implemented ObjectL AS INTEGER ' Object Not ImplementedEND TYPE'========Declare constants hereCONST NULL = 0CONST TRUE = 1CONST FALSE = 0' Screen constantsCONST ScrnXmax = 640, ScrnYmax = 480CONST ScrnXmid = ScrnXmax / 2, ScrnYmid = ScrnYmax / 2CONST ScrnXmin = 0, ScrnYmin = 0' Tile dimensionsCONST TileW = 20CONST TileH = 20' Number of Tiles per screen 640\20=32,480\20=24' Used to calculate the DrawMap subCONST ScrnTileXmax = ScrnXmax \ TileWCONST ScrnTileYmax = ScrnYmax \ TileH' Directional constants for easy sprite handling' DN=Neutral(not Moving), DR=Right...........CONST DN = 0, DR = 1, DU = 2, DL = 3, DD = 4' Our map array is declared here (must be able to fit the largest map)REDIM SHARED Map(50, 50) AS MapLayerTypeDIM SHARED Level AS LevelType ' Our Level (map)DIM SHARED Hero AS SpriteType ' Our HeroDIM SHARED NPC(15) AS SpriteType ' Our Non-Player Controled Sprites' Other shared variablesDIM SHARED OldX, OldY, mapcon$, OldDirDIM SHARED FPS, FPS2, OldNPCX, OldNPCYDIM SHARED MapXMax AS INTEGER, MapYMax AS INTEGER' GDK related variablesDIM SHARED TSheet AS Tileset ' The tile sheetDIM SHARED TileXY(1 TO 90) AS XY ' Tile positions on the sheet allways start from 1DIM SHARED SprSheet AS Sprite ' The sprite sheetDIM SHARED KB(1) AS KeyBoardState' Sets up the screenGDK_Screen_SetRes MainScrn&, ScrnXmax, ScrnYmax, 32, 0' Loads the tile and sprites sheet, and sets the transparent color' for spritesGDK_Tileset_New TSheet, "tiles.bmp", TileXY(), 16, 2, _RGB(0, 0, 0), 1, 1GDK_Sprite_New SprSheet, "sprites.bmp", 16, 1, 16, 1GDK_Sprite_Show SprSheetGDK_Sprite_SetAlpha SprSheet, &HFF00FFDIM SHARED FNT AS SPRITEFONTGDK_SpriteFont_New FNT, "24bitFNT3.BMP"GDK_SpriteFont_SetAlpha FNT, _RGBA(0, 0, 0, 0)MainScreen ' loads introducing screenInitMap "MAP1.MAP" 'Read our map array (from a file)InitVariables 'Initialize our variablesMainLoop ' Main LoopENDSUB AINPC (currentNPC) STATIC' This is a sub which moves the NPCs. Their direction' is changed randomly). In a proper game you should create a' real artificial smarts (AS) algorithm, where NPCs will' react on the hero.NPC(currentNPC).Move = TRUE ' in this example NPC are always moving.RANDOMIZE TIMERChangeDirec = INT(RND * 80) + 1' If a number randomized from 1 to 200 turns out to be 20' current NPC direction is changed.IF ChangeDirec = 20 THEN NPC(currentNPC).Direction = INT(RND * 4) + 1' According to direction move the NPC and check if the NPC is out' of map boundary.SELECT CASE NPC(currentNPC).Direction CASE 4 NPC(currentNPC).X = NPC(currentNPC).X + NPC(currentNPC).XV IF Engine.TileCollide(NPC(currentNPC)) THEN NPC(currentNPC).X = NPC(currentNPC).X - NPC(currentNPC).XV IF NPC(currentNPC).X > (Level.Xmax * TileW) - TileW THEN NPC(currentNPC).X = (Level.Xmax * TileW) - TileW CASE 2 NPC(currentNPC).Y = NPC(currentNPC).Y - NPC(currentNPC).YV IF Engine.TileCollide(NPC(currentNPC)) THEN NPC(currentNPC).Y = NPC(currentNPC).Y + NPC(currentNPC).YV IF NPC(currentNPC).Y < ScrnYmin THEN NPC(currentNPC).Y = ScrnYmin CASE 3 NPC(currentNPC).X = NPC(currentNPC).X - NPC(currentNPC).XV IF Engine.TileCollide(NPC(currentNPC)) THEN NPC(currentNPC).X = NPC(currentNPC).X + NPC(currentNPC).XV IF NPC(currentNPC).X < ScrnXmin THEN NPC(currentNPC).X = ScrnXmin CASE 1 NPC(currentNPC).Y = NPC(currentNPC).Y + NPC(currentNPC).YV IF Engine.TileCollide(NPC(currentNPC)) THEN NPC(currentNPC).Y = NPC(currentNPC).Y - NPC(currentNPC).YV IF NPC(currentNPC).Y > (Level.Ymax * TileH) - TileH THEN NPC(currentNPC).Y = (Level.Ymax * TileH) - TileH CASE ELSEEND SELECTEND SUBSUB Engine.DoCollision (currentNPC)' NOT IMPLEMENTED YETEND SUBSUB Engine.DrawMap STATIC'Notes:' 1.Mod our Cam with Tile size to get correct offset inside the tile' 2.Uses the constants TileH, TileW, ScrnTileXmax, ScrnTileYmax' 3.Constants are declared at the module level' ==========BaseLayer====================' Calculate the first tile to draw' depending on the camera position.Level.TileX = Level.CamX \ TileWLevel.TileY = Level.CamY \ TileH ' Get the offset inside the tileLevel.Xpos = Level.CamX MOD TileW ' Position in the tile (0 to 19)Level.Ypos = Level.CamY MOD TileH ' 20*20 tile size (change to fit your needs)' Example:' Formula for X (the loop below)' (X*TileW) => screen coordinates (0 to 640 step 20)' (X*TileW)-Level.Xpos => tile offset that we have to show from 0 to 20-Xpos' Assuming Level.Xpos = 4' So if X*TileW=0 then (X*TileW)-Level.Xpos=-4' We start drawing from -4 to -4+20' ie, first row of tiles is placed from pixel X = -4 to X = 16,' then the next row of tiles is placed from X = 17 to X = 17+16' Hope you understand.' Level.XPos and Level.YPos change as you move throught the map, always' calculating the right OFFSET!' Loop through our visible screen and draw tiles on it.' Always start from Level.TileX and Level.TileY (depends' on the camera position).FOR XT = 0 TO ScrnTileXmax ' 32 tiles (640/20) FOR YT = 0 TO ScrnTileYmax ' 24 tiles (480/20) ' We flag which tile to draw. Note how the Map array is used. tilep = Map(XT + Level.TileX, YT + Level.TileY).BaseL ' GDL drawing function (tilep > 0 condition is used for safety). IF tilep > 0 THEN GDK_Tile_Draw TSheet, tilep, TileXY(), (XT * TileW) - Level.Xpos, (YT * TileH) - Level.Ypos NEXT YTNEXT XT'==========FringeLayer====================' NOT IMPLEMENTED'==========ObjectLayer====================' NOT IMPLEMENTEDEND SUBSUB Engine.DrawNPCs STATIC' This is the main sub where we move our NPCs' Every 7th loop we change Frame2 which' animates the NPCs (with Frame1 we control' the speed of animation.Frame1 = (Frame1 MOD 7) + 1IF Frame1 = 3 THEN Frame2 = (Frame2 MOD 2) + 1FOR countNPC = 1 TO 15 ' We loop through all of our 15 NPC and move/paste them. ' 15 can be a variable (different for each level, ' different number of enemies for each level). OldNPCX = NPC(countNPC).X ' we save old position of NPCs for collision OldNPCY = NPC(countNPC).Y ' purposes Frame3 = 1 IF NPC(countNPC).Move = TRUE THEN Frame3 = Frame2 ' if the NPC is walking (Move = TRUE) then Frame3 variable ' is Frame2 which changes from 1 to 2. ' According to NPC direction we flag the proper ' frame (ie. for down, the Frame is either 9 or 10 (8 + 1 or 8 + 2). SELECT CASE NPC(countNPC).Direction CASE 1 NPC(countNPC).Frame = 8 + Frame3 CASE 2 NPC(countNPC).Frame = 10 + Frame3 CASE 3 NPC(countNPC).Frame = 12 + Frame3 CASE 4 NPC(countNPC).Frame = 14 + Frame3 END SELECT AINPC countNPC ' AI for NPCs. We use AI on the current NPC. ' Note how the AINPC sub is declared and how countNPC ' value is passed into the AINPC sub! Engine.DoCollision countNPC ' we check collision from a NPC toward ' other NPCs or Hero IF NPC(countNPC).Frame <> 0 THEN ' safety precaution GDK_Sprite_DrawXY SprSheet, (NPC(countNPC).X - Level.CamX), (NPC(countNPC).Y - Level.CamY), 0, NPC(countNPC).Frame END IFNEXT countNPCEND SUBSUB Engine.DrawPlayer (Player AS SpriteType) STATIC'Notes:'1. Uses the Directional Constants defined at module level (DN,DR...)IF Player.Direction <> OldDir THEN ' if Player changes direction DirChanged = 1 ' glag it to change base frameELSE ' Delays frame rotation for sprite(Electric legs :)) DirChanged = (DirChanged MOD 4) + 1 'Try to REM this :)END IFIF DirChanged = 1 THEN IF Player.Move = TRUE THEN ' Animate only if player moves. Frame = (Frame MOD 2) + 1 SELECT CASE Player.Direction CASE DR 'Calculate the Frame of the sprite to draw Player.Frame = Frame + 6 ' frame is 7 or 8 CASE DU Player.Frame = Frame + 2 ' frame is 3 or 4 CASE DL Player.Frame = Frame + 4 ' frame is 5 or 6 CASE DD Player.Frame = Frame ' frame is 1 or 2 CASE ELSE END SELECT Player.Move = FALSE ' Stops the Player from Swaying ' his arms while not moving. END IFEND IF' Formula: X (same goes for Y)' Player.X-Level.CamX' Just puts the player at the Center of screen' So if Player.X=500 then CamX=Player.X-ScrnYmid(ScrnYmid=Middle of 640=>320)' CamX:500-320=180' Player.X=500' Xcenter:500-180=320 (ScrnXmid)' Same goes for Y' See Engine.UpdateCamera for more Details :)IF Player.Frame <> 0 THEN 'Just to be sure to prevent errors GDK_Sprite_DrawXY SprSheet, (Player.X - Level.CamX), (Player.Y - Level.CamY), 0, Player.FrameEND IFOldDir = Player.DirectionEND SUBSUB Engine.DrawScreen STATICEngine.DrawMap ' Draws our map onto screenEngine.DrawPlayer Hero ' Draws our hero onto screenEngine.DrawNPCs ' Pastes our NPCs onto screen'RelFont256 VARSEG(Page(0)), 0, 0, "FPS:" + STR$(FPS2), FALSE, Font(), FontIndex()END SUBFUNCTION Engine.TileCollide (Player AS SpriteType) STATIC' Crappy tile*tile collision detection ;)' Returns TRUE if collision is detected, FALSE if not' Init the function to be FALSE (no collision)Engine.TileCollide = FALSE' 4 checks are done to be sure ;)' Up-Left corner of heroX = Player.X + 1 'Add 1 for 20*20 Box CollisionY = Player.Y + 10GOSUB CheckForTile 'check for the tile' Up-Right corner of heroX = Player.X + 19Y = Player.Y + 10GOSUB CheckForTile' Down-Right corner of heroX = Player.X + 19Y = Player.Y + 19GOSUB CheckForTile' Down-Left corner of heroX = Player.X + 1Y = Player.Y + 19GOSUB CheckForTileEXIT FUNCTION' Check for collision:(Bounding BoxType)CheckForTile:TX = X \ TileW 'Player.X\TileW=TileXTY = Y \ TileH 'Player.Y\TileH=TileHSELECT CASE Map(TX, TY).BaseL ' base layer check CASE IS > 5 ' tiles above 5 in the tile sheet are collidable Engine.TileCollide = TRUE CASE ELSE ' No collisionEND SELECTRETURN ' Check for next CoordEND FUNCTIONSUB Engine.UpdateCamera (Level AS LevelType, Player AS SpriteType) STATIC' Updates CamX, CamY in relation to Player.X, Player.Y to achieve' ZELDA style scrolling engine.' Sample Code:' CODE: CASE DR' Right Direction of movement' CODE: Level.CamX = Player.X - ScrnXmid' Center our player and Moves the camera to where' the player is going.' ie, Assume: Player.X = 1200, ScrnMid = 320 (constant, middle of the screen)' Level.CamX: 1200-320=880' to get the Level.TileX:' Level.TileX = Level.CamX\TileW=1040\20=44 (This will be used with Engine.DrawMap)' CODE: IF Level.CamX < ScrnXmin THEN Level.CamX = ScrnXmin' Check if Level.CamX < 0, zero it if its negative to prevent errors.' ScrnYmin = 0 (Constant)' CODE: IF Level.CamX > (Level.Xmax * TileW) - ScrnXmax THEN Level.CamX = (Level.Xmax * TileW) - ScrnXmax' Check if Level.CamX > (Level.Xmax * TileW) - ScrnXmax' (Level.Xmax * TileW) - ScrnXmax => Maximum number of PIXELS the Map has' Level.Xmax => max num of tile for map (shared variable Level.Element)' TileW => width of tile (constant)' ScrnXmax = 640 (constant, depends on screen resolution)' To calculate: Level.Xmax = MapXmax' Formula: (Level.Xmax * TileW) - ScrnXmax' Level.Xmax = 37' (37*20)-640 = 100' Level.CamX = 100' To calculate TileX:' Level.CamX\TileW' 100\20=5' Level.TileX = 5 (start Drawing from 5 to 37)' 5 is the first row of tiles to draw in X' 37-5=32 (see we have to draw 32 tiles horizontally!!!)' Same goes for Y.' See Engine.DrawMap SUB for more details. ;)SELECT CASE Player.Direction CASE DR Level.CamX = Player.X - ScrnXmid IF Level.CamX < ScrnXmin THEN Level.CamX = ScrnXmin IF Level.CamX > (Level.Xmax * TileW) - ScrnXmax THEN Level.CamX = (Level.Xmax * TileW) - ScrnXmax CASE DU Level.CamY = Player.Y - ScrnYmid IF Level.CamY < ScrnYmin THEN Level.CamY = ScrnYmin IF Level.CamY > (Level.Ymax * TileH) - ScrnYmax THEN Level.CamY = (Level.Ymax * TileH) - ScrnYmax CASE DL Level.CamX = Player.X - ScrnXmid IF Level.CamX < ScrnXmin THEN Level.CamX = ScrnXmin IF Level.CamX > (Level.Xmax * TileW) - ScrnXmax THEN Level.CamX = (Level.Xmax * TileW) - ScrnXmax CASE DD Level.CamY = Player.Y - ScrnYmid IF Level.CamY < ScrnYmin THEN Level.CamY = ScrnYmin IF Level.CamY > (Level.Ymax * TileH) - ScrnYmax THEN Level.CamY = (Level.Ymax * TileH) - ScrnYmax CASE ELSEEND SELECTEND SUBSUB Engine.UpdatePlayer (Player AS SpriteType, Direction)' Updates the player's position according to its direction' ZELDA style pixel*pixel free movement.' Sample Code:' CODE: CASE DR' Direction of Player movement' CODE: Player.X = Player.X + Player.XV' Add Xspeed to Player X position since we are moving right' CODE: IF Engine.TileCollide(Hero) THEN Player.X = Player.X - Player.XV' Check for collision. If collided with valid "collidable" tile' Subtract Xspeed to return the player to its original place.' Also prevents player sticking to tiles when changing direction.' Flag that the player is not moving (if we don't want "pushing" tile effect)' CODE: IF Player.X > (Level.Xmax * TileW) - TileW THEN Player.X = (Level.Xmax * TileW) - TileW' checks if player is outside of World map boudaries.' (Level.Xmax * TileW) - TileW=(3*20)-20' Subracting 20 (TileW) is necessary to prevent errors if your speed' is greater than 1. Also used for padding.SELECT CASE Direction CASE DR Player.X = Player.X + Player.XV Player.Move = TRUE IF Engine.TileCollide(Hero) THEN Player.X = Player.X - Player.XV Player.Move = FALSE END IF IF Player.X > (Level.Xmax * TileW) - TileW THEN Player.X = (Level.Xmax * TileW) - TileW CASE DU Player.Y = Player.Y - Player.YV Player.Move = TRUE IF Engine.TileCollide(Hero) THEN Player.Y = Player.Y + Player.YV Player.Move = FALSE END IF IF Player.Y < ScrnYmin THEN Player.Y = ScrnYmin CASE DL Player.X = Player.X - Player.XV Player.Move = TRUE IF Engine.TileCollide(Hero) THEN Player.X = Player.X + Player.XV Player.Move = FALSE END IF IF Player.X < ScrnXmin THEN Player.X = ScrnXmin CASE DD Player.Y = Player.Y + Player.YV Player.Move = TRUE IF Engine.TileCollide(Hero) THEN Player.Y = Player.Y - Player.YV Player.Move = FALSE END IF IF Player.Y > (Level.Ymax * TileH) - TileH THEN Player.Y = (Level.Ymax * TileH) - TileH CASE ELSEEND SELECT' Calculate what Tile the player is' Add 10 to X and Y to get center of Player since tile size is 20*20Player.TileX = (Player.X + 10) \ TileW ' Center of SpritePlayer.TileY = (Player.Y + 10) \ TileHEND SUBSUB InitMap (mapcon AS STRING)' This sub loads the map data from an external file. You are' advised to use some map editing tool. Map loading is not glued' to the very scrolling engine. This map loader first loads the' map's x size and map's y size (in number of tiles) and then the' very tiles. You should leave (create) one line of unused tiles' horizontally and vertically on the right and down edge of the' map to avoid errors.OPEN mapcon FOR INPUT AS #2INPUT #2, MapXMaxINPUT #2, MapYMaxFOR Y = 0 TO MapYMax FOR X = 0 TO MapXMax INPUT #2, Map(X, Y).BaseL ' Map is loaded from a file (note the INPUT #2 NEXT X ' statement) and stored into the map array.NEXT YCLOSE #2END SUBSUB InitVariables STATIC' Hero Starting Variables' Center-downHero.Typ = 1 'ID?????????Hero.X = 15 * TileW ' Change this to where you want the player toHero.Y = 20 * TileH ' Start (be sure not to go over the Map dimensions)Hero.XV = 2 ' XSpeed change this for faster/slower movementHero.YV = 2 ' YspeedHero.Frame = 1 ' We start at frame 1 (looking-at-screen-sprite) :)Hero.Move = FALSE ' Static(no movement) :)Hero.Active = TRUEHero.AI = 1Hero.Money = 200Hero.HP = 20Hero.Item = 1Hero.Direction = DUHero.TileX = 0Hero.TileY = 0FOR countNPC = 1 TO 15 'we loop thur our all 15 NPCs NPC(countNPC).XV = 0.8 ' X and Y speeds of out NPCs NPC(countNPC).YV = 0.8 RANDOMIZE TIMER ' We randomly choose the starting XRND = INT(RND * 30) + 2 ' positions of our NPCs. This is how YRND = INT(RND * 20) + 2 ' you randomize numbers. For example, ' for X position of a NPC we use a XRND ' variable which can be any number from ' 0 to 30 plus 2! ' NPC might be randomized into a ' solid tile so they will be stuck. ' Don't mind that since in your game ' you'll manualy put sprites on their ' positions or re-randomize sprites ' if the turn out to be on a solid ' tile. NPC(countNPC).Direction = 1 NPC(countNPC).X = XRND * 20 ' We multiply XRND or YRND with 20 to NPC(countNPC).Y = XRND * 20 ' put a NPC on XRND and YRND tile!NEXT countNPCLevel.Xmax = MapXMaxLevel.Ymax = MapYMaxLevel.CamX = 0Level.CamY = 0Level.TileX = 0Level.TileY = 0Level.Xpos = 0Level.Ypos = 0Hero.Direction = DU ' Two directions so that engine willEngine.UpdateCamera Level, Hero ' know where level TileX and TileY are.Hero.Direction = DR ' Positions from left to right.Engine.UpdateCamera Level, Hero ' No warping ;)END SUBDEFINT A-ZSUB MainLoop STATICDO _LIMIT 60 GDK_Keyboard_GetState KB(0) Engine.DrawScreen IF KB(0).ESC THEN EXIT DO 'Emergency Exit IF KB(0).Up THEN ' Pressed UP Hero.Direction = DU ' Direction = Up OldX = Hero.X ' Store our old variables OldY = Hero.Y ' (can be used with collision routines) Engine.UpdatePlayer Hero, DU ' Update Player position Engine.UpdateCamera Level, Hero ' Update camera position END IF IF KB(0).Down THEN Hero.Direction = DD OldX = Hero.X OldY = Hero.Y Engine.UpdatePlayer Hero, DD Engine.UpdateCamera Level, Hero END IF IF KB(0).Right THEN Hero.Direction = DR OldX = Hero.X OldY = Hero.Y Engine.UpdatePlayer Hero, DR Engine.UpdateCamera Level, Hero END IF IF KB(0).Left THEN Hero.Direction = DL OldX = Hero.X OldY = Hero.Y Engine.UpdatePlayer Hero, DL Engine.UpdateCamera Level, Hero END IF _DISPLAYLOOP UNTIL KB(0).ESCEND SUBSUB MainScreenDO _LIMIT 60 GDK_Keyboard_GetState KB(0) Text$ = "Pixel by pixel scrolling engine" GDK_Print ScrnXmid - (GDK_PrintWidth(Text$, FNT) / 2), 40, Text$, FNT Text$ = "by R.Eric.Lope" GDK_Print ScrnXmid - (GDK_PrintWidth(Text$, FNT) / 2), 60, Text$, FNT Text$ = "Tiles by Ingmar Steen and Lachie D." GDK_Print ScrnXmid - (GDK_PrintWidth(Text$, FNT) / 2), 80, Text$, FNT Text$ = "Recoded for QB64 with GDK by Lachie D. in 2011" GDK_Print ScrnXmid - (GDK_PrintWidth(Text$, FNT) / 2), 100, Text$, FNT Text$ = "PRESS SPACE TO START" GDK_Print ScrnXmid - (GDK_PrintWidth(Text$, FNT) / 2), 160, Text$, FNT _DISPLAYLOOP UNTIL KB(0).SPACEEND SUBREM $INCLUDE:'UnseenGDK_alt.bm'' GDK sprite functionsTYPE SPRITEFONT Image AS Sprite Spacing AS INTEGEREND TYPESUB GDK_SpriteFont_New (FNT AS SPRITEFONT, File$)GDK_Sprite_New FNT.Image, File$, 13, 8, 13 * 8, 1GDK_Sprite_Show FNT.ImageFNT.Spacing = 1END SUBSUB GDK_SpriteFont_SetAlpha (FNT AS SPRITEFONT, Alpha~&)GDK_Sprite_SetAlpha FNT.Image, Alpha~&END SUBSUB GDK_SpriteFont_SetSpacing (FNT AS SPRITEFONT, Spacing%)FNT.Spacing = Spacing%END SUBSUB GDK_SpriteFont_SetScale (FNT AS SPRITEFONT, Scale!)FNT.Image.Scale = Scale!END SUBFUNCTION GDK_PrintWidth (Text$, FNT AS SPRITEFONT)OldDest& = _DESTOldSource& = _SOURCEFONTSTRING$ = " ! #$%&'()*+ ,-./01234567 89:;<=>?@ABC DEFGHIJKLMNO PQRSTUVWXYZ[ \]^-`abcdefg hijklmnopqrs tuvwxyz{|}~ "FOR i% = 1 TO LEN(Text$) LtrVal% = INSTR(FONTSTRING$, MID$(Text$, i%, 1)) IF LtrVal% THEN IF LtrVal% > 1 THEN TmpImage& = _NEWIMAGE(((FNT.Image.Width / FNT.Image.XFrameCount) * FNT.Image.Scale), ((FNT.Image.Height / FNT.Image.YFrameCount) * FNT.Image.Scale), 32) _DEST TmpImage& GDK_Sprite_DrawXY FNT.Image, 0, 0, 0, LtrVal% H& = _HEIGHT(TmpImage&) - 1 W& = _WIDTH(TmpImage&) - 1 FirstY% = H& FirstX% = W& LastY% = 0 LastX% = 0 _SOURCE TmpImage& FOR k% = 0 TO H& FOR j% = 0 TO W& C& = POINT(j%, k%) IF C& <> FNT.Image.Alpha THEN IF j% < FirstX% THEN FirstX% = j% IF j% > LastX% THEN LastX% = j% IF k% < FirstY% THEN FirstY% = k% IF k% > LastY% THEN LastY% = k% END IF NEXT NEXT TotalWidth% = TotalWidth% + (LastX% - FirstX%) + (FNT.Spacing * FNT.Image.Scale) _DEST OldDest& _SOURCE OldSource& _FREEIMAGE TmpImage& ELSE '// a space TotalWidth% = TotalWidth% + W& + (FNT.Spacing * FNT.Image.Scale) END IF END IFNEXTGDK_PrintWidth = TotalWidth%END FUNCTIONFUNCTION GDK_PrintHeight (Text$, FNT AS SPRITEFONT)OldDest& = _DESTOldSource& = _SOURCEFONTSTRING$ = " ! #$%&'()*+ ,-./01234567 89:;<=>?@ABC DEFGHIJKLMNO PQRSTUVWXYZ[ \]^-`abcdefg hijklmnopqrs tuvwxyz{|}~ "FOR i% = 1 TO LEN(Text$) LtrVal% = INSTR(FONTSTRING$, MID$(Text$, i%, 1)) IF LtrVal% THEN IF LtrVal% > 1 THEN TmpImage& = _NEWIMAGE(((FNT.Image.Width / FNT.Image.XFrameCount) * FNT.Image.Scale), ((FNT.Image.Height / FNT.Image.YFrameCount) * FNT.Image.Scale), 32) _DEST TmpImage& GDK_Sprite_DrawXY FNT.Image, 0, 0, 0, LtrVal% H& = _HEIGHT(TmpImage&) - 1 W& = _WIDTH(TmpImage&) - 1 FirstY% = H& FirstX% = W& LastY% = 0 LastX% = 0 _SOURCE TmpImage& FOR k% = 0 TO H& FOR j% = 0 TO W& C& = POINT(j%, k%) IF C& <> FNT.Image.Alpha THEN IF j% < FirstX% THEN FirstX% = j% IF j% > LastX% THEN LastX% = j% IF k% < FirstY% THEN FirstY% = k% IF k% > LastY% THEN LastY% = k% END IF NEXT NEXT Height% = LastY% - FirstY% IF Height% > MaxHeight% THEN MaxHeight% = Height% _DEST OldDest& _SOURCE OldSource& _FREEIMAGE TmpImage& ELSE '// a space MaxHeight% = H& EXIT FOR END IF END IFNEXTGDK_PrintHeight = MaxHeight%END FUNCTIONSUB GDK_Print (X%, Y%, Text$, FNT AS SPRITEFONT)OldX% = X%OldDest& = _DESTOldSource& = _SOURCEFONTSTRING$ = " ! #$%&'()*+ ,-./01234567 89:;<=>?@ABC DEFGHIJKLMNO PQRSTUVWXYZ[ \]^-`abcdefg hijklmnopqrs tuvwxyz{|}~ "FOR i% = 1 TO LEN(Text$) LtrVal% = INSTR(FONTSTRING$, MID$(Text$, i%, 1)) IF LtrVal% THEN TmpImage& = _NEWIMAGE(((FNT.Image.Width / FNT.Image.XFrameCount) * FNT.Image.Scale), ((FNT.Image.Height / FNT.Image.YFrameCount) * FNT.Image.Scale), 32) _DEST TmpImage& GDK_Sprite_DrawXY FNT.Image, 0, 0, 0, LtrVal% H& = _HEIGHT(TmpImage&) - 1 W& = _WIDTH(TmpImage&) - 1 FirstY% = H& FirstX% = W& LastY% = 0 LastX% = 0 _SOURCE TmpImage& FOR k% = 0 TO H& FOR j% = 0 TO W& C& = POINT(j%, k%) IF C& <> FNT.Image.Alpha THEN IF j% < FirstX% THEN FirstX% = j% IF j% > LastX% THEN LastX% = j% IF k% < FirstY% THEN FirstY% = k% IF k% > LastY% THEN LastY% = k% END IF NEXT NEXT WidthDif% = (W& - LastX%) + FirstX% HeightDif% = (H& - LastY%) + FirstY% _DEST OldDest& _SOURCE OldSource& IF LtrVal% = 1 THEN _PUTIMAGE (X%, Y%)-(X% + W&, Y% + H&), TmpImage& XDif% = 1 + W& + FNT.Spacing ELSE _PUTIMAGE (X%, Y%)-(X% + (W& - WidthDif%), Y% + (H& - HeightDif%)), TmpImage&, , (FirstX%, FirstY%)-(LastX%, LastY%) XDif% = 1 + (W& - WidthDif%) + FNT.Spacing END IF _FREEIMAGE TmpImage& END IF X% = X% + XDif%NEXTX% = OldX%END SUB