Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SKTileMapNode vs SKTiled performance question #40

Open
sanderfrenken opened this issue Apr 20, 2022 · 8 comments
Open

SKTileMapNode vs SKTiled performance question #40

sanderfrenken opened this issue Apr 20, 2022 · 8 comments

Comments

@sanderfrenken
Copy link

Hi there!

First of thanks for this amazing project, it's very useful and nicely built.
Having the ability to import tmx files straight into SpriteKit is incredibly valuable :)

I have some question though. I am working on a new project, and setting up the toolset. One of the main challenges I see is that I want to be able to render quite large maps (eg 300300tiles) without loading screens etc. The tiles are 3232, but that is not really important now. I preferably design the maps in Tiled, though that is not a must.

Now I tried two API's to draw large tile maps having 90K tiles. I tried SKTiled, and I really liked the easiness of putting a tmx map in there and it works OOTB. But I found that the performance is not optimal, which I sort of expected with SpriteKit having to take care of 90K nodes, even though not all are rendered ofc. Especially tile cracking is an issue, which I couldn't resolve with the proposed actions here.
Enabling shouldEnableEffects also didn't really help, as the map is larger than the max framebuffer texture size is exceeded in my case, making parts of the map being cropped out.

I gave SKTileMapNode a shot as well. This has an obvious downside, being that built-in Xcode map editor is quite a mess. But drawing the map does result in only 1 node being drawn, instead of 90K. Moreover, the tile cracking isn't an issue as well.

So I conclude out of this that the usability of SKTiled greatly exceeds that of the SKTileMapNode and related API's and map building capabilities. But, the performance optimizations of SKTileMapNode are probably not to be matched.
Hence, I wonder if it might be possible to use tmx files, post process them in some way, and as a result have a SKTileMapNode being produced, either fully in code or as .sks file. Possible it probably is, whether its worth the effort that yet remains to be uncovered. But before I loose myself in this problem, what are your thoughts on this, and did you ever consider the same for example?

Sorry to create an issue for this, I couldn't find another way to reach out to you.

Cheers,

Sander

@jsclev
Copy link

jsclev commented Jun 30, 2022

I'm not sure if you ever got a response to your question, or discovered a resolution. I am only just now stumbling on SKTiled, it is an amazing project. I wanted to be able to load large tiled maps in my own project, such as 500 tile by 500 tile maps, where each tile is 128px by 64px. I am building an empire-building game.

