A lovely category telling if a volume-based SKPhysicsBody is contained by another.
BOOL putInto = [bodyA containsBody:bodyB];
The category adds a really valuable readonly property for every SKPhysicsBody
called path
.
This CGPathRef
holds the CGPath
representation of the currently transformed state for the
body (in the coordinate space of the containing node).
The rest is just the geometry to test containment actually, and some swizzling to live in peace
with SpriteKit
runtime. Everything is wrapped up into a threeliner interface.
@interface SKPhysicsBody (Containment)
@property (nonatomic, readonly) CGPathRef path;
-(BOOL)containsPoint:(CGPoint) point;
-(BOOL)containsBody:(SKPhysicsBody*) body;
@end
There is no API to enumerate CGPoints
of a given CGPath
. The category comes with some handy
helper function that does it for the category. Containment test now can go as the following.
// Test for containment for each point of body.
__block BOOL everyPointContained = YES;
enumeratePointsOfPath(body.path, ^(CGPoint eachPoint)
{
if (CGPathContainsPoint(path, NULL, eachPoint, NO) == NO)
{ everyPointContained = NO; }
});
Another issue is to store the initial CGPath
representation of the body upon creation. This initial
path can be transformed later on when it requested. This code should take place in the SKPhysicsBody
factory methods. Calling the default behaviour can be tricky (it could be a simple super
call if we were
extending the class).
Solution is to "save" the default SKPhysicsBody factory implementations, then override the factories, and call the default behaviour from within somewhere.
For +(SKPhysicsBody*)bodyWithCircleOfRadius:
it means that you create an implementation that is to
be override +(SKPhysicsBody*)bodyWithCircleOfRadius:
implementation, while you save the original
implementation into a method, called +(SKPhysicsBody*)__bodyWithCircleOfRadius:
in this case. See
his in action in SKPhysicsBody+Containment
searching for Augment factories
.
When you ask for an SKPhysicsBody
instance from SKPhysicsBody
class, you'd expect an
SKPhysicsBody
object in return. According to some design decision at Apple, it will spit you
up a PKPhysicsBody
instance (part of internal PhysicsKit), which of course won't have
any instance method from the category you made for SKPhysicsBody
. Extending that class can be
carried out only via method / property swizzlings, so this issue put a weight on this category.
// This is not what you'd expect.
SKPhysicsBody *body = [SKPhysicsBody bodyWithRectangleOfSize:size];
NSLog(@"%@", body.class)); // PKPhysicsBody
Another thing to consider, that class methods gonna stay in place. This is cool, but you cannot
swizzle every method automatically. Class methods implementations have to be added to SKPhysicsBody
,
but instance methods have to be added to PKPkysicsBody
(or whatever class that factories return at
runtime).
NSLog(@"%@", SKPhysicsBody.class); // SKPhysicsBody
So for the solution, I created a standalone "implementation donor class" SKPhysicsBodyContainment
that holds all the implementation that needs to be swizzled around. Then upon the +(void)load
of
this class, I distribute the implementations to the parties discussed above.
It actually wraps up the swizzling, and some other runtime class manipulation methods into an Objective-C
interface. Gonna put it into eppz!kit soon, hence the class prefix. You can see the header
EPPZSwizzler.h
for documentations.
// Copies method implementation from a donor class.
[EPPZSwizzler addInstanceMethod:@selector(containsBody:)
toClass:physicsBodyInstanceClass
fromClass:self];
// Like you was define a `@property (nonatomic, assign) NSString *pathType`.
[EPPZSwizzler synthesizeAssignedPropertyNamed:@"pathType"
ofTypeEncoding:@encode(NSString)
forClass:physicsBodyInstanceClass];
-
0.1.1
- Removed the need of
key
for property synthesizeing - Cleaned up interface (as it is close to production)
- Removed the need of
-
0.1.0
- Path-path containment
- Circle-path containment
- Circle-circle containment
-
0.0.3
- Spectacular swizzling features in
EPPZSwizzler
- Tracking body types in
pathType
property
- Spectacular swizzling features in
-
0.0.2
- Added containment geomery
- Works with path-path intersections for now
- Added containment geomery
-
0.0.1
path
property gets populated upon creating- Swizzling works just fine