Wrappers for b2Body and a tentative attempt to integrate the physics engine.
authordsc <david.schoonover@gmail.com>
Wed, 18 May 2011 01:58:57 +0000 (18:58 -0700)
committerdsc <david.schoonover@gmail.com>
Wed, 18 May 2011 01:58:57 +0000 (18:58 -0700)
21 files changed:
src/game/QQDisplayable.h
src/game/QQGame.h
src/game/QQGame.mm
src/game/QQPhysical.h [new file with mode: 0644]
src/game/actor/QQActor.h
src/game/actor/QQActor.mm
src/game/actor/QQUnit.h
src/game/actor/QQUnit.mm
src/game/actor/QQUnitDelegate.h
src/physics/QQBody.h [new file with mode: 0644]
src/physics/QQBody.mm [new file with mode: 0644]
src/physics/QQFixture.h [new file with mode: 0644]
src/physics/QQFixture.mm [new file with mode: 0644]
src/physics/QQJoint.h [new file with mode: 0644]
src/physics/QQJoint.mm [new file with mode: 0644]
src/physics/QQPhysicalEvents.h [new file with mode: 0644]
src/physics/QQPhysicalEvents.mm [new file with mode: 0644]
src/physics/QQPhysicalQueries.h [new file with mode: 0644]
src/physics/QQPhysicalQueries.mm [new file with mode: 0644]
src/physics/QQWorld.h
src/physics/QQWorld.mm

index ef6f1ef..3d69432 100644 (file)
@@ -5,4 +5,10 @@
 
 @property (nonatomic, retain, readonly) SPDisplayObject* shape;
 
+/**
+ * Called to update appearance after game actions have occurred
+ * and physics step has been processed.
+ */
+- (void) draw;
+
 @end
\ No newline at end of file
index 0857ebc..98e4cc4 100644 (file)
@@ -6,14 +6,23 @@
 
 @interface QQGame : SPStage {
     
-    long ticks;
+    long _ticks;
     
 @private
-    QQUnit*  _unit;
     QQWorld* _world;
+    NSMutableSet* _actors;
+    NSMutableSet* _awake;
+    NSMutableSet* _units;
+    NSMutableSet* _bullets;
 }
 
 @property (nonatomic, retain) QQWorld* world;
+@property (nonatomic, retain) NSSet* actors;
+@property (nonatomic, assign, readonly) long ticks;
+
+- (QQGame*) addActor:(QQActor*)actor;
+- (QQGame*) removeActor:(QQActor*)actor;
+
 
 - (void) onEnterFrame:(SPEnterFrameEvent*)event;
 
index bb9cd08..aa54899 100644 (file)
@@ -8,13 +8,14 @@ static QQGame* _currentGame = NULL;
 
 
 @interface QQGame ()
-@property (nonatomic, retain) QQUnit* unit;
 @end
 
 
 @implementation QQGame
+
+@synthesize ticks = _ticks;
 @synthesize world = _world;
-@synthesize unit  = _unit;
+@synthesize actors = _actors;
 
 
 - (id) init {
@@ -25,10 +26,11 @@ static QQGame* _currentGame = NULL;
     
     if ( (self = [super init]) ){
         _currentGame = self;
-        ticks = 0l;
-        
+        _ticks = 0l;
+        _actors = [[NSMutableArray alloc] initWithCapacity:10];
         _world = [[QQWorld alloc] init];
-        _unit = [[QQUnit alloc] init];
+        
+        [self addActor:[[[QQUnit alloc] init] autorelease]];
         
         [self addEventListener:@selector(onEnterFrame:) atObject:self forType:SP_EVENT_TYPE_ENTER_FRAME];
     }
@@ -36,17 +38,32 @@ static QQGame* _currentGame = NULL;
 }
 
 - (void) dealloc {
-    [_unit release];
+    [_actors removeAllObjects];
+    [_actors release];
     [_world release];
     _currentGame = NULL;
     [super dealloc];
 }
 
+- (QQGame*) addActor:(QQActor*)actor {
+    [_actors addObject:actor];
+    return self;
+}
+
+- (QQGame*) removeActor:(QQActor*)actor {
+    [_actors removeObject:actor];
+    return self;
+}
+
+
 - (void) onEnterFrame:(SPEnterFrameEvent*)event {
-    ticks++;
-    if ((ticks % 100) == 0)
-        NSLog(@"[%ld] Time passed since last 100 frames: %f", ticks, event.passedTime);
+    _ticks++;
+    if ((_ticks % 100) == 0)
+        NSLog(@"[%ld] Time passed since last 100 frames: %f", _ticks, event.passedTime);
+    
+    for (QQActor* actor in self.actors) [actor act];     // XXX: self.awakeAgents
     [self.world step];
+    for (QQActor* actor in self.actors) [actor draw];    // XXX: self.drawableAgents
 }
 
 