Since I previously did not know SKTiled existed, I wrote my own custom Tiled .tmx/.tsx SAX XML parsers to import Tiled files. I then dynamically construct a set of layered (i.e. controlled by zPosition) SKTileMapNodes, and add them to my SKScene. I am doing all of this in code (I have no experience with Xcode's map editor). But the performance so far is pretty amazing (at least to me). I can load a 500x500 map in less than 1 second, easily maintain 60fps, and I can keep my node count very low because of the SKTileMapNodes. I don't remember the exact numbers, but I also experimented with 1000x1000 tile maps, bumped up my assets to 256px by 128px, and my game had no problem loading quickly and maintaining 60fps.

I have been looking through the source code of SKTiled tonight, looking for ideas. Thought I would mention what I did, as it may help, or at least give you some ideas.

@sanderfrenken
Copy link
Author

Hi there! That is nice, I decided to build the same. I have developed it as part of MoreSpriteKit, you can find it here. I am still expanding it, but currently it can load tmx files (csv encoding only) and draw them to the scene.

It supports in addition pathfinding, zooming and moving of the camera and custom tile properties. The project's example also has some demo of it here.

Indeed the performance of SKTileMapNode is amazing. I am not even sure how it works internally, but no matter how large the map, it will always draw just one node. I think this is the best approach for drawing large tile maps with SpriteKit.

Maybe we can exchange some ideas on how to improve our TMX parsers, have a look around at MoreSpriteKit if you like and let me know what you think.

@sanderfrenken
Copy link
Author

I'm not sure if you ever got a response to your question, or discovered a resolution. I am only just now stumbling on SKTiled, it is an amazing project. I wanted to be able to load large tiled maps in my own project, such as 500 tile by 500 tile maps, where each tile is 128px by 64px. I am building an empire-building game.

Since I previously did not know SKTiled existed, I wrote my own custom Tiled .tmx/.tsx SAX XML parsers to import Tiled files. I then dynamically construct a set of layered (i.e. controlled by zPosition) SKTileMapNodes, and add them to my SKScene. I am doing all of this in code (I have no experience with Xcode's map editor). But the performance so far is pretty amazing (at least to me). I can load a 500x500 map in less than 1 second, easily maintain 60fps, and I can keep my node count very low because of the SKTileMapNodes. I don't remember the exact numbers, but I also experimented with 1000x1000 tile maps, bumped up my assets to 256px by 128px, and my game had no problem loading quickly and maintaining 60fps.

I have been looking through the source code of SKTiled tonight, looking for ideas. Thought I would mention what I did, as it may help, or at least give you some ideas.

a quick question, did you experience any tile cracking with your solution? If so, have you been able to get rid of it?

@jsclev
Copy link

jsclev commented Jun 30, 2022

I have not seen any tile cracking, at least not so far. And I've experimented with all manner of different sized maps and different tiles. I am planning on initially bypassing the need in my game to build a map editor, and instead rely on Tiled, so my goal was to build a custom Tiled importer process so that players could make their own maps. So I have experimented with many different tile sizes, map sizes, etc. I have not seen any tile cracking with the SKTileMapNode. Actually, I've been pleasantly surprised at how well the SKTileMapNode works. I'm a long-time software engineer but first-time game developer, so I don't know if SpriteKit is "normal" in terms of its performance and bugs, but it seems like the SKTileMapNode (in combination with Tile Sets), is an incredibly robust and efficient mechanism for scaling to huge tile maps. I thought for sure my huge maps I experimented with would cripple my iPad, but performance has been amazing, and I have not seen any sort of weird effects such as tile cracking. So far, the SKTileMapNode just seems to work as advertised, although I don't have a completed game, just a prototype, so who knows what I may still run into... I also use a SKCameraNode, and I think theoretically that means SpriteKit handles internally not needing to blit tiles that are on the map, but not actually visible on the screen.

@jsclev
Copy link

jsclev commented Jun 30, 2022

One other thing/idea to note, in case it is any help to you. My understanding of what causes tile cracking is due to a specific zoom level where the gpu is trying to render finitely sized pixels, but depending on the specific zoom level, it may cause situations where there are rounding problems. But I'm guessing here, since I don't have much experience with this stuff.

Anynow, I am doing all of my work against retina devices (iPhones and iPads), and I have also implemented a custom gesture recognizer that allows panning and zooming of the map. I have not seen any tile cracking from that either, but I gather vaguely from what I have read that the resolution of the target device plays a factor here, as well as the number of different zoom levels you want to allow. In my case, I wanted a dynamic fluid zoom of any amount on the whole map. But your needs may be different? Anyhow, throwing more info out there in case it might help.

I have personally had a really hard time learning SpriteKit as it seems to be an amazing framework, but seems to have very little adoption in the development community. I have yet to receive a single answer on several StackOverflow questions I have posted, as well as several Apple Developer forum questions. And while the documentation is good technically, the lack of code examples has made it hard for me to learn how to use the framework. The best learning step I made was to purchase the "Apple Game Frameworks and Technologies" book and just toil away reading that. I even sent questions to the author with no response. It has felt to me like everyone just uses Unity or Unreal, and that the number of developers out there using SpriteKit for anything more than just a weekend hobby project is slim to none. Which is kind of a shame, because my experience with it has been that once you learn how to use some of the things Apple has provided, they seem to work really really well. But getting there has been tough...

@sanderfrenken
Copy link
Author

So if I understand correct, you also load in a TMX file, which you parse in some way, and then construct SKTileMapNode instances out of it eventually? Or, do you have a different approach? If it is the same, do you also use SKTexture.rectIn (https://developer.apple.com/documentation/spritekit/sktexture/1520425-init) to create the individual tile groups?

I am also very pleasantly surprised by the performance of SpriteKit and in this case, SKTileMap. But yes the community is small. Which I can understand, with SpriteKit you are directly bound to iOS and OSX, which makes it commercially not a very interesting option. Nevertheless, personally I also opted for it. My game Herodom for iOS is also fully written in SpriteKit. I opted for SpriteKit as I am an iOS engineer for profession, so that made it easy to get into.

I didnt know that book yet! Will have a look at it:) I learned SpriteKit at first with the book iOS Games by Tutorials, which is probably quite outdated now:)

If you have specific questions I can help you with maybe, let me know. It's nice to have a fellow SpriteKit engineer :)

WRT the tile cracking, would you mind sharing some of your code how you do the camera movement? I have the same requirement as you, a fluid zoom and fluid movement. I see the tile cracking occur so now and then when using a specific zoom/ move location. I really have no idea how to solve it unfortunately, so any idea is welcome:) BTW I use tiles of 32*32 pixels.

