JNR Platform Example Code

Previous topic - Next topic

MrTAToad

This is the code for the JNR platform examples.  Included is currently example 1 and 2

Example 1:

Code (glbasic) Select
// ---------------------------------
// Project: JNRExample#1
// Start: Saturday, June 26, 2010
// IDE Version: 8.006

CONSTANT MAPWIDTH% = 32 //width of the map
CONSTANT MAPHEIGHT% = 24 //height of the map
CONSTANT TILESIZE% = 20 //size of the tiles, should be dynamically read

CONSTANT PLAYERHEIGHT% = 37 //should be dynamically read from a player definition file
CONSTANT PLAYERWIDTH% = 11 //--""--



CONSTANT WAITTIME% = 15 //delay between frames


CONSTANT GRAVITATION% = 1 //gravitation - 1 pixel per frame (too fast, will be changed somewhen (playerx,y -> float, grav: 0.xx))

CONSTANT VELMOVING% = 4 //velocity (speed) for moving left, right
CONSTANT VELJUMP% = 13 //velocity for jumping

TYPE tTile
solid%=FALSE
spr%=-1
ENDTYPE

TYPE TMap
tiles[] AS tTile
spr_t%[]

FUNCTION TMap_Initialise%:spr_t%[]
DIM self.tiles[MAPWIDTH%][MAPHEIGHT%]
self.spr_t%[]=spr_t%[]
RETURN TRUE
ENDFUNCTION

FUNCTION loadMap%:file$
LOCAL handle%
LOCAL t%,i%,j%

IF DOESFILEEXIST(file$)
handle%=GENFILE()
IF handle%>=0
IF OPENFILE(handle%,file$,1)
FOR j%=0 TO MAPHEIGHT%-1
FOR i%=0 TO MAPWIDTH%-1
READBYTE handle%,self.tiles[i%][j%].solid%
READLONG handle%,t%
IF t%<>0 THEN self.tiles[i%][j%].spr%=self.spr_t%[t%-1]
NEXT
NEXT
RETURN TRUE
ELSE
RETURN FALSE
ENDIF
ENDIF
ENDIF
ENDFUNCTION

FUNCTION TMap_draw%:
LOCAL i%,j%

FOR i%=0 TO MAPWIDTH%-1
FOR j%=0 TO MAPHEIGHT%-1
IF self.tiles[i%][j%].spr%<>-1 THEN DRAWSPRITE self.tiles[i%][j%].spr%,i%*TILESIZE%,j%*TILESIZE%
NEXT
NEXT
ENDFUNCTION

FUNCTION colmapxy%:x%,y%
RETURN self.tiles[x%][y%].solid
ENDFUNCTION
ENDTYPE

TYPE TPlayer
x%;y%
h%;w%
velx%;vely%
faceright%
lockjump%
spr_player%[]
map AS TMap

FUNCTION TPlayer_Initialise%:spr_player%[],map AS TMap
self.spr_player%[]=spr_player%[]
self.x%=100
self.y%=100
self.h%= PLAYERHEIGHT; //save player height and width
self.w%= PLAYERWIDTH;
self.velx%=0
self.vely%=0
self.lockjump%=FALSE
self.faceright%=TRUE
self.map=map
ENDFUNCTION

FUNCTION think%:
LOCAL tilecoord%

self.velx=0; //don't move left / right by default

IF KEY(205)
self.velx=VELMOVING% // Move right
self.faceright%=TRUE
ELSE
IF KEY(203)
self.velx=0-VELMOVING% // Move left
self.faceright=FALSE
ENDIF
ENDIF

IF KEY(42) AND self.lockjump=FALSE // Can the player jump ?
self.vely=0-VELJUMP%
self.lockjump%=TRUE
ENDIF

IF self.velx%>0
IF collision_ver(self.x+self.velx+self.w%,self.y%,tilecoord%) //collision on the right side.
self.x%=tilecoord%*20-self.w%-1; //move to the edge of the tile (tile on the right -> mind the player width)
ELSE
INC self.x%,self.velx%
ENDIF
ELSE
IF self.velx%<0
IF collision_ver(self.x+self.velx,self.y%,tilecoord%) //collision on the left side
self.x%=(tilecoord%+1)*20+1; //move to the edge of the tile
ELSE
INC self.x%,self.velx%
ENDIF
ENDIF
ENDIF