diff --git a/src/game/QQPhysical.h b/src/game/QQPhysical.h
new file mode 100644 (file)
index 0000000..bb664cc
--- /dev/null
@@ -0,0 +1,12 @@
+#include <Box2D/Box2D.h>
+
+/**
+ * Anything that has a physics representation.
+ */
+@protocol QQPhysical
+
+// FIXME: don't expose these, instead aggregate properties from body & fixtures/shapes
+@property (nonatomic, readonly) b2Body* body;
+@property (nonatomic, readonly) b2Fixture* fixture;
+
+@end
\ No newline at end of file
index abe284c..b7fc953 100644 (file)
@@ -1,12 +1,17 @@
+#include <Box2D/Box2D.h>
 #import "game/QQActive.h"
+#import "game/QQPhysical.h"
 #import "game/QQDisplayable.h"
 #import "physics/QQWorld.h"
 
 @class QQGame;
 
 
-@interface QQActor : NSObject <QQActive, QQDisplayable> {
+@interface QQActor : NSObject <QQActive, QQPhysical, QQDisplayable> {
     
+@protected
+    b2Body* _body;
+
 @private
     BOOL _active;
 }
@@ -14,6 +19,8 @@
 @property (nonatomic, readonly) QQGame*  game;
 @property (nonatomic, readonly) QQWorld* world;
 
+- (id) initAtX:(float)x y:(float)y;
+- (id) initType:(b2BodyType)type atX:(float)x y:(float)y;
 
 
 @end
index 944b26c..03529f9 100644 (file)
 
 - (SPDisplayObject*) shape { return nil; }
 
+- (b2Body*) body { return _body; }
+- (b2Fixture*) fixture { return nil; }
+
+
+- (id) initAtX:(float)x y:(float)y {
+    return [self initType:b2_dynamicBody atX:x y:y];
+}
+
+- (id) initType:(b2BodyType)type atX:(float)x y:(float)y {
+    if ((self = [super init])) {
+        b2BodyDef bd;
+        bd.type = type;
+        bd.position = b2Vec2(x, y);
+        bd.userData = (void*)self;
+        _body = self.world.world->CreateBody(&bd);
+    }
+    return self;
+}
+
 - (void) act {
     // stub
 }
 
+- (void) draw {
+    // stub
+}
+
 @end
index 2ffade5..77517c9 100644 (file)
@@ -1,3 +1,4 @@
+#include <Box2D/Box2D.h>
 #import "Sparrow.h"
 
 #import "render/QQSparrowExtensions.h"
 
 @private
     SPDisplayObject* _shape;
-    id <QQUnitDelegate> _delegate;
+    id<QQUnitDelegate> _delegate;
 }
 
 ////////////////////////////////////////////////////////////////////////////////////
 @property (nonatomic, retain, readwrite) SPDisplayObject* shape;
-@property (nonatomic, retain, readwrite) id <QQUnitDelegate> delegate;
+@property (nonatomic, retain, readwrite) id<QQUnitDelegate> delegate;
 
 ////////////////////////////////////////////////////////////////////////////////////
 - (id) initWithFile:(NSString*)fileName atX:(float)x y:(float)y;
-- (id) initWithShape:(SPDisplayObject*)aShape;
+- (id) initWithShape:(SPDisplayObject*)shape atX:(float)x y:(float)y;
 
 - (void) onTouch:(SPTouchEvent*)event;
 
index 646e064..e6c0cac 100644 (file)
@@ -1,3 +1,4 @@
+#include <Box2D/Box2D.h>
 #import "Sparrow.h"
 #import "QQUnit.h"
 
 
 ////////////////////////////////////////////////////////////////////////////////////
 - (id) init {
-    return [self initWithShape:[[SPQuad quadWithWidth:32 height:32 color:0xff0000] setPositionX:50 y:50]];
+    return [self initWithShape:[SPQuad quadWithWidth:50 height:50 color:0xff0000] atX:15 y:15];
 }
 
 ////////////////////////////////////////////////////////////////////////////////////
 - (id) initWithFile:(NSString*)fileName atX:(float)x y:(float)y {
-    return [self initWithShape:[[[[SPImage alloc] initWithContentsOfFile:fileName] autorelease] setPositionX:x y:y]];
+    return [self initWithShape:
+        [[[SPImage alloc] autorelease] initWithContentsOfFile:fileName]
+        atX:x y:y];
 }
 
 ////////////////////////////////////////////////////////////////////////////////////