@jsclev
Copy link

jsclev commented Jul 1, 2022

My project is at https://github.com/jsclev/astral-one, feel free to pull it. I'm theoretically building a commercial game, but my code thus far is messy prototyping code, so I'm keeping it a public repo for the foreseeable future. My primary interest by the way is actually related to AI development. I am a long-time Civ player and am essentially trying to build a Civ II clone but with great AI. I'm having a great time working on it.

All the map parsing stuff is in the Engine project, in the Tiled folder. All the SpriteKit SKTileMapNodes are in the Engine -> Views -> Map Layers folder. The class MapManager holds all of the "layers" of SKTileMapNodes. Theoretically you should be able to build the project and run it, but since it's just me and my son coding on it, I have not taken the time to make it easy to build, so let me know if you can't get it to build on your machine. There are two games within my project, one that is meant to be a normal human theme (Stone to Space), and another one that is meant to be a sci-fi theme (Astral One).

Search the workspace for "handleGestures" to see where some of the camera pan and zoom stuff is. We implemented a custom gesture recognizer in order to get the movement/zooming we were looking for.

Couple thoughts/ideas on the tile cracking. My tiles are 128x64 isometric tiles. Since your tiles are 32x32 I'm assuming you're not using isometric tiles. I wonder if the tile cracking in SpriteKit is unique to non-isometric? Also, what about different sized tiles other than 32x32? One other question: do you consistently observe the same tile cracking on all devices, including simulator devices? Are there any in which you do not get the tile cracking? Just throwing out ideas in case something might work. Also, if tile cracking is going to be an issue with SpriteKit, I'd like to know if you figure out a solution. That issue would be very problematic with what I am trying to do.

@sanderfrenken
Copy link
Author

sanderfrenken commented Jul 1, 2022

Awesome, thanks! I had a look, what a nice idea and so awesome you are making it with your son. That must be so much fun:)

I looked at the gesture handling, there was nothing that struck me there. But I think I found the root cause.
In my case, I bundle sprite sheets as used in Tiled, and using SKTexture.rectIn() I create the tile textures from it's contents (just like how Tiled works). Now, under the hood, the SKTextures all hold a reference to the original spritesheet.
If I use these SKTextures directly, I see the tile cracking occur so now and then.

But, I now do this workaround:

// Getting the tile texture from the sprite sheet
let tileTexture = SKTexture(rect: CGRect(x: tileSize.width*CGFloat(column)/sourceTexture.size().width, y: 1-(tileSize.height*CGFloat(row+1)/sourceTexture.size().height), width: tileSize.width/sourceTexture.size().width, height: tileSize.height/sourceTexture.size().height), in: sourceTexture)

// workaround to avoid tile cracking
let uimg = UIImage(cgImage: tileTexture.cgImage())
let newTexture = SKTexture(image: uimg)

Now, the new texture does not reference the complete sprite sheet texture anymore, instead it only represents the image data from the tile. I am not 100% sure why without the workaround the tile cracking happens, but it seems to be a consequence of how internally SKTexture.rectIn functions.

In your case, you don't rely on programmatically created TileDefintitions, as you created the tiles in Xcode. Hence I dont think you will run into this issue now.

Thanks for thinking along! It really helped :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants