Tiled map generation (removing & adding columns) When? - ios

I'm making an endless runner like game for iOS, right now I'm at the map generation.
It's working when the map is moving in a fixed speed, it's endless and it's random. I'm using an array to hold the columns of tiles and I remove the first object of the array if it's out of screen, and then I generate a new column at the end of it.
My problem is when I want to increase the speed of the map, the map generation starts to slow and after a while it becomes slower the the map itself, I can't figure it out why.
I created a helper SKSpriteNode which starts at (40,0) and I'm changing its position at the same time as the maps position, and when it's less than or equal to zero I remove a column and generate an other.
The starting mapNode.mapSpeed is 2.0f and I would like to increase it with various values over time.
self.helperNode.position = CGPointMake(self.helperNode.position.x+self.mapNode.mapSpeed, self.helperNode.position.y);
self.mapNode.position = CGPointMake(self.mapNode.position.x-self.mapNode.mapSpeed, self.mapNode.position.y);
if (self.helperNode.position.x <= 0.0f) {
[self.mapNode generateNextColumn];
self.helperNode.position = CGPointMake(40.0f, 0.0f);
NSArray *array = self.mapNode.map[self.mapNode.rowCount];
SKSpriteNode *row = [self.mapNode buildNextRowFromArray:array];
row.position = CGPointMake((self.mapNode.rowCount*40)-20, 0);
[self.mapNode addChild:row];
NSString *string = [NSString stringWithFormat:#"row%li",self.mapNode.rowCount-(long)self.mapNode.children.count];
SKNode *removableRow = [self.mapNode childNodeWithName:string];
[removableRow removeFromParent];
}

Frequently calling "create/remove node" is rather inefficient. More so if you have to get each row's nodes by string, that's highly inefficient.
You have a fixed number of active sprites (tiles) at any given time, so you should preallocate does sprites and put them in an array or dictionary.
Every column of sprites that has scrolled past the screen border simply get to update their positions (and textures) so they become the next column to appear on the screen.

Related

Optimizing SKLabelNode to reduce performance issues

Creating an RPG with SpriteKit has me creating SKLabelNode's for displaying all game text. I'm suffering performance issues (FPS dropping & the game lagging) every time my multi-line text box begins auto typing inputted text which uses SKLabelNode's. I also see performance hits when "summoning" UI elements, such as character stat panels (with lots of SKLabelNode's for skill levels, skills names, etc.).
Veteran developers say pre-loading data always helps, thus I did the following (which has reduced the performance hit but hasn't eliminated it & the text box still lags):
// Preloaded model label (private property that is initialized in the class init method).
_modelLabel = [[SKLabelNode alloc] initWithFontNamed:_fontName];
_modelLabel.fontColor = _fontColor;
_modelLabel.fontSize = _fontSize;
_modelLabel.text = #"T";
// Constantly created label.
SKLabelNode *rowText = [[SKLabelNode alloc] initWithFontNamed:self.modelLabel.fontName];
rowText.fontColor = self.modelLabel.fontColor;
rowText.fontSize = self.modelLabel.fontSize;
rowText.text = row;
...
[self addChild:rowText];
I would be grateful for getting some more optimizations tricks when using SKLabelNode's.
Here are the tips I have collected thus far:
Preloading is only needed when using a font that is not available via iOS.
To get the font to actually load you need to also set the text for the preload to work.
Try to preload SKLabelNode with all chars you need. Maybe, SpriteKit renders only required glyphs into texture.
// Preloaded model label (private property that is initialized in the class init method).
_modelLabel = [[SKLabelNode alloc] initWithFontNamed:_fontName];
_modelLabel.fontColor = _fontColor;
_modelLabel.fontSize = _fontSize;
_modelLabel.text = #"ABCDEFGHIJKLMNOPQRSTUVWXZabcdefghijklmnopqrstuvwxyz0123456789-+=_()";
And then add your model into scene with minimal alpha (but no zero, because if node has 0 alpha, it will not drawn); Try to keep that node in scene
_modelLabel.alpha = CGFLOAT_MIN;
[self addChild:_modelLabel];
If not helps:
Organize nodes by aligning their zPosition (aka z-index). You should disable ignoresSiblingOrder for having control over zPosition.
Make your all SKLabelNode be drawn at one z level.
Switch to bitmap fonts. Writing custom bitmap font renderer is super easy. The only thing you should remember - Make all glyphs be in only texture atlas, so you can draw your text in one draw call
Use native labels. I'm not sure, how much does it help. If you're having heavy FPS drops (~5..10FPS) This may help you
It seems to me you are creating new labels every time you need text. If I were you only create the label once, and just alter the text when needed as well as just hide the lines you are not using.
Another thing you can do is fill the text in on your label one time, then assign it to an SKCropNode. Then you can use the SKCropNode to bring the font characters back in, which in turn should give you a nicer scrolling effect since you are not doing it at one character at a time.

Using shader modifiers to animate texture in SceneKit leads to jittery textures over time

I'm writing a scene in SceneKit for iOS.
I'm trying to apply a texture to an object using a sprite sheet. I iterate through the images in that sheet with this code:
happyMaterial = [SCNMaterial new];
happyMaterial.diffuse.contents = happyImage;
happyMaterial.diffuse.wrapS = SCNWrapModeRepeat;
happyMaterial.diffuse.wrapT = SCNWrapModeRepeat;
happyMaterial.shaderModifiers = #{ SCNShaderModifierEntryPointGeometry : #"_geometry.texcoords[0] = vec2((_geometry.texcoords[0].x+floor(u_time*30.0))/10.0, (_geometry.texcoords[0].y+floor(u_time*30.0/10.0))/7.0);" };
All is good. Except over time, the texture starts to get random jitteriness in it, especially along the x-axis.
Someone mentioned it could be because of "floating-point precision issues," but I'm not sure how to diagnose or fix this.
Also: I'm not sure how to log data from the shader code. Would be awesome to be able to look into variables like "u_time" and see exactly what's going on.
It's definitely a floating point precision issue. you should probably try to do a modulo on (u_time*30.0) so that it loops within a reasonable range.
if you want to iterate over images your texture coordinate must stay the same for a short period of time (1 second for instance).
u_time is similar to CACurrentMediaTime(), it's a time in seconds.
Now let's say you have N textures. Then mod(u_time, N) will increase every second from 0 to N-1 and then go back to 0. If you divide this by N you've got your texture coordinate, and you don't need SCNWrapModeRepeat.
If you want your image to change every 0.04 second (25 times per second), then use mod(25 * u_time, N) / N.

Computing text size with NSLayoutManager

I'm working on a view which uses TextKit framework to typeset text in columns like this:
I use my UIView's bounds with edge insets (black rectangle) to compute 10 CGRects which I then transform into NSTextContainers (red rectangles). In drawRect: I pass those to the NSLayoutManager which typesets and draws the glyphs for me.
My question is: How can I compute the number of columns required? I can draw a constant number of columns but with varying text lengths, I need to adjust the number of columns programmatically.
I found the method [NSLayoutManager glyphRangeForTextContainer:] which returns range of length 0 when the text container is to be left empty. Therefore, I could loop to create text containers and use this method to determine if more containers are needed. However, this method is said to be inefficient as it triggers the layout computation and I'm not happy running it in a loop perhaps hundreds of times over.
There has to be a better way!
Thanks for your answers, Pete.
Well, after some digging through the TextKit framework I've finally found the answer.
My code works in a loop like this:
while ([self needsMoreColumns]) {
[self addColumn];
}
...
- (BOOL)needsMoreColumns {
// Always create at least one column
if (self.layoutManager.textContainers.count == 0)
return YES;
// Find out the glyph range of the last column
NSRange range = [self.layoutManager glyphRangeForTextContainer:[self.layoutManager.textContainers lastObject]];
NSUInteger glyphs = [self.layoutManager numberOfGlyphs];
// Compare it with the number of glyphs
return range.location + range.length < glyphs;
}
I didn't include the method [self addColumn] as it's a no brainer. It simply uses the geometry of my layout and position of the last column (if any) to compute the CGRect of the next one. Then, it creates NSTextContainer with respective size and stores the origin property of the rectangle in a dedicated array for drawing purposes.
I've also discovered methods [NSLayoutManager firstUnlaidCharacterIndex] and [NSLayoutManager firstUnlaidGlyphIndex] but they don't seem to work as expected. After laying out three columns worth of text in only one column, they returned the length of the entire string and not the position of the first character which didn't fit into the first column. That's why I rather used the range-based approach.
That's all folks, be safe!
Pete.

Check and Prevent Collision - TileEngine but units aren't bound to tiles

The map is made by a two dimensional array of a class I made called Tile.
Each tile has smaller tiles inside of it that either collide with units, or don't.
But how can I detect and prevent collision?
Let's say The player is 6x6 smaller(mentioned above) tiles large, and one normal tile is 8x8.
How do I know that when the player is moving to the right that I know a tile is there, and move enough so they won't overlap?
As required by the rules.. I need to show effort, so this is what I have so far:
If the player is 6x6 and one tile is 8x8, then that means the player will either be on one tile, or be between 2 tiles; when he is standing between tiles(Units aren't bound to tiles). So let's say the player is in between two tiles, and wants to move right:
I check the collision of the tile right of the first (of two tiles the player is in) tile, then I check the second. To be able to move right, both must be empty.
But if a unite is say 10x10 large, then It can be in 2 tiles, or 3 max. How do I constantly check collision if I know that sizes of each unit are different, and aren't constant.
Basically, I need help with the stuff above, or a new collision method.
If I understand your question correctly, it is the smaller tiles that cause collisions, not the larger ones. In this case, you can simply iterate through all of the small tiles the unit would cross in a given move, stopping if you encounter one that would halt the unit's progress.
As an example, here is a method that would handle moving a unit right:
void moveUnitRight(Unit unit, int tilesRight)
{
for(int col = unit.Right + 1 ; col <= unit.Right + tilesRight ; col++)
{
for(int row = unit.Top ; row <= unit.Bottom ; row++)
{
if (tiles[row,col].Collides) //stop unit here
{
unit.Right = col - 1; //set position to tile left of collision
return; //search no further
}
}
}
//if you reach this point, no collision was detected
unit.Right += tilesRight; //move the full distance
}
Once you understand this, you could write a more robust method that handles movement in any direction as well as checking for map boundaries.

Repeating 2d world

How to make a 2d world with fixed size, which would repeat itself when reached any side of the map?
When you reach a side of a map you see the opposite side of the map which merged togeather with this one. The idea is that if you didn't have a minimap you would not even notice the transition of map repeating itself.
I have a few ideas how to make it:
1) Keeping total of 3x3 world like these all the time which are exactly the same and updated the same way, just the players exists in only one of them.
2) Another way would be to seperate the map into smaller peaces and add them to required place when asked.
Either way it can be complicated to complete it. I remember that more thatn 10 years ago i played some game like that with soldiers following each other in a repeating wold shooting other AI soldiers.
Mostly waned to hear your thoughts about the idea and how it could be achieved. I'm coding in XNA(C#).
Another alternative is to generate noise using libnoise libraries. The beauty of this is that you can generate noise over a theoretical infinite amount of space.
Take a look at the following:
http://libnoise.sourceforge.net/tutorials/tutorial3.html#tile
There is also an XNA port of the above at: http://bigblackblock.com/tools/libnoisexna
If you end up using the XNA port, you can do something like this:
Perlin perlin = new Perlin();
perlin.Frequency = 0.5f; //height
perlin.Lacunarity = 2f; //frequency increase between octaves
perlin.OctaveCount = 5; //Number of passes
perlin.Persistence = 0.45f; //
perlin.Quality = QualityMode.High;
perlin.Seed = 8;
//Create our 2d map
Noise2D _map = new Noise2D(CHUNKSIZE_WIDTH, CHUNKSIZE_HEIGHT, perlin);
//Get a section
_map.GeneratePlanar(left, right, top, down);
GeneratePlanar is the function to call to get the sections in each direction that will connect seamlessly with the rest of your world.
If the game is tile based I think what you should do is:
Keep only one array for the game area.
Determine the visible area using modulo arithmetics over the size of the game area mod w and h where these are the width and height of the table.
E.g. if the table is 80x100 (0,0) top left coordinates with a width of 80 and height of 100 and the rect of the viewport is at (70,90) with a width of 40 and height of 20 you index with [70-79][0-29] for the x coordinate and [90-99][0-9] for the y. This can be achieved by calculating the index with the following formula:
idx = (n+i)%80 (or%100) where n is the top coordinate(x or y) for the rect and i is in the range for the width/height of the viewport.
This assumes that one step of movement moves the camera with non fractional coordinates.
So this is your second alternative in a little bit more detailed way. If you only want to repeat the terrain, you should separate the contents of the tile. In this case the contents will most likely be generated on the fly since you don't store them.
Hope this helped.

Resources