Box2D collisions now publish events via QQWorld and the involved game objects.
authordsc <david.schoonover@gmail.com>
Wed, 1 Jun 2011 09:33:01 +0000 (02:33 -0700)
committerdsc <david.schoonover@gmail.com>
Wed, 1 Jun 2011 09:33:01 +0000 (02:33 -0700)
18 files changed:
src/game/QQGame.h
src/game/QQGame.mm
src/game/actor/QQActor.mm
src/game/actor/QQUnit.mm
src/game/actor/bullet/QQBullet.mm
src/physics/QQPhysicalEvents.h [deleted file]
src/physics/QQPhysicalEvents.mm [deleted file]
src/physics/QQWorld.h
src/physics/QQWorld.mm
src/physics/event/QQContactNotification.h [new file with mode: 0644]
src/physics/event/QQContactNotification.mm [new file with mode: 0644]
src/physics/event/QQPhysicalEvents.h [new file with mode: 0644]
src/physics/event/QQPhysicalEvents.mm [new file with mode: 0644]
src/qq/QQNotification.h [new file with mode: 0644]
src/qq/QQNotification.mm [new file with mode: 0644]
src/qq/QQNotifier.h
src/qq/QQObservable.h
tanks.xcodeproj/project.pbxproj

index fd5d95b..789be17 100644 (file)
@@ -10,6 +10,7 @@
     
 @private
     QQWorld* _world;
+    
     QQLevel* _level;
     SPSprite* _root;
     
index 7130e52..60bddbc 100644 (file)
@@ -2,6 +2,7 @@
 
 #import "game/QQGame.h"
 #import "game/actor/QQActors.h"
+#import "physics/event/QQContactNotification.h"
 
 
 
@@ -10,9 +11,8 @@ static QQGame* _CurrentGame = NULL;
 
 
 @interface QQGame ()
-
 - (void) onEnterFrame:(SPEnterFrameEvent*)event;
-
+- (void) onCollide:(QQContactNotification*)msg;
 @end
 
 
@@ -35,6 +35,9 @@ static QQGame* _CurrentGame = NULL;
         _running = NO;
         
         _world = [[QQWorld alloc] init];
+        [_world addObserver:self selector:@selector(onCollide:) name:QQ_EVENT_CONTACT_BEGIN];
+        [_world addObserver:self selector:@selector(onCollide:) name:QQ_EVENT_CONTACT_END];
+        
         _root = [SPSprite sprite];
         [self addChild:_root];
         _level = [[QQLevel alloc] init];
@@ -117,10 +120,6 @@ static QQGame* _CurrentGame = NULL;
 }
 
 
-- (void) onEnterFrame:(SPEnterFrameEvent*)event {
-    if (_running) [self tick:event.passedTime];
-}
-
 - (void) tick:(float)elapsed {
     _ticks++;
     
@@ -154,6 +153,17 @@ static QQGame* _CurrentGame = NULL;
     }
 }
 
+/// event handlers
+
+
+- (void) onEnterFrame:(SPEnterFrameEvent*)event {
+    if (_running) [self tick:event.passedTime];
+}
+
+- (void) onCollide:(QQContactNotification*)msg {
+    NSLog(@"%@", msg);
+}
+
 
 
 + (QQGame*) current {
index a5ebb92..3c6b7a0 100644 (file)
@@ -32,7 +32,6 @@
 }
 - (void) setPositionX:(float)x y:(float)y {
     self.body->SetTransform(b2Vec2(x,y), self.body->GetAngle());
-    // [self updateShapeX:x y:y];
 }
 
 - (void) updateShapeX:(float)x y:(float)y {
@@ -42,9 +41,6 @@
 
 - (void) updateShapeX:(float)x y:(float)y rotation:(float)r {
     [self updateShapeX:x y:y];
-    // SPDisplayObject* shape = self.shape;
-    // shape.registrationX = self.shapeCenterX;
-    // shape.registrationY = self.shapeCenterY;
     self.shape.rotation = r;
 }
 
@@ -74,7 +70,7 @@
         b2BodyDef bd;
         bd.type = type;
         bd.position = b2Vec2(x, y);
-        bd.userData = (void*)self;
+        bd.userData = self;
         _body = self.world.world->CreateBody(&bd);
     }
     return self;
