SceneKit get current SCNNode position form Animated SCNNode - macos

I want to get a current node position form an animated SCNNode
I am aware of presentationNode and I tried to extract position form there, but no Luck
Here is my Code
SCNNode * pelvis = [_unit childNodeWithName:#"Bip01_Pelvis" recursively:YES];
SCNNode * present = pelvis.presentationNode;
SCNVector3 position = [self convertPosition:present.position toNode:self.parentNode];
The position I have is the position of the SCNNode in the "rest" mode before the CAAnimation was applied.
How to get properties like position form the 'presentationNode'?
Edit
Class itself relevant parts:
#interface UndeadSimple : RepresentationNode <CAAnimationDelegate>
{
//Animations
SCNNode * _undead;
CAAnimation * _currentAnimation;
NSString * _currAnimationkey;
CAAnimation * _death1;
...
SCNNode * _audioNode;
SCNAudioSource * _deathSource;
SCNAudioPlayer * _player;
}
Init:
NSString * sceneName = #ZOMBIE;
SCNScene *scene2 = [SCNScene sceneNamed:sceneName];
_undead = [SCNNode node];
for(SCNNode * node in scene2.rootNode.childNodes)
{
[_undead addChildNode:node];
[node removeAllAnimations];
}
[_undead setScale:(SCNVector3Make(0.024, 0.02, 0.024))];
[self addChildNode:_undead];
[_undead removeAllAnimations];
_currentAnimation = _idleAnimation;
_currAnimationkey = #"resting";
[_undead addAnimation:_currentAnimation forKey:_currAnimationkey];
Animation and event:
SCNAnimationEvent * event2 = [SCNAnimationEvent animationEventWithKeyTime:0.9 block:^(CAAnimation * _Nonnull animation, id _Nonnull animatedObject, BOOL playingBackward)
{
[self.delegate finishedDeathAnimation];
}];
_death1 = anims.death1.mutableCopy;
[_death1 setDelegate:self];
[_death1 setFillMode:kCAFillModeForwards];
_death1.removedOnCompletion = NO;
[_death1 setAnimationEvents:#[event2]];
Animation Call:
- (void)die
{
if([_currAnimationkey isEqualToString:#"dead"])
{
return;
}
[_undead removeAllAnimations];
CAAnimation * death = nil;
int anim = arc4random_uniform(3);
if(anim < 1)
{
death = _death1;
}
else if(anim < 2)
{
death = _death2;
}
else
{
death = _death3;
}
_currAnimationkey = #"dead";
_currentAnimation = death;
}
//Helper Method to get presentation Node
- (SCNNode *)getSnapNode
{
SCNNode * repNode = [_undead presentationNode];
_undead.transform = repNode.transform;
repNode = _undead.clone;
return repNode;
}
//On callback call and try to get position:
- (void)finishedDeathAnimation
{
SCNPlane * bloodgeometry = [SCNPlane planeWithWidth:4 height:4];
bloodgeometry.firstMaterial.diffuse.contents = [NSImage imageNamed:#"blood"];
SCNNode * bloodNode = [SCNNode node];
[bloodNode setEulerAngles:SCNVector3Make(-M_PI_2, 0, 0)];
bloodNode.geometry = bloodgeometry;
//Bip01_Pelvis
SCNNode * deadBody = [_unit getSnapNode];
SCNNode * pelvis = [deadBody childNodeWithName:#"Bip01_Pelvis" recursively:YES];
SCNNode * present = pelvis.presentationNode;
SCNVector3 position = [self convertPosition:present.position toNode:self.parentNode];
NSLog(#"pelvis position %lf,%lf,%lf", present.position.x, present.position.y, present.position.z); //Always {0,0,0}
bloodNode.position = SCNVector3Make(position.x, 0.0001, position.z);
[self.parentNode addChildNode:bloodNode];
[self removeFromParentNode];
}
Edit 2
I also tried to simplify it a bit:
another helper method:
//in undead/unit class
- (SCNVector3)getPosition
{
SCNNode * repNode = [[_undead childNodeWithName:#"Bip01_Pelvis" recursively:YES] presentationNode];
return repNode.position;
}
On callback:
- (void)finishedDeathAnimation
{
position = [_unit getPosition];
NSLog(#"pelvis position %lf,%lf,%lf", position.x, position.y, position.z); //Alway 0,0,0 as well :(
}

It is rare to find someone just like me still believe in Objective C rather than swift thats why i would like to offer full help.
I was having this issue for an animation exported from Blender as a Collada but have succeeded to get the actual position after few tries.
Using presentationNode is the correct way but you need to get it from the child while you making the animation for example this is how to run animation for a node Called _monkey notice that at the last Code i got the _present From the child.presentationNode Not from _monkey.presentationNode Also it have to be obtained during the enumeration not after
so first i need to define
NSNode * _present;
then i do play the animation
[_monkey enumerateChildNodesUsingBlock:^(SCNNode *child, BOOL *stop)
{
for(NSString *key in child.animationKeys)
{ // for every animation key
CAAnimation *animation = [child animationForKey:key]; // get the animation
animation.usesSceneTimeBase = NO; // make it system time based
animation.repeatCount = FLT_MAX; // make it repeat forever
animation.speed = 0.2;
[child addAnimation:animation forKey:key]; // animations are copied upon addition, so we have to replace the previous animation
}
_present = child.presentationNode;
}];
Then Under the render i can check the Results which is changing as the animation moves (Also if you have worked with iOS11 you can check worldPosition instead of position it gives great results too).
- (void)renderer:(id <SCNSceneRenderer>)renderer didSimulatePhysicsAtTime:(NSTimeInterval)time
{
NSLog(#" Location X:%f Y:%f Z:%f", _present.position.x, _present.position.y, _present.position.z);
}
Finally Here Is The Results
Y:-2.505034 Z:4.426160
2017-10-11 04:42:19.700435+0200 monkey[547:137368] Location X:-5.266642 Y:-2.480119 Z:4.427162
2017-10-11 04:42:19.720763+0200 monkey[547:137368] Location X:-5.215315 Y:-2.455228 Z:4.428163
2017-10-11 04:42:19.740449+0200 monkey[547:137346] Location X:-5.163589 Y:-2.430143 Z:4.429171
2017-10-11 04:42:19.760397+0200 monkey[547:137368] Location X:-5.112190 Y:-2.405216 Z:4.430173
2017-10-11 04:42:19.780557+0200 monkey[547:137346] Location X:-5.060925 Y:-2.380355 Z:4.431173
2017-10-11 04:42:19.800418+0200 monkey[547:137342] Location X:-5.008560 Y:-2.355031 Z:4.432024
This Give Me Good Results. Hope it would work with you.

There's no animation running, at least not in the code you've shown us. Check the presentation node's position while the animation is playing, perhaps in renderer:updateAtTime: (see SCNRendererDelegate).

Related

How to Draw circle around current Location like city mapper. (iOS 8 -Objective C)

I have a app which tag locations. I have successfully added pins to the Mapview. But i have no idea how to draw circle of radius 100 meters start from my current location and How to make it move with current location.
[iOS 8 Objective-C]
[Circle like city Mapper App!]1
Finally i have managed to create Circle.
MKCircle *circleoverlay = [MKCircle circleWithCenterCoordinate:mapView.userLocation.coordinate radius:100];
[circleoverlay setTitle:#"Circle"];
[mapView addOverlay:circleoverlay];
and than Rendering
- (MKOverlayRenderer *) mapView:(MKMapView *)mapView rendererForOverlay:(id)overlay
{ if([overlay isKindOfClass:[MKCircle class]])
{
MKCircleRenderer* aRenderer = [[MKCircleRenderer
alloc]initWithCircle:(MKCircle *)overlay];
aRenderer.fillColor = [[UIColor blueColor] colorWithAlphaComponent:0.0];
aRenderer.strokeColor = [[UIColor grayColor] colorWithAlphaComponent:0.9];
aRenderer.lineWidth = 2;
aRenderer.lineDashPattern = #[#2, #5];
aRenderer.alpha = 0.5;
return aRenderer;
}
else
{
return nil;
}
}
Now i only want to Overlay Text on the circle.
but i dont have any idea :(

Detecting and ignoring touch on non-transparent part of MKOverlay drawn Image in iOS8

I have a overlay image (.png) on my map that consists of a transparent bit in the middle, and colored sides so the user can only focus on the middle part. However do to the shape of that middle bit, quite a bit is visible at some sides.
I'm trying to detect a tap on the OverlayView so I can ignore it and only accept touches in the designated area.
I followed the following tut at Ray Wenderlich's site for adding the overlay:
The image overlay is drawn like this:
#implementation PVParkOverlayView
- (instancetype)initWithOverlay:(id<MKOverlay>)overlay overlayImage:(UIImage *)overlayImage {
self = [super initWithOverlay:overlay];
if (self) {
_overlayImage = overlayImage;
}
return self;
}
- (void)drawMapRect:(MKMapRect)mapRect zoomScale:(MKZoomScale)zoomScale inContext:(CGContextRef)context {
CGImageRef imageReference = self.overlayImage.CGImage;
//UIImage *imageTest = _overlayImage;
MKMapRect theMapRect = self.overlay.boundingMapRect;
CGRect theRect = [self rectForMapRect:theMapRect];
//orientation testing
//CGContextRotateCTM(context, 0);
CGContextScaleCTM(context, 1.0, -1.0);
CGContextTranslateCTM(context, 0.0, -theRect.size.height);
CGContextDrawImage(context, theRect, imageReference);
}
I have a gesture recognizer on my mapview and am trying to detect the tap there:
- (void)handleGesture:(UIGestureRecognizer *)gestureRecognizer
{
CGPoint tapPoint = [gestureRecognizer locationInView:self.mapView];
CLLocationCoordinate2D tapCoord = [self.mapView convertPoint:tapPoint toCoordinateFromView:self.mapView];
MKMapPoint mapPoint = MKMapPointForCoordinate(tapCoord);
CGPoint mapPointAsCGP = CGPointMake(mapPoint.x, mapPoint.y);
for (id<MKOverlay> overlay in self.mapView.overlays) {
if([overlay isKindOfClass:[PVParkOverlay class]]){
NSLog(#"overlay is present");
/*
MKPolygon *polygon = (MKPolygon*) overlay;
CGMutablePathRef mpr = CGPathCreateMutable();
MKMapPoint *polygonPoints = polygon.points;
for (int p=0; p < polygon.pointCount; p++){
MKMapPoint mp = polygonPoints[p];
if (p == 0)
CGPathMoveToPoint(mpr, NULL, mp.x, mp.y);
else
CGPathAddLineToPoint(mpr, NULL, mp.x, mp.y);
}
if(CGPathContainsPoint(mpr , NULL, mapPointAsCGP, FALSE)){
// ... found it!
NSLog(#"I've found it!");
}
//CGPathRelease(mpr);
*/
}
}
I know that the overlay is there, but since it is a drawn image I can't find a way to convert this to polygon points to use this code (if even possible).
Any other methods I can use for this?
I also found following sample code but the viewForOverlay method is deprecated:
- (void)mapTapped:(UITapGestureRecognizer *)recognizer
{
MKMapView *mapView = (MKMapView *)recognizer.view;
id<MKOverlay> tappedOverlay = nil;
for (id<MKOverlay> overlay in mapView.overlays)
{
MKOverlayView *view = [mapView viewForOverlay:overlay];
if (view)
{
// Get view frame rect in the mapView's coordinate system
CGRect viewFrameInMapView = [view.superview convertRect:view.frame toView:mapView];
// Get touch point in the mapView's coordinate system
CGPoint point = [recognizer locationInView:mapView];
// Check if the touch is within the view bounds
if (CGRectContainsPoint(viewFrameInMapView, point))
{
tappedOverlay = overlay;
break;
}
}
}
NSLog(#"Tapped view: %#", [mapView viewForOverlay:tappedOverlay]);
}

Sprite Kit: How To Subclass SKNode to create a Vehicle class (complex object with joints)

Has anyone figured out (natively) how to subclass SKNode to include multiple bodies connect by joints - for instance, a car?
There doesn't seem to be a way to add the sub class's joints to the parent scene's physicsWorld property.
Also, when trying to compile and run the object below, even without the joint, I get a BAD_EXC_ACCESS error.
Thank you #Smick for the initial vehicle code as posted here:Sprite Kit pin joints appear to have an incorrect anchor
Truck Class:
#import "Truck.h"
#implementation Truck
-(id)initWithPosition:(CGPoint)pos {
SKSpriteNode *carBody = [SKSpriteNode spriteNodeWithColor:[UIColor whiteColor] size:CGSizeMake(120, 8)];
carBody.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:carBody.size];
carBody.position = pos;
carBody.physicsBody.mass = 1.0;
SKSpriteNode *carTop = [SKSpriteNode spriteNodeWithColor:[UIColor whiteColor] size:CGSizeMake(50, 8)];
carTop.position = CGPointMake(230, 708);
carTop.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:carTop.size];
carTop.physicsBody.mass = 0;
SKPhysicsJointFixed *carBodyJoint = [SKPhysicsJointFixed jointWithBodyA:carBody.physicsBody
bodyB:carTop.physicsBody
anchor:CGPointMake(0, 0)];
return self;
}
+(Truck*)initWithPosition:(CGPoint)pos {
return [[self alloc] initWithPosition:pos];
}
#end
MyScene:
sorry for late post, but just hit this myself.
Physics joints dont work unless the nodes being attached have already been added to the SKScene's scene graph.
During the initWithPosition above, that's not the case.
Passing in a SKScene also didn't work for me, I suspect because the Vehicle node still hasn't been added to the scene graph.
You can still encapsulate your physics joints within the class, but you have to call another method after the
[self addChild:car]
Here's my refinement on what you already had:
Vehicle.h
#interface Vehicle : SKNode
#property (nonatomic) SKSpriteNode *leftWheel;
#property (nonatomic) SKSpriteNode *ctop;
-(id)initWithPosition:(CGPoint)pos;
-(void)initPhysics;
#end
Vehicle.m
//
#import "Vehicle.h"
#implementation Vehicle {
SKSpriteNode *chassis;
SKSpriteNode *rightWheel;
SKSpriteNode *leftShockPost;
SKSpriteNode *rightShockPost;
int wheelOffsetY;
CGFloat damping;
CGFloat frequency;
}
- (SKSpriteNode*) makeWheel
{
SKSpriteNode *wheel = [SKSpriteNode spriteNodeWithImageNamed:#"Wheel.png"];
// wheel.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:wheel.size.width/2];
return wheel;
}
-(id)initWithPosition:(CGPoint)pos {
if (self = [super init]) {
wheelOffsetY = 60;
damping = 1;
frequency = 4;
chassis = [SKSpriteNode spriteNodeWithColor:[UIColor whiteColor] size:CGSizeMake(120, 8)];
chassis.position = pos;
[self addChild:chassis];
_ctop = [SKSpriteNode spriteNodeWithColor:[UIColor greenColor] size:CGSizeMake(70, 16)];
_ctop.position = CGPointMake(chassis.position.x+20, chassis.position.y+12);
[self addChild:_ctop];
_leftWheel = [self makeWheel];
_leftWheel.position = CGPointMake(chassis.position.x - chassis.size.width / 2, chassis.position.y - wheelOffsetY); //Always set position before physicsBody
[self addChild:_leftWheel];
rightWheel = [self makeWheel];
rightWheel.position = CGPointMake(chassis.position.x + chassis.size.width / 2, chassis.position.y - wheelOffsetY);
[self addChild:rightWheel];
//------------- LEFT SUSPENSION ----------------------------------------------------------------------------------------------- //
leftShockPost = [SKSpriteNode spriteNodeWithColor:[UIColor blueColor] size:CGSizeMake(7, wheelOffsetY)];
leftShockPost.position = CGPointMake(chassis.position.x - chassis.size.width / 2, chassis.position.y - leftShockPost.size.height/2);
[self addChild:leftShockPost];
//------------- RIGHT SUSPENSION ----------------------------------------------------------------------------------------------- //
rightShockPost = [SKSpriteNode spriteNodeWithColor:[UIColor blueColor] size:CGSizeMake(7, wheelOffsetY)];
rightShockPost.position = CGPointMake(chassis.position.x + chassis.size.width / 2, chassis.position.y - rightShockPost.size.height/2);
[self addChild:rightShockPost];
}
return self;
}
-(void) initPhysics {
chassis.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:chassis.size];
_ctop.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:_ctop.size];
SKPhysicsJointFixed *cJoint = [SKPhysicsJointFixed jointWithBodyA:chassis.physicsBody
bodyB:_ctop.physicsBody
anchor:CGPointMake(_ctop.position.x, _ctop.position.y)];
_leftWheel.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:_leftWheel.size.width/2];
_leftWheel.physicsBody.allowsRotation = YES;
rightWheel.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:rightWheel.size.width/2];
rightWheel.physicsBody.allowsRotation = YES;
leftShockPost.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:leftShockPost.size];
SKPhysicsJointSliding *leftSlide = [SKPhysicsJointSliding jointWithBodyA:chassis.physicsBody
bodyB:leftShockPost.physicsBody
anchor:CGPointMake(leftShockPost.position.x, leftShockPost.position.y)
axis:CGVectorMake(0, 1)];
leftSlide.shouldEnableLimits = TRUE;
leftSlide.lowerDistanceLimit = 5;
leftSlide.upperDistanceLimit = wheelOffsetY;
SKPhysicsJointSpring *leftSpring = [SKPhysicsJointSpring jointWithBodyA:chassis.physicsBody bodyB:_leftWheel.physicsBody
anchorA:CGPointMake(chassis.position.x - chassis.size.width / 2, chassis.position.y)
anchorB:_leftWheel.position];
leftSpring.damping = damping;
leftSpring.frequency = frequency;
SKPhysicsJointPin *lPin = [SKPhysicsJointPin jointWithBodyA:leftShockPost.physicsBody bodyB:_leftWheel.physicsBody anchor:_leftWheel.position];
rightShockPost.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:rightShockPost.size];
SKPhysicsJointSliding *rightSlide = [SKPhysicsJointSliding jointWithBodyA:chassis.physicsBody
bodyB:rightShockPost.physicsBody
anchor:CGPointMake(rightShockPost.position.x, rightShockPost.position.y)
axis:CGVectorMake(0, 1)];
rightSlide.shouldEnableLimits = TRUE;
rightSlide.lowerDistanceLimit = 5;
rightSlide.upperDistanceLimit = wheelOffsetY;
SKPhysicsJointSpring *rightSpring = [SKPhysicsJointSpring jointWithBodyA:chassis.physicsBody bodyB:rightWheel.physicsBody
anchorA:CGPointMake(chassis.position.x + chassis.size.width / 2, chassis.position.y)
anchorB:rightWheel.position];
rightSpring.damping = damping;
rightSpring.frequency = frequency;
SKPhysicsJointPin *rPin = [SKPhysicsJointPin jointWithBodyA:rightShockPost.physicsBody bodyB:rightWheel.physicsBody anchor:rightWheel.position];
// Add all joints to the array.
// Add joints to scene's physics world
[self.scene.physicsWorld addJoint: cJoint];
[self.scene.physicsWorld addJoint: leftSlide];
[self.scene.physicsWorld addJoint: leftSpring];
[self.scene.physicsWorld addJoint: lPin];
[self.scene.physicsWorld addJoint: rightSlide];
[self.scene.physicsWorld addJoint: rightSpring];
[self.scene.physicsWorld addJoint: rPin];
}
#end
and call it from MyScene.m
_car = [[DMVehicle alloc] initWithPosition:location];
[self addChild:_car];
[_car initPhysics];
Hope that helps, I know it has helped me by working through it
So here's what I came up with.
My only issue with this is that it isn't completely self contained as joints must be added to the physics world outside of the class - simple enough as you'll see using two lines of code.
Editing my answer. The suspension requires the wheels to be attached to a sliding body versus attaching the wheels via the slide joint. Doing the former allows wheels to rotate. The latter does not.
Update: I've unmarked this as the answer. Reason being is that, while it runs fine on the simulator, I receive the following error when I attempt to run it on my iPad (running iOS7). When I remove my vehicle class and place one of the vehicle's components that use that UIColor method into my main scene, the error is not thrown.
UICachedDeviceWhiteColor addObject:]: unrecognized selector sent to instance 0x15e21360
2013-12-14 22:44:19.790 SKTestCase[1401:60b] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[UICachedDeviceWhiteColor addObject:]: unrecognized selector sent to instance
For some reason, I no longer receive the UICashed... error (yes, vehicle is still a class) Now I receive:
-[PKPhysicsJointWeld name]: unrecognized selector sent to instance 0x1464f810
2013-12-15 15:28:24.081 MTC[1747:60b] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[PKPhysicsJointWeld name]: unrecognized selector sent to instance 0x1464f810'
*** First throw call stack:
(0x30eaff53 0x3b5186af 0x30eb38e7 0x30eb21d3 0x30e01598 0x3352837d 0x335284d5 0x33527b97 0x33525ca5 0x87385 0x335064f5 0x87ccb 0x33620fe1 0x332aa24b 0x332a5a5b 0x332d4b7d 0x3369e39b 0x3369ca03 0x3369bc53 0x3369bbdb 0x3369bb73 0x33694681 0x3362703f 0x3369b8c1 0x3369b38d 0x3362c21d 0x33629763 0x33694a55 0x33691811 0x3368bd13 0x336266a7 0x336259a9 0x3368b4fd 0x35ab270d 0x35ab22f7 0x30e7a9e7 0x30e7a983 0x30e79157 0x30de3ce7 0x30de3acb 0x3368a799 0x33685a41 0x8a52d 0x3ba20ab7)
libc++abi.dylib: terminating with uncaught exception of type NSException
Vehicle.h:
#import <SpriteKit/SpriteKit.h>
#interface Vehicle : SKNode
#property (nonatomic,assign) NSMutableArray *joints;
#property (nonatomic) SKSpriteNode *leftWheel;
#property (nonatomic) SKSpriteNode *ctop;
-(id)initWithPosition:(CGPoint)pos;
#end
Vehicle.m
#import "Vehicle.h"
#implementation Vehicle
- (SKSpriteNode*) makeWheel
{
SKSpriteNode *wheel = [SKSpriteNode spriteNodeWithImageNamed:#"wheel.png"];
// wheel.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:wheel.size.width/2];
return wheel;
}
-(id)initWithPosition:(CGPoint)pos {
if (self = [super init]) {
_joints = [NSMutableArray array];
int wheelOffsetY = 60;
CGFloat damping = 1;
CGFloat frequency = 4;
SKSpriteNode *chassis = [SKSpriteNode spriteNodeWithColor:[UIColor whiteColor] size:CGSizeMake(120, 8)];
chassis.position = pos;
chassis.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:chassis.size];
[self addChild:chassis];
_ctop = [SKSpriteNode spriteNodeWithColor:[UIColor greenColor] size:CGSizeMake(70, 16)];
_ctop.position = CGPointMake(chassis.position.x+20, chassis.position.y+12);
_ctop.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:_ctop.size];
[self addChild:_ctop];
SKPhysicsJointFixed *cJoint = [SKPhysicsJointFixed jointWithBodyA:chassis.physicsBody
bodyB:_ctop.physicsBody
anchor:CGPointMake(_ctop.position.x, _ctop.position.y)];
_leftWheel = [self makeWheel];
_leftWheel.position = CGPointMake(chassis.position.x - chassis.size.width / 2, chassis.position.y - wheelOffsetY); //Always set position before physicsBody
_leftWheel.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:_leftWheel.size.width/2];
_leftWheel.physicsBody.allowsRotation = YES;
[self addChild:_leftWheel];
SKSpriteNode *rightWheel = [self makeWheel];
rightWheel.position = CGPointMake(chassis.position.x + chassis.size.width / 2, chassis.position.y - wheelOffsetY);
rightWheel.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:rightWheel.size.width/2];
rightWheel.physicsBody.allowsRotation = YES;
[self addChild:rightWheel];
//------------- LEFT SUSPENSION ----------------------------------------------------------------------------------------------- //
SKSpriteNode *leftShockPost = [SKSpriteNode spriteNodeWithColor:[UIColor blueColor] size:CGSizeMake(7, wheelOffsetY)];
leftShockPost.position = CGPointMake(chassis.position.x - chassis.size.width / 2, chassis.position.y - leftShockPost.size.height/2);
leftShockPost.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:leftShockPost.size];
[self addChild:leftShockPost];
SKPhysicsJointSliding *leftSlide = [SKPhysicsJointSliding jointWithBodyA:chassis.physicsBody
bodyB:leftShockPost.physicsBody
anchor:CGPointMake(leftShockPost.position.x, leftShockPost.position.y)
axis:CGVectorMake(0, 1)];
leftSlide.shouldEnableLimits = TRUE;
leftSlide.lowerDistanceLimit = 5;
leftSlide.upperDistanceLimit = wheelOffsetY;
SKPhysicsJointSpring *leftSpring = [SKPhysicsJointSpring jointWithBodyA:chassis.physicsBody bodyB:_leftWheel.physicsBody
anchorA:CGPointMake(chassis.position.x - chassis.size.width / 2, chassis.position.y)
anchorB:_leftWheel.position];
leftSpring.damping = damping;
leftSpring.frequency = frequency;
SKPhysicsJointPin *lPin = [SKPhysicsJointPin jointWithBodyA:leftShockPost.physicsBody bodyB:_leftWheel.physicsBody anchor:_leftWheel.position];
//------------- RIGHT SUSPENSION ----------------------------------------------------------------------------------------------- //
SKSpriteNode *rightShockPost = [SKSpriteNode spriteNodeWithColor:[UIColor blueColor] size:CGSizeMake(7, wheelOffsetY)];
rightShockPost.position = CGPointMake(chassis.position.x + chassis.size.width / 2, chassis.position.y - rightShockPost.size.height/2);
rightShockPost.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:rightShockPost.size];
[self addChild:rightShockPost];
SKPhysicsJointSliding *rightSlide = [SKPhysicsJointSliding jointWithBodyA:chassis.physicsBody
bodyB:rightShockPost.physicsBody
anchor:CGPointMake(rightShockPost.position.x, rightShockPost.position.y)
axis:CGVectorMake(0, 1)];
rightSlide.shouldEnableLimits = TRUE;
rightSlide.lowerDistanceLimit = 5;
rightSlide.upperDistanceLimit = wheelOffsetY;
SKPhysicsJointSpring *rightSpring = [SKPhysicsJointSpring jointWithBodyA:chassis.physicsBody bodyB:rightWheel.physicsBody
anchorA:CGPointMake(chassis.position.x + chassis.size.width / 2, chassis.position.y)
anchorB:rightWheel.position];
rightSpring.damping = damping;
rightSpring.frequency = frequency;
SKPhysicsJointPin *rPin = [SKPhysicsJointPin jointWithBodyA:rightShockPost.physicsBody bodyB:rightWheel.physicsBody anchor:rightWheel.position];
// Add all joints to the array.
[_joints addObject:cJoint];
[_joints addObject:leftSlide];
[_joints addObject:leftSpring];
[_joints addObject:lPin];
[_joints addObject:rightSlide];
[_joints addObject:rightSpring];
[_joints addObject:rPin];
}
return self;
}
#end
MyScene.m:
#import "MyScene.h"
#import "Vehicle.h"
#implementation MyScene {
Vehicle *car;
}
-(id)initWithSize:(CGSize)size {
if (self = [super initWithSize:size]) {
/* Setup your scene here */
self.physicsBody = [SKPhysicsBody bodyWithEdgeLoopFromRect:CGRectMake(0, 0, self.size.width, self.size.height)];
}
return self;
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
/* Called when a touch begins */
for (UITouch *touch in touches) {
CGPoint location = [touch locationInNode:self];
car = [[Vehicle alloc] initWithPosition:location];
[self addChild:car];
// Add joints to scene's physics world
for (SKPhysicsJoint *j in car.joints) {
[self.physicsWorld addJoint:j];
}
}
}
1) Adding joints: Why don't you just have the vehicle's init method takes an SKScene object? Then you can add the joint within that method. Otherwise, what you have done with the _joints array works, but seems less clean with the extra code needed there.
2) BAD_EXC_ACCESS: I got this as well when I was learning about joints. It went away when the nodes participating in the joint were added to SKScene and not some other sub nodes. The closest info in the Apple documentation that I can find is this: "Attach the physics bodies to a pair of SKNode objects in the scene." That doesn't specify whether this means directly to the SKScene, or to any node within the node tree in the SKScene.
I had the same issue, and this way works for me:
you will receive an Exc_bad_access if you try to add the joint(defined in the truck class)from your scene. You have to run
[self.scene.physicsWorld addJoint:joint]
in your Truck class.
However, you can't add the
[self.scene.physicsWorld addJoint:joint]
into your init method of the Truck, because the Truck has not been added into your scene when running the Truck's init method in your scene.
You'll have to write another method (let's say addJointsIntoScene) in your Truck class to run
[self.scene.physicsWorld addJoint:joint].
After adding your Truck to your scene, run the 'addJointsIntoScene' method to add the joint.
for example,
Truck.m
-(instancetype)initWithPosition:(CGPoint) triggerPosition{
....
joint = .....
....
}
//and another method
-(void)addJointsIntoScene{
[self.scene.physicsWorld addJoint:joint];enter code here
}
MyScene.m
Truck *truck = [[Truck alloc]initWithPosition:triggerPosition];
[self addChild:truck];
[truck addJointsIntoScene];

