I found a tutorial on creating a 2d platform editor.
The problem ive found with other free ones on here they have bugs.
or are just over complicate the issue
So here is a level editor that's fully documented

this editor will allow you to create a level bigger than the viewing window
allows you to save multiple levels
allows you to save as groups
allows a selection of tiles (saved with names 1.png 2.png etc)
Allows different sized tiles
has a magnetic drop that can be toggled on and off allowing so as tiles fit nicely
The instructions are text overlayed on the screen making it easier to know what keys etc
The arrow keys will move the viewport and the mouse is used to place sprites

// Project: platform
// Created: 2015-02-17
// set window properties
#constant xMaxView=1024 //maximum x viewing window
#constant yMaxView=600 //maximum y viewing window
#constant xMax=5024 //maximum width of level
#constant yMax=600 //maximum height of level
SetWindowTitle( "platform Editor" )
SetWindowSize( xMax, yMax, 0 )
SetScreenResolution( xMaxView, yMaxView )
// set display properties
SetVirtualResolution( xMaxView, yMaxView )
SetOrientationAllowed( 0, 0, 1, 1 )
//force sync rate to 60 for best cross platform support.
SetSyncRate(60,0)
rem ***** set up constants ******
#constant objectlimit = 150 : //Limit how many objects we can create so as to not go over the arrays size,which would
// cause an error.
#constant KEY_F1 = 112 : // these are scancode values for the keypresses,using variables rather than just values
#constant KEY_F2 = 113 : // is good practice and makes the code more readable for future modifying.
#constant KEY_C = 67
#constant KEY_G = 71
#constant KEY_H = 72
#constant KEY_Z = 90
#constant KEY_X = 88
#constant KEY_U = 85
#constant KEY_I = 73
#constant KEY_Left = 37
#constant KEY_Right = 39
#constant KEY_Up = 38
#constant KEY_Down = 40
#constant cursorsprite=1000
#constant displaysprite = 1001
// define a type for the objects
type objecttype
sprite as integer
x as integer
y as integer
group as integer
image as integer
endtype
dim object[objectlimit] as objecttype
// define a type for our mouse and screen coordinates
type TMouse
x1#
y1#
x2#
y2#
x3
y3
endtype
global Mouse as TMouse
global gridx#=64
global gridy#=64
global group=0 : // starting group number, we can use group numbers as a
// simple way to say what type of sprite you want,this
// depends on you the programmer to keep track of
// when it comes to using the saved level data...
// I will explain this when I write that very part!
// A hint here is to do it old style and write notes
// down on a physical paper notepad! again another good
// practice in programming. Especially when you are old'ish
//like me and have fried a few million brain cells so far! ;)
global objectnumber=1 : // starting sprite number
global image=1 : //starting sprite image number
global lev$="" : //string used to display current level number being edited
global obj$=""
global group$="" : //string to display the current group number
global viewoffsetX$=""
global viewoffsetY$=""
global level=1 : //actual level number,we use this for the conversion for lev$
global objectcount=0
global imagelimit = 11 : // set an image limit,depending how many we have!
global magnetic=1
global magnetic$="Magnetic =Off" //Magnetic on keeps the tile placement only within the grid
// I use functions where I can, another good practice! functions can be re-used
// and can keep clear of the main loop to make the whole code more readable,
// they have a very powerful feature of being able to have parameters
// As you learn more about programming, if you are an absolute beginner that is,
// then you will see how it all makes for wonderful programming structure!
init(): // no parameters to pass here! I always find it good practice to call a
// function like this and do whatever things need doing first just before we
// get to the main game loop which is next!
x=0:y=0
do
control()
SetViewOffset (x, y)
if GetRawKeyPressed(KEY_Left) and x>=gridx# //left
x=x-gridx#
endif
if GetRawKeyPressed(KEY_Right) and x<=(xMax-gridx#) //right
x=x+gridx#
endif
if GetRawKeyPressed(KEY_Up) and y>=gridy# //up
y=y-gridy#
endif
if GetRawKeyPressed(KEY_Down) and y<=(yMax-gridy#) //down
y=y+gridy#
endif
if GetRawKeyPressed(77) //M
if magnetic=0
magnetic=1
else
magnetic=0
endif
endif
lev$="Level Number = "+str(level)
SetTextString(1,lev$)
settextposition(1,10,10)
FixTextToScreen(1,1)
obj$="Object count = "+str(objectcount)
SetTextString(2,obj$)
settextposition(2,10,30)
FixTextToScreen(2,1)
group$="Group number = "+str(group)
settextstring(3,group$)
settextposition(3,10,50)
FixTextToScreen(3,1)
viewoffsetX$="View offset X="+str(x)
settextstring(4,viewoffsetX$)
settextposition(4,10,70)
FixTextToScreen(4,1)
viewoffsetY$="View offset Y="+str(y)
settextstring(5,viewoffsetY$)
settextposition(5,10,90)
FixTextToScreen(5,1)
if magnetic=0
magnetic$="Magnetic = Off"
else
magnetic$="Magnetic = On"
endif
settextstring(6,magnetic$)
settextposition(6,10,110)
FixTextToScreen(6,1)
setspriteposition(cursorsprite,Mouse.x3,Mouse.y3)
///////////////////
//SetSpriteImage(cursorsprite,image)
SetSpriteSize(cursorsprite,GetImageWidth(image),GetImageHeight(image))
Sync()
loop
function init()
load_images() : //a small function to load in images that have been save numerically
rem *************** Create cursor sprite ***************
createsprite(cursorsprite,1) : // Create the cursor sprite using the variable we predifined
// within the globals
//setspritesize(cursorsprite,64,64) : // We will stick to a set size of 64 wide and 64 tall
createtext(1,lev$) : // Create the text to display current level number ******
createtext(2,obj$): //create the text to keep track of how many objects we have so far
createtext(3,group$)
createtext(4,viewoffsetX$)
createtext(5,viewoffsetY$)
createtext(6,magnetic$)
CreateText(7,"F1 Load")
CreateText(8,"F2 Save")
CreateText(9,"C Clear")
CreateText(10,"G Dec Level")
CreateText(11,"H Inc Level")
CreateText(12,"Z Dec Image")
CreateText(13,"X Inc Image")
CreateText(14,"U Dec Group")
CreateText(15,"I Inc Group")
CreateText(16,"M Magnetic")
settextsize(1,20) // set the size of the lev$ text
settextsize(2,20) //set the size of the obj$ text
settextsize(3,20)
settextsize(4,20)
SetTextSize(5,20)
settextsize(6,20)
settextsize(7,20)
settextsize(8,20)
settextsize(9,20)
settextsize(10,20)
settextsize(11,20)
settextsize(12,20)
settextsize(13,20)
settextsize(14,20)
settextsize(15,20)
settextsize(16,20)
settextposition(7,10,140)
settextposition(8,10,160)
settextposition(9,10,180)
settextposition(10,10,200)
settextposition(11,10,220)
settextposition(12,10,240)
settextposition(13,10,260)
settextposition(14,10,280)
settextposition(15,10,300)
settextposition(16,10,330)
FixTextToScreen(7,1)
FixTextToScreen(8,1)
FixTextToScreen(9,1)
FixTextToScreen(10,1)
FixTextToScreen(11,1)
FixTextToScreen(12,1)
FixTextToScreen(13,1)
FixTextToScreen(14,1)
FixTextToScreen(15,1)
FixTextToScreen(16,1)
createsprite(displaysprite,image)
//setspritesize(displaysprite,128,128)
setspriteposition(displaysprite,850,10)
setspritedepth(displaysprite,0)
FixSpriteToScreen(displaysprite,1)
endfunction
function control()
//FillMouse(gridx#,gridy#): // variable to Check if user has just pressed the pointer
FillMouse(getSpritewidth(cursorsprite),getSpriteHeight(cursorsprite)): // variable to Check if user has just pressed the pointer
//FillMouse(16,16)
state=GetPointerReleased()
if state = 1 and objectnumber<objectlimit : // a value of 1 means it is pressed, and if within the array limit
// we can proceed to make another object
makeobject(objectnumber,Mouse.x3,Mouse.y3)
inc objectnumber
inc objectcount
endif
//Check for keypressed
if GetRawKeyReleased(KEY_F2) : // If we have just pressed the F2 key then we save the level
save_level(level)
endif
if getrawkeypressed(KEY_F1) : // If we have just pressed the F1 key then we load the level
// It will load the level according to whatever the current level
// number is set to,if it exists!
load_level(level)
endif
if getrawkeypressed(KEY_C)
clear_level() : // If the user presses the c key then we clear the level, this also gets called
// within the load_level function so we can clear out the types and
// values set to the defaults ready to receive a new or saved level
endif
if getrawkeyreleased(KEY_G) and level>1 : // If the user presses the G key then decrement the value of the
//variable level, and we also check that we do not go below a value
//of 1 or you would be going to level zero and lower!
dec level
endif
if getrawkeyreleased(KEY_H) : // If user presses key H then increment the variable level *************
inc level
endif
if getrawkeyreleased(KEY_Z) and image>1
dec image
setspriteimage(displaysprite,image)
SetSpriteSize(displaysprite,GetImageWidth(image),GetImageHeight(image))
endif
if getrawkeyreleased(KEY_X) and image<imagelimit
inc image
setspriteimage(displaysprite,image)
SetSpriteSize(displaysprite,GetImageWidth(image),GetImageHeight(image))
endif
if getrawkeypressed(KEY_U) and group>0 : // if U key is pressed then decrement group variable
dec group
endif
if getrawkeypressed(KEY_I) : // if I key is pressed then inccrement group variable
inc group
endif
endfunction
function FillMouse(x#,y#)
Mouse.x1#=getpointerx()
Mouse.y1#=getpointery()
Mouse.x2#=screentoworldx(Mouse.x1#)
Mouse.y2#=screentoworldy(Mouse.y1#)
if magnetic=1
Mouse.x3=floor(Mouse.x2#/x#)*x# //keeps it working in a tileable grid
Mouse.y3=floor(Mouse.y2#/y#)*y#+addToPlacement(y#) //grid placement allowing for flat on ground
else //remove addToPlacement if you want to place tiles from the top
Mouse.x3=(Mouse.x2#/x#)*x#
Mouse.y3=(Mouse.y2#/y#)*y#
endif
endfunction
function addToPlacement(y#)
yy=floor(yMax/y#)
result#=yMax-(yy*floor(y#))
endfunction result#
function makeobject(objectnumber,x,y)
createsprite(objectnumber,image)
SetSpritePosition(objectnumber,x,y)
//setspritesize(objectnumber,64,64)
object[objectnumber].x=x
object[objectnumber].y=y
object[objectnumber].image=image
object[objectnumber].sprite=objectnumber
object[objectnumber].image=image
object[objectnumber].group=group
endfunction
function load_images()
// because I have saved the images numerically,in other words they are named
// 1.png, 2.png. etc... I can make a simple loop to check if any exists using a simple
// value to string conversion, if it does we load it!
for n=1 to 100
f$=str(n) + ".png"
if getfileexists(f$)
loadimage (n,f$)
endif
next
endfunction
function save_level(level)
f$=""
f$="level"
f$=f$+str(level)
f$=f$+".lev"
myfile=opentowrite(f$,0)
for n=1 to objectlimit
writeinteger(myfile,object[n].sprite)
writeinteger(myfile,object[n].x)
writeinteger(myfile,object[n].y)
writeinteger(myfile,object[n].group)
writeinteger(myfile,object[n].image)
next n
closefile(myfile)
endfunction
function load_level(level)
f$=""
f$="level"
f$=f$+str(level)
f$=f$+".lev"
if GetFileExists(f$)
clear_level()
myfile=opentoread(f$)
for n=1 to objectlimit
objnum = readinteger(myfile)
xpos=readinteger(myfile)
ypos=readinteger(myfile)
gr=readinteger(myfile)
im=readinteger(myfile)
if objnum>0
createsprite(objnum,im)
//setspritesize(objnum,64,64)
setspriteposition(objnum,xpos,ypos)
object[n].sprite=objnum
object[n].x=xpos
object[n].y=ypos
object[n].image=im
object[n].group=gr
inc objectcount
endif
next n
objectnumber=objectcount+1
closefile(myfile)
else
e$="Error... File does not exist!"
createtext(20,e$)
settextsize(20,20)
settextposition(20,50,50)
sync()
sleep(2000)
deletetext(20)
endif
endfunction
function clear_level()
for n=1 to objectlimit
object[n].sprite = 0
object[n].x=0
object[n].y=0
object[n].image=0
object[n].group=0
if getspriteexists(n) = 1
deletesprite(n)
endif
next n
objectnumber=1
objectcount=0
endfunction