IF self.vely%<0
IF collision_hor(self.x%,self.y%+self.vely%,tilecoord%)
self.y%=(tilecoord%+1)*20+1
self.vely%=0
ELSE
INC self.y%,self.vely%
INC self.vely%,GRAVITATION%
ENDIF
ELSE
IF collision_hor(self.x%,self.y%+self.vely%+self.h%,tilecoord%)
self.y%=tilecoord%*20-self.h%-1
self.vely%=1 // so we test against the ground again int the next frame (0 would test against the ground in the next+1 frame)

IF KEY(42)=0
self.lockjump%=FALSE
ENDIF
ELSE
INC self.y%,self.vely%
INC self.vely%,GRAVITATION%

IF self.vely%>=TILESIZE%
self.vely%=TILESIZE%
ENDIF

self.lockjump%=TRUE
ENDIF
ENDIF
ENDFUNCTION

FUNCTION TPlayer_draw%:
IF self.faceright%
DRAWSPRITE self.spr_player%[1],self.x%-4,self.y%-2
ELSE
DRAWSPRITE self.spr_player%[0],self.x%-17,self.y%-2
ENDIF
ENDFUNCTION

FUNCTION collision_ver%:x%,y%,BYREF tilecoordx%
LOCAL tileypixels%,testend%,tilecoordy%

tileypixels% = y%-MOD(y%,20)
testend% = y% + self.h%;

tilecoordx% = x%/20;

tilecoordy% = tileypixels/20;

WHILE tileypixels <= testend
IF self.map.colmapxy(tilecoordx%,tilecoordy%)
RETURN TRUE
ENDIF

INC tilecoordy%
INC tileypixels%,20
WEND

RETURN FALSE
ENDFUNCTION

FUNCTION collision_hor%:x%,y%,BYREF tilecoordy%
LOCAL tilexpixels%,testend%,tilecoordx%

tilexpixels = self.x%-MOD(x%,20) //calculate the x position (pixels!) of the tiles we check against
testend = x% + self.w //calculate the end of testing (just to save the x+w calculation each for loop)

tilecoordy% = y%/20; //calculate the y position (map coordinates!) of the tiles we want to test

tilecoordx% = tilexpixels%/20; //calculate map x coordinate for first tile


//loop while the start point (pixels!) of the test tile is inside the players bounding box
WHILE tilexpixels <= testend
IF self.map.colmapxy(tilecoordx%, tilecoordy%) //is a solid tile is found at tilecoordx, tilecoordy?
RETURN TRUE;
ENDIF

INC tilecoordx //increase tile x map coordinate
INC tilexpixels,20; //increase tile x pixel coordinate
WEND

RETURN FALSE
ENDFUNCTION
ENDTYPE


LOCAL spr_t%[]; DIM spr_t%[3]
LOCAL spr_player%[]; DIM spr_player%[2]
LOCAL spr_background%
LOCAL map AS TMap
LOCAL player AS TPlayer

SETTRANSPARENCY RGB(255,0,255)
spr_t%[0]=GENSPRITE(); LOADSPRITE "Media/t1.bmp",spr_t%[0]
spr_t%[1]=GENSPRITE(); LOADSPRITE "Media/t2.bmp",spr_t%[1]
spr_t%[2]=GENSPRITE(); LOADSPRITE "Media/t3.bmp",spr_t%[2]
spr_player%[0]=GENSPRITE(); LOADSPRITE "Media/left.bmp",spr_player%[0]
spr_player%[1]=GENSPRITE(); LOADSPRITE "Media/right.bmp",spr_player%[1]
spr_background%=GENSPRITE(); LOADSPRITE "Media/bg.bmp",spr_background%

IF map.TMap_Initialise(spr_t%[])=FALSE
DEBUG "Map could not be initialised"
RETURN FALSE
ENDIF

IF map.loadMap("Media/maps/map01.map")=FALSE
DEBUG "Map could not be loaded"
RETURN FALSE
ENDIF

player.TPlayer_Initialise(spr_player%[],map)

WHILE TRUE
//update objects
player.think();


//draw everything
DRAWSPRITE spr_background%,0,0
map.TMap_draw()
player.TPlayer_draw()
SHOWSCREEN
WEND


Example 2 :

Code (glbasic) Select
// ---------------------------------
// Project: JNRExample#2
// Start: Sunday, June 27, 2010
// IDE Version: 8.006

CONSTANT MAPWIDTH% = 32 //width of the map
CONSTANT MAPHEIGHT% = 24 //height of the map
CONSTANT TILESIZE% = 20 //size of the tiles, should be dynamically read

CONSTANT PLAYERHEIGHT% = 37 //should be dynamically read from a player definition file
CONSTANT PLAYERWIDTH% = 11 //--""--



CONSTANT WAITTIME% = 16 //delay between frames


CONSTANT GRAVITATION% = 1 //gravitation - 1 pixel per frame (too fast, will be changed somewhen (playerx,y -> float, grav: 0.xx))

CONSTANT VELMOVING% = 4 //velocity (speed) for moving left, right
CONSTANT VELJUMP% = 13 //velocity for jumping

CONSTANT t_nonsolid% = 0
CONSTANT t_solid% = 1
CONSTANT t_slopeleft% = 2
CONSTANT t_sloperight% = 3

TYPE tTile
type%
spr%
ENDTYPE

TYPE TMap
tiles[] AS tTile
spr_t%[]

FUNCTION TMap_Initialise%:spr_t%[]
LOCAL x%,y%

DIM self.tiles[MAPWIDTH%][MAPHEIGHT%]
self.spr_t%[]=spr_t%[]

FOR y%=0 TO MAPHEIGHT%-1
FOR x%=0 TO MAPWIDTH%-1
self.tiles[x%][y%].type%=t_nonsolid% // Need to initialise here as constants cant be assigned in TYPES
self.tiles[x%][y%].spr%=-1
NEXT
NEXT

RETURN TRUE
ENDFUNCTION

FUNCTION loadMap%:file$
LOCAL handle%
LOCAL t%,i%,j%

IF DOESFILEEXIST(file$)
handle%=GENFILE()
IF handle%>=0
IF OPENFILE(handle%,file$,1)
FOR j%=0 TO MAPHEIGHT%-1
FOR i%=0 TO MAPWIDTH%-1
READLONG handle%,self.tiles[i%][j%].type%
READLONG handle%,t%
IF t%<>128 THEN self.tiles[i%][j%].spr%=self.spr_t%[t%]
NEXT
NEXT
RETURN TRUE
ELSE
RETURN FALSE
ENDIF
ENDIF
ENDIF
ENDFUNCTION

FUNCTION TMap_draw%:
LOCAL i%,j%

FOR i%=0 TO MAPWIDTH%-1
FOR j%=0 TO MAPHEIGHT%-1
IF self.tiles[i%][j%].spr%<>-1 THEN DRAWSPRITE self.tiles[i%][j%].spr%,i%*TILESIZE%,j%*TILESIZE%
NEXT
NEXT
ENDFUNCTION

FUNCTION TMap_map%:x%,y%
RETURN self.tiles[x%][y%].type%
ENDFUNCTION
ENDTYPE

TYPE TPlayer
x%;y% //x, y coordinate (top left of the player rectangle)
h%;w% //height, width
velx%;vely% //velocity on x, y axis
faceright% //player facing right? -> graphics
lockjump% //may the player jump
jumping%
spr_player%[]
map AS TMap
slope_prevtilex%//the tile at slopex, slopey in the last frame
slope_prevtiley%

// Passing the players array and the TMap TYPE is a bit inefficient
FUNCTION TPlayer_Initialise%:spr_players%[],map AS TMap
self.spr_player%[]=spr_players%[]
self.x%=100
self.y%=100
self.h%= PLAYERHEIGHT; //save player height and width
self.w%= PLAYERWIDTH;
self.velx%=0
self.vely%=0
self.lockjump%=FALSE
self.faceright%=TRUE
self.jumping%=FALSE
self.map=map
self.slope_prevtilex% = (self.x + ASR(self.w,1))/ TILESIZE;
self.slope_prevtiley% = (self.y + self.h) / TILESIZE;
ENDFUNCTION

FUNCTION unlockjump%: //this function is called if the player hits the ground
// this if is quite tricky:
// the player may jump again:
// a) if he fell of an edge (!jumping) - without releasing the jump key on the ground
// b) if he jumped - only when he releases the jump key on the ground
IF NOT(self.jumping%) OR NOT(KEY(42))
self.lockjump%=FALSE
self.jumping%=FALSE
ENDIF
ENDFUNCTION

FUNCTION think%:
LOCAL tilecoord%

self.velx%=0; //don't move left / right by default

IF KEY(205)
self.velx%=VELMOVING% //move right
self.faceright%=TRUE
ELSE
IF KEY(203)
self.velx%=0-VELMOVING% //move right
self.faceright%=FALSE
ENDIF
ENDIF

IF KEY(42) AND self.lockjump%=FALSE //if the player isn't jumping already
self.vely%=0-VELJUMP% //jump!
self.lockjump%=TRUE //player is not allowed to jump anymore
ENDIF

collision_detection_map()
ENDFUNCTION

FUNCTION TPlayer_draw%:
IF self.faceright%
DRAWSPRITE self.spr_player%[1],self.x%-4,self.y%-2
ELSE
DRAWSPRITE self.spr_player%[0],self.x%-17,self.y%-2
ENDIF
ENDFUNCTION

FUNCTION collision_hor_up%:x%,y%,BYREF tilecoordy%
LOCAL tilexpixels%,testend%,tilecoordx%

tilexpixels% = x%-MOD(x%,TILESIZE%)
testend% = x% + self.w

tilecoordy% = y%/TILESIZE%

tilecoordx% = tilexpixels%/TILESIZE

WHILE tilexpixels% <= testend%
IF self.map.TMap_map(tilecoordx%, tilecoordy%)<>t_nonsolid% //only this changed: when jumping (moving up) we don't want to go through slopes
RETURN TRUE;
ENDIF

INC tilecoordx%
INC tilexpixels%,TILESIZE%
WEND

RETURN FALSE
ENDFUNCTION

FUNCTION collision_hor_down%:x%,y%,BYREF tilecoordy%
LOCAL tilexpixels%,testend%,tilecoordx%

tilexpixels% = x%-MOD(x%,TILESIZE%) //calculate the x position (pixels!) of the tiles we check against
testend% = x% + self.w% //calculate the end of testing (just to save the x+w calculation each for loop)

tilecoordy% = y/TILESIZE //calculate the y position (map coordinates!) of the tiles we want to test

tilecoordx% = tilexpixels%/TILESIZE //calculate map x coordinate for first tile


//loop while the start point (pixels!) of the test tile is inside the players bounding box
WHILE tilexpixels% <= testend%
IF self.map.TMap_map(tilecoordx%, tilecoordy%)=t_solid //is a solid tile is found at tilecoordx, tilecoordy?
RETURN TRUE
ENDIF

INC tilecoordx% //increase tile x map coordinate
INC tilexpixels%,TILESIZE% //increase tile x pixel coordinate
WEND

RETURN FALSE
ENDFUNCTION

// FOR explanation see CPlayer::collision_hor()
FUNCTION collision_ver%:x%,y%,BYREF tilecoordx%
LOCAL tileypixels%,testend%,tilecoordy%

tileypixels = y%-MOD(y%,TILESIZE%)
testend = y% + self.h%

tilecoordx% = x/TILESIZE

tilecoordy% = tileypixels/TILESIZE

WHILE tileypixels <= testend
IF self.map.TMap_map(tilecoordx%, tilecoordy%) = t_solid%
RETURN TRUE
ENDIF

INC tilecoordy%
INC tileypixels%,TILESIZE
WEND

RETURN FALSE
ENDFUNCTION

FUNCTION collision_slope%:sx%,sy%,BYREF tsx%,BYREF tsy%
LOCAL t%

tsx = sx% / TILESIZE //map coordinates of the tile we check against
tsy = sy% / TILESIZE