index 6a899d6..b09e9f1 100644 (file)
         float dblpx = 2*self.world.scale;
         b2PolygonShape box;
         box.SetAsBox(shape.width/dblpx, shape.height/dblpx);
-        self.body->CreateFixture(&box, 1.0f);
+        b2FixtureDef fix;
+        fix.shape = &box;
+        fix.density = 1.0f;
+        fix.userData = self;
+        self.body->CreateFixture(&fix);
     }
     return self;
 }
index 3297852..597e347 100644 (file)
@@ -23,6 +23,7 @@
         fix.shape = &circle;
         fix.restitution = 1.0f;
         fix.density = 0.1f;
+        fix.userData = self;
         self.body->CreateFixture(&fix);
     }
     return self;
diff --git a/src/physics/QQPhysicalEvents.h b/src/physics/QQPhysicalEvents.h
deleted file mode 100644 (file)
index e69de29..0000000
diff --git a/src/physics/QQPhysicalEvents.mm b/src/physics/QQPhysicalEvents.mm
deleted file mode 100644 (file)
index e69de29..0000000
index 1533c25..c5ae17d 100644 (file)
@@ -1,13 +1,20 @@
 #include <Box2D/Box2D.h>
+#import "qq/QQObservable.h"
+#import "qq/QQNotifier.h"
+#import "physics/event/QQPhysicalEvents.h"
 #import "physics/debug/QQGLESDebugDraw.h"
 
 
-@interface QQWorld : NSObject {
+@interface QQWorld : NSObject <QQNotifier, QQContactNotifier> {
 
 @private
     QQGLESDebugDraw* _debugDraw;
+    QQContactListener _contactListener;
     b2World* _world;
-    NSMutableArray* _walls;
+    
+    NSNotificationCenter* _center;
+    
+    NSMutableArray* _walls; // Array of NSValue<b2Body*>
     
     float   _timestep;
     int     _velocityIterations;
index 9bbdd84..9e663ae 100644 (file)
@@ -1,7 +1,15 @@
 #import <UIKit/UIKit.h>
 #include <Box2D/Box2D.h>
 
-#import "QQWorld.h"
+#import "physics/QQWorld.h"
+#import "physics/event/QQContactNotification.h"
+
+
+// private interface
+@interface QQWorld ()
+- (void) postContactNotification:(b2Contact*)contact event:(NSString*)evt;
+- (void) createWalls;
+@end
 
 
 @implementation QQWorld
         _velocityIterations = 10;
         _positionIterations = 10;
         _scale = 5.0f;
+        _center = [[NSNotificationCenter alloc] init];
         
         _world = new b2World(b2Vec2(0.0f, 0.0f), true); // b2World(gravity, doSleep)
+        
+        _contactListener.notifier = self;
+        _world->SetContactListener(&_contactListener);
+        
         _debugDraw = new QQGLESDebugDraw(0.75f);
         _debugDraw->SetAllFlags();
         _world->SetDebugDraw(_debugDraw);
         
-        _walls = [[NSMutableArray alloc] initWithCapacity:4];
-        
-        CGSize screen = [UIScreen mainScreen].applicationFrame.size;
-        float padv = 5;
-        float w = screen.width  / _scale - 2*padv;
-        float h = screen.height / _scale - 2*padv;
-        
-        b2Vec2 origin = b2Vec2(0, 0);
-        b2Vec2 pad    = b2Vec2(padv, padv);
-        b2Vec2 bound  = b2Vec2(w, h);
-        
-        b2Vec2 horz   = b2Vec2(w, 0);
-        b2Vec2 hpos   = horz + pad;
-        b2Vec2 vert   = b2Vec2(0, h);
-        b2Vec2 vpos   = vert + pad;
-        
-        b2Vec2 positions[] = {pad, vpos, pad, hpos};
-        b2Vec2 endpoints[] = {horz, horz, vert, vert};
-        
-        for (int i=0; i<4; ++i) {
-            b2BodyDef bd;
-            bd.position = positions[i];
-            b2Body* wall = _world->CreateBody(&bd);
-            
-            b2PolygonShape shape;
-            shape.SetAsEdge(origin, endpoints[i]);
-            wall->CreateFixture(&shape, 0.0f);
-            // [_walls addObject:wall]; // doh, b2Body so does not descend from NSObject, and so is not an (id)
-        }
-        
+        _walls = [[NSMutableArray alloc] initWithCapacity:4]; // Array of NSValue<b2Body*>
+        [self createWalls];
     }
     return self;
 }
 
 - (void) dealloc {
-    // for (b2Body* wall in _walls) {
-    //     _world->DestroyBody(wall);
-    // }
     [_walls removeAllObjects];
     [_walls release];
+    [_center release];
     delete _debugDraw;
     delete _world; // b2World destructor destroys all physics entities and releases all heap memory.
     [super dealloc];
 }
 
