I have been trying to figure this out for a couple of days now but I haven't been able to figure out what my problem is.
So I am using cocos2d with chipmunk-spaceManager to create my game. So what I am doing is creating 4 cpShapes that are attached to one cpBody and attaching them to a CCSprite. Here is my code.
- (id) helmetInit:(Game*)game {
cpShape *helmet_1;
cpShape *helmet_2;
cpShape *helmet_3;
cpShape *reference = [game.spaceManager addCircleAt:cpvzero mass:STATIC_MASS radius:2];
helmet_1 = [game.spaceManager addCircleToBody:reference->body radius:20 offset:cpv(-5, 2)];
helmet_2 = [game.spaceManager addCircleToBody:reference->body radius:8 offset:cpv(16, -14)];
helmet_3 = [game.spaceManager addCircleToBody:reference->body radius:8 offset:cpv(8, -14)];
reference->group = 1;
// helmet_1->group = 1;
// helmet_2->group = 1;
// helmet_3->group = 1;
[self initWithFile:#"Helmet.png"];
[self setShape:reference];
//[self setBody:reference->body];
self.spaceManager = game.spaceManager;
self.autoFreeShapeAndBody = YES;
gameScreenSize = game.contentSize;
return self;
}
So my problem is the only time that I get any collisions is if one of my other shapes in my game collide with the (reference shape) which is the shape that is created with the body that all the shapes are sharing. If I understand the way chipmunk works, doesn't every shape have collisions, because the other shapes are like there not even there. Other shapes pass right through them until they collide with the reference shape which is the only shape that has any collisions right now. Am I doing something wrong or do I not understand the way chipmunk works?
Here is a screenshot of the 4 shapes that I have created.
Ok, with a lot of frustration I have finally figured out my problem. I was moving a static shape after the chipmunk space had begun simulating. So I fixed it by setting smgr.rehashStaticEveryStep = YES;
Related
I'v got trouble getting my sprite to appear on screen using my current method. I got multiple sprites on the screen already, but I'v added this new one in a different manner as I'm going to manipulate it later on (handling different interactions and such). But I don't see why it doesn't appear on the screen when I run the program. I'v tried changing the z order without luck, but that might be because the sprite wasn't added in the init method like the others so the z order doesn't affect it in comparison to the background and such created in the init method. Anyways, got any clue for why it won't draw on screen?
- (void) addMonster {
CCSprite * monster = [CCSprite spriteWithImageNamed:#"Ghost.png"];
// Determine where to spawn the monster along the Y axis
CGSize viewSize = [CCDirector sharedDirector].viewSize;
int minY = monster.contentSize.height / 2;
int maxY = viewSize.height - monster.contentSize.height/2;
int rangeY = maxY - minY;
int actualY = (arc4random() % rangeY) + minY;
// Create the monster slightly off-screen along the right edge,
// and along a random position along the Y axis as calculated above
monster.position = ccp(viewSize.width + monster.contentSize.width/2, actualY);
[self addChild:monster];
// Determine speed of the monster
int minDuration = 2.0;
int maxDuration = 4.0;
int rangeDuration = maxDuration - minDuration;
int actualDuration = (arc4random() % rangeDuration) + minDuration;
// Create the actions
CCActionMoveTo * actionMove = [CCActionMoveTo actionWithDuration:actualDuration
position:ccp(-monster.contentSize.width/2, actualY)];
CCActionCallBlock * actionMoveDone = [CCActionCallBlock actionWithBlock:^(CCNode *node) {
[monster removeFromParentAndCleanup:YES];
}];
[monster runAction:[CCActionSequence actions:actionMove, actionMoveDone, nil]];
}
//The call back function
-(void)gameLogic:(CCTime)dt {
[self addMonster];
}
- (id)init{
// Apple recommend assigning self with supers return value
self = [super init];
if (!self) return(nil);
if( (self = [super init]) ) {
self.userInteractionEnabled = YES;
//How often a new ghost gets produced
[self schedule:#selector(gameLogic:) interval:1.0];
//And some more code none relevant to this keeps going...
I am having the same problem.
The sprites i add do appear, but when i leave the scene and come back, they won't appear. As far as i know sprites needs to be added in the init method. When you add them at a later stage (in my case) they appear only once.
I am still looking for a solution to add the sprites at a later stage than the init method and appears more than once.
Maybe this answer helps you finding a solution.
ok so i've been messing around with chipmunk a bit, and i can get two sprites to bounce off of each other, but when i try to use the following method, it never fires,
-(BOOL)ccPhysicsCollisionBegin:(CCPhysicsCollisionPair *)pair tower:(CCNode *)nodeA BG:
(CCNode *)nodeB
{
NSLog(#"HELLO");
return YES;
}
Heres where I create the physics node:
_physics = [CCPhysicsNode node];
_physics.debugDraw = YES;
[self addChild:_physics z:1];
_physics.collisionDelegate = self;
I use this code to create the first sprite:
background = [CCSprite spriteWithImageNamed:gameLevelImage];
[background setPosition:ccp(winSize.width/2,winSize.height/2)];
background.physicsBody.collisionType = #"BG";
background.physicsBody = [CCPhysicsBody bodyWithCircleOfRadius:50 andCenter:self.position];
and this for the other :
tower = [[TowerType alloc] initWithTheGame:self location:ccp(winSize.width/2, winSize.height/2)];
[towers addObject:tower];
[self MenuItemsVisible];
tower.physicsBody = [CCPhysicsBody bodyWithCircleOfRadius:50 andCenter:tower.position];
tower.physicsBody.collisionType = #"tower";
I also have the protocol in the h file.
if anyone knows whats happening help would be greatly appreciated. (:
First of all, are both bodies under the same CCPhysicsNode?
Second, ccPhysicsCollisionBegin is just called when the collision BEGIN, so as both of your bodies are one over the other and they aparenttly will move together due to gravity the collision will never begin, because they started colliding. The cycle for collision evaluation is:
ccPhysicsCollisionBegin: called once both bodies start colliding
ccPhysicsCollisionPreSolve: called every frame update, before physics calculations
ccPhysicsCollisionPostSolve : called every frame, after physics calculations
ccPhysicsCollisionSeparates: called once they separate
Make sure your sprites are allocated properly before you try to set the collisionType. That was the issue for me in my similar case.
I think I'm just understanding scaling/positioning nodes/layers incorrectly. I'm setting up my node like this (node class is derived from CCNode):
-(id) init
{
if ((self = [super init]))
{
// Create parallax background node.
background = [BackgroundNode node];
[self addChild:background z:0];
// Create foreground node.
foreground = [ForegroundLayer node];
[self addChild:foreground z:0];
self.position.y = 500.0f;
self.scaleX = 1.5f;
self.scaleY = 1.5f;
}
return self;
}
It doesn't seem to matter what I set the self.position.y to - the scaled node is always displayed as though it was positioned in the bottom-left of the screen. I've tried playing around with anchorPoint as well, but it doesn't really seem to change anything.
The reason I'm asking is because I'd like to be able to pan vertically when I'm zoomed in - this doesn't seem to really be the right way to accomplish it, though. Any ideas?
self.position.y = 500.0f;
doesn't work. It should be
self.position = ccp(self.position.x, 500.0f);
Please refer to "Cocoa Objective-c Property C structure assign fails".
I have one question when infinite background scrolling is done, is the object remain fixed(like doodle in doodle jump, papy in papi jump) or these object really moves.Is only background move or both (background and object )move.plz someone help me.I am searching for this solution for 4/5 days,but can't get the solution.So plz someone help me. And if object does not move how to create such a illusion of object moving.
If you add the object to the same layer as the scrolling background, then it will scroll as the background scrolls.
If your looking for an effect like the hero in doodle jump, you may want to look at having two or more layers in a scene.
Layer 1: Scrolling Background Layer
Layer 2: Sprite layer
SomeScene.m
CCLayer *backgroundLayer = [[CCLayer alloc] init];
CCLayer *spriteLayer= [[CCLayer alloc] init];
[self addChild:backgroundLayer z:0];
[self addChild:spriteLayer z:1];
//Hero stays in one spot regardless of background scrolling.
CCSprite *squidHero = [[CCSprite alloc] initWithFile:#"squid.png"];
[spriteLayer addChild:squidHero];
If you want objects to scroll with the background add it to the background layer:
//Platform moves with background.
CCSprite *bouncePlatform= [[CCSprite alloc] initWithFile:#"bouncePlatform.png"];
[backgroundLayer addChild:bouncePlatform];
Another alternative is to use a CCFollow action. You would code as if the background is static (which it will be) and the player is moving (which it will be), but add a CCFollow action to the player. This essentially moves the camera so that it tracks your player.
You can also modify the classes so that you can get the CCFollow action to follow with an offset (i.e., so the player is not in the middle of the screen) as well as to have a smoothing effect to it, so that when the player moves, the follow action is not jerky. See the below code:
*NOTE I am using cocos2d-x, the c++ port. The methods are similar in cocos2d, and you should be able to modify these to fit the cocos2d syntax. Or search around -- I found these for cocos2d and then ported to c++.
//defines the action to constantly follow the player (in my case, "runner.p_sprite is the sprite pointing to the player)
FollowWithOffset* followAction = FollowWithOffset::create(runner.p_sprite, CCRectZero);
runAction(followAction);
And separately, I have copied the class definition for CCFollow to create my own class, CCFollowWithAction. This also has a smoothing effect (you can look this up more online) so that when the player moves, the actions are not jerky. I modified "initWithTarget," to take into account an offset, and "step," to add a smoothing action. You can see the modifications in the comments below.
bool FollowWithOffset::initWithTarget(CCNode *pFollowedNode, const CCRect& rect/* = CCRectZero*/)
{
CCAssert(pFollowedNode != NULL, "");
pFollowedNode->retain();
m_pobFollowedNode = pFollowedNode;
if (rect.equals(CCRectZero))
{
m_bBoundarySet = false;
}
else
{
m_bBoundarySet = true;
}
m_bBoundaryFullyCovered = false;
CCSize winSize = CCDirector::sharedDirector()->getWinSize();
m_obFullScreenSize = CCPointMake(winSize.width, winSize.height);
//m_obHalfScreenSize = ccpMult(m_obFullScreenSize, 0.5f);
m_obHalfScreenSize = CCPointMake(m_obFullScreenSize.x/2 + RUNNER_FOLLOW_OFFSET_X,
m_obFullScreenSize.y/2 + RUNNER_FOLLOW_OFFSET_Y);
if (m_bBoundarySet)
{
m_fLeftBoundary = -((rect.origin.x+rect.size.width) - m_obFullScreenSize.x);
m_fRightBoundary = -rect.origin.x ;
m_fTopBoundary = -rect.origin.y;
m_fBottomBoundary = -((rect.origin.y+rect.size.height) - m_obFullScreenSize.y);
if(m_fRightBoundary < m_fLeftBoundary)
{
// screen width is larger than world's boundary width
//set both in the middle of the world
m_fRightBoundary = m_fLeftBoundary = (m_fLeftBoundary + m_fRightBoundary) / 2;
}
if(m_fTopBoundary < m_fBottomBoundary)
{
// screen width is larger than world's boundary width
//set both in the middle of the world
m_fTopBoundary = m_fBottomBoundary = (m_fTopBoundary + m_fBottomBoundary) / 2;
}
if( (m_fTopBoundary == m_fBottomBoundary) && (m_fLeftBoundary == m_fRightBoundary) )
{
m_bBoundaryFullyCovered = true;
}
}
return true;
}
void FollowWithOffset::step(float dt)
{
CC_UNUSED_PARAM(dt);
if(m_bBoundarySet){
// whole map fits inside a single screen, no need to modify the position - unless map boundaries are increased
if(m_bBoundaryFullyCovered)
return;
CCPoint tempPos = ccpSub( m_obHalfScreenSize, m_pobFollowedNode->getPosition());
m_pTarget->setPosition(ccp(clampf(tempPos.x, m_fLeftBoundary, m_fRightBoundary),
clampf(tempPos.y, m_fBottomBoundary, m_fTopBoundary)));
}
else{
//custom written code to add in support for a smooth ccfollow action
CCPoint tempPos = ccpSub( m_obHalfScreenSize, m_pobFollowedNode->getPosition());
CCPoint moveVect = ccpMult(ccpSub(tempPos,m_pTarget->getPosition()),0.25); //0.25 is the smooth constant.
CCPoint newPos = ccpAdd(m_pTarget->getPosition(), moveVect);
m_pTarget->setPosition(newPos);
}
}
I just have a simple sprite - how can I get it to rotate?
A good answer would show how to rotate both a dynamic sprite and a static_mass sprite
If the sprite is dynamic / non-static, just do like so:
cpBodySetAngVel(ObjSmSprite.shape->body,0.25);
For a static body, you can do something like this:
[ObjSmStaticSprite.shape runAction:[CCRepeatForever actionWithAction:
[CCSequence actions:
[CCRotateTo actionWithDuration:2 angle:180],
[CCRotateTo actionWithDuration:2 angle:360],
nil]
]];
smgr.rehashStaticEveryStep = YES; //Setting this would make the smgr recalculate all static shapes positions every step
To Summarize, here is a spinning static sprite, following be spinning dynamic sprite.
// Add Backboard
cpShape *shapeRect = [smgr addRectAt:cpvWinCenter mass:STATIC_MASS width:200 height:10 rotation:0.0f ];// We're upgrading this
cpCCSprite * cccrsRect = [cpCCSprite spriteWithShape:shapeRect file:#"rect_200x10.png"];
[self addChild:cccrsRect];
// Make static object update moves in chipmunk
// Since Backboard is static, and since we're going to move it, it needs to know about spacemanager so its position gets updated inside chipmunk.
// Setting this would make the smgr recalculate all static shapes positions every step
// cccrsRect.integrationDt = smgr.constantDt;
// cccrsRect.spaceManager = smgr;
// Alternative method: smgr.rehashStaticEveryStep = YES;
smgr.rehashStaticEveryStep = YES;
// Spin the backboard
[cccrsRect runAction:[CCRepeatForever actionWithAction:
[CCSequence actions:
[CCRotateTo actionWithDuration:2 angle:180],
[CCRotateTo actionWithDuration:2 angle:360],
nil]
]];
// Add the hoop
cpShape *shapeHoop = [smgr addCircleAt:ccp(winSize.width/2.0f,winSize.height/2.0f - 55.0f) mass:1.0 radius: 50 ];
cpCCSprite * cccrsHoop = [cpCCSprite spriteWithShape:shapeHoop file:#"hoop_100x100.png"];
[self addChild:cccrsHoop];
cpBodySetAngVel(cccrsHoop.shape->body,0.25);
Disclaimer: I'm trying to answer my own question, and this answer might be missing important subtleties - it just represents what I know, so far.
If using cocos2d
use this code in tick to update positon all time _number1.position is the position you would update to, so when _number1 moves _logo2 will rotate to face it
_logo2.rotation = CC_RADIANS_TO_DEGREES(-ccpToAngle(ccpSub(_number1.position, _logo2.position)));
update _logo1 rotation by touch put this code in touch event handler
_logo2.rotation = CC_RADIANS_TO_DEGREES(-ccpToAngle(ccpSub(location, _logo2.position)));
use this as an action
[_logo2 runAction:[CCRotateTo actionWithDuration:0.0 angle:CC_RADIANS_TO_DEGREES(-ccpToAngle(ccpSub(location, _logo2.position)))]];
hope this helps someone took me ages to work it out