Assertion failure in +[CCSprite spriteWithSpriteFrameName:]?

I'm getting an Assertion failure in +[CCSprite spriteWithSpriteFrameName:] for some reason.
I'm basically trying to "updatePinkBerries" or in other words, creating more sprite 'berries' to be added to the screen. They are the 'enemies' in my game.
This is my code:
- (id) init
{
if((self = [super init]))
{
CCLOG(#"%# init", NSStringFromClass([self class]));
self.touchEnabled = YES;
[self scheduleUpdate];
}
return self;
}
-(void)updatePinkBerries:(ccTime)dt
{
CGSize winSize = [CCDirector sharedDirector].winSize;
double curTime = CACurrentMediaTime();
if (curTime > _nextPinkBerrySpawn)
{
// Figure out the next time to spawn an asteroid
float randSecs = randomValueBetween(0.20, 1.0); _nextPinkBerrySpawn = randSecs + curTime;
// Figure out a random Y value to spawn at
float randY = randomValueBetween(0.0, winSize.height);
// Figure out a random amount of time to move from right to left
float randDuration = randomValueBetween(2.0, 10.0);
// Create a new asteroid sprite
CCSprite *pinkBerry = [CCSprite spriteWithSpriteFrameName:#"ship.png"]; [_batchNode addChild:pinkBerry];
// Set its position to be offscreen to the right
pinkBerry.position = ccp(winSize.width + pinkBerry.contentSize.width/2, randY);
// Move it offscreen to the left, and when it's done, call removeNode
[pinkBerry runAction:
[CCSequence actions:
[CCMoveBy actionWithDuration:randDuration position:ccp(-winSize.width- pinkBerry.contentSize.width, 0)],
[CCCallFuncN actionWithTarget:self selector:#selector(removeNode:)], nil]];
}
}
EDIT: FULL ASSERTION LOG:
2013-03-31 19:39:09.225 Berry Muncher-iOS[19550:c07] -[CCFileUtils
fullPathForFilename:resolutionType:] : cocos2d: Warning: File not
found: Sprites.plist 2013-03-31 19:39:09.225 Berry
Muncher-iOS[19550:c07] cocos2d: CCSpriteFrameCache: Trying to use file
'Sprites.png' as texture 2013-03-31 19:39:09.425 Berry
Muncher-iOS[19550:c07] cocos2d: CCSpriteFrameCache: Frame 'ship.png'
not found 2013-03-31 19:39:09.426 Berry Muncher-iOS[19550:c07] ***
Assertion failure in +[CCSprite spriteWithSpriteFrameName:],
/Users/Suraya/Desktop/Kobold2D/Kobold2D-2.1.0/Kobold2D/libs/cocos2d-iphone/cocos2d/CCSprite.m:105
EDIT: screenshot of my plist
I recognize this from a Ray Wanderlich tutorial. Because he has different artwork than you might use, you should create your own CCSpriteBatchNode and accompanying plist file so item names from his project don't get mixed up with yours.

Cocos2d: Animation based on accelerometer

Anyone know of any good up to date tutorials out there that show how can one animate a sprite based on accelerometer movement. I want to animate a bird to sway to the position the device was pointed to. For example if the player decides to move the bird to the left via the accelerometer I would like for my bird to play an animation that is swaying to the left.
// Accelerometer
-(void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration {
birdSpeedY = 9.0 + acceleration.x*15;
birdSpeedX = -acceleration.y*20;
}
// Updating bird based on accelerometer
-(void)updateBird {
float maxY = winSize.height - bird.contentSize.height/2;
float minY = bird.contentSize.height/2;
float newY = bird.position.y + birdSpeedY;
newY = MIN(MAX(newY, minY), maxY);
float maxX = winSize.width - bird.contentSize.width/2;
float minX = bird.contentSize.width/2;
float newX = bird.position.x + birdSpeedX;
newX = MIN(MAX(newX, minX), maxX);
bird.position = ccp(newX, newY);
}
// Making background scroll automatically
-(void)update:(ccTime)dt {
[self updateBird];
CGPoint backgroundScrollVel = ccp(-100, 0);
parallaxNode.position = ccpAdd(parallaxNode.position, ccpMult(backgroundScrollVel, dt));
}
-(id)init {
self = [super init];
if (self != nil) {
winSize = [CCDirector sharedDirector].winSize;
CCSpriteFrameCache *cache=[CCSpriteFrameCache sharedSpriteFrameCache];
[cache addSpriteFramesWithFile:#"birdAtlas.plist"];
NSMutableArray *framesArray=[NSMutableArray array];
for (int i=1; i<10; i++) {
NSString *frameName=[NSString stringWithFormat:#"bird%d.png", i];
id frameObject=[cache spriteFrameByName:frameName];
[framesArray addObject:frameObject];
}
// animation object
id animObject=[CCAnimation animationWithFrames:framesArray delay:0.1];
// animation action
id animAction=[CCAnimate actionWithAnimation:animObject restoreOriginalFrame:NO];
animAction=[CCRepeatForever actionWithAction:animAction];
bird=[CCSprite spriteWithSpriteFrameName:#"bird1.png"];
bird.position=ccp(60,160);
CCSpriteBatchNode *batchNode=[CCSpriteBatchNode batchNodeWithFile:#"birdAtlas.png"];
[self addChild:batchNode z:100];
[batchNode addChild:bird];
[bird runAction:animAction];
self.isAccelerometerEnabled = YES;
[self scheduleUpdate];
[self addScrollingBackgroundWithTileMapInsideParallax];
}
return self;
}
- (void) dealloc
{
[super dealloc];
}
#end
You can try the Accelerometer methods with it and change the position of the Sprite using ccp(). You also need to know is that project for the Landscape or Portrait in the Mode.
You Can Try the Stuff below
- (void)accelerometer:(UIAccelerometer*)accelerometer didAccelerate:(UIAcceleration*)acceleration
{
[lbl setString:[NSString stringWithFormat:#"X=>%.2lf Y=>%.2lf",(double)acceleration.x,(double)acceleration.y]];
double x1= -acceleration.y *10;
double y1= acceleration.x *15;
if(acceleration.x >0.05)
{
y1*=spped_incr; // Make Movement Here
}
[Sprite_Name runAction:[CCMoveTo actionWithDuration:0.1f position:ccpAdd(ccp(x1,y1), Sprite_Name.position)]];
}
The Above Stuff is for the Landscape Mode.... if You need in Portrait Mode you need to change the Axis and use the TRY & Error Method.

Resources