+
+/// methods
+
+- (void) createWalls {
+    CGSize screen = [UIScreen mainScreen].applicationFrame.size;
+    float padv = 5;
+    float w = screen.width  / _scale - 2*padv;
+    float h = screen.height / _scale - 2*padv;
+    
+    b2Vec2 origin = b2Vec2(0, 0);
+    b2Vec2 pad    = b2Vec2(padv, padv);
+    b2Vec2 bound  = b2Vec2(w, h);
+    
+    b2Vec2 horz   = b2Vec2(w, 0);
+    b2Vec2 hpos   = horz + pad;
+    b2Vec2 vert   = b2Vec2(0, h);
+    b2Vec2 vpos   = vert + pad;
+    
+    b2Vec2 positions[] = {pad, vpos, pad, hpos};
+    b2Vec2 endpoints[] = {horz, horz, vert, vert};
+    
+    for (int i=0; i<4; ++i) {
+        b2BodyDef bd;
+        bd.position = positions[i];
+        b2Body* wall = _world->CreateBody(&bd);
+        
+        b2PolygonShape shape;
+        shape.SetAsEdge(origin, endpoints[i]);
+        wall->CreateFixture(&shape, 0.0f);
+        
+        [_walls addObject:[NSValue valueWithPointer:wall]];
+    }
+}
+
+
 - (void) step {
     _world->Step(self.timestep, self.velocityIterations, self.positionIterations);
 }
 }
 
 
+/// QQContactNotifier
+
+- (void) beginContact:(b2Contact*)contact {
+    [self postContactNotification:contact event:QQ_EVENT_CONTACT_BEGIN];
+}
+
+- (void) endContact:(b2Contact*)contact {
+    [self postContactNotification:contact event:QQ_EVENT_CONTACT_END];
+}
+
+- (void) postContactNotification:(b2Contact*)contact event:(NSString*)evt {
+    QQContactNotification* msg = [[QQContactNotification notification:evt contact:contact] retain];
+    msg.object = msg.actorA;
+    [self postNotification:msg];
+    msg.object = msg.actorB;
+    [self postNotification:msg];
+    msg.object = self;
+    [self postNotification:msg];
+    [msg release];
+}
+
+
+
+/// observable
+
+- (void) addObserver:(id)observer selector:(SEL)selector name:(NSString*)event {
+    [self addObserver:observer selector:selector name:event object:self];
+}
+
+// Remove a listener from this object
+- (void) removeObserver:(id)observer {
+    [self removeObserver:observer name:nil object:self];
+}
+- (void) removeObserver:(id)observer name:(NSString*)event {
+    [self removeObserver:observer name:event object:self];
+}
+
+// Send a notification from this object
+- (void) postNotificationName:(NSString*)name {
+    [self postNotificationName:name object:self];
+}
+- (void) postNotificationName:(NSString*)name userInfo:(NSDictionary*)info {
+    [self postNotificationName:name object:self userInfo:info];
+}
+
 
+/// notifier
 
