I've made a Tiled game. Right now I'm stress testing my phone's capabilities by increasing amount of nodes in the scene. There's physics based stuff, AI movement, Day & Night system, particles popping out here & there & plenty of other stuff going on under the hood for my scenes. What I want to know is there a performance difference in using 16x16 tiles, what I have now, versus using 32x32 tiles? These tiles are basically just an image that was added to the scene; they don't have any physics bodies or anything else of that sort. They do have properties I set upon them when making the map in Tiled, but I don't think that has any performance impact. Each map has several layers (background, vegetation, spawn points, buildings, sometimes a few more). Here is a code snippet of how tiles are rendered for 1 such layer:
if([map propertiesForGid:tileGid][#"shrub"])
{
SKSpriteNode *tile = [layer tileAtCoord:coord];
tile.name = #"vegShrub";
[self addChild:tile];
}
else if([map propertiesForGid:tileGid][#"tree"])
{
SKNode *tile = [[Breakable alloc] initWithWhole:[atlas textureNamed:#"tree"] broken:[atlas textureNamed:#"tree-stump"]];
tile.position = [self pointForCoord:coord];
[self addChild:tile];
[layer removeTileAtCoord:coord];
}
If I use 32x32 tiles over my current 16x16 tiles, will I somehow free up some memory or "relieve the load" off the system?
With tile maps, each tile is usually represented by a SKSpriteNode. So if your map is 320 x 320 and you're using 32x32 tiles, you will end up with 100 nodes. Using 16x16 tiles on the same map size will result in 400 nodes. The more nodes, the greater the load.
On another note, you should look into getting the SKAToolKit to parse tile maps in your app. It's open source, free and has a ton of built in features such as auto follow, mini map, etc...
Related
I'm building an app that provides an editable canvas similarly to photoshop or illustrator (currently using SpriteKit). I'm encountering some performance issues when displaying a large number of nodes (1000+) as you might imagine.
Currently, I've got an SKScene with SKShapeNodes on it. They presently do not have any images and are simply filled with a color. Their shape paths (CGPaths) vary from circles, to bezier paths. Each shape currently has an SKPhysicsBody with the same path as the rendered shape that is used to detect taps.
The performance issues can be described by:
slowness when adding 1000 nodes (circles), uses about 0.1mb of memory per node
slowness when moving 1000 nodes (circles)
slowness when generating a texture from 1000 nodes (circles)
Disabling PhysicsBodies doesn't substantially improve performance, but does improve CPU load (jumps from constant 60% to 1% or so)
Most users will not be working with 1000 nodes, but I'd like to implement the optimal solution.
What I'd like is two have two layers:
A render layer on which I'd like to be able to render CGPaths with strokes and fills (preferably choosing the stroke end cap style among other little things)
An interaction layer on which I'd like to be able to detect taps inside CGPaths and stroke CGPath's with a color to indicate highlighting.
How can I accomplish this or a similar solution that will improve the speed at which I can render 1000 circles?
Don't use SKShapeNode, it needs 1 draw call per shapenode. You can create a shape, then "cast" it to a SpriteNode before adding it to the scene:
func shapeToSprite(_ shape: SKShapeNode) -> SKSpriteNode {
let sprite = SKSpriteNode(texture: SKView().texture(from: shape))
sprite.physicsBody = shape.physicsBody // Or create a new PB from alpha mask (may be slower, IDK)
shape.physicsBody = nil
return sprite
}
override func didMove(to view: SKView) {
let shape = SKShapeNode(circleOfRadius: 60)
addChild(shapeToSprite(shape))
}
This would suck though if you needed to constantly edit the size / shape of your shape in a way that isn't doable via scaling or SKWarpGeometry
To detect taps like this, you just need to either 1, Set a name for the SpriteNode then look for it in touchesBegan, or 2, subclass a SpriteNode and override it's touchesBegan.
Also, if you need to just have a static image, you can bit-blit thousands of nodes onto 1 texture.
Sprite Kit: A Lot of sprites (1000+) with Bit Blitting
http://www.sdkboy.com/2014/01/spritekit-bit-blitting-by-drawing-to-a-texture-for-performance-optimization/
So if you don't need every node to be touchable at all times, you can alternate between having physicsBodys and not, or switch layers from bitblitted to active.
For my game I am trying to create custom textures from two other textures. This is to allow for a varietly of colours, etc in my sprites.
To do this, I'm creating a sprite by adding both textures together, then applying this to a new SKTexture by using
SKTexture *texture = [self.view textureFromNode:newSprite];
This works great on the whole and I get a nice custom texture. Except when trying my game on Retina devices, where the texture is the correct size on the screen, but clearly a lower resolution.
The textures are all there and properly named so I don't believe that that is an issue.
Has anyone encountered this, or know how I can create the proper #2x texture?
I finally (accidentally) figured out how to fix this. The node which you are creating a texture from has to be added to the scene. Otherwise you will get a non-retina size for your texture.
It's not ideal as it would be nice to create textures without having to add them onto the screen.
I've discovered another way of improving the fidelity of textures created from ShapeNodes, not quite related to this question - but useful intel.
Create your shape at x2 its size and width.
Create all the fonts and other shapes at the same oversized ratio.
Make sure your positioning is relative to this overall size, (e.g. don't use absolute sizes, use relative sizes to the container.)
When you create the texture as a sprite it'll be huge - but then apply
sprite.scale = 0.5; // if you were using 2x
I've found this makes it look much higher resolution, no graininess, no fuzziness on fonts, sharp corners.
I also used tex.filteringMode = SKTextureFilteringNearest;
Thus: it doesn't have to be added to the scene and then removed.
I am creating a SpriteKit game with a tiled map. Each tile is an SKSprite node. When I have about 800 tiles, there are no problems. But if I try to increase the size of the map to around 2000 tiles, my FPS goes from 60 to 20. The number of tile nodes on the screen doesn't change (about 80), just the number of nodes off-screen. Any ideas of what could be causing this, or how to remedy it?
There doesn't appear to be a defined max number of nodes. It really depends on the amount of available free memory on your device. For example consider the following code:
int NODE_LIMIT = 375000
....
for (int i = 0; i<NODE_LIMIT; i++) {
SKNode *node = [SKNode node];
[self addChild:node];
}
I can create 375000 nodes in my sprite kit game. But as I increase the number above that, my device runs out of memory. The amount of free memory on your device will vary depending on a number of factors. As mentioned in the comments, the reason your frame rate slows down, is because the physics simulation runs even for nodes which are not visible on screen.
To maintain a high frame rate, get rid of physics bodies which are not visible, or which do not need to be simulated every frame. You could do this by adding sprites / physics bodies only when they are in the viewable part of the screen, and removing them when they are not.
I try to design a cocos2d game that with a big world. The wolrd tiled map is very big, maybe 20 ipad screen sizes. I want to know how to load CCTMXTiledMap into screen? If I directly add it to screen, will it use too much memory? If so, is there a solution to solve this problem?
The memory usage of the tilemap depends on the size of the tiles and how many of the tiles need to be converted to sprites, for example if you want to run animate them.
Assuming your tiles are 32x32 pixels in size. 20 times iPad dimensions means 20.480x15.360 points. Divided by 32 gives you a tile size of the map of 640x480 tiles, or 307.200 tiles total. Assuming that each tile uses 64 Bytes memory (in an older cocos2d version it was less than 64 bytes) your map ends up using almost 19 MB of memory. That's not even counting the textures.
Now for every tile that you address via the CCSprite interface, for example by changing one of the sprite properties or running actions on it, cocos2d will convert that tile to a regular CCSprite. Each CCSprite object uses around 420 Bytes last time I checked. So if you were to change the color property of each tile thus converting all tiles to CCSprite objects, the tilemap would consume 123 MB of memory. Again, not including textures.
You could try the Tilemap renderer in KoboldTouch. It can handle very large tilemaps (millions of tiles, ie 4000x4000 tiles) and renders it at 60 fps.
If you try to load all the tiles at one, it will definitely occupy too much memory.
Have a look at this tutorial about CCTMXTilesMap.
I'm just starting game development and I thought a game like Tank wars or Worms would be nice.
The hardest part I can think of so far is making the terrain destructible and I want to know how it's done before doing the easy parts.
I thought that explosion could have a mask texture which could be scaled for different weapons. Then using that mask I should make underlying terrain transparent (and optionally draw a dark border).
(source: mikakolari.fi)
How do I achieve that?
Do I have to change the alpha value pixel by pixel or can I use some kind of masking technique? Drawing a blue circle on top of the terrain isn't an option.
I have versions 3.1 and 4.0 of XNA.
This tutorial is what you are searching:
http://www.riemers.net/eng/Tutorials/XNA/Csharp/series2d.php
Capter 20: Adding explosion craters
In short:
You have 2 textures: 1 Color Texture (visible), 1 Collision Texture (invisible)
You substract the explosion image from your collision texture.
To get the dark border: expand the explosion texture and darken the color in this area.
Now you generate a new Color Texture (old color - collison = new color).
This is a difficult question to answer - because there are many ways you could do it. And there are pros and cons to each method. I'll just give an overview:
As an overall design, you need to keep track of: the original texture, the "darkness" applied, and the "transparency" applied. One thing I can say almost for sure is you want to "accumulate" the results of the explosions somewhere - what you don't want to be doing is maintaining a list of all explosions that have ever happened.
So you have surfaces for texture, darkness and transparency. You could probably merge darkness and transparency into a single surface with a single channel that stores "normal", "dark" (or a level of darkness) and "transparent".
Because you probably don't want the dark rings to get progressively darker where they intersect, when you apply an explosion to your darkness layer with the max function (Math.Max in C#).
To produce your final texture you could just write from the darkness/transparency texture to your original texture or a copy of it (you only need to update the area that each explosion touches).
Or you could use a pixel shader to combine them - the details of which are beyond the scope of this question. (Also a pixel shader won't work on XNA 4.0 on Windows Phone 7.)
You should Make a new Texure2D with the Color of desired pixels.Alpha = 0.
Color[] bits = new Color[Texture.Width * Texture.Height];
Texture.GetData(bits);
foreach(Vector2D pixel in overlapedArea)
{
int x = (int)(pixel.X);
int y = (int)(pixel.Y);
bits[x + y * texture.Width] = Color.FromNonPremultiplied(0,0,0,0));
}
Texture2D newTexture = new Texture2D(texture.GraphicsDevice, texture.Width, texture.Height);
newTexture.SetData(bits);
Now replace the new Texture2D with the Last Texture and you're good to go!
For more code about Collision, or changing texture pixels color go to this page for codes:
http://www.codeproject.com/Articles/328894/XNA-Sprite-Class-with-useful-methods