t% = self.map.TMap_map(tsx, tsy)

//if we found a slope we set align y to the slope.
//take a look at jnrdev #2, why it's calculated this way
IF t% = t_sloperight%
//sloperight -> \
self.y =  (tsy%+1)*TILESIZE - (TILESIZE - MOD(sx%,TILESIZE))  - self.h% - 1;
RETURN TRUE;
ELSEIF t% = t_slopeleft%
//slopeleft -> /
self.y =  (tsy+1)*TILESIZE - MOD(sx%,TILESIZE%)  - self.h% - 1 ;
RETURN TRUE;
ENDIF

RETURN FALSE;
ENDFUNCTION

FUNCTION collision_detection_map%:
LOCAL tsx%, tsy%,sx%,sy%,what%,tilecoord% //slope tile coordinates

//check for slopes (only if moving down)
IF self.vely > 0
sx = self.x% + ASR(self.w%,1) + self.velx //slope chechpoint x coordinate

IF collision_slope(sx%, (self.y + self.h), tsx%, tsy%) //we entered a slope (y is set by collision_slope)
INC self.x%,self.velx% //move on
//y has been set by collision_slope

unlockjump(); //we hit the ground - the player may jump again

self.vely = 1 //test against the ground again in the next frame

self.slope_prevtilex% = tsx% //save slope koordinate
self.slope_prevtiley% = tsy%

RETURN TRUE
ELSE
//we're not on a slope this frame - check if we left a slope
//-1 ... we didn't move from slope to slope
//0 ... we left a slope after moving down
//1 ... we left a slope after moving up
what% = -1

IF self.map.TMap_map(self.slope_prevtilex%, self.slope_prevtiley%) = t_sloperight%
IF self.velx > 0 //sloperight + velx > 0  = we left a slope after moving up the slope
what = 0;
ELSE
what = 1; //we left after moving down the slope
ENDIF
ELSEIF self.map.TMap_map(self.slope_prevtilex, self.slope_prevtiley) = t_slopeleft%
IF self.velx < 0 //sloperight + velx > 0  = we left a slope after moving up the slope
what = 0;
ELSE
what = 1; //we left after moving down the slope
ENDIF
ENDIF

IF what%<>-1 //if we left a slope and now are on a slope
IF what% = 1
self.y% =  tsy*TILESIZE% - self.h -1; //move y to top of the slope tile
sy% = self.y + self.h%
ELSE
self.y% =  (tsy+1)*TILESIZE - self.h -1; //move y to the bottom of the slope tile
sy% = self.y + self.h% + TILESIZE% //test one tile lower than the bottom of the slope (to test if we move down a long slope)
//it's physically incorrect, but looks a lot smoother ingame
ENDIF

//check for slopes on new position
IF collision_slope(sx, sy, tsx, tsy) //slope on new pos (entered a slope after we left a slope)
INC self.x%,self.velx% //-> we moved from slope to slope

unlockjump();

self.vely% = 1;

self.slope_prevtilex% = tsx%
self.slope_prevtiley% = tsy%

RETURN TRUE
ENDIF
ENDIF
ENDIF
ENDIF

//no slope collisions were found -> check for collisions with the map
//x axis first (--)
IF self.velx > 0 //moving right
IF collision_ver(self.x%+self.velx%+self.w, self.y, tilecoord%) //collision on the right side.
self.x = tilecoord%*TILESIZE% -self.w-1; //move to the edge of the tile (tile on the right -> mind the player width)
ELSE //no collision
INC self.x%,self.velx
ENDIF
ELSEIF self.velx < 0 //moving left
IF collision_ver(self.x%+self.velx%, self.y%, tilecoord%) //collision on the left side
self.x% = (tilecoord%+1)*TILESIZE% +1; //move to the edge of the tile
ELSE
INC self.x%,self.velx%
ENDIF
ENDIF

