Proof of concept -- tank fires bullet, bullet interacts with other physical objects.
authordsc <david.schoonover@gmail.com>
Tue, 24 May 2011 20:01:03 +0000 (13:01 -0700)
committerdsc <david.schoonover@gmail.com>
Tue, 24 May 2011 20:01:03 +0000 (13:01 -0700)
39 files changed:
libs/sparrow/src/Extras/BEParallaxSprite.h
libs/sparrow/src/Extras/BEParallaxSprite.m
libs/sparrow/src/Extras/SparrowExtras.h
src/Tanks.h [new file with mode: 0644]
src/TanksMacros.h [new file with mode: 0644]
src/game/QQActive.h
src/game/QQDisplayable.h
src/game/QQGame.h
src/game/QQGame.mm
src/game/QQPhysical.h
src/game/ability/QQAbility.h [new file with mode: 0644]
src/game/ability/QQAbility.mm [new file with mode: 0644]
src/game/ability/QQCooldown.h
src/game/ability/QQCooldown.mm
src/game/ability/QQStat.h [new file with mode: 0644]
src/game/ability/QQStat.mm [new file with mode: 0644]
src/game/actor/QQActor.h
src/game/actor/QQActor.mm
src/game/actor/QQActorDelegate.h [moved from src/game/actor/QQUnitDelegate.h with 63% similarity]
src/game/actor/QQIUnit.h [new file with mode: 0644]
src/game/actor/QQUnit.h
src/game/actor/QQUnit.mm [moved from src/game/actor/QQUnitBase.mm with 54% similarity]
src/game/actor/QQUnitBase.h [deleted file]
src/game/actor/bullet/QQBullet.h
src/game/actor/bullet/QQBullet.mm
src/game/actor/unit/QQTank.h
src/game/actor/unit/QQTank.mm
src/main.mm
src/physics/QQPhysics.h [new file with mode: 0644]
src/physics/QQPoint.h [new file with mode: 0644]
src/physics/QQPoint.mm [new file with mode: 0644]
src/physics/QQWorld.mm
src/qq/QQNotifier.h [new file with mode: 0644]
src/qq/QQObject.h [new file with mode: 0644]
src/qq/QQObject.mm [new file with mode: 0644]
src/qq/QQObservable.h [new file with mode: 0644]
src/render/QQSparrowExtensions.h
src/render/QQSparrowExtensions.mm
tanks.xcodeproj/project.pbxproj

index b25c72d..3a0ec1a 100644 (file)
@@ -6,6 +6,7 @@
 //
 
 #import <Foundation/Foundation.h>
+#import "Sparrow.h"
 
 #define BE_PARALLAX_DIRECTION_LEFT 1
 #define BE_PARALLAX_DIRECTION_RIGHT 2
