Possible to retrieve the parameters used to create an SKPhysicsBody object? - ios

Is it possible to retrieve the parameters used to create an SKPhysicsBody object without keeping them around separately? In other words, is there a way to get the body type (i.e. circle, rectangle, polygon) and related information (i.e. radius, size, path) from the following objects after creation:
SKPhysicsBody *circleBody = [SKPhysicsBody bodyWithCircleOfRadius:100.0];
SKPhysicsBody *rectangleBody = [SKPhysicsBody bodyWithRectangleOfSize:CGSizeMake(100.0, 100.0)];
There don't appear to be any properties or methods in the SKPhysicsNode class reference along the lines of:
SKPhysicsBodyType type = circleBody.bodyType; // Doesn't exist
CGFloat radius = circleBody.radius; // Doesn't exist
I'm surprised that there is information you can pass to a SKPhysicsBody object at creation that isn't at least available through a read-only property later on. Any ideas?

One way you can do this is by analyzing the description string. For example logging a circle body prints this:
<SKPhysicsBody> type:<Circle> representedObject:[(null)]
A regular expression search will do the job, provided that the description string is consistent across shape types and future Sprite Kit versions. Potentially brittle solution, but legal.
The only other way is to use the ObjC runtime to read from properties or ivars. Though this may constitute a use of private APIs and may get the app rejected if you're doing this on a live app.
This code logs all of the private PKPhysicsBody class' properties and ivars.
SKPhysicsBody* circle = [SKPhysicsBody bodyWithCircleOfRadius:10];
NSLog(#"%#", circle);
NSLog(#"%#", NSStringFromClass([circle class])); // log the 'true' class name
unsigned int num;
objc_property_t* properties = class_copyPropertyList(NSClassFromString(#"PKPhysicsBody"), &num);
for (unsigned i = 0; i < num; i++)
{
objc_property_t property = properties[i];
NSLog(#"property: %s", property_getName(property));
}
Ivar* ivars = class_copyIvarList(NSClassFromString(#"PKPhysicsBody"), &num);
for (unsigned i = 0; i < num; i++)
{
Ivar ivar = ivars[i];
NSLog(#"ivar: %s", ivar_getName(ivar));
}
From this it looks like the _shapeType ivar will give you what you're looking for.

I've found a simple way of tracking this information, assuming every body gets bound statically to a single node (as is the case in my application):
SKPhysicsBody *body = [SKPhysicsBody bodyWithCircleOfRadius:10.0];
node.physicsBody = body;
node.userData = [NSMutableDictionary dictionary];
[node.userData setObject:#"circle" forKey:#"shapeType");
[node.userData setObject:[NSNumber numberWithFloat:10.0] forKey:#"radius"];

Related

Modifying SCNParticleSystem colors in an SCNParticleEventBlock doesn't work

Given the sample code provided for handle(_:forProperties:handler:), the following block should turn green particle red, but doesn't:
[system handleEvent:SCNParticleEventBirth
forProperties:#[SCNParticlePropertyColor]
withBlock:^(void **data, size_t *dataStride, uint32_t *indices , NSInteger count) {
for (NSInteger i = 0; i < count; ++i) {
float *color = (float *)((char *)data[0] + dataStride[0] * i);
if (rand() & 0x1) { // Switch the green and red color components.
color[0] = color[1];
color[1] = 0;
}
}
}];
With the following, I am able to see green particles, but no matter what I do, I can't seem to get various SCNParticlePropertys to do anything.
SCNParticleSystem *system = [SCNParticleSystem particleSystem];
system.particleColor = NSColor.greenColor;
system.birthRate = 1;
An interesting observation is that in the block above, dataStride only appears to have values for certain SCNParticlePropertys. position, angle, velocity, and angularVelocity all yield 16, life and frame yield 4, and all others show nil. I can only assume that either the API allows per-particle adjustment for a few properties, or additional configuration must be done on my SCNParticleSystem instance in order to "unlock" modification via a SCNParticleEventBlock block.
The above produces the same results in Swift, and both Xcode 8 and 9. I've tried assigning values to nearly every property of my SCNParticleSystem with no luck, and do not see any sample code provided by Apple other than what's in the header.
Thanks for any help.
For anyone stumbling across this question: check out the particle slide in the WWDC 2014 SceneKit sample project, they provide example for basically everything you can do with SceneKit.
As expected, the property that needed a value for this to work was particleColorVariation on my SCNParticleSystem instance. Setting it to anything besides SCNVector4Zero (eg. SCNVector4Make(0, 0, 0, 1)) results in dataStride in SCNParticleEventBlock having a non-nil value (16) at the index of the particle property.
It's unfortunate that this appears to be an undocumented requirement, so hopefully this post is useful for anyone running into this issue in the future.

Objective C iOS - objc_object::sidetable_retain/release chewing up CPU time in my loop?

I'm working on a Spritekit Tower Defence game. ARC is enabled. (And I intend to run this code in the background, though presently it's just running on the main thread.)
In my update loop (which is running up to 60 times a second) I call a method called getTargetsForTowers. After profiling this method, I've found two items in the list that are chewing up my CPU time: objc_object::sidetable_retain/release, and I'm trying to find out what they are.
I'd like to understand more about what this is and if I can improve performance by reducing them or getting rid of them altogether.
There are 300 enemies and 446 towers in my test scenario. The majority of the CPU time is reported in the tower loop.
- (void)getTargetsForTowers {
NSArray *enemiesCopy = [enemiesOnMap copy];
for (CCUnit *enemy in enemiesCopy) {
float edte = enemy.distanceToEnd;
CGPoint enemyPos = enemy.position;
[self calculateTravelDistanceForEnemy:enemy];
if (enemy.actualHealth > 0) {
NSArray *tiles = [self getTilesForEnemy:enemy];
for (CCTileInfo *tile in tiles) {
NSArray *tileTowers = tile.towers;
for (CCSKTower *tower in tileTowers) {
BOOL hasTarget = tower.hasTarget;
BOOL passes = !hasTarget;
if (!passes) {
CCUnit *tg = tower.target;
float tdte = tg.distanceToEnd;
passes = edte < tdte;
}
if (passes) {
BOOL inRange = [self circle:tower.position withRadius:tower.attackRange collisionWithCircle:enemyPos collisionCircleRadius:1];
if (inRange) {
tower.hasTarget = YES;
tower.target = enemy;
}
}
}
}
}
}
}
Screenshots from Time Profile (after 60 seconds of running):
image one http://imageshack.com/a/img22/2258/y18v.png
image two http://imageshack.com/a/img833/7969/7fy3.png
(I've been reading about blocks, arc, strong/weak references, etc., so I tried making the variables (such as CCSKTower *tower) __weak, which did get rid of those two items, but that added a whole bunch of new items related to retaining/creating/destroying the weak variables, and I think they consumed more CPU time than before.)
I'd appreciate any input on this. Thanks.
EDIT:
There's another method that I would like to improve as well which is:
- (NSArray *)getTilesForEnemy:(CCUnit *)enemy {
NSMutableArray *tiles = [[NSMutableArray alloc] init];
float enemyWidthHalf = enemy.size.width/2;
float enemyHeightHalf = enemy.size.height/2;
float enemyX = enemy.position.x;
float enemyY = enemy.position.y;
CGVector topLeft = [self getVectorForPoint:CGPointMake(enemyX-enemyWidthHalf, enemyY+enemyHeightHalf)];
CGVector topRight = [self getVectorForPoint:CGPointMake(enemyX+enemyWidthHalf, enemyY+enemyHeightHalf)];
CGVector bottomLeft = [self getVectorForPoint:CGPointMake(enemyX-enemyWidthHalf, enemyY-enemyHeightHalf)];
CGVector bottomRight = [self getVectorForPoint:CGPointMake(enemyX+enemyWidthHalf, enemyY-enemyHeightHalf)];
CCTileInfo *tile = nil;
for (float x = topLeft.dx; x < bottomRight.dx+1; x++) {
for (float y = bottomLeft.dy; y < topRight.dy+1; y++) {
if (x > -(gameHalfCols+1) && x < gameHalfCols) {
if (y < gameHalfRows && y > -(gameHalfRows+1)) {
int xIndex = (int)(x+gameHalfCols);
int yIndex = (int)(y+gameHalfRows);
tile = tileGrid[xIndex][yIndex];
if (tile != nil) {
[tiles addObject:tile];
}
}
}
}
}
return tiles;
}
I've looked over it repeatedly and there's nothing I really can see. Perhaps there's nothing more that can be done.
Screenshots:
One issue is that you create a new reference to tower.target, but only use that reference once. So simply rewriting that section should improve your performance, e.g.
if (!passes) {
float tdte = tower.target.distanceToEnd;
passes = edte < tdte;
}
Based on your comment, it seems that there's no way to avoid a retain/release if you access a property on tower.target. So let's try radical surgery. Specifically, try adding a distanceToEnd property to the tower, to keep track of the distanceToEnd for the tower's current target. The resulting code would look like this.
- (void)getTargetsForTowers {
// initialization to copy 'distanceToEnd' value to each tower that has a target
for ( CCSKTower *tower in towersOnMap )
if ( tower.hasTarget )
tower.distanceToEnd = tower.target.distanceToEnd;
NSArray *enemiesCopy = [enemiesOnMap copy];
for (CCUnit *enemy in enemiesCopy) {
float edte = enemy.distanceToEnd;
CGPoint enemyPos = enemy.position;
[self calculateTravelDistanceForEnemy:enemy];
if (enemy.actualHealth > 0) {
NSArray *tiles = [self getTilesForEnemy:enemy];
for (CCTileInfo *tile in tiles) {
NSArray *tileTowers = tile.towers;
for (CCSKTower *tower in tileTowers) {
if ( !tower.hasTarget || edte < tower.distanceToEnd ) {
BOOL inRange = [self circle:tower.position withRadius:tower.attackRange collisionWithCircle:enemyPos collisionCircleRadius:1];
if (inRange) {
tower.hasTarget = YES;
tower.target = enemy;
tower.distanceToEnd = edte; // update 'distanceToEnd' on the tower to match new target
}
}
}
}
}
}
}
My impression is that there's not much to be done about the getTilesForEnemy method. Looking at the Running Time image for getTilesForEnemy it's clear that the load is fairly evenly spread among the various components of the method, with only three items above 10%. The top item getVectorForPoint isn't even in the innermost loop. The second item insertObject is apparently the result of the addObject call in the inner loop, but there's nothing to be done for that call, it's required to generate the final result.
At the next level up (see the wvry.png image), you can see that getTilesForEnemy is now 15.3% of the total time spent in getTargetsForTowers. So even if it were possible to reduce getVectorForPoint from 17.3% to 7.3% there would not be a significant reduction in running time. The savings in getTilesForEnemy would be 10%, but because getTilesForEnemy is only 15.3% of the time in getTargetsForTowers, the overall savings would only be 1.53%.
Conclusion, because the components of getTilesForEnemy are balanced and below 20%, and because getTilesForEnemy is only 15.3% of the higher level method, no significant savings will be gained by trying to optimize getTilesForEnemy.
So once again the only option is radical surgery, and this time I mean a total rewrite of the algorithm. Such action should only be taken if the app still isn't performing up to spec. You've run into the limitations of ARC and NSArray's. Both of those technologies are extremely powerful and flexible, and are perfect for high-level development. However, they both have significant overhead which limits performance. So the question becomes, "How do you write the getTargetsForTowers without using ARC and NSArray's?". The answer is to use arrays of C structs to represent the objects. The resulting top level pseudo code would be something like this
copy the enemy information into an array of C structs
copy the tower information into an array of C structs
(note that the target for a tower is just an 'int', which is the index of an enemy in the enemy array)
for ( each enemy in the enemy array )
{
create an array of C structs for the tiles
for ( each tile )
for ( each tower in the tile )
update the tower target if needed
}
copy the updated tower information back into the NSArray of tower objects
For your second method, this part seems unclear and inefficient:
for (float x = topLeft.dx; x < bottomRight.dx+1; x++) {
for (float y = bottomLeft.dy; y < topRight.dy+1; y++) {
if (x > -(gameHalfCols+1) && x < gameHalfCols) {
if (y < gameHalfRows && y > -(gameHalfRows+1)) {
For instance, there's no point in spinning the y loop if your x is out of bounds. You could just do this:
for (float x = topLeft.dx; x < bottomRight.dx+1; x++) {
if (x > -(gameHalfCols+1) && x < gameHalfCols) {
for (float y = bottomLeft.dy; y < topRight.dy+1; y++) {
if (y < gameHalfRows && y > -(gameHalfRows+1)) {
More importantly, the point of the first for loop is to start x at some minimum and increment it to some maximum, and the if statement is there to make sure x is at least some minimum and less than some maximum, so there's no reason to have both a for() and an if(). I don't know what the values might look like for topLeft.dx and gameHalfCols, so I can't tell you the best way to do this.
But, for example, if topLeft.dx is always integral, you might say:
for (float x = MAX(topLeft.dx, ceil(-(gameHalfCols+1))); x < bottomRight.dx+1 && x < gameHalfCols; x++) {
for (float y = ...
You could similarly improve the 'y' for this way. This sin't just fewer lines of code, it also prevents the loops from spinning a bunch of extra times with no effect: the 'if' statements just make the loops spin quickly to their ends, but including the logic inside the 'for's themselves makes them only loop over values that you'll actually use in computations.
To expand my comments to a complete answer:
The normal, correct Objective-C behaviour when returning an object property is to retain and then autorelease it. That's because otherwise code like this (imagine you're in the world before ARC):
TYTemporaryWorker *object = [[TYTemporaryWorker alloc] initWithSomeValue:value];
NSNumber *someResult = object.someResult;
[object release];
return someResult;
would otherwise be invalid. object has been deallocated so if someResult hasn't been retained and autoreleased then it will become a dangling pointer. ARC makes this sort of slightly less direct (the strong reference in someResult would have retained the number beyond the lifetime of object but then it would have been autoreleased for the return) but the principle remains and, in any case, whether an individual .m file has been compiled with ARC is not supposed to affect callers.
(aside: notice that weak isn't just strong without retains — is has related costs because the runtime has to establish a link from the object to the weak reference in order to know find it again and nil it if the object begins deallocation)
Supposing you wanted to create a new type of property that isn't strong and isn't unsafe_unretained but is rather defined to be that the object returned is safe for use for as long as the original owner is alive but unsafe afterwards. So it's a strong set but an unsafe_unretained get.
It's untested but I think the correct means to do that would be:
// we can't write want a synthesised getter that doesn't attempt to retain
// or autorelease, so we'd better flag up the pointer as potentially being
// unsafe to access
#property (nonatomic, unsafe_unretained) NSNumber *someResult;
...
#implementation TYWhatever
{
NSNumber *_retainedResult; // a strong reference, since
// I haven't said otherwise —
// this reference is not publicly exposed
}
- (void)setSomeResult:(NSNumber *)number
{
// set the unsafe and unretained version,
// as the default setter would have
_someResult = number;
// also take a strong reference to the object passed in,
// to extend its lifecycle to match ours
_retainedResult = number;
}
It's going to get quite verbose as you add more properties but what you're doing is contrary to normal Objective-C conventions so limited compiler help is probably to be expected.

Is it possible to deactivate collisions in physics bodies in spriteKit?

I'm looking at doing the best way to collect items with my hero in my spriteKit game for iOs, and after to try a few ways to do it, my conclusion is the best way would be to have an item with a physic body which can detect collisions but don't collide with my hero. Is it possible to do it? to deactivate collisions of a physic body without deactivating its capabilities to detect collisions?? Sounds a bit contradictory I know... Because, the other way would be to create only a SKSpriteNode without physic body, then there wouldn't being collisions, but the way to "detect" collisions would be hand made and much more harder, because i would need to set a coordinate system detection in my hero, that when he will be in those specifics coordinates (over the item) then i'll make the item disappears. Any idea of how to do any of the two ways easier?
Check out collisionBitMask, categoryBitMask, and contactTestBitMask in the SKPhysicsBody class.
Essentially, physics bodies with the same collisionBitMask value will "pass-through" each other.
Correction: If the category and collision bits match, they will interact. If they do not match, those two will not interact. And if the collision bits, and category bits, are both zero, of course that item will interact with nothing whatsoever.
Then, you set the categoryBitMask and contactTestBitMask values to create an SKPhysicsContact Object on contact. Finally, your Class should adopt the SKPhysicsContactDelegate protocol. Use the - didBeginContact: method to detect and handle the SKPhysicsContact object.
static const uint8_t heroCategory = 1;
static const uint8_t foodCategory = 2;
--
food.physicsBody.categoryBitMask = foodCategory;
food.physicsBody.contactTestBitMask = heroCategory;
food.physicsBody.collisionBitMask = 0;
--
hero.physicsBody.categoryBitMask = heroCategory;
hero.physicsBody.contactTestBitMask = foodCategory;
hero.physicsBody.collisionBitMask = 0;
--
-(void)didBeginContact:(SKPhysicsContact *)contact {
SKPhysicsBody *firstBody = contact.bodyA;
SKPhysicsBody *secondBody = contact.bodyB;
}
Short answer:
yourHero.physicsBody.collisionBitMask = 0;
The default value of collisionBitMask is 0xFFFFFFFF (all bits set), that's why the node collides with others
you can do this by setting the categoryBitMask and contactBitMasks of the player and the item objects, but making sure that you do not set the collisionBitMask for either to interact with each other (see below)
static const int playerCategory = 1;
static const int worldCategory = 2;
static const int objectCategory = 4;
....
SKSpriteNode *player, *item;
....
player.physicsBody.categoryBitMask = playerCategory;
player.physicsBody.collisionBitMask = worldCategory;
player.physicsBody.contactTestBitMask = worldCategory;
....
item.physicsBody.categoryBitMask = objectCategory;
item.physicsBody.contactTestBitMask = playerCategory | worldCategory;
item.physicsBody.collisionBitMask = worldCategory;
this way the physics body will pick up collisions between the player and world objects, the item and world objects, but not between the player and items. It will trigger a call to didBeginContact, where you can delete your item node, add health, etc.
Hope this helps!
contactTestBitMask is used to trigger didBeginContact. collisionBitMask is used to activate physics on nodes.
// add a physics body
ship.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:ship.size.width/2];
// set the category for ship
ship.physicsBody.categoryBitMask = shipCategory;
// detect collisions with asteroids and edges
ship.physicsBody.contactTestBitMask = asteroidCategory | edgeCategory;
// physically collide with asteroids
ship.physicsBody.collisionBitMask = asteroidCategory;

How do I fold text in iOS 7?

I feel like an idiot not even posting some code, but after reading several articles stating iOS7 Text Kit adds support for Text Folding, I can't actually find any sample code or an attribute to set on the text to fold it and Apple's documentation seems mute on it.
http://asciiwwdc.com/2013/sessions/220 makes me think I set a region of text into its own text container and then display/hide it, perhaps by overriding setTextContainer:forGlyphRange:
Am I anywhere close?
Thanks
There's a WWDC 2013 video that talks a bit about it when they're doing custom text truncation. Basically you implement the NSLayoutManagerDelegate method layoutManager: shouldGenerateGlyphs: properties: characterIndexes: font: forGlyphRange:
It took me way too much struggling to actually come up with code for this, but here's my implementation based on a property hideNotes
-(NSUInteger)layoutManager:(NSLayoutManager *)layoutManager shouldGenerateGlyphs:(const CGGlyph *)glyphs
properties:(const NSGlyphProperty *)props characterIndexes:(const NSUInteger *)charIndexes
font:(UIFont *)aFont forGlyphRange:(NSRange)glyphRange {
if (self.hideNotes) {
NSGlyphProperty *properties = malloc(sizeof(NSGlyphProperty) * glyphRange.length);
for (int i = 0; i < glyphRange.length; i++) {
NSUInteger glyphIndex = glyphRange.location + i;
NSDictionary *charAttributes = [_textStorage attributesAtIndex:glyphIndex effectiveRange:NULL];
if ([[charAttributes objectForKey:CSNoteAttribute] isEqualToNumber:#YES]) {
properties[i] = NSGlyphPropertyNull;
} else {
properties[i] = props[i];
}
}
[layoutManager setGlyphs:glyphs properties:properties characterIndexes:charIndexes font:aFont forGlyphRange:glyphRange];
return glyphRange.length;
}
[layoutManager setGlyphs:glyphs properties:props characterIndexes:charIndexes font:aFont forGlyphRange:glyphRange];
return glyphRange.length;
}
The NSLayoutManager method setGlyphs: properties: characterIndexes: font: forGlyphRange: is called in the default implementation and basically does all of the work. The return value is the number of glyphs to actually generate, returning 0 tells the layout manager to do its default implementation so I just return the length of the glyph range it passes in. The main part of the method goes through all of the characters in the text storage and if it has a certain attribute, sets the associated property to NSGlyphPropertyNull which tells the layout manager to not display it, otherwise it just sets the property to whatever was passed in for it.

Adding and accessing CCSprites

I'm having trouble inserting multiple children of same sprite and accessing it (or setting positions for them on runtime). Kindly advise any suitable method preferably point out my mistake. Here is my approach.
//In the Init Method...
//int i is defined in the start.
for (i = 1; i < 4; i++)
{
hurdle = [CCSprite spriteWithFile:#"hurdle1.png"];
[self addChild:hurdle z:i tag:i];
hurdle.position = CGPointMake(150 * i, 0);
}
It spreads all the sprites on the canvas. then in some "UPDATE Function" I'm calling this.
hurdle.position = CGPointMake(hurdle.position.x - 5, 10);
if (hurdle.position.x <= -5) {
hurdle.position = ccp(480, 10);
}
It works but as expected only one instance moves horizontally. I want all the instances to be moved so I am trying to use this....
for (i = 1; i < 4; i++){
[hurdle getChildByTag:i].position = CGPointMake(hurdle.position.x - 5, 10);
//OR
[hurdle getChildByTag:i].position = CGPointMake([hurdle getChildByTag:i].position.x - 5, 10);
}
I've tried getting LOGs on various places and realized that getChildByTag doesn't work the way I'm trying to use it.
The problem is in the last block of code. You should make a local reference to each CCSprite within your for loop.
Since you added the sprites to self, you will retrieve them as children of self
for (i = 1; i < 4; i++){
CCSprite * enumHurdle = [self getChildByTag:i];
enumHurdle.position = CGPointMake(enumHurdle.position.x - 5, 10);
}
Be careful if you create any other sprites this way in the same scene. It is bad design to give any two sprites the same tag.
EDIT about avoiding duplicate tags.
If you know how many sprites you will have. Use an enum of tags and refer to the sprites by name.
If not, knowing how many groups and putting a limit on the size of groups could make it managable.
ie
say you have 3 parts of code where you are generating sprites like this. You can include an enum in your .m (under #implementation line) and put the limits there
// Choose names that describe the groups of sprites
enum { kGroupOne = 0, // limiting the size of each group to 100
kGroupTwo = 100, // (besides the last group, but that is not important)
kGroupThree = 200,
};
Then when you create each group
// group 1
for (i = kGroupOne; i < 4; i++){
// set up code here
}
// group 2
// g2_size is made up, insert whatever you want
for (i = kGroupTwo; i < g2_size; i++) {
// set up code here
}
.
.
.
Then to retrieve in groups
for (i = kGroupOne; i < 4; i++){
CCSprite * enumHurdle = [self getChildByTag:i];
enumHurdle.position = CGPointMake(enumHurdle.position.x - 5, 10);
}
.
.
.
Hopefully that sparks your creativity. Now have some fun.
Something that I do often is group objects of like kind that I want to act on in a similar way by adding them to a CCNode and add that CCNode to the layer.
I would create a class that derives from CCNode
Then I can put all my logic in that node and access then via [self children]
for(CCSprite *hurdle in [self children]) {
// Do what you need to do
}

Resources