+- (void) addObserver:(id)observer selector:(SEL)selector name:(NSString*)event object:(id)sender {
+    [_center addObserver:observer selector:selector name:event object:sender];
+}
+
+- (void) removeObserver:(id)observer name:(NSString*)event object:(id)sender {
+    [_center removeObserver:observer name:event object:sender];
+}
+- (void) removeObservers:(id)observer {
+    [_center removeObserver:observer];
+}
+
+- (void) postNotification:(NSNotification*)msg {
+    [_center postNotification:msg];
+}
+- (void) postNotificationName:(NSString*)name object:(id)sender {
+    [_center postNotificationName:name object:sender];
+}
+- (void) postNotificationName:(NSString*)name object:(id)sender userInfo:(NSDictionary*)info {
+    [_center postNotificationName:name object:sender userInfo:info];
+}
 
 @end
diff --git a/src/physics/event/QQContactNotification.h b/src/physics/event/QQContactNotification.h
new file mode 100644 (file)
index 0000000..4c173f3
--- /dev/null
@@ -0,0 +1,30 @@
+#include "qq/QQNotification.h"
+#include <Box2D/Box2D.h>
+#include "game/actor/QQActor.h"
+
+
+@interface QQContactNotification : QQNotification {
+@private
+    b2Contact* _contact;
+}
+
+- (id) initWithName:(NSString*)name contact:(b2Contact*)contact;
+- (id) initWithName:(NSString*)name object:(id)obj contact:(b2Contact*)contact;
+- (id) initWithName:(NSString*)name object:(id)obj userInfo:(NSDictionary*)info contact:(b2Contact*)contact;
+
+@property (nonatomic, readonly) b2Contact* contact;
+
+@property (nonatomic, readonly) QQActor* actorA;
+@property (nonatomic, readonly) b2Fixture* fixtureA;
+@property (nonatomic, readonly) b2Body* bodyA;
+
+@property (nonatomic, readonly) QQActor* actorB;
+@property (nonatomic, readonly) b2Fixture* fixtureB;
+@property (nonatomic, readonly) b2Body* bodyB;
+
+
++ (QQContactNotification*) notification:(NSString*)name contact:(b2Contact*)contact;
++ (QQContactNotification*) notification:(NSString*)name object:(id)obj contact:(b2Contact*)contact;
++ (QQContactNotification*) notification:(NSString*)name object:(id)obj userInfo:(NSDictionary*)info contact:(b2Contact*)contact;
+
+@end
diff --git a/src/physics/event/QQContactNotification.mm b/src/physics/event/QQContactNotification.mm
new file mode 100644 (file)
index 0000000..2f36a4b
--- /dev/null
@@ -0,0 +1,60 @@
+#import "physics/event/QQContactNotification.h"
+
+
+@implementation QQContactNotification
+
+
+- (id) initWithName:(NSString*)name contact:(b2Contact*)contact {
+    return [self initWithName:name object:nil userInfo:nil contact:contact];
+}
+- (id) initWithName:(NSString*)name object:(id)obj contact:(b2Contact*)contact {
+    return [self initWithName:name object:obj userInfo:nil contact:contact];
+}
+- (id) initWithName:(NSString*)name object:(id)obj userInfo:(NSDictionary*)info contact:(b2Contact*)contact {
+    if ((self = [super initWithName:name object:obj userInfo:info])) {
+        _contact = contact;
+    }
+    return self;
+}
+
+
+@synthesize contact = _contact;
+
+- (QQActor*) actorA {
+    return (QQActor*) (self.fixtureA->GetUserData() ? self.fixtureA->GetUserData() : self.bodyA->GetUserData());
+}
+- (b2Fixture*) fixtureA {
+    return _contact->GetFixtureA();
+}
+- (b2Body*) bodyA {
+    return _contact->GetFixtureA()->GetBody();
+}
+
+- (QQActor*) actorB {
+    return (QQActor*) (self.fixtureB->GetUserData() ? self.fixtureB->GetUserData() : self.bodyB->GetUserData());
+}
+- (b2Fixture*) fixtureB {
+    return _contact->GetFixtureB();
+}
+- (b2Body*) bodyB {
+    return _contact->GetFixtureB()->GetBody();
+}
+
+
+- (NSString*) description {
+    return [NSString stringWithFormat:@"[%@ actorA=%@, actorB=%@]",
+            [super description], self.actorA, self.actorB];
+}
+
+
++ (QQContactNotification*) notification:(NSString*)name contact:(b2Contact*)contact {
+    return [[[QQContactNotification alloc] initWithName:name object:nil userInfo:nil contact:contact] autorelease];
+}
++ (QQContactNotification*) notification:(NSString*)name object:(id)obj contact:(b2Contact*)contact {
+    return [[[QQContactNotification alloc] initWithName:name object:obj userInfo:nil contact:contact] autorelease];
+}
++ (QQContactNotification*) notification:(NSString*)name object:(id)obj userInfo:(NSDictionary*)info contact:(b2Contact*)contact {
+    return [[[QQContactNotification alloc] initWithName:name object:obj userInfo:info contact:contact] autorelease];
+}
+
+@end
diff --git a/src/physics/event/QQPhysicalEvents.h b/src/physics/event/QQPhysicalEvents.h
new file mode 100644 (file)
index 0000000..efc79d2
--- /dev/null
@@ -0,0 +1,31 @@
+#ifndef QQPHYSICAL_EVENTS_H
+#define QQPHYSICAL_EVENTS_H
+
+
+#define QQ_EVENT_CONTACT_BEGIN  @"QQ_BeginContact"
+#define QQ_EVENT_CONTACT_END    @"QQ_EndContact"
+
+
+#include <Box2D/Box2D.h>
+
+
+@protocol QQContactNotifier
+
+- (void) beginContact:(b2Contact*)contact;
+- (void) endContact:(b2Contact*)contact;
+
+@end
+
+
+class QQContactListener : public b2ContactListener {
+public:
+    id<QQContactNotifier> notifier;
+    
+    virtual void BeginContact(b2Contact* contact);
+    virtual void EndContact(b2Contact* contact);
+    
+    virtual void PreSolve(b2Contact* contact, const b2Manifold* oldManifold) { B2_NOT_USED(contact); B2_NOT_USED(oldManifold); }
+    virtual void PostSolve(b2Contact* contact, const b2ContactImpulse* impulse) { B2_NOT_USED(contact); B2_NOT_USED(impulse); }
+};
+
+#endif
\ No newline at end of file
diff --git a/src/physics/event/QQPhysicalEvents.mm b/src/physics/event/QQPhysicalEvents.mm
new file mode 100644 (file)
index 0000000..6d722f0
--- /dev/null
@@ -0,0 +1,16 @@
+#include "QQPhysicalEvents.h"
+
+
+void QQContactListener::BeginContact(b2Contact* contact) {
+    [notifier beginContact:contact];
+}
+
+void QQContactListener::EndContact(b2Contact* contact) {
+    [notifier endContact:contact];
+}
+
+// void QQContactListener::PreSolve(b2Contact* contact, const b2Manifold* oldManifold) { B2_NOT_USED(contact); B2_NOT_USED(oldManifold); }
+// void QQContactListener::PostSolve(b2Contact* contact, const b2ContactImpulse* impulse) { B2_NOT_USED(contact); B2_NOT_USED(impulse); }
+
+
+
diff --git a/src/qq/QQNotification.h b/src/qq/QQNotification.h
new file mode 100644 (file)
index 0000000..b2a24bd
--- /dev/null
@@ -0,0 +1,21 @@
+
+@interface QQNotification : NSNotification {
+@private
+    NSString* mName;
+    id mObject;
+    NSDictionary* mUserInfo;
+}
+
+@property (nonatomic, readwrite, retain) NSString* name;
+@property (nonatomic, readwrite, retain) id object;
+@property (nonatomic, readwrite, retain) NSDictionary* userInfo;
+
+- (id) initWithName:(NSString*)name;
+- (id) initWithName:(NSString*)name object:(id)obj;
+- (id) initWithName:(NSString*)name object:(id)obj userInfo:(NSDictionary*)info;
+
++ (QQNotification*) notification:(NSString*)name;
++ (QQNotification*) notification:(NSString*)name object:(id)obj;
++ (QQNotification*) notification:(NSString*)name object:(id)obj userInfo:(NSDictionary*)info;
+
+@end
diff --git a/src/qq/QQNotification.mm b/src/qq/QQNotification.mm
new file mode 100644 (file)
index 0000000..e4b2448
--- /dev/null
@@ -0,0 +1,67 @@
+#include "qq/QQNotification.h"
+
+
+@implementation QQNotification
+
+- (id) initWithName:(NSString*)name {
+    return [self initWithName:name object:nil userInfo:nil];
+}
+
+- (id) initWithName:(NSString*)name object:(id)object {
+    return [self initWithName:name object:object userInfo:nil];
+}
+
+- (id) initWithName:(NSString*)name object:(id)object userInfo:(NSDictionary*)userInfo {
+    // From: http://developer.apple.com/library/ios/#documentation/Cocoa/Reference/Foundation/Classes/NSNotification_Class/Reference/Reference.html#//apple_ref/doc/c_ref/NSNotification
+    // 
+    // You can choose any designated initializer you like, but be sure that your initializer 
+    // **does not call** NSNotification’s implementation of init (via [super init]). 
+    // NSNotification is not meant to be instantiated directly, and its init method raises an exception.
+    if (self) {
+        self.name     = name;
+        self.object   = object;
+        self.userInfo = userInfo;
+    }
+    return self;
+}
+
+- (void) dealloc {
+    [mName release];
+    [mObject release];
+    [mUserInfo release];
+    [super dealloc];
+}
+
+
+- (NSString*) name { return mName; }
+- (void) setName:(NSString*)str {
+    [mName release];
+    mName = [str retain];
+}
+
+- (id) object { return mObject; }
+- (void) setObject:(id)obj {
+    [mObject release];
+    mObject = [obj retain];
+}
+
+- (NSDictionary*) userInfo { return mUserInfo; }
+- (void) setUserInfo:(NSDictionary*)info {
+    [mUserInfo release];
+    mUserInfo = [info retain];
+}
+
+
++ (QQNotification*) notification:(NSString*)name {
+    return [[[QQNotification alloc] initWithName:name] autorelease];
+}
++ (QQNotification*) notification:(NSString*)name object:(id)obj {
+    return [[[QQNotification alloc] initWithName:name object:obj] autorelease];
+}
++ (QQNotification*) notification:(NSString*)name object:(id)obj userInfo:(NSDictionary*)info {
+    return [[[QQNotification alloc] initWithName:name object:obj userInfo:info] autorelease];
+}
+
+
+
+@end
index 2986b99..839154f 100644 (file)
@@ -4,8 +4,8 @@
 
 - (void) addObserver:(id)observer selector:(SEL)selector name:(NSString*)event object:(id)sender;
 
