This is the code for the JNR platform examples. Included is currently example 1 and 2
Example 1:
// ---------------------------------
// 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 :
// ---------------------------------
// 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]
Very interesting, its a great tutorial, well done!
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
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 ?
This might well come in useful. Thanks. :)
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!