I am using CCParticleSystemQuad to create a particle effect in Cocos2d.
Now I would like to test each particle for collisions with a CCRect.
How do I get the postions of each particle in the particle engine so I can do this?
Any help or examples would be appreciated. I've looked for hours on the net expecting to find tutorials on this. I am surprised I can't find much as I would expect collisions with particles to be essential; Perhaps I wasn't looking in the right place :)
Create a subclass of CCParticleSystemQuad and override update: method or updateQuadWithParticle:newPosition: method.
#interface MyParticleSystem : CCParticleSystemQuad
#end
#implementation MyParticleSystem
- (void)updateQuadWithParticle:(tCCParticle*)particle newPosition:(CGPoint)pos
{
/* use pos */
[super updateQuadWithParticle:particle newPosition:pos];
}
#end
EDITED:
You can set any data (position, color, or so on) to the particles as the following.
#interface MyParticleSystem : CCParticleSystemQuad
#end
#implementation MyParticleSystem
- (void)update:(ccTime)dt
{
/* implement as cocos2d/CCParticleSystem.m -update: */
}
#end
Try something like
CCParticleSystemQuad* particle_system = ...;
for(int i = 0; i < particle_system->particleCount; i++)
{
particle_system->particles[idx]->pos; // Here is your position
}
The header file for the interface is here: http://www.cocos2d-iphone.org/api-ref/latest-stable/_c_c_particle_system_8h_source.html
Warning: Take this answer with a grain of salt as I don't use Cocos2d, or Objective-C.
Related
First try at Chipmunk.
Not getting collision detection registering is the problem.
My code:
#implementation MainPlayScene
{
CCPhysicsNode *_physics;
CCNode *MyPhysicsBody;
CCNode *bottomBody;
}
+ (instancetype)scene
{
return [[self alloc] init];
}
- (instancetype)init
{
// Apple recommend assigning self with supers return value, and handling self not created
self = [super init];
if (!self) return(nil);
_physics = [CCPhysicsNode node];
_physics.debugDraw = YES;
[self addChild:_physics z:1];
/// BOTTOM
CGRect bottomRect = CGRectMake(0, 0, [CCDirector sharedDirector].viewSize.width, 10);
bottomBody = [CCNode node];
bottomBody.physicsBody = [CCPhysicsBody bodyWithPolylineFromRect:bottomRect cornerRadius:0];
bottomBody.physicsBody.collisionCategories = #[#"Bottom"];
bottomBody.physicsBody.type = CCPhysicsBodyTypeStatic;
[_physics addChild:bottomBody];
/// MyBody to bounce around
MyPhysicsBody = [CCSprite spriteWithImageNamed:#"MyBody-64x64-24.png"];
MyPhysicsBody.position = ccp((self.contentSize.width/2),(self.contentSize.height/2));
MyPhysicsBody = [CCNode node];
MyPhysicsBody.physicsBody = [CCPhysicsBody bodyWithRect:(CGRect){CGPointZero, MyPhysicsBody.contentSize.height,MyPhysicsBody.contentSize.width} cornerRadius:0];
MyPhysicsBody.physicsBody.collisionCategories = #[#"MyBody"];
[_physics addChild:MyPhysicsBody z:150];
self.userInteractionEnabled = YES;
return self;
}
Detecting touch events and applying force to physics body to have it bounce up and down onto bottom body
- (void)touchBegan:(UITouch *)touch withEvent:(UIEvent *)event
{
CCLOG(#"Touch Detected");
[MyPhysicsBody.physicsBody applyImpulse:ccp(0, 300.f)];
}
Now I try to detect a collision on “Bottom” but nothing is being registered even though I see debug lines of 2 objects touch.
/// try onCollisionEnter first ... nothing
-(void)onCollisionEnter:(CCNode *)entity collisionPair:(CCPhysicsCollisionPair *)pair
{
if ([entity.physicsBody.collisionCategories isEqual: #"Bottom"]) {
CCLOG(#"Hit bottomBody");
}
}
/// try ccPhysicsCollisionBegin pair ... nothing
-(BOOL)ccPhysicsCollisionBegin:(CCPhysicsCollisionPair *)pair MyBody:(CCNode *) MyBody Botton:(CCNode *)Bottom
{
CCLOG(#"Hit bottomBody");
return TRUE;
}
Obviously I’m missing something critical here …
Any help is VERY appreciated!
Thanks
I am finding it difficult to see the relevant code with all the things you posted.
I will give you an example of 2 CCSprite objects collision detection. One is called _arrowand the other one is _obstacle.
First step is to define the collision types like so :
_arrow.physicsBody.collisionType = #"arrow";
_obstacle.physicsBody.collisionType = #"obstacle";
Second step is to define the callback
-(BOOL)ccPhysicsCollisionBegin:(CCPhysicsCollisionPair *)pair arrow:(CCNode *)arrow obstacle:(CCNode *)obstacle
{
// Do some cool stuff
return TRUE;
}
Notice the naming, arrow and obstacle according to the collision type of theses sprites.
And third, the one that you forgot to do is set the delegate so you actually get these methods called on your physicsNode object
_physics.collisionDelegate = self;
And self (which is typically just your scene) should implement the CCPhysicsCollisionDelegate protocol.
Tibor has the correct answer, but there's an important clue that I was missing which was driving me crazy. From the comments in CCPhysicsNode.h section on the CCPhysicsCollisionDelegate:
The final two parameter names (typeA/typeB) should be replaced
with names used with CCPhysicsBody.collisionType or
CCPhysicsShape.collisionType.
If both final parameter names are "default" then the collision
method is called when a more specific method isn't found.
So I was incorrectly trying to implement something like this:
-(BOOL) ccPhysicsCollisionBegin:(CCPhysicsCollisionPair *)pair typeA:(CCNode *)nodeA typeB:(CCNode *)nodeB
{
// Compare if we're ignored collisions
if (([nodeA.physicsBody.collisionType isEqualToString: #"foo"])
|| ([nodeB.physicsBody.collisionType isEqualToString: #"foo"]) )
{
return NO;
}
return YES;
}
But that's WRONG!!! The correct thing I wanted to implement was this:
-(BOOL) ccPhysicsCollisionBegin:(CCPhysicsCollisionPair *)pair foo:(CCNode *)nodeA foo:(CCNode *)nodeB
{
return NO;
}
Note how the ignored collision type is part of the method signature.
Whew!
EDIT:
For completeness, don't forget to do:
_physics.collisionDelegate = self;
You are setting collision categories which along with collisions mask allow you to discard certain collisions. The collision type property is what works with the delegate methods.
I'm currently trying out a few things. I already did this tutorial: https://www.makegameswith.us/tutorials/getting-started-with-spritebuilder/
However I'm a little stuck now.
I created a CCNode in SpriteBuilder and added 3 images. carbody.png and 2 wheel.png.
I made all objects physicsObjects.
In code I tried to connect them with joints and move them. However the carBody moves but the wheels keep staying on their place.
#import "Car.h"
#implementation Skater{
CCNode *_wheel1;
CCNode *_wheel2;
CCNode *_carBody;
CCPhysicsJoint *_bodyWheelJoint1;
CCPhysicsJoint *_bodyWheelJoint2;
}
- (id)init {
self = [super init];
if (self) {
[_wheel1.physicsBody setCollisionGroup: _carBody];
[_wheel2.physicsBody setCollisionGroup: _carBody;
[_carBody.physicsBody setCollisionGroup: _carBody];
_bodyWheelJoint1 = [CCPhysicsJoint connectedPivotJointWithBodyA:_wheel1.physicsBody bodyB: _carBody.physicsBody anchorA:_wheel1.anchorPointInPoints];
_bodyWheelJoint2 = [CCPhysicsJoint connectedPivotJointWithBodyA:_wheel2.physicsBody bodyB: _carBody.physicsBody anchorA:_wheel2.anchorPointInPoints];
}
return self;
}
And in Gameplay.m I only did this:
-(void)didLoadFromCCB{
[_car runAction:
[CCActionMoveTo actionWithDuration:10 position:CGPointMake(2000,_car.position.y)]];
}
The carBody moves, the wheels don't...
What am I missing?
Edit:
I made a few changes now but my wheels still keep staying on their place...
#import "Car.h"
#implementation Car{
CCNode *_wheel1;
CCNode *_wheel2;
CCNode *_carBody;
CCPhysicsJoint *_bodyWheelJoint1;
CCPhysicsJoint *_bodyWheelJoint2;
}
- (id)init {
self = [super init];
if (self) {
CCLOG(#"Car created");
[_wheel1.physicsBody setCollisionGroup:_carBody];
[_wheel2.physicsBody setCollisionGroup:_carBody];
[_carBody.physicsBody setCollisionGroup:_carBody];
_bodyWheelJoint1 = [CCPhysicsJoint connectedPivotJointWithBodyA:_wheel1.physicsBody bodyB:_carBody.physicsBody anchorA:_wheel1.anchorPointInPoints];
_bodyWheelJoint2 = [CCPhysicsJoint connectedPivotJointWithBodyA:_wheel2.physicsBody bodyB:_carBody.physicsBody anchorA:_wheel2.anchorPointInPoints];
}
return self;
}
-(void)moveCar:(int)distance{
CCLOG(#"Car should move");
CGPoint launchDirection = ccp(1, 0);
CGPoint force = ccpMult(launchDirection, distance);
[self.physicsBody applyForce:force];
}
#end
You're simply missing the point that move (and other) actions bypass physics. Once a node has a physics body, the use of CCAction* classes that change position and rotation is a no-no, and most others (ie scale) will not be applied to the physics body either but can still be used safely.
To move a physics object, apply an impulse or force to the physics body.
I'm trying to get CCTableView working with cocos2d 3.0 but I really don't know where to start. Does anyone have a good tutorial or anything for 3.0? I see there are a few for older versions of cocos2d but nothing for 3.0. Any help is appreciated! Thanks!
Here is a short sample. I do recommend just trying out writing some code on your own and then posting what problems you are having with it. It definitely makes for easier answering of questions. You can also create your own subclass of CCTableViewCell if you want.
The Header file, SampleTableView.h
#import "cocos2d.h"
#import "cocos2d-ui.h"
#interface SampleTableView : CCNode <CCTableViewDataSource>
#end
The source file: SampleTableView.m
float const kNumberOfRows = 30.0f;
#implementation SampleTableView
- (instancetype)init
{
self = [super init];
if (self) {
CCTableView* table = [CCTableView node];
table.dataSource = self; // make our class the data source
table.block = ^(CCTableView* table) {
NSLog(#"Cell %d was pressed", (int) table.selectedRow);
};
[self addChild:table];
}
return self;
}
- (CCTableViewCell*) tableView:(CCTableView*)tableView nodeForRowAtIndex:(NSUInteger) index {
CCTableViewCell* cell = [CCTableViewCell node];
cell.contentSizeType = CCSizeTypeMake(CCSizeUnitNormalized, CCSizeUnitUIPoints);
cell.contentSize = CGSizeMake(1.0f, 32.0f);
float colorFactor = (index / kNumberOfRows);
// Just a sample node that changes color with each index value
CCNodeColor* colorNode = [CCNodeColor nodeWithColor:[CCColor colorWithRed:colorFactor green:(1.0f - colorFactor) blue:(0.2f + 0.5 * colorFactor) ] width:100.0f height:18.0f];
[cell addChild:colorNode];
return cell;
}
- (NSUInteger) tableViewNumberOfRows:(CCTableView*) tableView {
return kNumberOfRows; // just a demo
}
And how to add it in your hello world scene or elsewhere:
SampleTableView* table = [SampleTableView node];
table.contentSizeType = CCSizeTypeNormalized;
table.contentSize = CGSizeMake(1.0, 1.0);
Here is a sample screenshot of what it looks like:
You should check out the cocos2d-tests-ios.xcodeproj in the cocos2d source.
It has a sample for pretty much every thing implemented in cocos2d, Including a CCTableView sample.
https://github.com/cocos2d/cocos2d-iphone
git clone cocos2d source.
Open the cocos2d-tests-ios.xcodeproj project and build/run the cocos2d-tests-ios target.
I'm following "Learn Cocos2D" and in chapter 4 I'm met with the following directive:
And in the GameLayer init method, add the call to the initSpiders method discussed next, right after scheduleUpdate:
-(id) init {
if ((self=[super init])) {
...
[self scheduleUpdate];
[self initSpiders];
}
return self;
}
I get and ARC error message: no visible #interface for 'GameLayer' declares the selector 'initSpiders'
I get the same message at the line: self resetSpiders
what am i missing? everything builds and runs great up to that point.
This issue derives from the fact that the initSpiders and resetSpiders are not declared in your class interface and are defined in the .m file after the point where they are used.
If they are not missing altogether, you can fix this in either of 2 ways:
move the definition of the initSpiders and resetSpiders methods above your init method and the errors will disappear;
add a declaration for both methods in the #interface of the class.
(If you do both, it will also work)
Check your code to see if the implementation for those methods is available.
Your error appears to be that you haven't followed the next bit of the book too. Completing the next section should allow you to compile your code without warnings like this.
A more complete extract of that section of the book is:
And in the GameScene init method add the call to the initSpiders method discussed next, right after scheduleUpdate:
-(id) init {
if ((self = [super init]))
{
… 96 CHAPTER 4: Your First Game
[self scheduleUpdate];
[self initSpiders];
}
return self;
}
After that a fair bit of code is added to the GameScene class, beginning with the initSpiders method in Listing 4–8, which is creating the spider sprites.
Listing 4–8. For Easier Access, Spider Sprites Are Initialized and Added to a CCArray
-(void) initSpiders
{
CGSize screenSize = [[CCDirector sharedDirector] winSize];
// using a temporary spider sprite is the easiest way to get the image's size
CCSprite* tempSpider = [CCSprite spriteWithFile:#"spider.png"];
float imageWidth = [tempSpider texture].contentSize.width;
// Use as many spiders as can fit next to each other over the whole screen width.
int numSpiders = screenSize.width / imageWidth;
// Initialize the spiders array using alloc.
spiders = [[CCArray alloc] initWithCapacity:numSpiders];
for (int i = 0; i < numSpiders; i++)
{
CCSprite* spider = [CCSprite spriteWithFile:#"spider.png"];
[self addChild:spider z:0 tag:2];
// Also add the spider to the spiders array.
[spiders addObject:spider];
}
// call the method to reposition all spiders
[self resetSpiders];
}
Hey people,
I'm creating a game in cocos2d, (I'm very new to it, and was trying to solve this thing)
in the game I'm making I created a "Bomb" class, and a "Player" class,
I want the bomb to check for collision with the player, if a collision detected, explode.
My problem is that I have no idea how to get the player's position from the bomb class,
I'd be happy if you guys could help me out here,
Thanks!
You did add the CCSprites to a CCLayer, didn't you? Then that CCLayer should have the access to both of them. So, you can use the CCLayer's tick function to track the positions of the CCSprites and trigger actions if their bounding boxes overlap.
Some sample code to illustrate:
#interface MyLayer : CCLayer {
BombSprite *bomb;
PlayerSprite *player;
}
...
#end
#implementation MyLayer
- (id)init {
if ((self = [super init])) {
bomb = ...
player = ...
[self schedule:#selector(tick:)];
}
return self;
}
- (id)tick:(ccTime)dt {
if (CGRectContainsRect([bomb boundingBox], [player boundingBox])) {
NSLog(#"Collision!");
// call [player didCollideWith:bomb] or something
...
}
}
#end