-//- (void) removeObserver:(id)observer; // XXX: supposed to remove all from all objects
 - (void) removeObserver:(id)observer name:(NSString*)event object:(id)sender;
+- (void) removeObservers:(id)observer; // remove all from all objects
 
 - (void) postNotification:(NSNotification*)msg;
 - (void) postNotificationName:(NSString*)name object:(id)sender;
index 103ec6f..ff4e25d 100644 (file)
@@ -1,16 +1,21 @@
 
-
+/**
+ * An observable object implicitly uses itself as the sender or target of calls
+ * that would normally take an object if the call was made to an NSNotificationCenter.
+ */
 @protocol QQObservable
-
 //- (void) observe:(NSString*)event notify:(id)observer selector:(SEL)selector;
+
+/** Adds an observer of this object. */
 - (void) addObserver:(id)observer selector:(SEL)selector name:(NSString*)event;
 
+// Remove a listener from this object
 - (void) removeObserver:(id)observer;
 - (void) removeObserver:(id)observer name:(NSString*)event;
 
-- (void) postNotification:(NSNotification*)msg;
-- (void) postNotificationName:(NSString*)name object:(id)sender;
-- (void) postNotificationName:(NSString*)name object:(id)sender userInfo:(NSDictionary*)info;
+// Send a notification from this object
+- (void) postNotificationName:(NSString*)name;
+- (void) postNotificationName:(NSString*)name userInfo:(NSDictionary*)info;
 
 
 @end
