From 5be5e526b4d871eac2f83c9d69b16b27214a49b0 Mon Sep 17 00:00:00 2001 From: dsc Date: Tue, 17 May 2011 18:58:57 -0700 Subject: [PATCH] Wrappers for b2Body and a tentative attempt to integrate the physics engine. --- src/game/QQDisplayable.h | 6 +++ src/game/QQGame.h | 13 ++++++- src/game/QQGame.mm | 35 ++++++++++++++----- src/game/QQPhysical.h | 12 ++++++ src/game/actor/QQActor.h | 9 ++++- src/game/actor/QQActor.mm | 23 ++++++++++++ src/game/actor/QQUnit.h | 7 ++-- src/game/actor/QQUnit.mm | 26 ++++++++++---- src/game/actor/QQUnitDelegate.h | 5 ++- src/physics/QQBody.h | 30 ++++++++++++++++ src/physics/QQBody.mm | 43 +++++++++++++++++++++++ src/physics/QQWorld.h | 22 +++++++----- src/physics/QQWorld.mm | 69 ++++++++++++++++++++++++++++---------- 13 files changed, 250 insertions(+), 50 deletions(-) create mode 100644 src/game/QQPhysical.h create mode 100644 src/physics/QQBody.h create mode 100644 src/physics/QQBody.mm create mode 100644 src/physics/QQFixture.h create mode 100644 src/physics/QQFixture.mm create mode 100644 src/physics/QQJoint.h create mode 100644 src/physics/QQJoint.mm create mode 100644 src/physics/QQPhysicalEvents.h create mode 100644 src/physics/QQPhysicalEvents.mm create mode 100644 src/physics/QQPhysicalQueries.h create mode 100644 src/physics/QQPhysicalQueries.mm diff --git a/src/game/QQDisplayable.h b/src/game/QQDisplayable.h index ef6f1ef..3d69432 100644 --- a/src/game/QQDisplayable.h +++ b/src/game/QQDisplayable.h @@ -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 diff --git a/src/game/QQGame.h b/src/game/QQGame.h index 0857ebc..98e4cc4 100644 --- a/src/game/QQGame.h +++ b/src/game/QQGame.h @@ -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; diff --git a/src/game/QQGame.mm b/src/game/QQGame.mm index bb9cd08..aa54899 100644 --- a/src/game/QQGame.mm +++ b/src/game/QQGame.mm @@ -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 index 0000000..bb664cc --- /dev/null +++ b/src/game/QQPhysical.h @@ -0,0 +1,12 @@ +#include + +/** + * 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 diff --git a/src/game/actor/QQActor.h b/src/game/actor/QQActor.h index abe284c..b7fc953 100644 --- a/src/game/actor/QQActor.h +++ b/src/game/actor/QQActor.h @@ -1,12 +1,17 @@ +#include #import "game/QQActive.h" +#import "game/QQPhysical.h" #import "game/QQDisplayable.h" #import "physics/QQWorld.h" @class QQGame; -@interface QQActor : NSObject { +@interface QQActor : NSObject { +@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 diff --git a/src/game/actor/QQActor.mm b/src/game/actor/QQActor.mm index 944b26c..03529f9 100644 --- a/src/game/actor/QQActor.mm +++ b/src/game/actor/QQActor.mm @@ -11,8 +11,31 @@ - (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 diff --git a/src/game/actor/QQUnit.h b/src/game/actor/QQUnit.h index 2ffade5..77517c9 100644 --- a/src/game/actor/QQUnit.h +++ b/src/game/actor/QQUnit.h @@ -1,3 +1,4 @@ +#include #import "Sparrow.h" #import "render/QQSparrowExtensions.h" @@ -13,16 +14,16 @@ @private SPDisplayObject* _shape; - id _delegate; + id _delegate; } //////////////////////////////////////////////////////////////////////////////////// @property (nonatomic, retain, readwrite) SPDisplayObject* shape; -@property (nonatomic, retain, readwrite) id delegate; +@property (nonatomic, retain, readwrite) id 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; diff --git a/src/game/actor/QQUnit.mm b/src/game/actor/QQUnit.mm index 646e064..e6c0cac 100644 --- a/src/game/actor/QQUnit.mm +++ b/src/game/actor/QQUnit.mm @@ -1,3 +1,4 @@ +#include #import "Sparrow.h" #import "QQUnit.h" @@ -11,18 +12,26 @@ //////////////////////////////////////////////////////////////////////////////////// - (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; @@ -51,8 +60,11 @@ 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()); } } diff --git a/src/game/actor/QQUnitDelegate.h b/src/game/actor/QQUnitDelegate.h index f41c05e..d52c081 100644 --- a/src/game/actor/QQUnitDelegate.h +++ b/src/game/actor/QQUnitDelegate.h @@ -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 index 0000000..7eeca99 --- /dev/null +++ b/src/physics/QQBody.h @@ -0,0 +1,30 @@ +#include + +/* +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 index 0000000..743cffb --- /dev/null +++ b/src/physics/QQBody.mm @@ -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 index 0000000..e69de29 diff --git a/src/physics/QQFixture.mm b/src/physics/QQFixture.mm new file mode 100644 index 0000000..e69de29 diff --git a/src/physics/QQJoint.h b/src/physics/QQJoint.h new file mode 100644 index 0000000..e69de29 diff --git a/src/physics/QQJoint.mm b/src/physics/QQJoint.mm new file mode 100644 index 0000000..e69de29 diff --git a/src/physics/QQPhysicalEvents.h b/src/physics/QQPhysicalEvents.h new file mode 100644 index 0000000..e69de29 diff --git a/src/physics/QQPhysicalEvents.mm b/src/physics/QQPhysicalEvents.mm new file mode 100644 index 0000000..e69de29 diff --git a/src/physics/QQPhysicalQueries.h b/src/physics/QQPhysicalQueries.h new file mode 100644 index 0000000..e69de29 diff --git a/src/physics/QQPhysicalQueries.mm b/src/physics/QQPhysicalQueries.mm new file mode 100644 index 0000000..e69de29 diff --git a/src/physics/QQWorld.h b/src/physics/QQWorld.h index 6087703..91f505a 100644 --- a/src/physics/QQWorld.h +++ b/src/physics/QQWorld.h @@ -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; diff --git a/src/physics/QQWorld.mm b/src/physics/QQWorld.mm index e6c3d71..ed3abd2 100644 --- a/src/physics/QQWorld.mm +++ b/src/physics/QQWorld.mm @@ -1,46 +1,79 @@ -#import "QQWorld.h" +#import +#include +#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 { -- 1.7.0.4