scrolling.pp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400
  1. (*
  2. Easy GL2D
  3. Relminator 2011
  4. Richard Eric M. Lope BSN RN
  5. Http://Rel.Phatcode.Net
  6. A very small, simple, yet very fast DS 2D rendering lib using the DS' 3D core.
  7. --
  8. Translated in Object Pascal by Francesco Lombardi - 2012
  9. http://itaprogaming.free.fr
  10. *)
  11. program scrolling;
  12. {$mode objfpc}
  13. {$H+}
  14. {$L build/crono.o}
  15. {$L build/tiles.o}
  16. uses
  17. ctypes, nds9, gl2d, uvcoord_crono;
  18. const
  19. cronoBitmapLen = 32768;
  20. cronoPalLen = 512;
  21. tilesBitmapLen = 65536;
  22. tilesPalLen = 512;
  23. var
  24. cronoBitmap: array [0..0] of cuint; cvar; external;
  25. cronoPal: array [0..0] of cushort; cvar; external;
  26. tilesBitmap: array [0..0] of cuint; cvar; external;
  27. tilesPal: array [0..0] of cushort; cvar; external;
  28. const
  29. MAP_WIDTH = 32;
  30. MAP_HEIGHT = 32;
  31. (*
  32. I'm using the struct of player from the
  33. Animate simple man/woman exmple in the
  34. "nds/examples" folder
  35. You might want to read up on that too to
  36. see the differnce in handling sprites via OAM
  37. and Easy GL2D.
  38. *)
  39. const
  40. P_RIGHT = 0;
  41. P_UP = 1;
  42. P_DOWN = 2;
  43. P_LEFT = 3;
  44. type
  45. TPlayer = record
  46. x, y: integer;
  47. gfx_frame: integer;
  48. state: integer;
  49. anim_frame: integer;
  50. is_walking: boolean; // an animation flag whether crono is walking or not
  51. end;
  52. PPlayer = ^TPlayer;
  53. // Our level struct
  54. TLevel = record
  55. width: integer; // dimensions of the map
  56. height: integer;
  57. camera_x: integer; // top-left cooordinates of our virtual camera
  58. camera_y: integer; // Works almost the same the 2d BG scroller
  59. tile_x: integer; // current tile the top-left coordinate of our
  60. tile_y: integer; // camera occupies
  61. pixel_x: integer; // scrolling tile offsets
  62. pixel_y: integer;
  63. end;
  64. PLevel = ^TLevel;
  65. TMapArray = array [0..MAP_WIDTH-1, 0..MAP_HEIGHT-1] of cushort;
  66. // Animates crono
  67. procedure AnimatePlayer(p: PPlayer);
  68. const
  69. FRAMES_PER_ANIMATION = 6; // 6 crono animations
  70. frame: integer = 0; // a static frame counter
  71. begin
  72. // Only animate if crono is walking
  73. if (p^.is_walking) then
  74. begin
  75. inc(frame);
  76. // Animate only every 8th frame
  77. // I used an if() block instead of % since % is slow
  78. // on the DS (not that it would matter in this demo)
  79. if ((frame and 7) = 0) then
  80. begin
  81. inc(p^.anim_frame);
  82. if (p^.anim_frame >= (FRAMES_PER_ANIMATION)) then
  83. p^.anim_frame := 0;
  84. end;
  85. end;
  86. // P_RIGHT, P_UP and P_DOWN is calculated normally.
  87. // P_LEFT is P_RIGHT flipped.
  88. case (p^.state) of
  89. P_RIGHT: p^.gfx_frame := p^.anim_frame + p^.state * FRAMES_PER_ANIMATION;
  90. P_UP: p^.gfx_frame := p^.anim_frame + p^.state * FRAMES_PER_ANIMATION;
  91. P_DOWN: p^.gfx_frame := p^.anim_frame + p^.state * FRAMES_PER_ANIMATION;
  92. P_LEFT: p^.gfx_frame := p^.anim_frame + P_RIGHT * FRAMES_PER_ANIMATION;
  93. else
  94. p^.gfx_frame := p^.anim_frame + p^.state * FRAMES_PER_ANIMATION;
  95. end;
  96. end;
  97. // Draws a full screen map
  98. procedure DrawMap(lvl: PLevel; map: TMapArray; tiles: pglImage);
  99. const
  100. // tiles are 16x16 pixels
  101. TILE_SIZE = 16;
  102. // calculate number of tiles per row and column
  103. SCREEN_TILE_X = SCREEN_WIDTH div TILE_SIZE;
  104. SCREEN_TILE_Y = SCREEN_HEIGHT div TILE_SIZE;
  105. var
  106. x, y: integer; // counters
  107. tile_x, tile_y: integer; // current tile to draw
  108. screen_x, screen_y: integer; // actual screen position (in pixel)
  109. i: integer; // tile index to draw
  110. begin
  111. // we need to draw an extra tile at the bottom and right
  112. // since we are scrolling
  113. for y := 0 to SCREEN_TILE_Y do
  114. begin
  115. for x := 0 to SCREEN_TILE_X do
  116. begin
  117. tile_x := lvl^.tile_x + x; // get relative tile positions
  118. tile_y := lvl^.tile_y + y;
  119. i := map[tile_x, tile_y]; // get map index
  120. screen_x := (x * TILE_SIZE) - lvl^.pixel_x; //Calculate where to put a
  121. screen_y := (y * TILE_SIZE) - lvl^.pixel_y; //particular tile
  122. glSprite(screen_x, screen_y, GL_FLIP_NONE , @tiles[i]);
  123. end;
  124. end;
  125. end;
  126. // Update's the camera's position relative to the player
  127. procedure CameraUpdate(lvl: PLevel; p: PPlayer);
  128. const
  129. // set constants for middle of screen
  130. SCREEN_MID_WIDTH = SCREEN_WIDTH div 2;
  131. SCREEN_MID_HEIGHT = SCREEN_HEIGHT div 2;
  132. TILE_SIZE = 16;
  133. begin
  134. // update the camera
  135. lvl^.camera_x := p^.x - SCREEN_MID_WIDTH;
  136. lvl^.camera_y := p^.y - SCREEN_MID_HEIGHT;
  137. // limit camera X values
  138. if ( lvl^.camera_x < 0 ) then lvl^.camera_x := 0;
  139. if ( lvl^.camera_x > ((lvl^.width-2) * TILE_SIZE ) - SCREEN_WIDTH ) then
  140. lvl^.camera_x := ((lvl^.width-2) * TILE_SIZE ) - SCREEN_WIDTH;
  141. // limit camera Y values
  142. if ( lvl^.camera_y < 0 ) then lvl^.camera_y := 0;
  143. if ( lvl^.camera_y > ((lvl^.height-2) * TILE_SIZE ) - SCREEN_HEIGHT ) then
  144. lvl^.camera_y := ((lvl^.height-2) * TILE_SIZE ) - SCREEN_HEIGHT;
  145. // calculate level starting tiles
  146. lvl^.tile_x := lvl^.camera_x div TILE_SIZE;
  147. lvl^.tile_y := lvl^.camera_y div TILE_SIZE;
  148. // calculate tile pixel offsets
  149. // Only works with power of 2 tilesize
  150. // use "%" for non-power of 2 sizes
  151. lvl^.pixel_x := lvl^.camera_x and (TILE_SIZE - 1);
  152. lvl^.pixel_y := lvl^.camera_y and (TILE_SIZE - 1);
  153. end;
  154. // Just a simple map
  155. // A real engine should use a map editor
  156. procedure InitMap(var map: TMapArray);
  157. var
  158. x, y: integer;
  159. begin
  160. for y := 0 to MAP_HEIGHT - 1 do
  161. for x := 0 to MAP_WIDTH - 1 do
  162. map[x, y] := ((y and 15)*16 + (x and 15)) and 255;
  163. end;
  164. var
  165. // This imageset would use our texture packer generated coords so it's kinda
  166. // safe and easy to use
  167. // CRONO_NUM_IMAGES is a value from "uvcoord_crono.h"
  168. crono_images: array [0..CRONO_NUM_IMAGES-1] of glImage;
  169. // This tileset won't make use of our texture packer generated coords
  170. // messy, manual and prone to errors
  171. // BMP is 256x256 and tiles are 16x16 so.. (256/16) * (256 /16) = 16 * 16
  172. tiles_images: array [0..((256 div 16) * (256 div 16)) - 1] of glImage;
  173. // Our level map
  174. // I used shorts since we would be able to reference 65535
  175. // uinique tiles with shorts.
  176. // You should use malloc() or new[] to dimension
  177. // your maps for a real game though.
  178. level_map: TMapArray;
  179. // Our crono guy
  180. crono: TPlayer;
  181. // the level
  182. lvl: TLevel;
  183. crono_textureID: cint;
  184. tiles_textureID: cint;
  185. TextureSize: integer;
  186. frame: integer = 0; // ever present frame counter
  187. key: integer; // for key input
  188. i: integer;
  189. begin
  190. // crono starting positions
  191. crono.x := 16 * 5; // 5th tile
  192. crono.y := 16 * 5;
  193. crono.state := P_RIGHT; // facing right
  194. crono.anim_frame := 0; // starting frame
  195. lvl.width := MAP_WIDTH; // init map dimesions
  196. lvl.height := MAP_HEIGHT;
  197. InitMap(level_map); // load a randomized map (too lazy to make a proper one)
  198. videoSetMode(MODE_5_3D); // favorite mode
  199. consoleDemoInit();
  200. // Initialize GL in 3d mode
  201. glScreen2D();
  202. // set Bank A to texture (128 kb)
  203. vramSetBankA( VRAM_A_TEXTURE );
  204. vramSetBankE(VRAM_E_TEX_PALETTE); // Allocate VRAM bank for all the palettes
  205. // Our texture handle for crono
  206. // I used glLoadSpriteSet since the texture was made
  207. // with my texture packer.
  208. crono_textureID := glLoadSpriteSet(crono_images, // pointer to glImage array
  209. CRONO_NUM_IMAGES, // Texture packer auto-generated #define
  210. @crono_texcoords, // Texture packer auto-generated array
  211. GL_RGB256, // texture type for glTexImage2D() in videoGL.h
  212. TEXTURE_SIZE_256, // sizeX for glTexImage2D() in videoGL.h
  213. TEXTURE_SIZE_128, // sizeY for glTexImage2D() in videoGL.h
  214. GL_TEXTURE_WRAP_S or GL_TEXTURE_WRAP_T or TEXGEN_OFF or GL_TEXTURE_COLOR0_TRANSPARENT, // param for glTexImage2D() in videoGL.h
  215. 256, // Length of the palette to use (256 colors)
  216. @cronoPal, // Load our 256 color crono palette
  217. @cronoBitmap // image data generated by GRIT
  218. );
  219. // Our texture handle for our tiles
  220. // I used glLoadTileSet since the texture
  221. // is just a bunch of 16x16 tiles in a 256x256
  222. // tileset so we don't need a texture packer for this.
  223. tiles_textureID := glLoadTileSet(tiles_images, // pointer to glImage array
  224. 16, // sprite width
  225. 16, // sprite height
  226. 256, // bitmap width
  227. 256, // bitmap height
  228. GL_RGB256, // texture type for glTexImage2D() in videoGL.h
  229. TEXTURE_SIZE_256, // sizeX for glTexImage2D() in videoGL.h
  230. TEXTURE_SIZE_256, // sizeY for glTexImage2D() in videoGL.h
  231. GL_TEXTURE_WRAP_S or GL_TEXTURE_WRAP_T or TEXGEN_OFF or GL_TEXTURE_COLOR0_TRANSPARENT, // param for glTexImage2D() in videoGL.h
  232. 256, // Length of the palette to use (256 colors)
  233. @tilesPal, // Load our 256 color tiles palette
  234. @tilesBitmap // image data generated by GRIT
  235. );
  236. iprintf(#$1b'[1;1HSCROLLING TEST');
  237. iprintf(#$1b'[3;1HArrow Keys to move');
  238. iprintf(#$1b'[6;1HRelminator');
  239. iprintf(#$1b'[7;1HHttp://Rel.Phatcode.Net');
  240. iprintf(#$1b'[9;1HCrono = %i', crono_textureID);
  241. iprintf(#$1b'[10;1HTiles = %i', tiles_textureID);
  242. iprintf(#$1b'[13;1HTiles by unknown');
  243. iprintf(#$1b'[14;1HCrono by Square Enix');
  244. // calculate the amount of
  245. // memory uploaded to VRAM in KB
  246. TextureSize := cronoBitmapLen + tilesBitmapLen;
  247. iprintf(#$1b'[17;1HTotal Texture size= %i kb', TextureSize div 1024);
  248. while true do
  249. begin
  250. // increment frame counter
  251. inc(frame);
  252. crono.is_walking := false; // crono is lazily standing to the right
  253. scanKeys();
  254. key := keysHeld();
  255. // process input and move crono
  256. if (key and KEY_RIGHT) <> 0 then
  257. begin
  258. inc(crono.x);
  259. crono.state := P_RIGHT;
  260. crono.is_walking := true;
  261. end;
  262. if (key and KEY_LEFT)<>0 then
  263. begin
  264. dec(crono.x);
  265. crono.state := P_LEFT;
  266. crono.is_walking := true;
  267. end;
  268. if (key and KEY_UP) <> 0 then
  269. begin
  270. dec(crono.y);
  271. crono.state := P_UP;
  272. crono.is_walking := true;
  273. end;
  274. if (key and KEY_DOWN) <> 0 then
  275. begin
  276. inc(crono.y);
  277. crono.state := P_DOWN;
  278. crono.is_walking := true;
  279. end;
  280. // Update player animations
  281. AnimatePlayer(@crono);
  282. // Update level camera relative to crono's position
  283. CameraUpdate(@lvl, @crono);
  284. glBegin2D();
  285. // Draw our map layer
  286. DrawMap( @lvl, level_map, @tiles_images );
  287. // Process crono
  288. // Left and right share the same frames
  289. // I just flipped the sprite depending on where crono faces.
  290. if (crono.state < P_LEFT) then
  291. glSpriteRotate(crono.x - lvl.camera_x, crono.y - lvl.camera_y, 0,GL_FLIP_NONE , @crono_images[crono.gfx_frame])
  292. else
  293. glSpriteRotate(crono.x - lvl.camera_x, crono.y - lvl.camera_y, 0,GL_FLIP_H , @crono_images[crono.gfx_frame]);
  294. // Draw a translucent gradient box to emulate dialogboxes
  295. // giving it a unique Polygon ID
  296. glPolyFmt(POLY_ALPHA(16) or POLY_CULL_NONE or POLY_ID(1));
  297. glBoxFilledGradient( 0, 150, 255, 191,
  298. RGB15( 31, 0, 0 ),
  299. RGB15( 0, 31, 0 ),
  300. RGB15( 31, 0, 31 ),
  301. RGB15( 0, 31, 31 )
  302. );
  303. //back to opaque mode
  304. // and draw the border of the "dialog box"
  305. glPolyFmt(POLY_ALPHA(31) or POLY_CULL_NONE );
  306. for i := 0 to 4 do
  307. glBox(i, 150 + i, 255 - i , 191 - i,
  308. RGB15( 31-i*5, i*5, 31 - i * 3 )
  309. );
  310. glEnd2D();
  311. glFlush(0);
  312. swiWaitForVBlank();
  313. end;
  314. end.