index 7c01729..6e4995c 100644 (file)
 @synthesize running = mRunning;
 
 - (id)initWithTexture:(SPTexture *)texture {
-       if (self = [super init]) {
+       if ((self = [super init])) {
                [self initWithTexture:texture speed:1 direction:BE_PARALLAX_DIRECTION_LEFT];
        }
        return self;
 }
 
 - (id)initWithTexture:(SPTexture *)texture speed:(float)speed {
-       if (self = [super init]) {
+       if ((self = [super init])) {
                [self initWithTexture:texture speed:speed direction:BE_PARALLAX_DIRECTION_LEFT];
        }
        return self;
 }
 
 - (id)initWithTexture:(SPTexture *)texture speed:(float)speed direction:(int)direction {
-       if (self = [super init]) {
+       if ((self = [super init])) {
                mRunning = YES;
                mDirection = direction;
                if (direction < 1 || direction > 4) {
index a597333..a53eaab 100644 (file)
@@ -3,6 +3,6 @@
 #import "SHCircle.h"
 #import "SHLine.h"
 #import "SHPolygon.h"
-#import "BEParallaxSprite.h"
+//import "BEParallaxSprite.h"
 #import "SXFPSMeter.h"
 
diff --git a/src/Tanks.h b/src/Tanks.h
new file mode 100644 (file)
index 0000000..5234a2e
--- /dev/null
@@ -0,0 +1,2 @@
+
+#import "render/QQSparrowExtensions.h"
diff --git a/src/TanksMacros.h b/src/TanksMacros.h
new file mode 100644 (file)
index 0000000..2a9ef11
--- /dev/null
@@ -0,0 +1,7 @@
+#ifndef TANKS_MACROS_H
+#define TANKS_MACROS_H
+
+
+
+
+#endif
\ No newline at end of file
index ec6fe08..9335dab 100644 (file)
@@ -7,6 +7,7 @@
 
 @property (nonatomic, getter=isActive) BOOL active;
 
+- (void) tick:(float)elapsed;
 - (void) act;
 
 @end
\ No newline at end of file
index a559892..ab60dbf 100644 (file)
@@ -6,7 +6,13 @@
  */
 @protocol QQDisplayable
 
-@property (nonatomic, retain, readonly) SPDisplayObject* shape;
+@property (nonatomic, readwrite, assign, getter=isDirty) BOOL dirty; // TODO: implement this and fix various [shape setPosition] calls
+@property (nonatomic, readwrite, retain) SPDisplayObject* shape;
+
+/**
+ * Called to setup the Sparrow shape.
+ */
+- (SPDisplayObject*) setupShape;
 
 /**
  * Called to update appearance after game actions have occurred
index 98e4cc4..835b132 100644 (file)
@@ -1,7 +1,7 @@
 #import "Sparrow.h"
 
-#import "game/actor/QQUnit.h"
 #import "physics/QQWorld.h"
+#import "game/actor/QQActor.h"
 
 
 @interface QQGame : SPStage {
     NSMutableSet* _bullets;
 }
 
+@property (nonatomic, assign, readonly) long ticks;
+
 @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;
-
 + (QQGame*) current;
 
 @end
index 512e165..51b05b4 100644 (file)
@@ -1,5 +1,6 @@
 #import "QQGame.h"
 #import "game/actor/unit/QQTank.h"
+#import <Box2D/Box2D.h>
 
 
 
@@ -8,6 +9,7 @@ static QQGame* _CurrentGame = NULL;
 
 
 @interface QQGame ()
+- (void) onEnterFrame:(SPEnterFrameEvent*)event;
 @end
 
 
@@ -21,16 +23,20 @@ static QQGame* _CurrentGame = NULL;
 - (id) init {
     if (_CurrentGame) {
         [self release];
-        [NSException raise:@"TooManyGames" format:@"cannot instantiate more than one QQGame at a time!"];
+        #ifdef DEBUG
+            [NSException raise:@"TooManyGames" format:@"cannot instantiate more than one QQGame at a time!"];
+        #else
+            return _CurrentGame;
+        #endif
     }
     
     if ( (self = [super init]) ){
         _CurrentGame = self;
-        _ticks = 0l;
+        _ticks = 0;
         _actors = [[NSMutableArray alloc] initWithCapacity:10];
         _world = [[QQWorld alloc] init];
         
-        [self addActor:[[[QQTank alloc] init] autorelease]];
+        [[[QQTank alloc] init] autorelease];
         
         [self addEventListener:@selector(onEnterFrame:) atObject:self forType:SP_EVENT_TYPE_ENTER_FRAME];
     }
@@ -38,6 +44,7 @@ static QQGame* _CurrentGame = NULL;
 }
 
 - (void) dealloc {
+    [self removeEventListener:@selector(onEnterFrame:) atObject:self forType:SP_EVENT_TYPE_ENTER_FRAME];
     [_actors removeAllObjects];
     [_actors release];
     [_world release];
@@ -58,10 +65,15 @@ static QQGame* _CurrentGame = NULL;
 
 - (void) onEnterFrame:(SPEnterFrameEvent*)event {
     _ticks++;
-    if ((_ticks % 100) == 0)
+    if ((_ticks % 100) == 0) {
         NSLog(@"[%ld] Time passed since last 100 frames: %f", _ticks, event.passedTime);
+        for (QQActor* actor in self.actors) {
+            b2Vec2 v = actor.body->GetLinearVelocity();
+            NSLog(@"[%@ pos:(%f,%f) impulse:(%f,%f)]", actor, actor.x,actor.y, v.x,v.y);
+        }
+    }
     
-    for (QQActor* actor in self.actors) [actor act];     // XXX: self.awakeAgents
+    for (QQActor* actor in self.actors) [actor tick:event.passedTime]; // XXX: self.awakeAgents
     [self.world step];
     for (QQActor* actor in self.actors) [actor draw];    // XXX: self.drawableAgents
 }
index 0dd0ce3..ec83ded 100644 (file)
@@ -5,13 +5,21 @@
  */
 @protocol QQPhysical
 
-// @property (nonatomic, readwrite, assign) float x;
-// @property (nonatomic, readwrite, assign) float y;
+@property (nonatomic, readwrite, assign) float x;
+@property (nonatomic, readwrite, assign) float y;
+@property (nonatomic, readwrite) CGPoint position;
+
+- (void) setPositionX:(float)x y:(float)y;
 
 
 // FIXME: don't expose these, instead aggregate properties from body & fixtures/shapes
 @property (nonatomic, readonly) b2Body* body;
 @property (nonatomic, readonly) b2Fixture* fixture;
 
+/**
+ * Called after initialization to create the various bodies, fixtures, and shapes
+ * that represent the Unit in Box2D.
+ */
+- (void) setupPhysics;
 
 @end
\ No newline at end of file
diff --git a/src/game/ability/QQAbility.h b/src/game/ability/QQAbility.h
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/game/ability/QQAbility.mm b/src/game/ability/QQAbility.mm
new file mode 100644 (file)
index 0000000..e69de29
index f58e0a8..490a473 100644 (file)
 @property (nonatomic, readwrite, assign) float elapsed;
 
 /** Whether Cooldown timer is ready to be activated. */
-@property (nonatomic, readwrite, getter=isReady) BOOL ready;
+@property (nonatomic, readwrite) BOOL ready;
 
 /** Percent completed (elapsed / duration), clamped [0, 1.0]. */
 @property (nonatomic, readwrite) float ratio;
 
 /**
- * Implies ready=NO.
+ * Implies ready=YES.
  */
 - (id) initWithDuration:(float)duration;
 - (id) initWithDuration:(float)duration andReady:(BOOL)ready;
index 9bf369a..72d9934 100644 (file)
@@ -32,7 +32,7 @@
 /// initializers
 
 - (id) initWithDuration:(float)duration {
-    return [self initWithDuration:duration andReady:NO];
+    return [self initWithDuration:duration andReady:YES];
 }
 
 - (id) initWithDuration:(float)duration andReady:(BOOL)ready {
diff --git a/src/game/ability/QQStat.h b/src/game/ability/QQStat.h
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/game/ability/QQStat.mm b/src/game/ability/QQStat.mm
new file mode 100644 (file)
index 0000000..e69de29
index 3aefbe4..c638de6 100644 (file)
@@ -3,6 +3,7 @@
 #import "game/QQActive.h"
 #import "game/QQPhysical.h"
 #import "game/QQDisplayable.h"
+#import "game/actor/QQActorDelegate.h"
 #import "physics/QQWorld.h"
 
 @class QQGame;
 
 @private
     BOOL _active;
+    BOOL _dirty;
+    id<QQActorDelegate> _delegate;
 }
 
 @property (nonatomic, readonly) QQGame*  game;
 @property (nonatomic, readonly) QQWorld* world;
+@property (nonatomic, readwrite, retain) id<QQActorDelegate> delegate;
 
 - (id) initAtX:(float)x y:(float)y;
 - (id) initType:(b2BodyType)type atX:(float)x y:(float)y;
index 03529f9..ee0876b 100644 (file)
@@ -1,26 +1,56 @@
 #import "QQActor.h"
+#import "render/QQSparrowExtensions.h"
 #import "game/QQGame.h"
 
 
 @implementation QQActor
 
-@synthesize active;
+/// properties
+
+@synthesize active   = _active;
+@synthesize dirty    = _dirty;
+@synthesize delegate = _delegate;
 
 - (QQGame*)  game  { return QQGame.current; }
 - (QQWorld*) world { return self.game.world; }
 
 - (SPDisplayObject*) shape { return nil; }
+- (void) setShape:(SPDisplayObject*)newShape {}
 
 - (b2Body*) body { return _body; }
 - (b2Fixture*) fixture { return nil; }
 
 
+- (CGPoint) position {
+    b2Vec2 pos = self.body->GetPosition();
+    return CGPointMake(pos.x, pos.y);
+}
+- (void) setPosition:(CGPoint)pos {
+    [self setPositionX:pos.x y:pos.y];
+}
+- (void) setPositionX:(float)x y:(float)y {
+    self.body->SetTransform(b2Vec2(x,y), self.body->GetAngle());
+    float px = self.world.scale;
+    [self.shape setPositionX:x*px y:y*px];
+}
+
+- (float) x             { return self.body->GetPosition().x;    }
+- (void) setX:(float)x  { [self setPositionX:x y:self.y];       }
+
+- (float) y             { return self.body->GetPosition().y;    }
+- (void) setY:(float)y  { [self setPositionX:self.x y:y];       }
+
+
+/// initializers
+
 - (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])) {
+        [self.game addActor:self];
+        
         b2BodyDef bd;
         bd.type = type;
         bd.position = b2Vec2(x, y);
     return self;
 }
 
-- (void) act {
-    // stub
+
+/// methods
+
+- (void) tick:(float)elapsed {
+    [self act];
+}
+
+- (void) act {}
+
+- (SPDisplayObject*) setupShape {
+    return self.shape;
 }
 
 - (void) draw {
-    // stub
+    float px = self.world.scale;
+    b2Vec2 pos = self.body->GetPosition();
+    [self.shape setPositionX:pos.x*px y:pos.y*px];
 }
 
+- (void) setupPhysics {}
+
 @end
similarity index 63%
rename from src/game/actor/QQUnitDelegate.h
rename to src/game/actor/QQActorDelegate.h
index d52c081..bae4a18 100644 (file)
@@ -1,5 +1,5 @@
 
-@protocol QQUnitDelegate
+@protocol QQActorDelegate
 
 - (void) updateWithTick:(float)time;
 
diff --git a/src/game/actor/QQIUnit.h b/src/game/actor/QQIUnit.h
new file mode 100644 (file)
index 0000000..15878ea
--- /dev/null
@@ -0,0 +1,16 @@
+#import "game/actor/QQActor.h"
+#import "game/QQThing.h"
+
+
+@protocol QQUnit <QQThing>
+
+@property (nonatomic, readonly) BOOL canAttack;
+
+- (void) attackTarget:(QQActor*)actor;
+- (void) attackToward:(CGPoint)point;
+- (void) attackTowardX:(float)x y:(float)y;
+
+
+@end
+
+//typedef id<QQUnit> QQUnit;
index 15878ea..e1b509d 100644 (file)
@@ -1,16 +1,31 @@
+#include <Box2D/Box2D.h>
+#import "Sparrow.h"
+
+#import "physics/QQWorld.h"
 #import "game/actor/QQActor.h"
-#import "game/QQThing.h"
+#import "game/actor/QQActorDelegate.h"
+#import "render/QQSparrowExtensions.h"
+
 
+@interface QQUnit : QQActor {
 
-@protocol QQUnit <QQThing>
+@private
+    SPDisplayObject* _shape;
+}
 
+/// properties
 @property (nonatomic, readonly) BOOL canAttack;
+- (void) setShapeFromFile:(NSString*)filename;
+
+/// initializers
+- (id) initAtX:(float)x y:(float)y withShape:(SPDisplayObject*)shape;
 
-- (void) attackTarget:(QQActor*)actor;
+
+/// methods
+
+- (void) attack:(QQActor*)actor;
 - (void) attackToward:(CGPoint)point;
 - (void) attackTowardX:(float)x y:(float)y;
 
 
 @end
-
-//typedef id<QQUnit> QQUnit;
similarity index 54%
rename from src/game/actor/QQUnitBase.mm
rename to src/game/actor/QQUnit.mm
index 2c52e58..1687b13 100644 (file)
@@ -1,64 +1,56 @@
 #include <Box2D/Box2D.h>
 #import "Sparrow.h"
-#import "QQUnit.h"
-#import "QQUnitBase.h"
 
-// private interface
-@interface QQUnitBase ()
+#import "QQUnit.h"
+#import "render/QQSparrowExtensions.h"
 
 
-@end
 
 
-////////////////////////////////////////////////////////////////////////////////////
-@implementation QQUnitBase
+@implementation QQUnit
 
 @dynamic world; // Eliminates compiler warnings due to inheritance (!)
 @dynamic game;
 
-////////////////////////////////////////////////////////////////////////////////////
 @synthesize shape = _shape;
-@synthesize delegate = _delegate;
 
 - (BOOL) canAttack { return NO; }
 
 
-////////////////////////////////////////////////////////////////////////////////////
+/// initializers
+
 - (id) init {
-    return [self initAtX:15 y:15 withShape:[SPQuad quadWithWidth:50 height:50 color:0xff0000]];
+    return [self initAtX:50 y:50 withShape:[SPQuad quadWithWidth:50 height:50 color:0xff0000]];
 }
 
 - (id) initAtX:(float)x y:(float)y withShape:(SPDisplayObject*)shape {
     if ((self = [super initAtX:x y:y])) {
-        float px = self.world.scale;
-        self.shape = [shape setPositionX:x*px y:y*px];
+        self.shape = shape;
         
+        float px = self.world.scale;
         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;
 }
 
-////////////////////////////////////////////////////////////////////////////////////
 - (void) dealloc {
-    [self.game removeEventListener:@selector(onTouch:) atObject:self forType:SP_EVENT_TYPE_TOUCH];
     [self.game removeChild:_shape];
     [_shape release];
     [super dealloc];
 }
 
-////////////////////////////////////////////////////////////////////////////////////
+/// methods
+
 - (void) setShape:(SPDisplayObject*)newShape {
     if (_shape != newShape) {
         [self.game removeChild:_shape];
         [_shape release];
         
         _shape = [newShape retain];
-        // float px = self.world.scale;
-        // [_shape setPositionX:x*px y:y*px]
+        float px = self.world.scale;
+        [_shape setPositionX:self.x*px y:self.y*px];
         [self.game addChild:_shape];
     }
 }
 
 
 
-- (void) attackTarget:(QQActor*)actor {
-    
+- (void) attack:(QQActor*)actor {
+    [self attackTowardX:actor.x y:actor.y];
 }
-
 - (void) attackToward:(CGPoint)pt {
     [self attackTowardX:pt.x y:pt.y];
 }
-
 - (void) attackTowardX:(float)x y:(float)y {
     
 }
diff --git a/src/game/actor/QQUnitBase.h b/src/game/actor/QQUnitBase.h
deleted file mode 100644 (file)
index 6af3e83..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-#include <Box2D/Box2D.h>
-#import "Sparrow.h"
-
-#import "render/QQSparrowExtensions.h"
-
-#import "game/actor/QQActor.h"
-#import "game/actor/QQUnit.h"
-#import "game/actor/QQUnitDelegate.h"
-
-#import "physics/QQWorld.h"
-
-
-////////////////////////////////////////////////////////////////////////////////////
-@interface QQUnitBase : QQActor <QQUnit> {
-
-@private
-    SPDisplayObject* _shape;
-    id<QQUnitDelegate> _delegate;
-}
-
-////////////////////////////////////////////////////////////////////////////////////
-@property (nonatomic, retain, readwrite) SPDisplayObject* shape;
-@property (nonatomic, retain, readwrite) id<QQUnitDelegate> delegate;
-
-////////////////////////////////////////////////////////////////////////////////////
-- (id) initAtX:(float)x y:(float)y withShape:(SPDisplayObject*)shape;
-
-- (void) setShapeFromFile:(NSString*)filename;
-
-@end
index e3bcfa3..6d66f6b 100644 (file)
@@ -1,15 +1,15 @@
-#import "game/actor/QQUnitBase.h"
 #import "game/actor/QQUnit.h"
 
 
-@interface QQBullet : QQUnitBase {
+@interface QQBullet : QQUnit {
 @private
-    id<QQUnit>* _owner;
+    QQUnit* _owner;
 }
 
-@property (nonatomic, readonly) id<QQUnit>* owner;
+@property (nonatomic, readonly) QQUnit* owner;
 
-- (id) initAtX:(float)x y:(float)y withOwner:(id<QQUnit>*)owner;
+- (id) init:(QQUnit*)owner x:(float)x y:(float)y;
 
+- (void) applyLinearImpulseX:(float)xNs y:(float)yNs;
 
 @end
index 7c489bc..3297852 100644 (file)
@@ -1,22 +1,36 @@
-#import "QQBullet.h"
-
+#include <Box2D/Box2D.h>
+#import "SparrowExtras.h"
 
-// private interface
-@interface QQBullet ()
+#import "QQBullet.h"
 
-@end
 
 
 @implementation QQBullet
 
 @synthesize owner = _owner;
 
-- (id) initAtX:(float)x y:(float)y withOwner:(id<QQUnit>*)owner {
-    if ((self = [super init])){
+- (id) init:(QQUnit*)owner x:(float)x y:(float)y {
+    if ((self = [super initAtX:x y:y])){
+        _owner = owner;
         
+        float px = self.world.scale;
+        float radius = 1.5f;
+        self.shape = [SHCircle circleWithWidth:2*radius*px height:2*radius*px];
+        
+        b2CircleShape circle;
+        circle.m_radius = radius;
+        b2FixtureDef fix;
+        fix.shape = &circle;
+        fix.restitution = 1.0f;
+        fix.density = 0.1f;
+        self.body->CreateFixture(&fix);
     }
     return self;
 }
 
+- (void) applyLinearImpulseX:(float)xNs y:(float)yNs {
+    self.body->ApplyLinearImpulse(b2Vec2(xNs, yNs), self.body->GetPosition());
+}
+
 
 @end
index 9d58ed2..ee8c4c1 100644 (file)
@@ -1,7 +1,10 @@
-#import "game/actor/QQUnitBase.h"
+#import "game/actor/QQUnit.h"
+#import "game/ability/QQCooldown.h"
 
-@interface QQTank : QQUnitBase {
-    
+@interface QQTank : QQUnit {
+    QQCooldown* _coolAtk;
 }
 
+@property (nonatomic, readonly, retain) QQCooldown* coolAtk;
+
 @end
index f80056d..8587589 100644 (file)
@@ -1,28 +1,53 @@
 #import "QQTank.h"
+#import "render/QQSparrowExtensions.h"
+#import "game/actor/bullet/QQBullet.h"
 
 
 // private interface
 @interface QQTank ()
-
+@property (nonatomic, readwrite, retain) QQCooldown* coolAtk;
 - (void) onTouch:(SPTouchEvent*)event;
-
 @end
 
 
+
 @implementation QQTank
 
+@synthesize coolAtk = _coolAtk;
+
 - (id) init {
     if ((self = [super init])){
-        
+        self.coolAtk = [QQCooldown cooldownWithDuration:2];
+        [self.game addEventListener:@selector(onTouch:) atObject:self forType:SP_EVENT_TYPE_TOUCH];
     }
     return self;
 }
 
+- (void) dealloc {
+    [self.game removeEventListener:@selector(onTouch:) atObject:self forType:SP_EVENT_TYPE_TOUCH];
+    [super dealloc];
+}
+
+- (void) tick:(float)elapsed {
+    [self.coolAtk tick:elapsed];
+    [super tick:elapsed];
+}
+
 - (void) onTouch:(SPTouchEvent*)event {
     SPTouch* touch = [[event touchesWithTarget:self.shape.parent] anyObject];
-    if (touch) {
-        SPPoint* touchPosition = [touch locationInSpace:self.shape.parent];
+    if (touch && [self.coolAtk activate]) {
+        // Touch's normal vector in local coords (rel unit origin)
+        SPPoint* normVec = [[[touch locationInSpace:self.shape.parent] subtractPoint:self.shape.position] normalize];
+        
+        // Bullet starting position
+        float offset = 7.5f + fmaxf(self.shape.width, self.shape.height) * 1.41421356237f; // bullet_radius + tank_diagonal_length
+        SPPoint* start = [[self.shape.position addPoint:[normVec scaleBy:offset]] scaleBy:1.0f/self.world.scale];
+        QQBullet* bullet = [[[QQBullet alloc] init:self x:start.x y:start.y] autorelease];
         
+        // Bullet impulse vector
+        SPPoint* imp = [normVec scaleBy:50];
+        [bullet applyLinearImpulseX:imp.x y:imp.y];
+        NSLog(@"[%@ pos:(%f,%f) impulse:(%f,%f)]", bullet, bullet.x,bullet.y, imp.x,imp.y);
     }
 }
 
@@ -34,7 +59,8 @@
         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());
+        [self setPositionX:x/px y:y/px];
+        // self.body->SetTransform(b2Vec2(x/px, y/px), self.body->GetAngle());
     }
 }
 
index af502d5..9e6716c 100644 (file)
@@ -1,4 +1,5 @@
 #import <UIKit/UIKit.h>
+#import "Tanks.h"
 
 
 int main(int argc, char *argv[])
diff --git a/src/physics/QQPhysics.h b/src/physics/QQPhysics.h
new file mode 100644 (file)
index 0000000..459f73a
--- /dev/null
@@ -0,0 +1,4 @@
+#include <Box2D/Box2D.h>
+#import "Sparrow.h"
+
+(CGPoint) CGPointFromB2Vec2(const b2Vec2 vec){ return CGPointMake(vec.x, vec.y); }
diff --git a/src/physics/QQPoint.h b/src/physics/QQPoint.h
new file mode 100644 (file)
index 0000000..78102cd
--- /dev/null
@@ -0,0 +1,6 @@
+#import "QQProtocol.h"
+
+@interface QQPoint
+
+
+@end
diff --git a/src/physics/QQPoint.mm b/src/physics/QQPoint.mm
new file mode 100644 (file)
index 0000000..8898774
--- /dev/null
@@ -0,0 +1,12 @@
+#import "QQPoint.h"
+
+
+@implementation QQPoint
+
+- (id) init {
+    if ((self = [super init])){
+        
+    }
+    return self;
+}
+@end
index ed3abd2..ed986f9 100644 (file)
         _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);
+        float pad = 5;
+        float w = screen.width  / _scale;
+        float h = screen.height / _scale;
+        b2Vec2 origin = b2Vec2(pad, pad);
+        b2Vec2 horz   = b2Vec2(w-2*pad, pad);
+        b2Vec2 vert   = b2Vec2(pad, h-2*pad);
         
         b2Vec2 positions[] = {
             b2Vec2(w*0.5f, h*0.0f),
diff --git a/src/qq/QQNotifier.h b/src/qq/QQNotifier.h
new file mode 100644 (file)
index 0000000..2986b99
--- /dev/null
@@ -0,0 +1,15 @@
+#import "qq/QQObservable.h"
+
+@protocol QQNotifier <QQObservable>
+
+- (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) postNotification:(NSNotification*)msg;
+- (void) postNotificationName:(NSString*)name object:(id)sender;
+- (void) postNotificationName:(NSString*)name object:(id)sender userInfo:(NSDictionary*)info;
+
+
+@end
diff --git a/src/qq/QQObject.h b/src/qq/QQObject.h
new file mode 100644 (file)
index 0000000..e1a151f
--- /dev/null
@@ -0,0 +1,10 @@
+
+
+@interface QQObject : NSObject {
+@private
+    
+}
+
+
+
+@end
diff --git a/src/qq/QQObject.mm b/src/qq/QQObject.mm
new file mode 100644 (file)
index 0000000..9bd7ea9
--- /dev/null
@@ -0,0 +1,12 @@
+#import "QQObject.h"
+
+
+@implementation QQObject
+
+- (id) init {
+    if ((self = [super init])){
+        
+    }
+    return self;
+}
+@end
diff --git a/src/qq/QQObservable.h b/src/qq/QQObservable.h
new file mode 100644 (file)
index 0000000..103ec6f
--- /dev/null
@@ -0,0 +1,16 @@
+
+
+@protocol QQObservable
+
+//- (void) observe:(NSString*)event notify:(id)observer selector:(SEL)selector;
+- (void) addObserver:(id)observer selector:(SEL)selector name:(NSString*)event;
+
+- (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;
+
+
+@end
index 076ee38..d38f677 100644 (file)
@@ -1,9 +1,23 @@
 #import "SPDisplayObject.h"
+#import "SPPoint.h"
 
+struct b2Vec2;
 
 @interface SPDisplayObject (QQSparrowExtensions)
 
+/** Coordinates of the object relative to the local coordinates of the parent. */
+@property (nonatomic, readwrite, assign) SPPoint* position;
+
+/** Sets coordinates of the object relative to the local coordinates of the parent. */
 - (id) setPositionX:(float)x y:(float)y;
 
 @end
 
+
+
+@interface SPPoint (QQSparrowExtensions)
+- (CGPoint)  toCGPoint;
++ (SPPoint*) pointFromCGPoint:(CGPoint)pt;
++ (SPPoint*) pointFromB2Vec2:(b2Vec2)vec;
+@end
+
index 8226e98..74ec9b1 100644 (file)
@@ -1,12 +1,37 @@
 #import "QQSparrowExtensions.h"
+#import <Box2D/Box2D.h>
 
 
 @implementation SPDisplayObject (QQSparrowExtensions)
 
+- (SPPoint*) position {
+    return [SPPoint pointWithX:mX y:mY];
+}
+- (void) setPosition:(SPPoint*)pt {
+    [self setPositionX:pt.x y:pt.y];
+}
+
 - (id) setPositionX:(float)x y:(float)y {
     self.x = x;
     self.y = y;
     return self;
 }
 
-@end
\ No newline at end of file
+@end
+
+
+
+@implementation SPPoint (QQSparrowExtensions)
+- (CGPoint) toCGPoint {
+    return CGPointMake(mX, mY);
+}
+
++ (SPPoint*) pointFromCGPoint:(CGPoint)pt {
+    return [SPPoint pointWithX:pt.x y:pt.y];
+}
+
++ (SPPoint*) pointFromB2Vec2:(b2Vec2)vec {
+    return [SPPoint pointWithX:vec.x y:vec.y];
+}
+
+@end
index bd5f63c..531b7af 100644 (file)
@@ -8,9 +8,6 @@
 
 /* Begin PBXBuildFile section */
                492D80CC138B231B0042D918 /* QQPhysical.h in Headers */ = {isa = PBXBuildFile; fileRef = 492D80CB138B231B0042D918 /* QQPhysical.h */; };
-               492D80D5138B233E0042D918 /* QQUnitBase.h in Headers */ = {isa = PBXBuildFile; fileRef = 492D80CF138B233E0042D918 /* QQUnitBase.h */; };
-               492D80D6138B233E0042D918 /* QQUnitBase.mm in Sources */ = {isa = PBXBuildFile; fileRef = 492D80D0138B233E0042D918 /* QQUnitBase.mm */; };
-               492D80DA138B4B110042D918 /* QQUnitDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 492D80D9138B4B110042D918 /* QQUnitDelegate.h */; };
                492D80E4138BA4910042D918 /* QQCooldown.h in Headers */ = {isa = PBXBuildFile; fileRef = 492D80DE138BA4910042D918 /* QQCooldown.h */; };
                492D80E5138BA4910042D918 /* QQCooldown.mm in Sources */ = {isa = PBXBuildFile; fileRef = 492D80DF138BA4910042D918 /* QQCooldown.mm */; };
                492D80EB138BA4B40042D918 /* QQTank.h in Headers */ = {isa = PBXBuildFile; fileRef = 492D80E9138BA4B40042D918 /* QQTank.h */; };
                492D80F0138BA4BC0042D918 /* QQBullet.h in Headers */ = {isa = PBXBuildFile; fileRef = 492D80EE138BA4BC0042D918 /* QQBullet.h */; };
                492D80F1138BA4BC0042D918 /* QQBullet.mm in Sources */ = {isa = PBXBuildFile; fileRef = 492D80EF138BA4BC0042D918 /* QQBullet.mm */; };
                492D80F3138BA4CE0042D918 /* QQThing.h in Headers */ = {isa = PBXBuildFile; fileRef = 492D80F2138BA4CE0042D918 /* QQThing.h */; };
+               492D80FD138BCA840042D918 /* QQUnit.mm in Sources */ = {isa = PBXBuildFile; fileRef = 492D80FC138BCA840042D918 /* QQUnit.mm */; };
+               492D80FF138BCC9F0042D918 /* QQActorDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 492D80FE138BCC9F0042D918 /* QQActorDelegate.h */; };
+               492D810C138C07540042D918 /* BEParallaxSprite.h in Headers */ = {isa = PBXBuildFile; fileRef = 492D8101138C07540042D918 /* BEParallaxSprite.h */; };
+               492D810D138C07540042D918 /* BEParallaxSprite.m in Sources */ = {isa = PBXBuildFile; fileRef = 492D8102138C07540042D918 /* BEParallaxSprite.m */; };
+               492D810E138C07540042D918 /* SHCircle.h in Headers */ = {isa = PBXBuildFile; fileRef = 492D8103138C07540042D918 /* SHCircle.h */; };
+               492D810F138C07540042D918 /* SHCircle.m in Sources */ = {isa = PBXBuildFile; fileRef = 492D8104138C07540042D918 /* SHCircle.m */; };
+               492D8110138C07540042D918 /* SHLine.h in Headers */ = {isa = PBXBuildFile; fileRef = 492D8105138C07540042D918 /* SHLine.h */; };
+               492D8111138C07540042D918 /* SHLine.m in Sources */ = {isa = PBXBuildFile; fileRef = 492D8106138C07540042D918 /* SHLine.m */; };
+               492D8112138C07540042D918 /* SHPolygon.h in Headers */ = {isa = PBXBuildFile; fileRef = 492D8107138C07540042D918 /* SHPolygon.h */; };
+               492D8113138C07540042D918 /* SHPolygon.m in Sources */ = {isa = PBXBuildFile; fileRef = 492D8108138C07540042D918 /* SHPolygon.m */; };
+               492D8114138C07540042D918 /* SparrowExtras.h in Headers */ = {isa = PBXBuildFile; fileRef = 492D8109138C07540042D918 /* SparrowExtras.h */; };
+               492D8115138C07540042D918 /* SXFPSMeter.h in Headers */ = {isa = PBXBuildFile; fileRef = 492D810A138C07540042D918 /* SXFPSMeter.h */; };
+               492D8116138C07540042D918 /* SXFPSMeter.m in Sources */ = {isa = PBXBuildFile; fileRef = 492D810B138C07540042D918 /* SXFPSMeter.m */; };
                494DE9971376927C00FDB3D7 /* libBox2D.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 494DE9961376927C00FDB3D7 /* libBox2D.a */; };
                4995ABB213816CCE00334646 /* QQGame.h in Headers */ = {isa = PBXBuildFile; fileRef = 49E834A513812427007A6598 /* QQGame.h */; };
                4995ABB313816CD400334646 /* QQUnit.h in Headers */ = {isa = PBXBuildFile; fileRef = 49E834A213812427007A6598 /* QQUnit.h */; };
 
 /* Begin PBXFileReference section */
                492D80CB138B231B0042D918 /* QQPhysical.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = QQPhysical.h; path = src/game/QQPhysical.h; sourceTree = SOURCE_ROOT; };
-               492D80CF138B233E0042D918 /* QQUnitBase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = QQUnitBase.h; sourceTree = "<group>"; };
-               492D80D0138B233E0042D918 /* QQUnitBase.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = QQUnitBase.mm; sourceTree = "<group>"; };
-               492D80D9138B4B110042D918 /* QQUnitDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = QQUnitDelegate.h; sourceTree = "<group>"; };
                492D80DE138BA4910042D918 /* QQCooldown.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = QQCooldown.h; sourceTree = "<group>"; };
                492D80DF138BA4910042D918 /* QQCooldown.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = QQCooldown.mm; sourceTree = "<group>"; };
                492D80E9138BA4B40042D918 /* QQTank.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = QQTank.h; sourceTree = "<group>"; };
                492D80EE138BA4BC0042D918 /* QQBullet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = QQBullet.h; sourceTree = "<group>"; };
                492D80EF138BA4BC0042D918 /* QQBullet.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = QQBullet.mm; sourceTree = "<group>"; };
                492D80F2138BA4CE0042D918 /* QQThing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = QQThing.h; sourceTree = "<group>"; };
+               492D80FC138BCA840042D918 /* QQUnit.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = QQUnit.mm; sourceTree = "<group>"; };
+               492D80FE138BCC9F0042D918 /* QQActorDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = QQActorDelegate.h; sourceTree = "<group>"; };
+               492D8101138C07540042D918 /* BEParallaxSprite.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BEParallaxSprite.h; sourceTree = "<group>"; };
+               492D8102138C07540042D918 /* BEParallaxSprite.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BEParallaxSprite.m; sourceTree = "<group>"; };
+               492D8103138C07540042D918 /* SHCircle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SHCircle.h; sourceTree = "<group>"; };
+               492D8104138C07540042D918 /* SHCircle.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SHCircle.m; sourceTree = "<group>"; };
+               492D8105138C07540042D918 /* SHLine.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SHLine.h; sourceTree = "<group>"; };
+               492D8106138C07540042D918 /* SHLine.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SHLine.m; sourceTree = "<group>"; };
+               492D8107138C07540042D918 /* SHPolygon.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SHPolygon.h; sourceTree = "<group>"; };
+               492D8108138C07540042D918 /* SHPolygon.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SHPolygon.m; sourceTree = "<group>"; };
+               492D8109138C07540042D918 /* SparrowExtras.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SparrowExtras.h; path = ../Extras/SparrowExtras.h; sourceTree = "<group>"; };
+               492D810A138C07540042D918 /* SXFPSMeter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SXFPSMeter.h; sourceTree = "<group>"; };
+               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; };
                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>"; };
                        path = actor/bullet;
                        sourceTree = "<group>";
                };
+               492D8100138C07540042D918 /* Extras */ = {
+                       isa = PBXGroup;
+                       children = (
+                               492D8101138C07540042D918 /* BEParallaxSprite.h */,
+                               492D8102138C07540042D918 /* BEParallaxSprite.m */,
+                               492D8103138C07540042D918 /* SHCircle.h */,
+                               492D8104138C07540042D918 /* SHCircle.m */,
+                               492D8105138C07540042D918 /* SHLine.h */,
+                               492D8106138C07540042D918 /* SHLine.m */,
+                               492D8107138C07540042D918 /* SHPolygon.h */,
+                               492D8108138C07540042D918 /* SHPolygon.m */,
+                               492D810A138C07540042D918 /* SXFPSMeter.h */,
+                               492D810B138C07540042D918 /* SXFPSMeter.m */,
+                       );
+                       name = Extras;
+                       path = ../Extras;
+                       sourceTree = "<group>";
+               };
                4995ABC61381C46B00334646 /* assets */ = {
                        isa = PBXGroup;
                        children = (
                49E8349F13812427007A6598 /* actor */ = {
                        isa = PBXGroup;
                        children = (
-                               492D80D9138B4B110042D918 /* QQUnitDelegate.h */,
-                               492D80CF138B233E0042D918 /* QQUnitBase.h */,
-                               492D80D0138B233E0042D918 /* QQUnitBase.mm */,
+                               492D80FE138BCC9F0042D918 /* QQActorDelegate.h */,
                                49E834A013812427007A6598 /* QQActor.h */,
                                49E834A113812427007A6598 /* QQActor.mm */,
                                49E834A213812427007A6598 /* QQUnit.h */,
+                               492D80FC138BCA840042D918 /* QQUnit.mm */,
                        );
                        path = actor;
                        sourceTree = "<group>";
                                49F2DA1A13764ED5000B6B8C /* Textures */,
                                49F2DA1B13764ED5000B6B8C /* Animation */,
                                49F2DA1C13764ED5000B6B8C /* Utils */,
+                               492D8100138C07540042D918 /* Extras */,
                                49F2DA1D13764ED5000B6B8C /* Sparrow.h */,
+                               492D8109138C07540042D918 /* SparrowExtras.h */,
                        );
                        name = Sparrow;
                        path = libs/sparrow/src/Classes;
                                49E834CD13814F7D007A6598 /* QQGLESDebugDraw.h in Headers */,
                                49E834D3138166A6007A6598 /* QQSparrowExtensions.h in Headers */,
                                492D80CC138B231B0042D918 /* QQPhysical.h in Headers */,
-                               492D80D5138B233E0042D918 /* QQUnitBase.h in Headers */,
-                               492D80DA138B4B110042D918 /* QQUnitDelegate.h in Headers */,
                                492D80E4138BA4910042D918 /* QQCooldown.h in Headers */,
                                492D80EB138BA4B40042D918 /* QQTank.h in Headers */,
                                492D80F0138BA4BC0042D918 /* QQBullet.h in Headers */,
                                492D80F3138BA4CE0042D918 /* QQThing.h in Headers */,
+                               492D80FF138BCC9F0042D918 /* QQActorDelegate.h in Headers */,
+                               492D810C138C07540042D918 /* BEParallaxSprite.h in Headers */,
+                               492D810E138C07540042D918 /* SHCircle.h in Headers */,
+                               492D8110138C07540042D918 /* SHLine.h in Headers */,
+                               492D8112138C07540042D918 /* SHPolygon.h in Headers */,
+                               492D8114138C07540042D918 /* SparrowExtras.h in Headers */,
+                               492D8115138C07540042D918 /* SXFPSMeter.h in Headers */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                                49E834C713812555007A6598 /* QQViewport.mm in Sources */,
                                49E834CE13814F7D007A6598 /* QQGLESDebugDraw.mm in Sources */,
                                49E834D4138166A6007A6598 /* QQSparrowExtensions.mm in Sources */,
-                               492D80D6138B233E0042D918 /* QQUnitBase.mm in Sources */,
                                492D80E5138BA4910042D918 /* QQCooldown.mm in Sources */,
                                492D80EC138BA4B40042D918 /* QQTank.mm in Sources */,
                                492D80F1138BA4BC0042D918 /* QQBullet.mm in Sources */,
+                               492D80FD138BCA840042D918 /* QQUnit.mm in Sources */,
+                               492D810D138C07540042D918 /* BEParallaxSprite.m in Sources */,
+                               492D810F138C07540042D918 /* SHCircle.m in Sources */,
+                               492D8111138C07540042D918 /* SHLine.m in Sources */,
+                               492D8113138C07540042D918 /* SHPolygon.m in Sources */,
+                               492D8116138C07540042D918 /* SXFPSMeter.m in Sources */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };