SpriteKit Detect End of Contact After Detecting Begin of Contact - objective-c

This is what I try to accomplish:
Two sprite nodes in the scene, and self is an edge loop.
If nodeA touches nobeB, and stop. >> Win
If nodeA touches self. >> Lose
If nodeA touches nobeB but didn't stop and touches self. >> Lose
Therefore I need something that works like this:
typedef NS_OPTIONS(aero, SpriteNodeCategory)
{
SpriteNodeCategoryA = 1 << 0,
SpriteNodeCategoryB = 1 << 1,
};
-(void)didBeginContact:(SKPhysicsContact *)contact
{
aero collision = (contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask);
if (collision == (SpriteNodeCategoryA|SpriteNodeCategoryB)) {
//something here I don't know...
}
if (collision == (SpriteNodeCategoryA|SpriteNodeCategoryB)) {
NSLog(#"FAIL");
}
}

You have another method delegate (SKPhysicsContactDelegate) which do the job you asking about:
- (void)didEndContact:(SKPhysicsContact *)contact

Okay so this is going to be a little difficult.
First of, you can use "didSimulatePhysics" to get the update of your simulation
-(void)didSimulatePhysics
{
if (_yourNode.physicsBody.angularVelocity == 0 && newGame) {
if (_yourNode.userData[#"winningCondition"]) {
[self win];
};
}
}
this does is that it updates and see if you have a winning condition - which is gotten from collision (think: reverse engineering)
P.S. _yourNode.physicsBody.angularVelocity == 0 meaning your node is completely still
-(void)didBeginContact:(SKPhysicsContact *)contact
{
uint32_t collision = (contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask);
if (collision == (CNPhysicsCategoryNodeA|CNPhysicsCategoryNodeB)) {
_yourNode.userData = [#{#"winningCondition":#(YES)} mutableCopy];
}
if (collision == (CNPhysicsCategoryNodeA|CNPhysicsCategoryEdge)) {
[self lose];
}
}
-(void)didEndContact:(SKPhysicsContact *)contact
{
uint32_t collision = (contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask);
if (collision == (CNPhysicsCategoryNodeA|CNPhysicsCategoryNodeB)) {
_yourNode.userData = [#{#"winningCondition":#(NO)} mutableCopy];
}
}
So basically when A touches B, it gives you a winning condition through adding a "userData" to your node.
The rest is pretty straight forward. I think you get it.

Related

Two finger swipe in Yosemite 10.10

I have been using a similar method to as this:
https://github.com/oscardelben/CocoaNavigationGestures
To capture two finger swipes on the Mac, under Yosemite it is no longer working. Anyone know what has change, or what I need to change for this to work.
The accepted answer didn't work well for me - it would often not detect the swipe. Instead, I overrode wantsScrollEventsForSwipeTrackingOnAxis:(NSEventGestureAxis)axis to return YES for the appropriate axis, then overrode scrollWheel:(NSEvent *)theEvent to detect scrolling. Works perfect every time.
The complete answer in Swift 5.3, based on #bmuller would be:
override func wantsScrollEventsForSwipeTracking(on axis: NSEvent.GestureAxis) -> Bool {
return axis == .horizontal
}
override func scrollWheel(with event: NSEvent) {
if event.scrollingDeltaX < 0 {
print("Go forward")
}
else {
print("Go back")
}
}
Multiple swipe events might be sent in a single gesture with this code. You may need to add phase(NSEventPhase) handling code to the scrollWheel(...) function e.g.
override func scrollWheel(with event: NSEvent) {
guard event.phase == .began else {
return
}
if event.scrollingDeltaX < 0 {
print("Go forward")
}
else {
print("Go back")
}
}
On your Mac, go to system preferences and you will find all the different settings of the trackpad.
From here: https://discussions.apple.com/thread/5710582
Hope I helped.
This was my solution, seems to be working for me.
#define kSwipeMinimumLength 0.2
- (void)touchesBeganWithEvent:(NSEvent *)event{
if(event.type == NSEventTypeGesture){
NSSet *touches = [event touchesMatchingPhase:NSTouchPhaseAny inView:self];
if(touches.count == 2){
self.twoFingersTouches = [[NSMutableDictionary alloc] init];
for (NSTouch *touch in touches) {
[self.twoFingersTouches setObject:touch forKey:touch.identity];
}
}
}
}
- (void)touchesMovedWithEvent:(NSEvent*)event {
NSSet *touches = [event touchesMatchingPhase:NSTouchPhaseEnded inView:self];
if(touches.count > 0){
NSMutableDictionary *beginTouches = [self.twoFingersTouches copy];
self.twoFingersTouches = nil;
NSMutableArray *magnitudes = [[NSMutableArray alloc] init];
for (NSTouch *touch in touches)
{
NSTouch *beginTouch = [beginTouches objectForKey:touch.identity];
if (!beginTouch) continue;
float magnitude = touch.normalizedPosition.x - beginTouch.normalizedPosition.x;
[magnitudes addObject:[NSNumber numberWithFloat:magnitude]];
}
float sum = 0;
for (NSNumber *magnitude in magnitudes)
sum += [magnitude floatValue];
// See if absolute sum is long enough to be considered a complete gesture
float absoluteSum = fabsf(sum);
if (absoluteSum < kSwipeMinimumLength) return;
// Handle the actual swipe
// This might need to be > (i am using flipped coordinates), you can use an else to go forward also.
if (sum > 0){
NSLog(#"go back");
}
}
}

SpriteKit detect end of Contact

I'm trying to figure out how to detect when the contact between two bodies ends. I'm working on a car game something like alpine crawler and only when the rear wheel is touching the ground the car is able to accelerate.
This is my code for now but it doesn't work proper:
- (void) didBeginContact:(SKPhysicsContact *)contact
{
if (([contact.bodyB.node.name isEqualToString:#"rearWheel"] &&
[contact.bodyA.node.name isEqualToString:#"ground"])) {
isWheelOnGround = YES;
}
}
-(void) didEndContact:(SKPhysicsContact *)contact {
if (([contact.bodyB.node.name isEqualToString:#"rearWheel"] &&
[contact.bodyA.node.name isEqualToString:#"ground"])) {
isWheelOnGround = NO;
}
}
You need to implement the contact delegate correctly and set bitmasks on bodies for the contact notifications to fire up. To do that, put this beneath your imports:
typedef NS_OPTIONS(uint32_t, CNPhysicsCategory) {
CNPhysicsCategoryWheel = 1 << 0, // 0001 = 1
CNPhysicsCategoryGround = 1 << 1, // 0010 = 2
};
#interface YourSceneNameHere() <SKPhysicsContactDelegate>
#end
Then, as you initialize, add the scene as the contact delegate:
self.physicsWorld.contactDelegate = self;
Now, to apply those masks to your bodies - the category they are in, and what category of bodies they will be sending contact notifications for:
wheel.physicsBody.categoryBitMask = CNPhysicsCategoryWheel;
wheel.physicsBody.contactTestBitMask = CNPhysicsCategoryGround;
ground.physicsBody.categoryBitMask = CNPhysicsCategoryGround;
ground.physicsBody.contactTestBitMask = CNPhysicsCategoryWheel;
In the contact method, you have no control over which body is bodyA, and which bodyB:
- (void) didBeginContact:(SKPhysicsContact *)contact {
uint32_t contactTest = (contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask);
if (contactTest == (CNPhysicsCategoryWheel | CNPhysicsCategoryGround)) {
isWheelOnGround = YES;
}
}
Similarly for didEndContact. Good luck!
For another example of this and a thorough explanation, refer to the 'Working with collisions and contacts' section of the SK Programming Guide.

For loop in Sprite Kit seems to be emptying an array?

I'm just starting to wrap my brain around Sprite Kit and I am encountering a very strange error when attempting to change the property of a node in a for loop im using.
I have two SKSpriteNode objects, one is the child of a SKScene (BLATheBugs) and the other is a child of the first (BLAEmptySpaces). I have a grid laid out with BLAEmptySpaces, and BLATheBugs on top of those empty spaces which are supposed to take UITouch, and move to an empty space if its bool isOccpupied property == False. When the scene is set up, the SKScene triggers a method in TheBugs:
-(void) spawnEmptySpacesInitialize
{
[self addChild:[self spawnEmptySpaces]];
}
which in turn triggers:
-(BLAEmptySpaces *) spawnEmptySpaces
{
emptySpace = [[BLAEmptySpaces alloc] init];
emptySpace.numberOfEmptySpacesNeeded = 12;
[emptySpace spawnEmptySpaces];
[emptySpace positionTheEmptySpaces];
return emptySpace;
}
which finally triggers a method in the EmptySpaces object:
-(BLAEmptySpaces *) spawnEmptySpaces
{
_emptySpacesArray = [NSMutableArray new];
for (int x = 0; x < _numberOfEmptySpacesNeeded; x++)
{
_anEmptySpace = [[BLAEmptySpaces alloc] initWithImageNamed:#"BlueLight.png"];
_anEmptySpace.zPosition = 50;
[_emptySpacesArray addObject:_anEmptySpace];
[self addChild: _anEmptySpace];
}
return self;
}
everything seems fine, (except for needing the additional "addChild" in the EmptySpaces object to get them to be drawn on the screen which i have also been trying to fix) but when i call the method to move TheBugs:
-(void) moveLeftOneSpace
{
NSLog(#"%d", emptySpace.emptySpacesArray.count);
for (emptySpace in emptySpace.emptySpacesArray)
{
NSLog(#"cycle");
if (emptySpace.isOccupied == NO)
{
for (_yellowBug in yellowBugArray)
{
if (_positionOfFingerTouchX > _yellowBug.position.x - variableOne && _positionOfFingerTouchX < _yellowBug.position.x + variableTwo && _positionOfFingerTouchY > _yellowBug.position.y - variableOne && _positionOfFingerTouchY < _yellowBug.position.y + variableTwo && emptySpace.position.x == _yellowBug.position.x - 80 && emptySpace.position.y == _yellowBug.position.y)
{
_yellowBug.position = CGPointMake(_yellowBug.position.x - spaceBetweenBugs, _yellowBug.position.y);
emptySpace.isOccupied = YES;
NSLog(#"execute");
}
}
}
}
}
It at first tells me there are 12 objects in the array and runs the operation. if I try to move any piece again, it tells me there are now NO objects in the array (yellowBugArray). It is also probably worth noting that it will not let me access emptySpace.anEmptySpace. Throws me an error.
Sorry for the long post, but hopefully somewhere in here is the cause of my problem.
Thank you very much guys!

Collision detection Objective-C (cocos2d)

I´m making an iphone app in objective-c with cocos2d, in the code below I try to detect a collision and then run an animation. (The box1 is moved by touch)
When the "[self getChildByTag:d]" and "box1" collide AND overlap I get the "JUMP NOW!" displayed but I don't get the jump itself, but when the box1 is moved away from the "[self getChildByTag:d]" the jump occurs.
I understand that this probably has to do with the fact that the action is called many times, but please explain to me exactly what happens and please help me with a solution!
- (void)update:(ccTime)dt {
for (int d = lowestAvailableTag; d <= highestAvailableTag; d++) {
if ([self getChildByTag:d].position.y < (box1.position.y+45)&&
[self getChildByTag:d].position.x > (box1.position.x-45) &&
[self getChildByTag:d].position.x < (box1.position.x+45) ) {
NSLog(#"JUMP NOW!");
if ([self getChildByTag:d].position.x < 150) {
[[self getChildByTag:d] runAction:
[CCJumpTo actionWithDuration:1.5
position:ccp(240, 140) height:110 jumps:1]];
}
}
}
}
//albar
You can add some BOOL flag to detect if your jump occured. Smth like:
- (void) update:(ccTime)dt
{
if( jumpOccured == false )
{
BOOL needToJump = // your jump condition
if( needToJump == true )
{
// your jump code
jumpOccured = true;
}
}
}
by the way, if you have many possible collisions, you can use box2d to detect them

Simple Cocos2d Box2d Action On Collision

I have been looking for weeks for an OBJ-C based tutorial on how to call a method when a specific b2body collides with something else (not everything).
Basically, a block falls to the ground every second. This works fine, but when it hits the ground or the player, the block should get deleted and pieces of it (different object) should be spawned.
Can anyone tell me how to do this?
Any help would be hot
Thanks
I think this is the one that you need..You need to include MycontactListener
-(void) checkForHit{
std::vector<b2Body *>toDestroy;
std::vector<MyContact>::iterator pos;
for(pos = _contactListener->_contacts.begin(); pos != _contactListener->_contacts.end(); ++pos) {
MyContact contact = *pos;
bodyA = contact.fixtureA->GetBody();
bodyB = contact.fixtureB->GetBody();
if (bodyA->GetUserData() != NULL && bodyB->GetUserData() != NULL) {
spriteA = (CCSprite *) bodyA->GetUserData();
spriteB = (CCSprite *) bodyB->GetUserData();
//NSLog(#”sprite tag is %d”,spriteA.tag);
if (spriteA.tag == 50) {
if (std::find(toDestroy.begin(), toDestroy.end(), bodyB) == toDestroy.end()) {
toDestroy.push_back(bodyB);
}
}
std::vector<b2Body *>::iterator pos2;
for(pos2 = toDestroy.begin(); pos2 != toDestroy.end(); ++pos2) {
b2Body *body = *pos2;
if (body->GetUserData() != NULL) {
CCSprite *sprite = (CCSprite *) body->GetUserData();
[self removeChild:sprite cleanup:YES];
}
_world->DestroyBody(body);
}
or if you dont't want to use contact listener.you can create a fixture for ground body and a body that u want to destroy and use the following code to check if it is intersecting and destroy it...
if((contact.fixtureA == groundFixture && contact.fixtureB == bodyFixture) ||
(contact.fixtureA == bodyFixture&& contact.fixtureB == groundFixture ))
{
//destroy the body
}
I have realized that as my object fall to the ground, i could create my own VERY simple physics by creating a start velocity, and then increasing it in the update method by a specific amount.
For the collision, my objects all fit in a 48pt wide grid so i could do simple rectangular collision detection.