Basically surface is the wrong solution to this problem
First things first. what is the cause of your slowdown? Likely the texture swaps. If you dont have proper setup for your texture groups then you would get a large amount of swaps.
call
show_debug_overlay(true) when the game starts and note the (swaps)(batch) value at the top of the screen
Since you are changing the depth you cant really control the swaps much asides being very careful to define sprites dedicated for each levels of your game and a texture group for said levels. for example, the castle levels with all the castle sprites would go on the castle texture groups
and your character would go on it's own dedicated group so no matter where your character position is depth wise, you would get 1 swap to the castle group to draw stuff behind the player, then a swap for the player to draw him, then a swap back to the castle group to finish off the remaining elements of the castle level in front of the player...
As you have multiple characters I can see, if you have AI only for that castle, they too should be on the castle group...
It may not be possible to put the castle level AI in the castle level group if your animated sprite is big ant your texture size is too small.... so they may need to be in the another texture group. if all the castel characters can be on the same texture page then that would be cool. Each character in the level would cause an extra swap.
Target max swaps for android: 10
Target max swaps for Windows: 40
Second cause of slowdown: Depth change
the depth = -y sure looks innocent enough does it?
As implied above this causes a great deal of swaps which can cause slowdowns but the other issue rarely mentioned effect this has on the internal ordered draw list.
Basically every time you change a depth it causes studio to re-order the internal draw list... which has a huge impact if you have a ton of instances
This video for example, initially was limited top 500 lemmings
the way I approached the solution from Mike's input was to override the depth handling with my own.
How it works is I have an array based on the height of the room... or the height of the view if you want more efficiency...
so if the room is 1000 pixels tall I have an array of 1000 items.
each item holds an second array of sprites to draw. so it's an array inside an array....
each end step if the thing is visible/in view, I add the sprite to draw and required values to draw the sprite. I actually use my script batch system, get it while it's free on the store
on room start create array the size of the room height. I set a script batch container to the array item
pcode
for i = 0, i<room_height, i++
global.horizArr
= create script batch();
end step of object
if(in view)
batch = global.horizArr[y]
add script to batch, draw_this_scr, x,y
a controller loops through all the items in global.horizArr, processes the script batch, causing the items to draw themselves back to front, without having touched the depth...
you could have a global.horizArr for the level static element and a second global.horizArr for the moving characters... populate the static array once and the end step only need to loop through fewer elements that are moving.
your controller can do the static element then the moving element in the draw loop.