sprite not rotating correctly (cocos2d) - iphone

I have a turret on top of a tank base, both inside the same sprite sheet with frames correctly specified. When i build and run the project, I want the turret to rotate along the anchor point of the center of the tank. The turret is a child of the tank so that they will stay together when moving. However, when I try to rotate it, the turret seems to set its anchor point somewhere far off into the distance, like this:
This is my init:
- (id)initWithLayer:(HelloWorldLayer *)layer type:(int)type hp:(int)hp {
NSString *spriteFrameName = [NSString stringWithFormat:#"tank%d_base.png", type];
if ((self = [super initWithSpriteFrameName:spriteFrameName])) {
_layer = layer;
_type = type;
self.hp = hp;
_turret = [CCSprite spriteWithSpriteFrameName:[NSString stringWithFormat:#"tank%d_turret.png", type]];
_turret.anchorPoint = ccp(0.5, 0.25);
[self scheduleUpdateWithPriority:-1];
[self addChild:_turret];
_turret.position = ccp(40, 50);
}
return self;
}
and I rotate the sprite here (I use a 360 degree shooting range)
- (void)shootToward:(CGPoint)targetPosition {
//CGPoint offset = ccpSub(targetPosition, self.position);
float x = abs(targetPosition.x) - self.position.x;
float y = abs(targetPosition.y) - self.position.y;
float angle;
float angleDegrees;
if (x < 0 && y > 0) {
angle = atanf(y/x);
angleDegrees = -CC_RADIANS_TO_DEGREES(angle) - 90;
}
else if (x < 0 && y < 0) {
angle = atanf(y/x);
angleDegrees = -CC_RADIANS_TO_DEGREES(angle) - 90;
}
else if (x > 0 && y < 0) {
angle = atanf(y/x);
angleDegrees = 90 - CC_RADIANS_TO_DEGREES(angle);
}
else if(x > 0 && y > 0) {
angle = atanf(y/x);
angleDegrees = 90 - CC_RADIANS_TO_DEGREES(angle);
}
switch (_layer.rotateInt) {
case 0:
angleDegrees = angleDegrees;
break;
case 1:
angleDegrees = angleDegrees+ 180;
break;
case 2:
angleDegrees = angleDegrees;
break;
case 3:
angleDegrees = angleDegrees + 90;
break;
case 4:
angleDegrees = angleDegrees - 90;
break;
default:
break;
}
NSLog(#"%f",angleDegrees);
_turret.rotation = angleDegrees;

Hmm. The only thing that I think could be causing this is that the turret asset is not centered or has a lot of blank space. That said, I'd try playing with the anchor point of the turret.

Since the turret is a child of the tank, this:
_turret.position = ccp(40, 50);
offsets the turret by 40x50 away from the tank's origin (lower left corner of bounding box). Try settings the position so the turret is centered on the tank:
_turret.position = ccp(tank.contentSize.width * tank.anchorPoint.x,
tank.contentSize.height * tank.anchorPoint.y);

Related

Collision detection (tutorial: How to make a platform game like Super Mario Brothers)

guys!
I am following this tutorial How to make a platform game like Super Mario Brothers to create a simple platform game. In the tutorial, the character sprite's size is a about a size of a single tile, and collision detection is calculated for 8 surrounding tiles. I modified the sprite's size to be equal about 4 tiles (2x2) and calculate collisions with 12 surrounding tiles. It works fine for the bottom and right sprite's edges, but the left and upper edges a little bit overlap with an obstacle before collision occurs, that is obviously wrong.
I feel that I have some mistakes in there, but as I am quite new in Spritekit I cannot spot them. I would really appreciate if someone could help me with it. Thanks in advance.
Here is the update function for the character:
- (void)update:(NSTimeInterval)delta
{
CGPoint gravity = CGPointMake(0.0, -450.0);
CGPoint gravityStep = CGPointMultiplyScalar(gravity, delta);
CGPoint forwardMove = CGPointMake(800.0, 0.0);
CGPoint forwardMoveStep = CGPointMultiplyScalar(forwardMove, delta);
CGPoint backwardMove = CGPointMake(-800.0, 0.0);
CGPoint backwardMoveStep = CGPointMultiplyScalar(backwardMove, delta);
if (self.forwardMarch)
{
self.velocity = CGPointAdd(self.velocity, forwardMoveStep);
}
if (self.backwardMarch)
{
self.velocity = CGPointAdd(self.velocity, backwardMoveStep);
}
self.velocity = CGPointAdd(self.velocity, gravityStep);
self.velocity = CGPointMake(self.velocity.x * 0.9, self.velocity.y);
// setup minimum and maximum limits for the motion speed
CGPoint minMovement = CGPointMake(0.0, -450);
CGPoint maxMovement = CGPointMake(0.0, 0.0);
// forward motion
if (self.velocity.x >= 0)
{
minMovement = CGPointMake(0.0, -450);
maxMovement = CGPointMake(120.0, 250.0);
}
// backward motion
if (self.velocity.x < 0)
{
minMovement = CGPointMake(-120.0, -450);
maxMovement = CGPointMake(0.0, 250.0);
}
self.velocity = CGPointMake(Clamp(self.velocity.x, minMovement.x, maxMovement.x), Clamp(self.velocity.y, minMovement.y, maxMovement.y));
CGPoint velocityStep = CGPointMultiplyScalar(self.velocity, delta);
self.newPosition = CGPointAdd(self.position, velocityStep);
}
This function find the bounding box of the sprite
- (CGRect)collisionBoundingBox
{
CGPoint diff = CGPointSubtract(self.newPosition, self.position);
return CGRectOffset(self.frame, diff.x, diff.y);
}
And the function where I handle collisions
- (void)handleObstacleCollisionsForPlayer:(Player *)player forLayer:(TMXLayer *)layer
{
NSInteger indices[12] = {13, 14, 1, 2, 4, 8, 7, 11, 0, 3, 12, 15};
player.onGround = NO;
for (NSUInteger i = 0; i < 12; i++)
{
NSInteger tileIndex = indices[i];
CGRect playerRect = [player collisionBoundingBox];
CGPoint playerCoord = [layer coordForPoint:player.newPosition];
NSInteger tileColumn = tileIndex % 4;
NSInteger tileRow = tileIndex / 4;
CGPoint tileCoord = CGPointMake(playerCoord.x + (tileColumn - 1), playerCoord.y + (tileRow - 1));
NSInteger gid = [self tileGIDAtTileCoord:tileCoord forLayer:layer];
if (gid)
{
CGRect tileRect = [self tileRectFromTileCoords:tileCoord];
if (CGRectIntersectsRect(playerRect, tileRect))
{
CGRect intersection = CGRectIntersection(playerRect, tileRect);
if (tileIndex == 13 || tileIndex == 14)
{
//tile is below
player.newPosition = CGPointMake(player.newPosition.x, player.newPosition.y + intersection.size.height);
player.velocity = CGPointMake(player.velocity.x, 0.0);
player.onGround = YES;
}
else if (tileIndex == 1 || tileIndex == 2)
{
//tile is directly above
player.newPosition = CGPointMake(player.newPosition.x, player.newPosition.y - intersection.size.height);
}
else if (tileIndex == 4 || tileIndex == 8)
{
//tile is left
player.newPosition = CGPointMake(player.newPosition.x + intersection.size.width, player.newPosition.y);
}
else if (tileIndex == 7 || tileIndex == 11)
{
//tile is right
player.newPosition = CGPointMake(player.newPosition.x - intersection.size.width, player.newPosition.y);
}
}
}
}
player.position = player.newPosition;
}
inside your GameViewController.m enable debug physics mode
that will draw a thick outline around every box and give u exact information about collision tiles may i think either your hero or tile sizes are differ than the tutorial you following
skView.showsPhysics= YES;

Rotate sprite image according to user touch location?

I m start learning game development. As a beginner i create one demo game in which one cannon hit bullets to the enemies (coming toward cannon from different direction).
Now i stuck on cannon sprite image rotation anywhere user touch on the screen or enemies. How i do that, My initial code as following,
void HelloWorld:: ccTouchesBegan(CCSet *touches, CCEvent * event)
{
CCSize winSize = CCDirector::sharedDirector()->getWinSize();
CCTouch* touch = (CCTouch*)( touches->anyObject() );
CCPoint location = touch->locationInView(touch->view());
location = CCDirector::sharedDirector()->convertToGL(location);
//Rotate cannon direction toward touch point
CCPoint diffPoint = ccpSub(_cannonImage->getPosition(), location);
float angleRadians = atanf((float)diffPoint.y/(float)diffPoint.x);
float angleOffset = CC_DEGREES_TO_RADIANS(180);
if(diffPoint.x < 0){
angleRadians += angleOffset;
}else{
angleRadians -= angleOffset;
}
CCLog("angle to be rotate = %f", angleRadians);
_cannonImage->runAction(CCRotateBy::actionWithDuration(0.1, angleRadians));
}
The code is written in cocos2d-x . I also accepting answer by someone who written in plain cocos2d.
Thanks
iHungry
The perfect answer as follows,
float HelloWorld::changingAngleAccordingToCoordinateSystem(CCPoint imageCenter, CCPoint touchLocation, float calculatedAngle){
//Consideration :- all Angles is in Degree
if((calculatedAngle >= 0 && calculatedAngle <= 90) || (calculatedAngle >= 90 && calculatedAngle <= 180)){
//Ist quardant
calculatedAngle = calculatedAngle;
}
else if(calculatedAngle < 0 && calculatedAngle >= -90){
//IInd quardant
calculatedAngle = 270 + (90 + calculatedAngle);
}
else if(calculatedAngle < -90 && calculatedAngle >= -180){
calculatedAngle = 180 + (180 + calculatedAngle);
}
return calculatedAngle;
}
//Rotate cannon direction toward touch point
float diff_X = touchLocation.x - myImage->getPosition().x;
float diff_Y = touchLocation.y - myImage->getPosition().y;
CCPoint diffPoint = CCPoint(diff_X, diff_Y);
float angleRadians = atan2f(diffPoint.y,diffPoint.x);
angleRadians = CC_RADIANS_TO_DEGREES(angleRadians);
angleRadians = HelloWorld::changingAngleAccordingToCoordinateSystem(myImage->getPosition(), touchLocation, angleRadians);
myImage->setRotation(-angleRadians);
i used this code to rotate my sprite. i Was moving the sprite according to my accelerometer reading.
float angleRadians =-accelX;
float angleDegrees = CC_RADIANS_TO_DEGREES(angleRadians);
objGlider->sprite_Glider.rotation = cocosAngle;
Check it. The reason of slow may be that you may be using CClog or NSLog in the code.
Here goes the complete code.
if(!boolPlayerDied)
{
static float prevX=0, prevY=0;
#define kFilterFactor 1.0f// don't use filter. the code is here just as an example
float accelX = (float) acceleration.x * kFilterFactor + (1- kFilterFactor)*prevX;
float accelY = (float) acceleration.y * kFilterFactor + (1- kFilterFactor)*prevY;
prevX = accelX;
prevY = accelY;
accelX = accelX-0.5;// Angle check fot tgfor the player to play
float angleRadians =-accelX;
float angleDegrees = CC_RADIANS_TO_DEGREES(angleRadians);
if(accelX>0)
{
cocosAngle = 1.1 * angleDegrees;
}
else
{
if(accelX<-0.5)
accelX=-0.5;
cocosAngle = 1.1 * angleDegrees;
}
objGlider->sprite_Glider.rotation = cocosAngle;
}
objGlider is the object of the class which creates glider sprite and sprite_Glider is the sprite used in glider class.
you can use rotation property with your sprite to be rotated. In cocos2Dx it might be setRotation.
First of all, replace
float angleRadians = atanf((float)diffPoint.y/(float)diffPoint.x);
float angleOffset = CC_DEGREES_TO_RADIANS(180);
if(diffPoint.x < 0){
angleRadians += angleOffset;
}else{
angleRadians -= angleOffset;
}
by
float angleRadians = atan2f(diffPoint.y, diffPoint.x);
Then it would be better to set rotation immediately (without actions) to process multiple frequent touches.
_cannonImage->setRotation(angleRadians);
Maybe you will need to adjust rotation like setRotation(-angleRadians) or setRotation(angleRadians+90) - it depends on your coordinate system.

Accelerometer in Box2D

I am new in Box2D....
I have ball image in CCSprite. I want to move ball in whole screen using accelerometer...
tell me
How to use accelerometer in box2d??
Thanks...in advance
The standard cocos2d-box2d template file moves boxes using the accelerometer by applying gravity relative to the accelerometer value.
- (void)accelerometer:(UIAccelerometer*)accelerometer didAccelerate:(UIAcceleration*)acceleration
{
static float prevX=0, prevY=0;
//#define kFilterFactor 0.05f
#define kFilterFactor 1.0f // don't use filter. the code is here just as an example
float accelX = (float) acceleration.x * kFilterFactor + (1- kFilterFactor)*prevX;
float accelY = (float) acceleration.y * kFilterFactor + (1- kFilterFactor)*prevY;
prevX = accelX;
prevY = accelY;
// accelerometer values are in "Portrait" mode. Change them to Landscape left
// multiply the gravity by 10
b2Vec2 gravity( -accelY * 10, accelX * 10);
world->SetGravity( gravity );
}
You need to be more specific on what you want the ball to do dependent on how you move the phone. Your question is difficult to answer in its current form.
Get the accelerometer measurements and say Force = coefficient*measurements. The apply this force to your b2Body
Let your Ball Sprite having tag is 1.
Replace this code with your Accelerometer delegate,
I test it on device, its working.
and your ball will move with accelerometer.
-(void)accelerometer:(UIAccelerometer*)accelerometer didAccelerate:(UIAcceleration*)acceleration
{
#define kFilterFactor 0.75
accelerometer.updateInterval = 1.0f/60.0f;
static UIAccelerationValue rollingX = 0, rollingY = 0;
for (b2Body *b = world->GetBodyList(); b; b = b->GetNext())
{
if (b->GetUserData() != NULL)
{
CCSprite *sprite = (CCSprite*)b->GetUserData();
if (sprite.tag == 1) {
rollingX = (acceleration.x * kFilterFactor) + (rollingX * 0.25);
rollingY = (acceleration.y * kFilterFactor) + (rollingY * 0.25);
float accelX = rollingX;
float accelY = rollingY;
CGPoint moveNewPosition = sprite.position;
if (accelX > 0.1) {
moveNewPosition.y += 2;
} if (accelX < 0.1) {
moveNewPosition.y -= 2;
}
if (accelY > 0.1) {
moveNewPosition.x -= 2;
} if (accelY < -0.1) {
moveNewPosition.x += 2;
}
b->SetLinearVelocity(b2Vec2(2,2));
sprite.position = ccp(moveNewPosition.x , moveNewPosition.y );
sprite.rotation = -1 * CC_RADIANS_TO_DEGREES(b->GetAngle());
}
}
}
}
I hope it'll work.

Problem to detect diamond shape in box2d

I make a simple game.
It has many diamond image and one ball. when ball touch with diamond shape then collision occur. in my application when ball touch on the edge then collision working correctly but when ball touch on the corner then collision is not working.
Code is hear..
- (id)init {
if ((self=[super init])) {
CGSize winSize = [CCDirector sharedDirector].winSize;
self.isTouchEnabled = YES;
self.isAccelerometerEnabled=YES;
// Create a world
b2Vec2 gravity = b2Vec2(0.0f, 0.0f);
bool doSleep = true;
_world = new b2World(gravity, doSleep);
// Create edges around the entire screen
b2BodyDef groundBodyDef;
groundBodyDef.position.Set(0,0);
_groundBody = _world->CreateBody(&groundBodyDef);
b2PolygonShape groundBox;
b2FixtureDef groundBoxDef;
groundBoxDef.shape = &groundBox;
groundBox.SetAsEdge(b2Vec2(0,0), b2Vec2(winSize.width/PTM_RATIO, 0));
_bottomFixture = _groundBody->CreateFixture(&groundBoxDef);
groundBox.SetAsEdge(b2Vec2(0,0), b2Vec2(0, winSize.height/PTM_RATIO));
_groundBody->CreateFixture(&groundBoxDef);
groundBox.SetAsEdge(b2Vec2(0, winSize.height/PTM_RATIO), b2Vec2(winSize.width/PTM_RATIO, winSize.height/PTM_RATIO));
_groundBody->CreateFixture(&groundBoxDef);
groundBox.SetAsEdge(b2Vec2(winSize.width/PTM_RATIO, winSize.height/PTM_RATIO), b2Vec2(winSize.width/PTM_RATIO, 0));
_groundBody->CreateFixture(&groundBoxDef);
// Create sprite and add it to the layer
CCSprite *ball = [CCSprite spriteWithFile:#"ball1.png" rect:CGRectMake(0, 0, 16,16)];
// ball.position = ccp(180, 400);
ball.tag = 1;
// Create ball body
b2BodyDef ballBodyDef;
ballBodyDef.type = b2_dynamicBody;
ballBodyDef.position.Set(180/PTM_RATIO, 450/PTM_RATIO);
ballBodyDef.userData = ball;
ballBody = _world->CreateBody(&ballBodyDef);
// Create circle shape
b2CircleShape circle;
circle.m_radius = 16/PTM_RATIO;
//circle.m_radius = 50/PTM_RATIO;
// Create shape definition and add to body
b2FixtureDef ballShapeDef;
ballShapeDef.shape = &circle;
ballShapeDef.density = 1.0f;
ballShapeDef.friction = 0.0f; // We don't want the ball to have friction!
ballShapeDef.restitution = 0.0f;
_ballFixture = ballBody->CreateFixture(&ballShapeDef);
// Give shape initial impulse...
b2Vec2 force = b2Vec2(0, 0);
ballBody->ApplyLinearImpulse(force, ballBodyDef.position);
for(int i = 0; i < 5; i++)
{
static int padding=25;
CCSprite *block = [CCSprite spriteWithFile:#"diamond2.png"];
int xOffset = padding+block.contentSize.width/5+((block.contentSize.width+padding)*i);
block.position = ccp(xOffset, 250);
block.tag = 2;
[self addChild:block];
// Create block body
b2BodyDef blockBodyDef;
// blockBodyDef.type = b2_dynamicBody;
blockBodyDef.position.Set(xOffset/PTM_RATIO, 400/PTM_RATIO);
blockBodyDef.userData = block;
b2Body *blockBody = _world->CreateBody(&blockBodyDef);
// Create block shape
b2PolygonShape blockShape;
blockShape.SetAsBox(block.contentSize.width/PTM_RATIO/8,
block.contentSize.height/PTM_RATIO/8
);
// Create shape definition and add to body
b2FixtureDef blockshapeDef;
blockshapeDef.shape = &blockShape;
blockshapeDef.density = 0.0;
blockshapeDef.friction = 10.0;
blockshapeDef.restitution = 0.1f;
blockBody->CreateFixture(&blockshapeDef);
}
[self addChild:ball];
// Create contact listener
_contactListener = new MyContactListener();
_world->SetContactListener(_contactListener);
[[SimpleAudioEngine sharedEngine] playBackgroundMusic:#"background-music-aac.caf"];
[self schedule:#selector(tick:)];
}
return self;
}
- (void)tick:(ccTime) dt {
// bool blockFound = false;
_world->Step(dt, 10, 10);
for(b2Body *b = _world->GetBodyList(); b; b=b->GetNext()) {
if (b->GetUserData() != NULL) {
CCSprite *sprite = (CCSprite *)b->GetUserData();
if (sprite.tag == 1) {
static int maxSpeed = 20;
b2Vec2 velocity = b->GetLinearVelocity();
float32 speed = velocity.Length();
// When the ball is greater than max speed, slow it down by
// applying linear damping. This is better for the simulation
// than raw adjustment of the velocity.
if (speed > maxSpeed) {
b->SetLinearDamping(0.2);
} else if (speed < maxSpeed) {
b->SetLinearDamping(0.0);
}
}
sprite.position = ccp(b->GetPosition().x * PTM_RATIO,
b->GetPosition().y * PTM_RATIO);
sprite.rotation = 1 * CC_RADIANS_TO_DEGREES(b->GetAngle());
}
}
}
Plz help me for detect correct collsion detection near corner of the diamond shape....
I'm not sure i understood but it seems you are not making a diamond, you are making a square/box:
blockShape.SetAsBox(block.contentSize.width/PTM_RATIO/8,block.contentSize.height/PTM_RATIO/8);
maybe that's why you're saying collision is not working.
If your body is a square and your sprite is a diamond there are three options:
Or diamond has equal width and height and it's only necessary to rotate the square. Should be no problem with collision this way.
Or other shapes collide on the (invisible)corner of square when is not supposed to be there (when sprite/diamond fits inside square and square has no rotation or diamond has width != height needing a customized shape)
Or other shapes are not colliding with diamond sprite corner (when square is smaller and fits inside diamond sprite).
There are other methods to create customized shapes like:
void b2PolygonShape::Set(const b2Vec2* vertices, int32 count)
void b2PolygonShape::SetAsEdge(const b2Vec2& v1, const b2Vec2& v2)

cocos2d fruit ninja parabola math

i am making a game similar to fruit ninja. birds flying down the water and then up (just like fruits up side down)
but some of the birds fly too far and the others too near.
can someone check my code? vy should quite close to each other.(vx is not a problem)
static float tuna = 10.0f;
-(void) reset
{
float vy = 0.0f;
float vx = 0.0f;
int sign = 1;
if (CCRANDOM_0_1() >= 0.5) {
sign = -1;
}
float hurry = 0.0f;
if (CCRANDOM_0_1() <= 0.1) {
hurry = 1.0f;
}
switch (birdType) {
case BirdType1:
vx = 1.0f * sign + (CCRANDOM_0_1() - 0.5f) * 0.08f;
vy = -6.5f;
break;
case BirdType2:
vx = 1.5f * sign + (CCRANDOM_0_1() - 0.5f) * 0.08f;
vy = -6.2f + (CCRANDOM_0_1() - 0.5f) * 0.1f;
break;
case BirdType3:
vx = 1.0f * sign + (CCRANDOM_0_1() - 0.5f) * 0.1f;
vy = -5.8f - hurry;
break;
default:
[NSException exceptionWithName:#"BirdMoveComponent exception" reason:#"unhandled bird type" userInfo:nil];
break;
}
velocity = CGPointMake(vx * 5, vy * 5);
if ((int)([[GameManager sharedManager] score] / 100) >= prevLevel) {
if (tuna <= 12.0f) {
tuna += 0.01f;
}
prevLevel = (int)[[GameManager sharedManager] score] / 100;
}
}
-(void) update:(ccTime) delta
{
if (self.parent.visible) {
NSAssert([self.parent isKindOfClass:[BirdEntity class]], #"node is not an entity");
BirdEntity* bird = (BirdEntity*) self.parent;
bird.position = ccpAdd(bird.position, ccpMult(velocity, delta * tuna));
velocity = ccpAdd(velocity, ccpMult(acceleration, delta * tuna));
acceleration = ccp(0, 0.3f);
float birdHeight = CGRectGetHeight([bird boundingBox]);
//20 is the bottom trap
if (bird.position.y <= (birdHeight / 2) + 20) {
[bird dieAccidently];
}
if (CGRectIntersectsRect([GameScene screenRect], [bird boundingBox]) == NO)
{
bird.visible = NO;
[bird stopAllActions];
[bird unscheduleAllSelectors];
[bird unscheduleUpdate];
[self reset];
}
}
}
thoght your question not programmatical but physical (mechanical).
position of object can be calculated from the system of equation:
x = Vx * t + x0
y = (-g*t*t)/2 + Vy * t + y0
, where g - Gravitational acceleration, V - initial speed, Vx and Vy - its projections on axes X and Y, respectively.
Question is what's the highest point, i.e. we need to found MAX(y(t)).
derivative: y'(t) = -g*t + Vy.
y'(t) should equals zero, -g*t + Vy = 0; t = Vy/g; MAX(y) = y(Vy/g) = Vy*Vy/2g.
MAX(y) = Vy*Vy/2g + y0 // ballistic trajectory
MIN(y) = y0 - Vy*Vy/2g // your case
End you should calculate velocity accroding to this, if you want your bird Y to be in certain range.
Addition:
btw is there a sample cocos2d code for
parabola?
Here is my working code.
- (void) update: (ccTime)dt
{
t += dt*20;
...
[self getVertices];
}
- (void) getVertices
{
//for every index: {
...
//getting initial position (x0, y0)
...
vertices[index] = ccpAdd(vertices[index], ccpMult(velocity[index/3], t * screenFactor)); //+velocity*t
vertices[index] = ccpAdd(vertices[index], ccpMult(gravity, (t*t/2) * screenFactor)); //+acceleration*t^2 /2
//}
}
1) As you can see, there's no need to calculate Velocity every time: use initial speed.
2) Vertices is CGPoint array of current Sprite positions.
3) t (current time), vertices, gravity, velocity are instance variables of common class.