float mY;
float mScaleX;
float mScaleY;
+ float mRegX;
+ float mRegY;
float mRotationZ;
float mAlpha;
BOOL mVisible;
/// @name Properties
/// ----------------
-/// The x coordinates of the object relative to the local coordinates of the parent.
+/// The x coordinate of the object relative to the local coordinates of the parent.
@property (nonatomic, assign) float x;
-/// The y coordinates of the object relative to the local coordinates of the parent.
+/// The y coordinate of the object relative to the local coordinates of the parent.
@property (nonatomic, assign) float y;
/// The horizontal scale factor. "1" means no scale, negative values flip the object.
/// The height of the object in points.
@property (nonatomic, assign) float height;
+/// The x coordinate of the object's rotational origin in local coordinates.
+@property (nonatomic, assign) float registrationX;
+
+/// The y coordinate of the object's rotational origin in local coordinates.
+@property (nonatomic, assign) float registrationY;
+
/// The rotation of the object in radians. (In Sparrow, all angles are measured in radians.)
@property (nonatomic, assign) float rotation;
@synthesize y = mY;
@synthesize scaleX = mScaleX;
@synthesize scaleY = mScaleY;
+@synthesize registrationX = mRegX;
+@synthesize registrationY = mRegY;
@synthesize rotation = mRotationZ;
@synthesize parent = mParent;
@synthesize alpha = mAlpha;
mAlpha = 1.0f;
mScaleX = 1.0f;
mScaleY = 1.0f;
+ mRegX = 0;
+ mRegY = 0;
+ mRotationZ = 0;
mVisible = YES;
mTouchable = YES;
}
{
SPMatrix *matrix = [[SPMatrix alloc] init];
- if (mScaleX != 1.0f || mScaleY != 1.0f) [matrix scaleXBy:mScaleX yBy:mScaleY];
- if (mRotationZ != 0.0f) [matrix rotateBy:mRotationZ];
if (mX != 0.0f || mY != 0.0f) [matrix translateXBy:mX yBy:mY];
+ if (mRotationZ != 0.0f) {
+ bool regMatters = (mRegX != 0.0f || mRegY != 0.0f);
+ if (regMatters) [matrix translateXBy:mRegX yBy:mRegY];
+ [matrix rotateBy:mRotationZ];
+ if (regMatters) [matrix translateXBy:-mRegX yBy:-mRegY];
+ }
+ if (mScaleX != 1.0f || mScaleY != 1.0f) [matrix scaleXBy:mScaleX yBy:mScaleY];
return [matrix autorelease];
}
- (id)init
{
- if (self = [super init])
+ if ((self = [super init]))
{
mBoundTextureID = UINT_MAX;
mPremultipliedAlpha = YES;
{
float x = object.x;
float y = object.y;
+ float regX = object.registrationX;
+ float regY = object.registrationY;
float rotation = object.rotation;
float scaleX = object.scaleX;
float scaleY = object.scaleY;
if (x != 0.0f || y != 0.0f) glTranslatef(x, y, 0);
- if (rotation != 0.0f) glRotatef(SP_R2D(rotation), 0.0f, 0.0f, 1.0f);
- if (scaleX != 0.0f || scaleY != 0.0f) glScalef(scaleX, scaleY, 1.0f);
+ if (rotation != 0.0f) {
+ bool regMatters = (regX != 0.0f || regY != 0.0f);
+ if (regMatters) glTranslatef(regX, regY, 0);
+ glRotatef(SP_R2D(rotation), 0.0f, 0.0f, 1.0f);
+ if (regMatters) glTranslatef(-regX, -regY, 0);
+ }
+ if (scaleX != 0.0f || scaleY != 0.0f) glScalef(scaleX, scaleY, 1.0f);
}
+ (void)setupOrthographicRenderingWithLeft:(float)left right:(float)right
@property (nonatomic, readwrite, assign, getter=isDirty) BOOL dirty; // TODO: implement this and fix various [shape setPosition] calls
@property (nonatomic, readwrite, retain) SPDisplayObject* shape;
+/** Intended center of the shape in points. */
+@property (nonatomic, readwrite, assign) float shapeCenterX;
+@property (nonatomic, readwrite, assign) float shapeCenterY;
+
/**
* Called to setup the Sparrow shape.
*/
#import "Sparrow.h"
#import "physics/QQWorld.h"
+#import "physics/debug/QQPhysicsDebugView.h"
#import "game/actor/QQActor.h"
+#import "game/map/QQLevel.h"
@interface QQGame : SPStage {
- long _ticks;
-
@private
QQWorld* _world;
+ QQLevel* _level;
+ SPSprite* _root;
+
NSMutableSet* _actors;
NSMutableSet* _awake;
NSMutableSet* _units;
NSMutableSet* _bullets;
+
+ QQPhysicsDebugView* _debugView;
+ BOOL _debugDrawingEnabled;
+
+ long _ticks;
+ BOOL _running;
}
-@property (nonatomic, assign, readonly) long ticks;
+@property (nonatomic, readonly) QQWorld* world;
+@property (nonatomic, readonly) QQLevel* level;
+@property (nonatomic, readonly) NSSet* actors;
+
+@property (nonatomic, readonly, assign) long ticks;
+@property (nonatomic, readonly, getter=isPaused) BOOL paused;
+@property (nonatomic, readwrite, assign) BOOL debugDrawingEnabled;
+
+- (void) start;
+- (void) stop;
-@property (nonatomic, retain) QQWorld* world;
-@property (nonatomic, retain) NSSet* actors;
- (void) tick:(float)elapsed;
@implementation QQGame
-@synthesize ticks = _ticks;
@synthesize world = _world;
+@synthesize level = _level;
@synthesize actors = _actors;
+@synthesize ticks = _ticks;
+@synthesize paused = _running;
+@synthesize debugDrawingEnabled = _debugDrawingEnabled;
+
- (id) init {
if (_CurrentGame) {
if ( (self = [super init]) ){
_CurrentGame = self;
_ticks = 0;
- _actors = [[NSMutableArray alloc] initWithCapacity:10];
+ _running = NO;
+
_world = [[QQWorld alloc] init];
- _world.debugDrawingEnabled = YES;
+ _root = [SPSprite sprite];
+ [self addChild:_root];
+ _level = [[QQLevel alloc] init];
+ [_root addChild:_level];
+ _debugView = [[QQPhysicsDebugView alloc] initWithWorld:_world];
+ [_root addChild:_debugView];
+ self.debugDrawingEnabled = YES;
+
+ _actors = [[NSMutableArray alloc] initWithCapacity:10];
- CGSize screen = [UIScreen mainScreen].bounds.size;
- float wMax = screen.width / _world.scale;
- float hMax = screen.height / _world.scale;
+ CGSize frame = [UIScreen mainScreen].applicationFrame.size;
+ float wMax = frame.width / _world.scale;
+ float hMax = frame.height / _world.scale;
[[[QQTank alloc] initAtX:wMax/6 y:hMax/6 width:50 height:50 color:0xFF0071] autorelease];
[[[QQUnit alloc] initAtX:wMax/3 y:hMax/2 width:50 height:50 color:0x4596FF] autorelease];
-
- [self addEventListener:@selector(onEnterFrame:) atObject:self forType:SP_EVENT_TYPE_ENTER_FRAME];
}
return self;
}
- (void) dealloc {
[self removeEventListener:@selector(onEnterFrame:) atObject:self forType:SP_EVENT_TYPE_ENTER_FRAME];
+ [self removeAllChildren];
+
[_actors removeAllObjects];
[_actors release];
+
+ [_level release];
+ [_debugView release];
[_world release];
+
_CurrentGame = NULL;
[super dealloc];
}
+
+/// properties
+
+- (void) setDebugDrawingEnabled:(BOOL)enable {
+ _debugDrawingEnabled = _debugView.visible = enable;
+}
+
+
+
+/// methods
+
+- (void) start {
+ if (!_running) {
+ _running = YES;
+ [self addEventListener:@selector(onEnterFrame:) atObject:self forType:SP_EVENT_TYPE_ENTER_FRAME];
+ }
+}
+
+- (void) stop {
+ if (_running) {
+ [self removeEventListener:@selector(onEnterFrame:) atObject:self forType:SP_EVENT_TYPE_ENTER_FRAME];
+ _running = NO;
+ }
+}
+
- (QQGame*) addActor:(QQActor*)actor {
[_actors addObject:actor];
return self;
- (void) onEnterFrame:(SPEnterFrameEvent*)event {
- [self tick:event.passedTime];
+ if (_running) [self tick:event.passedTime];
}
- (void) tick:(float)elapsed {
_ticks++;
+
+#ifdef DEBUG_LOG
if ((_ticks % 100) == 0) {
NSLog(@"[%ld] Time passed since last 100 frames: %f", _ticks, elapsed);
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);
+ NSLog(@"[%@ impulse:(%f,%f)]", actor, v.x,v.y);
}
}
+#endif
for (QQActor* actor in self.actors) [actor tick:elapsed]; // XXX: self.awakeAgents
[self.world step];
*/
@protocol QQPhysical
+/// The x coordinate in the simulation.
@property (nonatomic, readwrite, assign) float x;
+
+/// The y coordinate in the simulation.
@property (nonatomic, readwrite, assign) float y;
@property (nonatomic, readwrite) CGPoint position;
-
- (void) setPositionX:(float)x y:(float)y;
+@property (nonatomic, readwrite, assign) float rotation;
+
+/// The width of the object in simulation units.
+//@property (nonatomic, assign) float width;
+
+/// The height of the object in simulation units.
+//@property (nonatomic, assign) float height;
+
+
+
// FIXME: don't expose these, instead aggregate properties from body & fixtures/shapes
@property (nonatomic, readonly) b2Body* body;
- (id) initAtX:(float)x y:(float)y;
- (id) initType:(b2BodyType)type atX:(float)x y:(float)y;
+/// internal methods
+
+/** Updates the Shape using values from Box2D. */
+- (void) updateShape;
+
+/** Updates the Shape's position, pulling rotation from Box2D. */
+- (void) updateShapeX:(float)x y:(float)y;
+
+/** Updates the Shape's position and rotation. */
+- (void) updateShapeX:(float)x y:(float)y rotation:(float)r;
+
@end
@synthesize dirty = _dirty;
@synthesize delegate = _delegate;
+@dynamic shapeCenterX;
+@dynamic shapeCenterY;
+
+
- (QQGame*) game { return QQGame.current; }
- (QQWorld*) world { return self.game.world; }
- (b2Body*) body { return _body; }
- (b2Fixture*) fixture { return nil; }
+- (float) rotation { return self.body->GetAngle(); }
+- (void) setRotation:(float)r { self.body->SetTransform(self.body->GetPosition(), r); }
- (CGPoint) position {
b2Vec2 pos = self.body->GetPosition();
}
- (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 {
float px = self.world.scale;
- [self.shape setPositionX:x*px y:y*px];
+ [self.shape setCenterX:x*px y:y*px];
+}
+
+- (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;
+ shape.rotation = r;
+}
+
+- (void) updateShape {
+ b2Transform trans = self.body->GetTransform();
+ [self updateShapeX:trans.position.x y:trans.position.y rotation:trans.GetAngle()];
}
+
- (float) x { return self.body->GetPosition().x; }
- (void) setX:(float)x { [self setPositionX:x y:self.y]; }
}
- (void) draw {
- float px = self.world.scale;
- b2Vec2 pos = self.body->GetPosition();
- [self.shape setPositionX:pos.x*px y:pos.y*px];
+ [self updateShape];
}
- (void) setupPhysics {}
+- (NSString *)description {
+ b2Transform trans = self.body->GetTransform();
+ b2Vec2 pos = trans.position;
+ //b2Vec2 v = actor.body->GetLinearVelocity();
+ SPDisplayObject* s = self.shape;
+ return [NSString stringWithFormat:@"[%@ w=%f, h=%f, pos=(%f,%f), rotation=%f @ (%f,%f), scale=(%f,%f), shape=[%@ center=(%f,%f)]]",
+ [super description], s.width,s.height, pos.x,pos.y, trans.GetAngle(), s.registrationX,s.registrationY,
+ s.scaleX,s.scaleY, s, self.shapeCenterX,self.shapeCenterY];
+}
+
+
+
@end
@private
SPDisplayObject* _shape;
+ float _shapeCenterX;
+ float _shapeCenterY;
}
/// properties
#include <Box2D/Box2D.h>
#import "Sparrow.h"
-#import "QQUnit.h"
+#import "game/actor/QQUnit.h"
+#import "game/QQGame.h"
#import "render/QQSparrowExtensions.h"
@dynamic game;
@synthesize shape = _shape;
+@synthesize shapeCenterX = _shapeCenterX;
+@synthesize shapeCenterY = _shapeCenterY;
+
- (BOOL) canAttack { return NO; }
- (void) setShape:(SPDisplayObject*)newShape {
if (_shape != newShape) {
- [self.game removeChild:_shape];
+ [self.game.level removeChild:_shape];
[_shape release];
_shape = [newShape retain];
- float px = self.world.scale;
- [_shape setPositionX:self.x*px y:self.y*px];
- [self.game addChild:_shape];
+ _shapeCenterX = _shape.width * 0.5f;
+ _shapeCenterY = _shape.height * 0.5f;
+ [self updateShape];
+ [self.game.level addChild:_shape];
}
}
[self setShape:[[[SPImage alloc] autorelease] initWithContentsOfFile:filename]];
}
+- (void) act {
+ if ((self.game.ticks % 50) == 0) {
+ self.rotation += b2_pi / 8;
+ }
+}
+
+- (void) draw {
+ [super draw];
+ if ((self.game.ticks % 50) == 0) NSLog(@"%@", self);
+}
- (void) attack:(QQActor*)actor {
--- /dev/null
+#import "Sparrow.h"
+
+@interface QQLevel : SPDisplayObjectContainer {
+@private
+
+}
+
+
+
+@end
--- /dev/null
+#import "QQLevel.h"
+
+
+@implementation QQLevel
+
+- (id) init {
+ if ((self = [super init])){
+
+ }
+ return self;
+}
+
+- (void) render:(SPRenderSupport*)support {
+ // glTranslatef(100, 100, 0);
+ [super render:support];
+}
+
+@end
float _timestep;
int _velocityIterations;
int _positionIterations;
- BOOL _debugDrawingEnabled;
+
float _scale;
}
@property (nonatomic, assign) float timestep;
@property (nonatomic, assign) int velocityIterations;
@property (nonatomic, assign) int positionIterations;
-@property (nonatomic, assign) BOOL debugDrawingEnabled;
-@property (nonatomic, assign) float scale; // pixels per meter
+
+@property (nonatomic, assign) float scale; // pixels per meter // TODO: move this somewhere else!
-- (QQWorld*) init;
+- (id) init;
- (void) setGravityX:(float)x y:(float)y;
@synthesize timestep = _timestep;
@synthesize velocityIterations = _velocityIterations;
@synthesize positionIterations = _positionIterations;
-@synthesize debugDrawingEnabled = _debugDrawingEnabled;
@synthesize scale = _scale;
@synthesize world = _world;
-- (QQWorld*) init {
+- (id) init {
if ((self = [super init])) {
_timestep = 1.0f/60.0f;
_velocityIterations = 10;
_positionIterations = 10;
- _debugDrawingEnabled = NO;
_scale = 5.0f;
_world = new b2World(b2Vec2(0.0f, 0.0f), true); // b2World(gravity, doSleep)
- _debugDraw = new QQGLESDebugDraw();
+ _debugDraw = new QQGLESDebugDraw(0.75f);
+ _debugDraw->SetAllFlags();
_world->SetDebugDraw(_debugDraw);
_walls = [[NSMutableArray alloc] initWithCapacity:4];
- CGSize screen = [UIScreen mainScreen].bounds.size;
- 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);
+ CGSize screen = [UIScreen mainScreen].applicationFrame.size;
+ float padv = 5;
+ float w = screen.width / _scale - 2*padv;
+ float h = screen.height / _scale - 2*padv;
- 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};
+ 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;
b2Body* wall = _world->CreateBody(&bd);
b2PolygonShape shape;
- shape.SetAsEdge(origin, bounds[i]);
-
+ 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 removeAllObjects];
[_walls release];
+ delete _debugDraw;
delete _world; // b2World destructor destroys all physics entities and releases all heap memory.
[super dealloc];
}
}
- (void) drawDebug {
- if (_debugDrawingEnabled) _world->DrawDebugData();
+ _world->DrawDebugData();
}
- (void) setGravityX:(float)x y:(float)y {
public:
float32 globalAlpha;
- QQGLESDebugDraw();
+ QQGLESDebugDraw(float32 alpha=1);
+
+ void SetAllFlags();
void DrawPolygon(const b2Vec2* vertices, int32 vertexCount, const b2Color& color);
-
+
void DrawSolidPolygon(const b2Vec2* vertices, int32 vertexCount, const b2Color& color);
-
+
void DrawCircle(const b2Vec2& center, float32 radius, const b2Color& color);
-
+
void DrawSolidCircle(const b2Vec2& center, float32 radius, const b2Vec2& axis, const b2Color& color);
-
+
void DrawSegment(const b2Vec2& p1, const b2Vec2& p2, const b2Color& color);
-
+
void DrawTransform(const b2Transform& xf);
-
+
void DrawPoint(const b2Vec2& p, float32 size, const b2Color& color);
-
+
void DrawString(int x, int y, const char* string, ...);
-
+
void DrawAABB(b2AABB* aabb, const b2Color& color);
};
#include "QQGLESDebugDraw.h"
-
#include <cstdio>
#include <cstdarg>
-
#include <cstring>
-QQGLESDebugDraw::QQGLESDebugDraw() {
- globalAlpha = 1;
+
+
+QQGLESDebugDraw::QQGLESDebugDraw(float32 alpha) {
+ globalAlpha = alpha;
+ m_drawFlags = 0;
+}
+
+void QQGLESDebugDraw::SetAllFlags() {
+ // uint32 flags = 0;
+ // flags += b2DebugDraw::e_shapeBit;
+ // flags += b2DebugDraw::e_jointBit;
+ // flags += b2DebugDraw::e_aabbBit;
+ // flags += b2DebugDraw::e_pairBit;
+ // flags += b2DebugDraw::e_centerOfMassBit;
+
+ SetFlags((1 << 6) - 1); // should be all flag bits
}
void QQGLESDebugDraw::DrawPolygon(const b2Vec2* vertices, int32 vertexCount, const b2Color& color)
-#import <UIKit/UIKit.h>
-#import <OpenGLES/EAGL.h>
-#import <OpenGLES/ES1/gl.h>
-#import <OpenGLES/ES1/glext.h>
-
#include <Box2D/Box2D.h>
+#import "Sparrow.h"
+#import "SPRenderSupport.h"
#import "physics/QQWorld.h"
-/*
-This class wraps the CAEAGLLayer from CoreAnimation into a convenient UIView subclass.
-The view content is basically an EAGL surface you render your OpenGL scene into.
-Note