//THEN y axis (|)
IF self.vely%<0
IF collision_hor_up(self.x%,self.y%+self.vely%,tilecoord%)
self.y% = (tilecoord%+1)*TILESIZE% +1;
self.vely% = 0
ELSE
INC self.y%,self.vely%
INC self.vely%,GRAVITATION%
ENDIF
ELSE
//moving down / on ground
IF collision_hor_down(self.x%, self.y%+self.vely%+self.h%, tilecoord%) //on ground
self.y% = tilecoord%*TILESIZE% -self.h%-1;
self.vely% = 1; //1 so we test against the ground again int the next frame (0 would test against the ground in the next+1 frame)

unlockjump()
ELSE //falling (in air)
INC self.y,self.vely%
INC self.vely%,GRAVITATION%

IF self.vely%>= TILESIZE% //if the speed is higher than this we might fall through a tile
self.vely% = TILESIZE%
ENDIF

self.lockjump = TRUE; //don't allow jumping after falling of an edge
ENDIF
ENDIF

self.slope_prevtilex = (self.x% + ASR(self.w%,1)) / TILESIZE%
self.slope_prevtiley = (self.y% + self.h%) / TILESIZE%
RETURN FALSE
ENDFUNCTION
ENDTYPE

LOCAL spr_t%[]; DIM spr_t%[7]
LOCAL spr_player%[]; DIM spr_player%[2]
LOCAL spr_background%
LOCAL map AS TMap
LOCAL player AS TPlayer

LIMITFPS 30
SETTRANSPARENCY RGB(255,0,255)
spr_t%[0]=GENSPRITE(); LOADSPRITE "Media/gfx/t1.bmp",spr_t%[0]
spr_t%[1]=GENSPRITE(); LOADSPRITE "Media/gfx/t2.bmp",spr_t%[1]
spr_t%[2]=GENSPRITE(); LOADSPRITE "Media/gfx/t3.bmp",spr_t%[2]
spr_t%[3]=GENSPRITE(); LOADSPRITE "Media/gfx/tsloper.bmp",spr_t%[3]
spr_t%[4]=GENSPRITE(); LOADSPRITE "Media/gfx/tslopel.bmp",spr_t%[4]
spr_t%[5]=GENSPRITE(); LOADSPRITE "Media/gfx/t6.bmp",spr_t%[5]
spr_t%[6]=GENSPRITE(); LOADSPRITE "Media/gfx/t7.bmp",spr_t%[6]
spr_player%[0]=GENSPRITE(); LOADSPRITE "Media/gfx/left.bmp",spr_player%[0]
spr_player%[1]=GENSPRITE(); LOADSPRITE "Media/gfx/right.bmp",spr_player%[1]
spr_background%=GENSPRITE(); LOADSPRITE "Media/gfx/bg.bmp",spr_background%

IF map.TMap_Initialise(spr_t%[])=FALSE
DEBUG "Map could not be initialised"
RETURN FALSE
ENDIF

IF map.loadMap("Media/maps/map01.map")=FALSE
DEBUG "Map could not be loaded"
RETURN FALSE
ENDIF

player.TPlayer_Initialise(spr_player%[],map)

WHILE TRUE
//update objects
player.think()


//draw everything
DRAWSPRITE spr_background%,0,0
map.TMap_draw()
player.TPlayer_draw()
SHOWSCREEN
WEND


Example #3 :

Code can now be downloaded

[attachment deleted by admin]

bigsofty

Very interesting, its a great tutorial, well done!
Cheers,

Ian.

"It is practically impossible to teach good programming style to students that have had prior exposure to BASIC.  As potential programmers, they are mentally mutilated beyond hope of regeneration."
(E. W. Dijkstra)

MrTAToad

Thanks for that.  There is one more tutorial left to do now, and that's with scrolling.

One thing I should mention is that due to use extended types and NOT, the code will only work with V8

com_1

Congratulations. (how long(time) have you killed for this ?)

By the way, "Example 2" is slower than "Example 1".

I have no time to understand your code, but I think that the problem relates to the definition of position on the diagonal. (right ?)

Need help ?

Wampus

This might well come in useful. Thanks.  :)

MrTAToad

QuoteCongratulations. (how long(time) have you killed for this ?)
I used LIMITFPS to slow down the program - remove or change the value of 30 to -1 to get the program to run as fast as possible.

QuoteThis might well come in useful. Thanks.
Glad you like it!