Cocos2D: Two objects of the same class colliding? - objective-c

Working on game where plates will be falling from top to bottom. Some plates will also "bounce" on the ground and then start moving upwards again. This leads situations where a falling plate collides with a "rising plate".
My problem? I don´t know how to detect this collision.
Since all the plates comes from the same class I can´t write
if(CGRectIntersectsRect([self boundingBox], [self boudingBox]))
since this statement will always be true.
I create the plates with a for-loop:
for(i=0; i<9; i++){
Plate *plate = [Plate initPlate];
}
and then just reuse these plates throughout the game.
Any ideas or work arounds on how I detect a collision between two plates? Any advice would be greatly appreciated.
Regards.

You need to have a class that manages (for example using a NSMutableArray) the set of plates, and instead of checking for collisions on the Plate class you do it on this new class.
Assuming your array is:
NSMuttableArray *plateSet
You can do this:
for (Plate *bouncingPlate in plateSet)
{
if ([bouncingPlate bouncing])
{
for (Plate *fallingPlate in plateSet)
{
if (![fallingPlate bouncing])
{
/* Check for collision here between fallingPlate and bouncingPlate */
}
}
}
}
Or, more elegantly:
for (Plate *bouncingPlate in plateSet)
{
if (![bouncingPlate bouncing])
{
continue;
}
for (Plate *fallingPlate in plateSet)
{
if ([fallingPlate bouncing])
{
continue;
}
/* Check for collision here between fallingPlate and bouncingPlate */
}
}

yes...you need to add them to a NSMutableArray and then just use ccpDistance to check collision
something like this:
for (int i=0;i<8,i++){
for (int j=i+1,j<9,j++){
if(ccpDistance([[plates objectAtIndex:i]position],[[plates objectAtIndex:j]position])<plateRadius*2) {
//collision detected
}}}
of course this works if plates are circles
for squares use CGRectIntersectsRect:
for (int i=0;i<8,i++){
for (int j=i+1,j<9,j++){
if([plates objectAtIndex:i].visible && [plates objectAtIndex:j].visible &&(CGRectIntersectsRect([[plates objectAtIndex:i]boundingBox],[[plates objectAtIndex:j]boundingBox])) {
//collision detected
}}}

Related

remove objects from arraylist is doing strange things

In the code below, i want the balls to change from ArrayList ballList to another ArrayList oldBalls as soon as their age turns higher than a threshold value.
This code should be very simple but i can't figure out why when the age is same as the threshold (not larger) they disappear and then they come back 2 frames later.
I have checked related questions using iterators for ArrayLists in java but i think there should be a way to do this in processing without java.
Also i cant seem to post any question on the processing forum even though i can sign in, no idea why...
I have reduced the code to the minimum able to reproduce the error.
ArrayList <Ball> ballList;
ArrayList <Ball> oldBalls;
int threshold=4;
PFont font;
void setup(){
size(400,400);
frameRate(0.5);
font=createFont("Georgia", 18);
textFont(font);
textAlign(CENTER, CENTER);
ballList=new ArrayList<Ball>();
oldBalls=new ArrayList<Ball>();
noFill();
strokeWeight(2);
}
void draw(){
background(0);
Ball b=new Ball(new PVector(10, random(height/10,9*height/10)),new PVector(10,0),0);
ballList.add(b);
stroke(0,0,200);
for(int i=0;i<oldBalls.size();i++){
Ball bb=oldBalls.get(i);
bb.update();
bb.render();
text(bb.age,bb.pos.x,bb.pos.y);
}
stroke(200,0,0);
for(int i=0;i<ballList.size();i++){
Ball bb=ballList.get(i);
bb.update();
bb.render();
bb.migrate();
text(bb.age,bb.pos.x,bb.pos.y);
}
}
class Ball{
PVector pos;
PVector vel;
int age;
Ball(PVector _pos, PVector _vel, int _age){
pos=_pos;
vel=_vel;
age=_age;
}
void migrate(){
if(age>threshold){
oldBalls.add(this);
ballList.remove(this);
}
}
void update(){
pos.add(vel);
age+=1;
}
void render(){
ellipse(pos.x,pos.y,24,24);
}
}
Note how balls labelled with age=threshold suddenly disappear...
i guess the problem is here:
for(int i=0;i<ballList.size();i++){
Ball bb=ballList.get(i);
bb.update();
bb.render();
//add this
if(bb.migrate())
i--;
text(bb.age,bb.pos.x,bb.pos.y);
}
and
boolean migrate(){
if(age>threshold){
oldBalls.add(this);
ballList.remove(this);
//and this
return true;
}
return false;
}
migrate() will remove the object from the ballList and reduce it's size by 1.
What it looks like is happening here is because you're altering the List's whilst iterating through them. Consider this for loop you have here
for(int i=0;i<ballList.size();i++){
Ball bb=ballList.get(i);
bb.update();
bb.render();
bb.migrate();
text(bb.age,bb.pos.x,bb.pos.y);
}
Say ballList has 2 balls in it both age 3, the first loops gets ball[0] and then removes it from the list, i will increment and the loop will immediately exit because ballList.size() is now 1. So it's not the ball which gets to age 4 that vanishes but the subsequent one.

How to write a box2d contact listener using cocos2d?

I've been reading various tutorials on how to write a contact listener, and I can't wrap my head around it.
Here is what I have so far:
In each of the classes that I have representing a physics object I do:
_body->SetUserData(self);
I write a contact listener class containing the following two methods:
void ContactListener::BeginContact(b2Contact* contact)
{
// Box2d objects that collided
b2Fixture* fixtureA = contact->GetFixtureA();
b2Fixture* fixtureB = contact->GetFixtureB();
// Sprites that collided
MyNode* actorA = (MyNode*) fixtureA->GetBody()->GetUserData();
MyNode* actorB = (MyNode*) fixtureB->GetBody()->GetUserData();
}
void ContactListener::EndContact(b2Contact* contact)
{
// Box2d objects that collided
b2Fixture* fixtureA = contact->GetFixtureA();
b2Fixture* fixtureB = contact->GetFixtureB();
// Sprites that collided
MyNode* actorA = (MyNode*) fixtureA->GetBody()->GetUserData();
MyNode* actorB = (MyNode*) fixtureB->GetBody()->GetUserData();
}
I don't know what to do next. I now have the two sprites which are colliding, but I want to do the following:
1) When they collide, I want to remove one of the sprites from the world, based on the type of object. (for example if one a cat object and the other is a mouse object, I want to remove the mouse object.
2) I want to let the cat object know it ate a mouse
3) I want the cat to continue moving as if it didn't contact with the mouse.
4) I still wan't the cat to collide normally with things like the terrain.
What do I do next ? I'm pretty clueless on what to do? How do I get the cat to continue to collide normally with the terrain, but not with the mouse? When do I remove the mouse?
Having an "Entity" class that hold the reference to the Box2D body and does the manipulations on it is definitely a good way to go. If you have a Spaceship class vs. a Meteor class, each of them can supply their own derived methods of controlling the body (AI), but each of them has common logic and code to support operations on "Things that have a body" (e.g. common "Entity" base class). I think you are on the right track.
It gets a little murky when the contacts start happening. This is where you start getting into the architecture of your overall system, not just the structure of the physics world or a single Coco2d Scene.
Here is how I have done this in the past:
First I set up the contact listener, listed below:
class EntityContactListener : public ContactListener
{
private:
GameWorld* _gameWorld;
EntityContactListener() {}
typedef struct
{
Entity* entA;
Entity* entB;
} CONTACT_PAIR_T;
vector<CONTACT_PAIR_T> _contactPairs;
public:
virtual ~EntityContactListener() {}
EntityContactListener(GameWorld* gameWorld) :
_gameWorld(gameWorld)
{
_contactPairs.reserve(128);
}
void NotifyCollisions()
{
Message* msg;
MessageManager& mm = GameManager::Instance().GetMessageMgr();
for(uint32 idx = 0; idx < _contactPairs.size(); idx++)
{
Entity* entA = _contactPairs[idx].entA;
Entity* entB = _contactPairs[idx].entB;
//DebugLogCPP("Contact Notification %s<->%s",entA->ToString().c_str(),entB->ToString().c_str());
msg = mm.CreateMessage();
msg->Init(entA->GetID(), entB->GetID(), Message::MESSAGE_COLLISION);
mm.EnqueueMessge(msg, 0);
msg = mm.CreateMessage();
msg->Init(entB->GetID(), entA->GetID(), Message::MESSAGE_COLLISION);
mm.EnqueueMessge(msg, 0);
}
_contactPairs.clear();
}
void PreSolve(b2Contact* contact, const b2Manifold* oldManifold)
{
b2Fixture* fixtureA = contact->GetFixtureA();
b2Body* bodyA = fixtureA->GetBody();
Entity* entityA = bodyA->GetUserData();
b2Fixture* fixtureB = contact->GetFixtureB();
b2Body* bodyB = fixtureB->GetBody();
Entity* entityB = bodyB->GetUserData();
if(test if entityA and entityB should not have collision response)
{
contact->SetEnabled(false);
}
// Do this if you want there to be collision notification, even if
// there is no response.
AddContactPair(entA,entB);
}
void AddContactPair(Entity* entA, Entity* entB)
{
for(uint32 idx = 0; idx < _contactPairs.size(); idx++)
{
if(_contactPairs[idx].entA == entA && _contactPairs[idx].entB == entB)
return;
// Not sure if this is needed...
if(_contactPairs[idx].entA == entB && _contactPairs[idx].entA == entB)
return;
}
CONTACT_PAIR_T pair;
pair.entA = entA;
pair.entB = entB;
_contactPairs.push_back(pair);
}
// BEWARE: You may get multiple calls for the same event.
void BeginContact(b2Contact* contact)
{
Entity* entA = (Entity*)contact->GetFixtureA()->GetBody()->GetUserData();
Entity* entB = (Entity*)contact->GetFixtureB()->GetBody()->GetUserData();
assert(entA != NULL);
assert(entB != NULL);
// Not sure this is still needed if you add it in the pre-solve.
// May not be necessary...
AddContactPair(entA, entB);
}
// BEWARE: You may get multiple calls for the same event.
void EndContact(b2Contact* contact)
{
}
};
Because of the way the engine works, you can get multiple contact hits for the same bodies. This listener filters them so if two Entities collide, you only get one message.
The Listener only stores the collisions that have occurred. It could be modified to further separate them into "begins" and "ends" for other purposes. Here, contact meant "you've been hit by something". I didn't need to know if it stopped contacting.
The call to NotifyCollisions is the "secret sauce". It sends out a message to both the contacted entities (via a message system) that they hit something and the other Entity is what they hit. Bullet hit ship. Bullet destroys self. Ship damages self based on bullet properties (GetDamageInflicted() method). This in turn signals the graphics system to remove the bullet from the display. If the ship was destroyed, it is destroyed as well.
From an overall execution standpoint:
Before you begin, assign the contact listener.
Each Cycle of your game:
Call "Update" on all your Entities. This updates their physics forces, etc.
Update Box2d world.
Call NotifyCollisions on Listener.
Remove dead Entities from system.
Was this helpful?

respondsToSelector: equivalent for CoreFoundation?

I have a CFArrayRef which mostly has CFDictionaryRef, but sometimes it'll contain other things. I'd like to access a value from the dictionary in the array if I can, and not crash if I can't. Here's the code:
bool result = false;
CFArrayRef devices = CFArrayCreateCopy(kCFAllocatorDefault, SDMMobileDevice->deviceList);
if (devices) {
for (uint32_t i = 0; i < CFArrayGetCount(devices); i++) {
CFDictionaryRef device = CFArrayGetValueAtIndex(devices, i);
if (device) { // *** I need to verify this is actually a dictionary or actually responds to the getObjectForKey selector! ***
CFNumberRef idNumber = CFDictionaryGetValue(device, CFSTR("DeviceID"));
if (idNumber) {
uint32_t fetched_id = 0;
CFNumberGetValue(idNumber, 0x3, &fetched_id);
if (fetched_id == device_id) {
result = true;
break;
}
}
}
}
CFRelease(devices);
}
return result;
Any suggestions for how I can ensure that I only treat device like a CFDictionary if it's right to do so?
(I'm dealing with some open source code that isn't particularly well documented, and it doesn't seem to be particularly reliable either. I'm not sure if it's a bug that the array contains non-dictionary objects or a bug that it doesn't detect when it contains non-dictionary objects, but it seems to me that adding a check here is less likely to break other code then forcing it to only contain dictionaries elsewhere. I don't often work with CoreFoundation, so I'm not sure if I'm using the proper terms.)
In this case, since it looks like you are traversing the I/O Registry, you can use CFGetTypeId():
CFTypeRef device = CFArrayGetValueAtIndex(devices, i); // <-- use CFTypeRef
if(CFGetTypeID(device) == CFDictionaryGetTypeID()) { // <-- ensure it's a dictionary
...
}
If you really need to send messages to NSObject's interface from your C code, you can (see #include <objc/objc.h> and friends, or call to a C helper function in a .m file), but these strategies are not as straight forward as CFGetTypeID(), and much more error-prone.

Ways to compare two CCSprite's runAction

I have a CCSprite "_wo1" and it has a BOOL property "attack". I have the following code in my update method and I want it to constantly check if "attack" is YES or NO and make my sprite do different kinds of runAction.
So my question is:
How to compare two runAction? I tried "==" and "isEqual" and they are not working..
MySprite *_wo1 = ... // initialize _wo1 using my own class that sub-classed CCSprite
if (![_wo1 attack])
{
_wo1.position = ccp(_wo1.position.x + 10 * dt, _wo1.position.y); // walking forward
if (currentAction == attAction) // currentAction is defined in setting its runAction in the beginning: "currentAction = [wo1 runAction:[s01WalkAction copy]];"
{
[_wo1 stopAction:attAction];
[_wo1 runAction:[walkAction copy]];
}
}
else{
if (currentAction == walkAction)
{
[_wo1 stopAction:walkAction];
[_wo1 runAction:[attAction copy]];
}
}
Any helps or suggestions will be highly appreciated. Thank you!
You can keep an action state of your sprite in the MySprite class which easily can tell you which action is used in the sprite.
When you run an action on the sprite just set the state properly. For example :
mySprite.actionState = ACTION_WALKING;
and then you can easily check which action is used:
if (mySprite.actionState == ACTION_WALKING)
{
// Your handling code
}

Creating Touchjoints/Mousejoints on small objects in box2d

I'm using Cocos2d iPhone with Box2D to create a basic physics engine.
Occasionally the user is required to drag around a small box2D object.
Creation of touchjoints on small objects is a bit hit and miss, with the game engine seeing it as a tap on blank space as often as actually creating the appropriate touchjoint. In practice this means the user is constantly mashing their fingers against the screen in vain attempts to move a stubborn object. I want the game to select small objects easily without this 'hit and miss' effect.
I could create the small objects with larger sensors around them, but this is not ideal because objects above a certain size (around 40px diameter) don't need this extra layer of complexity; and the small objects are simply the big objects scaled down to size.
What are some strategies I could use to allow the user experience to be better when moving small objects?
Here's the AABB code in ccTouchBegan:
b2Vec2 locationWorld = b2Vec2(touchLocation.x/PTM_RATIO, touchLocation.y/PTM_RATIO);
b2AABB aabb;
b2Vec2 delta = b2Vec2(1.0/PTM_RATIO, 1.0/PTM_RATIO);
//Changing the 1.0 here to a larger value doesn't make any noticeable difference.
aabb.lowerBound = locationWorld - delta;
aabb.upperBound = locationWorld + delta;
SimpleQueryCallback callback(locationWorld);
world->QueryAABB(&callback, aabb);
if(callback.fixtureFound){
//dragging code, updating sprite location etc.
}
SimpleQueryCallback code:
class SimpleQueryCallback : public b2QueryCallback
{
public:
b2Vec2 pointToTest;
b2Fixture * fixtureFound;
SimpleQueryCallback(const b2Vec2& point) {
pointToTest = point;
fixtureFound = NULL;
}
bool ReportFixture(b2Fixture* fixture) {
b2Body* body = fixture->GetBody();
if (body->GetType() == b2_dynamicBody) {
if (fixture->TestPoint(pointToTest)) {
fixtureFound = fixture;
return false;
}
}
return true;
}
};
What about a minimum collision box for touches? Objects with less than 40px diameter use the 40px diameter, all larger objects use their actual diameter.
What I ended up doing - thanks to iforce2d, was change ReportFixture in SimpileQueryCallback to:
bool ReportFixture(b2Fixture* fixture) {
b2Body* body = fixture->GetBody();
if (body->GetType() == b2_dynamicBody) {
//if (fixture->TestPoint(pointToTest)) {
fixtureFound = fixture;
return true;
//}
}
return true;
}
And increase the delta to 10.0/PTM_RATIO.