index ce9facc..236f4b9 100644 (file)
                492D8116138C07540042D918 /* SXFPSMeter.m in Sources */ = {isa = PBXBuildFile; fileRef = 492D810B138C07540042D918 /* SXFPSMeter.m */; };
                494DE9971376927C00FDB3D7 /* libBox2D.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 494DE9961376927C00FDB3D7 /* libBox2D.a */; };
                4978AD0E1395E5CE00930447 /* QQActors.h in Headers */ = {isa = PBXBuildFile; fileRef = 4978AD0D1395E5CE00930447 /* QQActors.h */; };
+               4978AD141396139100930447 /* Tanks.h in Headers */ = {isa = PBXBuildFile; fileRef = 4978AD131396139100930447 /* Tanks.h */; };
+               4978AD1A1396302300930447 /* QQContactNotification.h in Headers */ = {isa = PBXBuildFile; fileRef = 4978AD161396302300930447 /* QQContactNotification.h */; };
+               4978AD1B1396302300930447 /* QQContactNotification.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4978AD171396302300930447 /* QQContactNotification.mm */; };
+               4978AD1C1396302300930447 /* QQPhysicalEvents.h in Headers */ = {isa = PBXBuildFile; fileRef = 4978AD181396302300930447 /* QQPhysicalEvents.h */; };
+               4978AD1D1396302300930447 /* QQPhysicalEvents.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4978AD191396302300930447 /* QQPhysicalEvents.mm */; };
+               4978AD251396302F00930447 /* QQNotification.h in Headers */ = {isa = PBXBuildFile; fileRef = 4978AD1F1396302F00930447 /* QQNotification.h */; };
+               4978AD261396302F00930447 /* QQNotification.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4978AD201396302F00930447 /* QQNotification.mm */; };
+               4978AD271396302F00930447 /* QQNotifier.h in Headers */ = {isa = PBXBuildFile; fileRef = 4978AD211396302F00930447 /* QQNotifier.h */; };
+               4978AD2A1396302F00930447 /* QQObservable.h in Headers */ = {isa = PBXBuildFile; fileRef = 4978AD241396302F00930447 /* QQObservable.h */; };
                4995ABB213816CCE00334646 /* QQGame.h in Headers */ = {isa = PBXBuildFile; fileRef = 49E834A513812427007A6598 /* QQGame.h */; };
                4995ABB313816CD400334646 /* QQUnit.h in Headers */ = {isa = PBXBuildFile; fileRef = 49E834A213812427007A6598 /* QQUnit.h */; };
                4995ABF91381C46B00334646 /* Icon-iPad.png in Resources */ = {isa = PBXBuildFile; fileRef = 4995ABCA1381C46B00334646 /* Icon-iPad.png */; };
                492D810B138C07540042D918 /* SXFPSMeter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SXFPSMeter.m; sourceTree = "<group>"; };
                494DE9961376927C00FDB3D7 /* libBox2D.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libBox2D.a; sourceTree = SOURCE_ROOT; };
                4978AD0D1395E5CE00930447 /* QQActors.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = QQActors.h; sourceTree = "<group>"; };
+               4978AD131396139100930447 /* Tanks.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Tanks.h; sourceTree = "<group>"; };
+               4978AD161396302300930447 /* QQContactNotification.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = QQContactNotification.h; sourceTree = "<group>"; };
+               4978AD171396302300930447 /* QQContactNotification.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = QQContactNotification.mm; sourceTree = "<group>"; };
+               4978AD181396302300930447 /* QQPhysicalEvents.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = QQPhysicalEvents.h; sourceTree = "<group>"; };
+               4978AD191396302300930447 /* QQPhysicalEvents.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = QQPhysicalEvents.mm; sourceTree = "<group>"; };
+               4978AD1F1396302F00930447 /* QQNotification.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = QQNotification.h; sourceTree = "<group>"; };
+               4978AD201396302F00930447 /* QQNotification.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = QQNotification.mm; sourceTree = "<group>"; };
+               4978AD211396302F00930447 /* QQNotifier.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = QQNotifier.h; sourceTree = "<group>"; };
+               4978AD241396302F00930447 /* QQObservable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = QQObservable.h; sourceTree = "<group>"; };
                4995ABCA1381C46B00334646 /* Icon-iPad.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-iPad.png"; sourceTree = "<group>"; };
                4995ABCB1381C46B00334646 /* Icon-iPhone.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-iPhone.png"; sourceTree = "<group>"; };
                4995ABCC1381C46B00334646 /* Icon-iPhone@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-iPhone@2x.png"; sourceTree = "<group>"; };
                        path = ../Extras;
                        sourceTree = "<group>";
                };
+               4978AD151396302300930447 /* event */ = {
+                       isa = PBXGroup;
+                       children = (
+                               4978AD161396302300930447 /* QQContactNotification.h */,
+                               4978AD171396302300930447 /* QQContactNotification.mm */,
+                               4978AD181396302300930447 /* QQPhysicalEvents.h */,
+                               4978AD191396302300930447 /* QQPhysicalEvents.mm */,
+                       );
+                       path = event;
+                       sourceTree = "<group>";
+               };
+               4978AD1E1396302E00930447 /* qq */ = {
+                       isa = PBXGroup;
+                       children = (
+                               4978AD1F1396302F00930447 /* QQNotification.h */,
+                               4978AD201396302F00930447 /* QQNotification.mm */,
+                               4978AD211396302F00930447 /* QQNotifier.h */,
+                               4978AD241396302F00930447 /* QQObservable.h */,
+                       );
+                       path = qq;
+                       sourceTree = "<group>";
+               };
                4995ABC61381C46B00334646 /* assets */ = {
                        isa = PBXGroup;
                        children = (
                49F2D9AE13764666000B6B8C /* src */ = {
                        isa = PBXGroup;
                        children = (
+                               4978AD1E1396302E00930447 /* qq */,
                                49F2D9B313764666000B6B8C /* render */,
                                49E834AF13812555007A6598 /* ui */,
                                49F2D9B113764666000B6B8C /* physics */,
                                49E8349D13812427007A6598 /* game */,
                                49F2D9B013764666000B6B8C /* main.mm */,
                                49F2D9B213764666000B6B8C /* prefix.pch */,
+                               4978AD131396139100930447 /* Tanks.h */,
                        );
                        path = src;
                        sourceTree = "<group>";
                49F2D9B113764666000B6B8C /* physics */ = {
                        isa = PBXGroup;
                        children = (
+                               4978AD151396302300930447 /* event */,
                                49E834C813814F7C007A6598 /* debug */,
                                49DA67D2137847A7004841E9 /* QQWorld.h */,
                                49DA67D3137847A7004841E9 /* QQWorld.mm */,
                                49193BF5139280180005B3DD /* QQLevel.h in Headers */,
                                49D8645D1392DB2800BC341C /* QQShape.h in Headers */,
                                4978AD0E1395E5CE00930447 /* QQActors.h in Headers */,
+                               4978AD141396139100930447 /* Tanks.h in Headers */,
+                               4978AD1A1396302300930447 /* QQContactNotification.h in Headers */,
+                               4978AD1C1396302300930447 /* QQPhysicalEvents.h in Headers */,
+                               4978AD251396302F00930447 /* QQNotification.h in Headers */,
+                               4978AD271396302F00930447 /* QQNotifier.h in Headers */,
+                               4978AD2A1396302F00930447 /* QQObservable.h in Headers */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                                49193BF6139280180005B3DD /* QQLevel.mm in Sources */,
                                49F2D9C413764666000B6B8C /* main.mm in Sources */,
                                49D8645E1392DB2800BC341C /* QQShape.mm in Sources */,
+                               4978AD1B1396302300930447 /* QQContactNotification.mm in Sources */,
+                               4978AD1D1396302300930447 /* QQPhysicalEvents.mm in Sources */,
+                               4978AD261396302F00930447 /* QQNotification.mm in Sources */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };