Ben_Hurr
|
|
« on: February 03, 2010, 12:48:10 PM » |
|
Alright duders, if you had to load a dozen sequentially named files (ie durr01.dur, durr02.dur, durr03.dur etc) how would you go about it? How would you reject non-matching files in a way that isn't easily broken? edit: I'm using BlitzMax to do this. :c
|
|
« Last Edit: February 03, 2010, 02:00:52 PM by Ben_Hurr »
|
Logged
|
|
|
|
brog
|
|
« Reply #1 on: February 03, 2010, 01:01:44 PM » |
|
Uh.. char buffer[BUFFER_SIZE]; for (int i=1; i<=12; ++i) { sprintf(buffer, "durr%2d.dur", i); ifstream f(buffer); //do stuff with f } I guess. But what's your platform? And I'm not sure what you mean by "reject non-matching files". Do you mean files that are missing from the sequence, or what?
|
|
|
Logged
|
|
|
|
mewse
|
|
« Reply #2 on: February 03, 2010, 01:03:19 PM » |
|
FILE *file; char filename[20]; int i = 1; do { snprintf(filename,20,"durr%02d.durr", i); // build filename filename[19] = 0; // set string terminator (in case string was too long for snprintf to add it) i++; file = fopen(filename, "r"); if ( file ) { //process file here } }while(file);
The magic is done inside snprintf, which formats the "durr01.durr" filename strings for each file in sequence.
|
|
« Last Edit: February 03, 2010, 01:06:24 PM by mewse »
|
Logged
|
|
|
|
Ben_Hurr
|
|
« Reply #3 on: February 03, 2010, 01:29:50 PM » |
|
Yeeeeeeah C++ isn't going to work here, because I'm using BlitzMax. So I'm going to have to play with strings directly. But as to your question about rejection, I don't want to bother trying to open files that obviously don't match my naming convention, that's all.
|
|
|
Logged
|
|
|
|
Mikademus
|
|
« Reply #4 on: February 03, 2010, 03:33:47 PM » |
|
You have two answers, just adapt them to Blitz already will ya. Or get a blitz IO tutorial somewhere
|
|
|
Logged
|
\\\"There\\\'s a tendency among the press to attribute the creation of a game to a single person,\\\" says Warren Spector, creator of Thief and Deus Ex. --IGN<br />My compilation of game engines for indies
|
|
|
gnat
|
|
« Reply #5 on: February 03, 2010, 03:53:31 PM » |
|
Also if load times get too high, you may want to consider zipping them and loading from the one big archive file.
|
|
|
Logged
|
|
|
|
Ben_Hurr
|
|
« Reply #6 on: February 03, 2010, 03:56:06 PM » |
|
You have two answers, just adapt them to Blitz already will ya. Or get a blitz IO tutorial somewhere Already done, actually! Now I just need to pad out a string with zeros haha
|
|
|
Logged
|
|
|
|
nikki
|
|
« Reply #7 on: February 03, 2010, 04:39:11 PM » |
|
couldn't you do something like this (blitzmax pseudo code) superstrict
function read_file(filename) ' readbyte etc. ' endfunction
for local i:int = 0 until number_files local str:string str:+"durr" if i<10 then str:+"0" str:+i str:+".dur" read_file(str)
next
i am tired, so i havent tested this. but for an example its ok i guess hope
|
|
|
Logged
|
|
|
|
Ben_Hurr
|
|
« Reply #8 on: February 03, 2010, 05:19:18 PM » |
|
i am tired, so i havent tested this. but for an example its ok i guess hope Actually, I think I solved it. '// '// ROOM NUMBERING '// ' Find a consistant way of loading and numbering levels consecutively superstrict
const FILE_PATH:string = "maps/" '// folder to look for files in const FILE_PREFIX:string = "room" '// 1st part of file name - all room files need to start ' with this prefix to work. const FILE_EXT:string = "map" '// 3rd part of file name - extension needs to match ' to work as well.
local room:string[0] 'room array - each index corresponds to a room
'Make x amount of dummy level files named sequentially 'Format = 'room####.map' for local i:int = 0 to 4 local num:string = i 'pad out num string with 0's if too short if(num.length < 4) num = "000"[..4-num.length]+num end if
local file:tstream = writestream(FILE_PATH+FILE_PREFIX+num+"."+FILE_EXT) writeline(file, num) 'put room index in file closestream(file) next
'Load up each one in the map folder and assign them an index based on their filename 'Only recognizes files in this format >> 'room####.map', where #### represents numbers local files:string[] = loaddir(FILE_PATH)
for local url:string = eachin files local ind:int = IsValid(url)
'Add to array if file is valid 'File # determines where it's stuffed in the array if(ind>=0) room = room[..ind+1] 'expand array as necessary room[ind] = stripdir(url) else print "'"+url+"' Rejected!" end if next
'Run through the room array to see how it did for local i:int = 0 to room.length-1 print "room["+i+"] ="+room[i] next
end
'// Checks if a file is valid as a mapfile '// Returns the maps index number if valid, otherwise returns -1 ' url = File path function IsValid:int( url:string ) local fname:string = stripall(url).tolower() 'case independant filename local num:string = fname[4..8] 'room index is 4 chars in - 4 chars long local ext:string = extractext(url).tolower() 'case independant file ext.
'check if filename is the right size (it should be 8 characters) if(fname.length <> 8) return -1
'check if extension is correct if(ext <> FILE_EXT) return -1
'check if beginning of filename is 'room' if(fname.startswith(FILE_PREFIX) = false) return -1
'determine if num string contains *only* numbers for local i:byte = 0 to num.length-1 if(num[i]<48 or num[i]>57) return -1 '// ASCII chars 48-57 are numbers 0-9 next
return num.toint() '// if all checks out, return room index end function
Hopefully this might help someone else doing this. =O
|
|
|
Logged
|
|
|
|
nikki
|
|
« Reply #9 on: February 03, 2010, 05:52:55 PM » |
|
i like that isvalid method, will have a closer look one of these days is there a good reason your using arrays instead of linked lists ? because always when i see resizing of arrays i think linked list, but maybe that's just me.. but anyway cool that you figured it out!
|
|
|
Logged
|
|
|
|
Ben_Hurr
|
|
« Reply #10 on: February 03, 2010, 06:06:22 PM » |
|
Oh yes, I'm using an array because that's how my room system will work: Each index in the array will hold a room object, and the game can switch the active room by using that index. The list of rooms wouldn't change during the game, so arrays were ideal. I also kinda wanted to automate filling that array with map files so I don't have to hardcode filepaths in.
|
|
|
Logged
|
|
|
|
nikki
|
|
« Reply #11 on: February 04, 2010, 02:15:51 AM » |
|
I suspect these are the 2kb rooms you were loading in during playtime earlier ? tell us, how long does it take to read in , well lets say 1000 rooms, at loadtime. And is that fast enough for your wishes?
|
|
|
Logged
|
|
|
|
Ben_Hurr
|
|
« Reply #12 on: February 04, 2010, 08:30:20 AM » |
|
At the moment, loading 1000 1x1 rooms takes about 0.5-2 seconds for me. '// '// LEVEL LOADING TEST '// superstrict
local ft:int local map:TLevel = TLevel.create(1,1,1,1,15) 'max size is 5x5 print "Saving map..." map.save("level.dat") print "...Map saved!" 'debugstop() 'debugging to inspect object
ft = millisecs() print "Loading map..." for local i:int = 1 to 1000 'load multiple times to simulate loading multiple maps - important to see ON STARTUP loading times map = TLevel.load("level.dat") next print "...Map loaded!" 'debugstop() 'debugging to inspect object ft = millisecs() - ft
print ft+"ms"
end
'Object that represents a tilebased level in a videogame type TLevel field tileset:byte field music:byte field w:byte, h:byte field bg:TBg field tlayer0:byte[0,0] field tlayer1:byte[0,0] field tlayer2:byte[0,0] field mlayer:byte[0,0] field obj_list:TDummy[]
'Make a new empty level ' tileset = tileset id ' music = music id ' w,h = map dimensions in screens ' num_boj = number of dummy objects in the map function create:TLevel( tileset:byte, music:byte, w:byte, h:byte, num_obj:int ) local obj:TLevel = new TLevel obj.tileset = tileset obj.music = music obj.w = w obj.h = h obj.bg = TBg.create(1,1,1,1,1) obj.tlayer0 = new byte[w*20, h*15] obj.tlayer1 = new byte[w*20, h*15] obj.tlayer2 = new byte[w*20, h*15] obj.mlayer = new byte[w*20, h*15] obj.obj_list = new TDummy[num_obj]
local i:int for i = 0 to len(obj.obj_list)-1 obj.obj_list[i] = TDummy.create(1,1,1) next return obj end function
'Recreate a level from a file ' url = Filepath to save level to function load:TLevel( url:string ) local lev:TLevel = new TLevel local file:tstream = readstream(url) '// Load general level settings lev.tileset = readbyte(file) '// tileset index byte lev.music = readbyte(file) '// music index byte lev.w = readbyte(file) '// level width byte lev.h = readbyte(file) '// level height byte
'// Load BG settings lev.bg = new TBg '// make a new BG object lev.bg.id = readbyte(file) '// BG image index byte lev.bg.x = readfloat(file) '// BG x offset float lev.bg.y = readfloat(file) '// BG y offset float lev.bg.xvel = readfloat(file) '// BG xvel float lev.bg.yvel = readfloat(file) '// BG yvel float
'// Load Tile layer 0 lev.tlayer0 = new byte[20*lev.w, 15*lev.h]
local x:int, y:int for y = 0 to (15*lev.h)-1 for x = 0 to (20*lev.w)-1 lev.tlayer0[x, y] = readbyte(file) '// current tile value byte next next
'// Load Tile layer 1 lev.tlayer1 = new byte[20*lev.w, 15*lev.h]
for y = 0 to (15*lev.h)-1 for x = 0 to (20*lev.w)-1 lev.tlayer1[x, y] = readbyte(file) '// current tile value byte next next
'// Load Tile layer 2 lev.tlayer2 = new byte[20*lev.w, 15*lev.h]
for y = 0 to (15*lev.h)-1 for x = 0 to (20*lev.w)-1 lev.tlayer2[x, y] = readbyte(file) '// current tile value byte next next
'// Load Mask layer lev.mlayer = new byte[20*lev.w, 15*lev.h]
for y = 0 to (15*lev.h)-1 for x = 0 to (20*lev.w)-1 lev.mlayer[x, y] = readbyte(file) '// current tile value byte next next
'// Load object list lev.obj_list = new TDummy[readshort(file)] '// list size byte
local i:int for i = 0 to len(lev.obj_list)-1 local obj:TDummy = new TDummy '// make new blank object obj.id = readshort(file) '// current object's id short obj.x = readfloat(file) '// current object's x float obj.y = readfloat(file) '// current object's y float lev.obj_list[i] = obj '// put object in list next
closestream(file) '// close file once finished
return lev end function
'Write the level to disk ' url = filepath of level file method save:TLevel( url:string ) local file:tstream = writestream(url)
'// Save general level settings writebyte(file, tileset) '// tilset byte writebyte(file, music) '// music byte writebyte(file, w) '// level width byte writebyte(file, h) '// level height byte
'// Save BG settings writebyte(file, bg.id) '// BG image index byte writefloat(file, bg.x) '// BG x offset float writefloat(file, bg.y) '// BG y offset float writefloat(file, bg.xvel) '// BG xvel float writefloat(file, bg.yvel) '// BG yvel float
'// Save Tile layer 0 local x:int, y:int for y = 0 to (15*h)-1 for x = 0 to (20*w)-1 writebyte(file, tlayer0[x, y]) '// each byte in the array next next
'// Save Tile layer 1 for y = 0 to (15*h)-1 for x = 0 to (20*w)-1 writebyte(file, tlayer1[x, y]) '// each byte in the array next next
'// Save Tile layer 2 for y = 0 to (15*h)-1 for x = 0 to (20*w)-1 writebyte(file, tlayer2[x, y]) '// each byte in the array next next
'// Save Mask layer for y = 0 to (15*h)-1 for x = 0 to (20*w)-1 writebyte(file, mlayer[x, y]) '// each byte in the array next next
'// Save object list writeshort(file, len(obj_list)) '// list size byte
'// MARK: IT'd probably be better to make read/write methods for each object that needs saving local obj:TDummy for obj = eachin obj_list writeshort(file, obj.id) '// current object's id byte writefloat(file, obj.x) '// current object's x float writefloat(file, obj.y) '// current object's y float next
closestream(file) '// close file once finished end method end type
'Object that represents a level's background layer type TBg field id:byte field x:float, y:float field xvel:float, yvel:float
function create:TBg( id:byte, x:float, y:float, xvel:float, yvel:float ) local obj:TBg = new TBg obj.id = id obj.x = x obj.y = y obj.xvel = xvel obj.yvel = yvel
return obj end function end type
'Object that represents an object in the level type TDummy field id:short field x:float, y:float
function create:TDummy( id:short, x:float, y:float ) local obj:TDummy = new TDummy obj.id = id obj.x = x obj.y = y
return obj end function end type
Hoho, try it for yourself?
|
|
|
Logged
|
|
|
|
|