-- (id) initWithShape:(SPDisplayObject*)aShape {
-    if ((self = [super init])) {
-        self.shape = aShape;
+- (id) initWithShape:(SPDisplayObject*)shape atX:(float)x y:(float)y {
+    if ((self = [super initAtX:x y:y])) {
+        float px = self.world.scale;
+        self.shape = [shape setPositionX:x*px y:y*px];
+        
+        b2PolygonShape box;
+        box.SetAsBox(shape.width/px, shape.height/px);
+        self.body->CreateFixture(&box, 5.0f);
+        
         [self.game addEventListener:@selector(onTouch:) atObject:self forType:SP_EVENT_TYPE_TOUCH];
     }
     return self;
     SPTouch* touch = [[event touchesWithTarget:self.shape.parent] anyObject];
     if (touch) {
         SPPoint* touchPosition = [touch locationInSpace:self.shape.parent];
-        self.shape.x = touchPosition.x - self.shape.width / 2.0f;
-        self.shape.y = touchPosition.y - self.shape.height / 2.0f;
+        float x = self.shape.x = touchPosition.x - self.shape.width / 2.0f;
+        float y = self.shape.y = touchPosition.y - self.shape.height / 2.0f;
+        
+        float px = self.world.scale;
+        self.body->SetTransform(b2Vec2(x/px, y/px), self.body->GetAngle());
     }
 }
 
index f41c05e..d52c081 100644 (file)
@@ -1,3 +1,6 @@
+
 @protocol QQUnitDelegate
--(void)updateWithTick:(float)time;
+
+- (void) updateWithTick:(float)time;
+
 @end
diff --git a/src/physics/QQBody.h b/src/physics/QQBody.h
new file mode 100644 (file)
index 0000000..7eeca99
--- /dev/null
@@ -0,0 +1,30 @@
+#include <Box2D/Box2D.h>
+
+/*
+b2BodyDef defaults:
+    userData = NULL;
+    type = b2_staticBody;
+
+    position.Set(0.0f, 0.0f);
+    angle = 0.0f;
+
+    linearVelocity.Set(0.0f, 0.0f);
+    angularVelocity = 0.0f;
+
+    linearDamping = 0.0f;
+    angularDamping = 0.0f;
+    inertiaScale = 1.0f;
+
+    allowSleep = true;
+    fixedRotation = false;
+    bullet = false;
+
+    awake = true;
+    active = true;
+*/
+
+@interface QQBody : NSObject {
+    
+}
+
+@end
diff --git a/src/physics/QQBody.mm b/src/physics/QQBody.mm
new file mode 100644 (file)
index 0000000..743cffb
--- /dev/null
@@ -0,0 +1,43 @@
+#import "QQBody.h"
+
+// private interface
+@interface QQBody ()
+    
+@end
+
+
+/*
+b2BodyDef defaults:
+    userData = NULL;
+    type = b2_staticBody;
+
+    position.Set(0.0f, 0.0f);
+    angle = 0.0f;
+
+    linearVelocity.Set(0.0f, 0.0f);
+    angularVelocity = 0.0f;
+
+    linearDamping = 0.0f;
+    angularDamping = 0.0f;
+    inertiaScale = 1.0f;
+
+    allowSleep = true;
+    fixedRotation = false;
+    bullet = false;
+
+    awake = true;
+    active = true;
+*/
+
+@implementation QQBody
+
+- (id) init {
+    if ((self = [super init])){
+        
+    }
+    return self;
+}
+
+
+@end
\ No newline at end of file
diff --git a/src/physics/QQFixture.h b/src/physics/QQFixture.h
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/physics/QQFixture.mm b/src/physics/QQFixture.mm
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/physics/QQJoint.h b/src/physics/QQJoint.h
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/physics/QQJoint.mm b/src/physics/QQJoint.mm
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/physics/QQPhysicalEvents.h b/src/physics/QQPhysicalEvents.h
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/physics/QQPhysicalEvents.mm b/src/physics/QQPhysicalEvents.mm
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/physics/QQPhysicalQueries.h b/src/physics/QQPhysicalQueries.h
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/physics/QQPhysicalQueries.mm b/src/physics/QQPhysicalQueries.mm
new file mode 100644 (file)
index 0000000..e69de29
index 6087703..91f505a 100644 (file)
@@ -5,23 +5,27 @@
 @interface QQWorld : NSObject {
 
 @private
-    float _timestep;
-    int _velocityIterations;
-    int _positionIterations;
-    
-    b2World* _world;
     QQGLESDebugDraw debugDraw;
+    b2World* _world;
+    NSMutableArray* _walls;
+    
+    float   _timestep;
+    int     _velocityIterations;
+    int     _positionIterations;
+    BOOL    _drawDebugLayer;
+    float   _scale;
 }
 
 @property (nonatomic, readonly) b2World* world;
+
 @property (nonatomic, assign) float timestep;
-@property (nonatomic, assign) int velocityIterations;
-@property (nonatomic, assign) int positionIterations;
+@property (nonatomic, assign) int   velocityIterations;
+@property (nonatomic, assign) int   positionIterations;
+@property (nonatomic, assign) BOOL  drawDebugLayer;
+@property (nonatomic, assign) float scale; // pixels per meter
 
 
 - (QQWorld*) init;
-- (QQWorld*) initWithTimestep:(float)hz;
-- (QQWorld*) initWithTimestep:(float)hz gravityX:(float)x y:(float)y;
 
 - (void) setGravityX:(float)x y:(float)y;
 
index e6c3d71..ed3abd2 100644 (file)
@@ -1,46 +1,79 @@
-#import "QQWorld.h"
+#import <UIKit/UIKit.h>
+#include <Box2D/Box2D.h>
 
+#import "QQWorld.h"
 
 
 @implementation QQWorld
 
-@synthesize world = _world;
-@synthesize timestep = _timestep;
+@synthesize timestep           = _timestep;
 @synthesize velocityIterations = _velocityIterations;
 @synthesize positionIterations = _positionIterations;
+@synthesize drawDebugLayer     = _drawDebugLayer;
+@synthesize scale              = _scale;
 
+@synthesize world              = _world;
 
-- (QQWorld*) init {
-    return [self initWithTimestep:60.0f];
-}
-
-- (QQWorld*) initWithTimestep:(float)hz {
-    return [self initWithTimestep:hz gravityX:0.0f y:0.0f];
-}
 
-- (QQWorld*) initWithTimestep:(float)hz gravityX:(float)x y:(float)y {
+- (QQWorld*) init {
     if ((self = [super init])) {
-        _timestep = 1.0f/hz;
+        _timestep = 1.0f/60.0f;
         _velocityIterations = 10;
         _positionIterations = 10;
+        _drawDebugLayer = NO;
+        _scale = 5.0f;
         
-        b2Vec2 gravity = b2Vec2(x,y);
-        bool doSleep = true;
-        _world = new b2World(gravity, doSleep);
+        _world = new b2World(b2Vec2(0.0f, 0.0f), true); // b2World(gravity, doSleep)
         // _world->SetDebugDraw(&debugDraw);
+        
+        _walls = [[NSMutableArray alloc] initWithCapacity:4];
+        
+        CGSize screen = [UIScreen mainScreen].bounds.size;
+        float w = screen.width;
+        float h = screen.height;
+        b2Vec2 origin = b2Vec2(0.0f, 0.0f);
+        b2Vec2 horz   = b2Vec2(w, 0.0f);
+        b2Vec2 vert   = b2Vec2(0.0f, h);
+        
+        b2Vec2 positions[] = {
+            b2Vec2(w*0.5f, h*0.0f),
+            b2Vec2(w*0.5f, h*1.0f),
+            b2Vec2(w*0.0f, h*0.5f),
+            b2Vec2(w*1.0f, h*0.5f)
+        };
+        b2Vec2 bounds[] = {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, bounds[i]);
+            
+            wall->CreateFixture(&shape, 0.0f);
+            // [_walls addObject:wall]; // doh, b2Body so does not descend from NSObject, and so is not an (id)
+        }
+        
     }
     return self;
 }
 
 - (void) dealloc {
-    // TODO: track and release all known bodies etc
-    delete _world;
+    // for (b2Body* wall in _walls) {
+    //     _world->DestroyBody(wall);
+    // }
+    [_walls removeAllObjects];
+    [_walls release];
+    delete _world; // b2World destructor destroys all physics entities and releases all heap memory.
     [super dealloc];
 }
 
 - (void) step {
     _world->Step(self.timestep, self.velocityIterations, self.positionIterations);
-    // _world->DrawDebugData();
+    #ifdef DEBUG
+        if (_drawDebugLayer) _world->DrawDebugData();
+    #endif
 }
 
 - (void) setGravityX:(float)x y:(float)y {