I am looking to shoot a SKSpritenode (a cannonball) from a another Sprite node (enemy ship).
The cannonball should travel directly down toward bottom of screen from the enemy ship node.
I can't seem to get positioning right, cannonball seems to shoot from the corner of the screen and not move far, the enemy-ship node is randomly chosen from an array of all halos on screen at once and the cannonball position assigned to the front of halo:
// the cannonball
ball.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:6];
ball.physicsBody.velocity = CGVectorMake(SHOT_SPEED, SHOT_SPEED);
// we dont want ball to lose speed or momentum when it hots edges so..
ball.physicsBody.restitution=1.0; // max bounceness of node
ball.physicsBody.linearDamping =0.0; // dont reduce linear velocity ove time
ball.physicsBody.friction=0.0;
ball.physicsBody.categoryBitMask = kCCBallCategory;
// ball.physicsBody.contactTestBitMask = kCCEdgeCategory;
ball.physicsBody.collisionBitMask= kCCEdgeCategory;
ball.physicsBody.contactTestBitMask = kCCEdgeCategory | KCCShieldUPCategory | kCCMultiUpCategory ; // | KCCShieldUPCategory notify
// the array of enemy ship nodes
NSMutableArray *allHalos = [NSMutableArray array];
[_mainLayer enumerateChildNodesWithName:#"halos" usingBlock:^(SKNode *node, BOOL *stop) {
[allHalos addObject:node];
}];
if ([allHalos count]>0) {
NSUInteger allHalosInteger = arc4random_uniform([allHalos count]);
SKSpriteNode *shooterHalo=[allHalos objectAtIndex:allHalosInteger];
ball.position = CGPointMake(shooterHalo.position.x, shooterHalo.position.y - shooterHalo.frame.size.height/2 + ball.frame.size.height / 2);
CGPoint bulletDestination = CGPointMake(shooterHalo.position.x, - ball.frame.size.height / 2);
[self fireBullet:ball toDestination:bulletDestination withDuration:2.0 ];
}
-(void)fireBullet:(SKNode*)ball toDestination:(CGPoint)destination withDuration:(NSTimeInterval)duration {
//1
SKAction* bulletAction = [SKAction sequence:#[[SKAction moveTo:destination duration:duration],
[SKAction waitForDuration:3.0/60.0],
[SKAction removeFromParent]]];
[ball runAction:[SKAction group:#[bulletAction, _soundLaser]]];
[self addChild:ball];
}
Any input appreciated.
Related
I am currently creating an app which requires numerous overlays of different sizes to be drawn on a map. The app currently grabs 2 map points which represent the corners of a bounding box in which the overlay should sit. I am then converting these in to a MKMapRect for use within an MKOverlay class. The overlay is drawing in the correct place but the image seems to be flipped vertically, I can tell this as I tried applying a CGAffineTransformMakeRotation(180) and the image appeared the right way around but the writing was backwards.
If anyone can help me figure out why it's doing this it would be much appreciated, please see my code below.
Setup code:
// First grab the overlay co-ordinates
double left = [[self.storedWildMapObject.arrayBBox objectAtIndex:0] doubleValue], bottom = [[self.storedWildMapObject.arrayBBox objectAtIndex:1] doubleValue], right = [[self.storedWildMapObject.arrayBBox objectAtIndex:2] doubleValue], top = [[self.storedWildMapObject.arrayBBox objectAtIndex:3] doubleValue];
// Store these in 2 coordinates representing top left / bot right
CLLocationCoordinate2D upperLeftCoord =
CLLocationCoordinate2DMake(top,left);
CLLocationCoordinate2D lowerRightCoord =
CLLocationCoordinate2DMake(bottom,right);
//Convert to map points
MKMapPoint upperLeft = MKMapPointForCoordinate(upperLeftCoord);
MKMapPoint lowerRight = MKMapPointForCoordinate(lowerRightCoord);
// Work out sizes
double rightToLeftDiff = lowerRight.y - upperLeft.y;
double topToBottDiff = lowerRight.x - upperLeft.x;
MKMapRect bounds = MKMapRectMake(upperLeft.x, upperLeft.y, topToBottDiff, rightToLeftDiff);
// Add to overlay
MKMapOverlay *mapOverlay = [[MKMapOverlay alloc] initWithMapRect:bounds andCoord:upperLeftCoord];
[self.mapV addOverlay:mapOverlay];
// Map overlay
- (id)initWithMapRect:(MKMapRect)rect andCoord:(CLLocationCoordinate2D)coord
{
self = [super init];
if (self)
{
self.centerPos = coord;
self.mkMapRect = rect;
}
return self;
}
-(CLLocationCoordinate2D)coordinate
{
return self.centerPos;
}
- (MKMapRect)boundingMapRect
{
return self.mkMapRect;
}
// MapOverlayView
- (id)initWithOverlay:(id <MKOverlay>)overlay andImage:(UIImage *)img
{
self = [super initWithOverlay:overlay];
if (self)
{
self.image = img;
}
return self;
}
/** Degrees to Radian **/
#define degreesToRadians( degrees ) ( ( degrees ) / 180.0 * M_PI )
/** Radians to Degrees **/
#define radiansToDegrees( radians ) ( ( radians ) * ( 180.0 / M_PI ) )
- (void)drawMapRect:(MKMapRect)mapRect zoomScale:(MKZoomScale)zoomScale inContext:(CGContextRef)ctx
{
CGImageRef imageReference = self.image.CGImage;
MKMapRect theMapRect = [self.overlay boundingMapRect];
CGRect theRect = [self rectForMapRect:theMapRect];
CGRect clipRect = [self rectForMapRect:mapRect];
CGContextAddRect(ctx, clipRect);
CGContextClip(ctx);
//CGContextRotateCTM(ctx, degreesToRadians(180.0f));
CGContextDrawImage(ctx, theRect, imageReference);
}
Just incase it helps I also have a debugger print out of theMapRect as used in MapOverlayView:
(lldb) p theMapRect
(MKMapRect) $17 = {
(MKMapPoint) origin = {
(double) x = 1.27662e+08
(double) y = 7.86099e+07
}
(MKMapSize) size = {
(double) width = 8.12378e+06
(double) height = 1.27474e+07
}
}
Below is what it looks like:
Have you verified that the storedwildmapobject has the coordinates in the order you are expecting them?
The answer found here seems to draw my image the correct way around with the same mapRect:
https://stackoverflow.com/a/3599448/1090024
So I'm using this for now.
Resize MKAnnotationView Image When map zooms in and out? This methord are successful on iOS5, but failed on iOS6.
I change the MKAnnotationView's transform directly, and no luck. The MKAnnotationView only resize in a flash(When touch up inside the MKMapView, after the touch up finish, the MKAnnotationView will restore the original size).
My code are below:
- (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated {
for (id <MKAnnotation>annotation in _mapView.annotations) {
// if it's the user location, just return nil.
if ([annotation isKindOfClass:[MKUserLocation class]])
continue;
// handle our custom annotations
//
if ([annotation isKindOfClass:[XPMKAnnotation class]])
{
// try to retrieve an existing pin view first
MKAnnotationView *pinView = [_mapView viewForAnnotation:annotation];
//resize the pin view
double zoomLevel = [_mapView getZoomLevel];
double scale = (1.0 * zoomLevel / 16) + 0.5;
pinView.transform = CGAffineTransformMakeScale(scale, scale);
}
}
}
Can we resize the MKAnnotationView on iOS6? Anybody know any way?
Apple suggests resizing the .image property of an annotation view. In my case shown below, I looked at the UIImage that was set in the viewforAnnotation and re-scaled it to the zoom level in a UIPinchGestureRecognizer
UIImage *orangeImage = [UIImage imageNamed:#"Orange210.PNG"];
CGRect resizeRect;
//rescale image based on zoom level
resizeRect.size.height = orangeImage.size.height * scale;
resizeRect.size.width = orangeImage.size.width * scale ;
NSLog(#"height = %f, width = %f, zoomLevel = %f", resizeRect.size.height, resizeRect.size.width,zoomLevel );
resizeRect.origin = (CGPoint){0,0};
UIGraphicsBeginImageContext(resizeRect.size);
[orangeImage drawInRect:resizeRect];
UIImage *resizedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
pinView.image = resizedImage;
I am trying to built an animated circle which would be drawn clockwise until it becomes complete circle as illustrated in iPhone Core Animation - Drawing a Circle
Problem is that CALayer object is not added or build. I tested and saw that it is not accessing my drawInContext:CGContextRef and animatingArc methods.
What so far I have done is:
In AnimateArc.h
#interface AnimateArc : CALayer {
CAShapeLayer *circle;
}
-(void) animatingArc;
#end
In AnimateArc.m
-(void) drawInContext:(CGContextRef)ctx
{
CGFloat radius = 50.0;
circle = [CAShapeLayer layer];
//make a circular shape
circle.path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(0.0, 0.0, 2 * radius, 2 * radius) cornerRadius:radius].CGPath;
CGPoint centerPoint = CGPointMake(CGRectGetWidth(self.bounds)/2, CGRectGetHeight(self.bounds)/2);
//center the shape in self.view
circle.position = centerPoint;
//configure appearence of circle
circle.fillColor = [UIColor clearColor].CGColor;
circle.strokeColor = [UIColor blackColor].CGColor;
circle.lineWidth = 5;
/*CGPointMake((self.contentsCenter.size.width), (self.contentsCenter.size.height));*/
//path the circle
CGContextAddArc(ctx, centerPoint.x, centerPoint.y, radius, 0.0, 2 * M_PI, 0);
CGContextClosePath(ctx);
//fill it
CGContextSetFillColorWithColor(ctx, [UIColor redColor].CGColor);
CGContextFillPath(ctx); }
///////////////////////////////////////////////////////////////////////
-(void) animatingArc
{
CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:#"arcEnd"];
anim.duration = 20.0; //animate over 20 seconds
anim.repeatCount = 1.0; //animate only once
anim.removedOnCompletion = NO; //Reamin there after completion
//animate from start to end
anim.fromValue = [NSNumber numberWithFloat:50.0f];
anim.toValue = [NSNumber numberWithFloat:150.0f];
//experiment with timing to get appearence to look the way you want
anim.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];
//add animation to circle
[circle addAnimation:anim forKey:#"animatingArc"];
}
/////////////////////
//needed since key not part of animatable properties
+(BOOL) needsDisplayForKey:(NSString *)key
{
if([key isEqualToString:#"arcEnd"])
return YES;
else
return [super needsDisplayForKey:key];
}
//ensure custom properties copied to presentation layer
-(id) initWithLayer:(id)layer
{
if((self = [super initWithLayer:layer]))
{
if ([layer isKindOfClass:[AnimateArc class]])
{
AnimateArc *other = (AnimateArc *) layer;
[other setNeedsDisplay];
}
}
return self; }
And finally in my viewController,
- (void)viewDidLoad
{
[super viewDidLoad];
[self.view.layer addSublayer:AnimateArcObject];
[AnimateArcObject animatingArc];
}
Apology for bad formatting.... Please can someone tell me what am I doing wrong? I have also doubt that my code can crash at any place after accessing those two functions since I am novice about Core Animation and haven't got any idea that I am in right direction or not.
Any help will be appreciated. Thanks.
From my painful experience with CoreAnimation, you must always set the bounds property of any CALayer you instantiate.
So, you're layer is not showing because you are missing something like:
layer.bounds = CGRectMake(0, 0, width, height);
you should place this as soon as you instantiate the layer, and make it a habit to do so, so you don't fall into it again.
As for your code crashing, sorry. It's too distributed and I am not sure how it's linked together, so I can't help you there.
After little searching, I thought that I am going in wrong direction. So I deleted this AnimateArc file and added new one which is inheriting from UIViewController.
Then in viewDidLoad Method, I wrote the code from this link to create circle and animations using path.
In parent view controller, I added AnimatedArc ViewController's subview. Now its working perfectly :)
I have used the classes provided by apple CrumbPath.o and CrumbPathView.o, but it supports only iphone 5.0,when I try the same code with iphone 4.0 ,it does not update the route.
Code :
if (newLocation)
{
if (oldLocation.coordinate.latitude == 0.0) {
initialLocation = [[[CLLocation alloc]initWithLatitude:newLocation.coordinate.latitude longitude:newLocation.coordinate.longitude]retain];
}
// make sure the old and new coordinates are different
if((oldLocation.coordinate.latitude != newLocation.coordinate.latitude) &&
(oldLocation.coordinate.longitude != newLocation.coordinate.longitude))
{
if (!crumbs)
{
// This is the first time we're getting a location update, so create
// the CrumbPath and add it to the map.
//
crumbs = [[CrumbPath alloc] initWithCenterCoordinate:newLocation.coordinate];
[mapView addOverlay:crumbs];
// On the first location update only, zoom map to user location
MKCoordinateRegion region =
MKCoordinateRegionMakeWithDistance(newLocation.coordinate, 2000, 2000);
[mapView setRegion:region animated:YES];
}
else
{
// This is a subsequent location update.
// If the crumbs MKOverlay model object determines that the current location has moved
// far enough from the previous location, use the returned updateRect to redraw just
// the changed area.
//
// note: iPhone 3G will locate you using the triangulation of the cell towers.
// so you may experience spikes in location data (in small time intervals)
// due to 3G tower triangulation.
//
Count++;
double latitude = 0.000500 * Count;
double longitude = 0.000020 * Count;
_bean = [[TempBean alloc]init];
_bean.lat = newLocation.coordinate.latitude + latitude;
_bean.lon = newLocation.coordinate.longitude - longitude;
CLLocation *locationToDraw = [[CLLocation alloc]initWithLatitude:_bean.lat longitude:_bean.lon];
// UIAlertView *alert = [[UIAlertView alloc]initWithTitle:#"Update_Loc" message:[NSString stringWithFormat:#"Lat:%f , Lon:%f",locationToDraw.coordinate.latitude,locationToDraw.coordinate.longitude] delegate:self cancelButtonTitle:#"ok" otherButtonTitles:#"Cancel",nil];
// [alert show];
// [alert release];
MKMapRect updateRect = [crumbs addCoordinate:locationToDraw.coordinate];
if (!MKMapRectIsNull(updateRect))
{
// There is a non null update rect.
// Compute the currently visible map zoom scale
MKZoomScale currentZoomScale = (CGFloat)(mapView.bounds.size.width / mapView.visibleMapRect.size.width);
// Find out the line width at this zoom scale and outset the updateRect by that amount
CGFloat lineWidth = MKRoadWidthAtZoomScale(currentZoomScale);
updateRect = MKMapRectInset(updateRect, -lineWidth, -lineWidth);
// Ask the overlay view to update just the changed area.
[crumbView setNeedsDisplayInMapRect:updateRect];
}
[self calDistance:initialLocation SecondCor:locationToDraw];
[locationToDraw release];
locationToDraw =nil;
[_bean release];
_bean = nil;
}}
If that the case then, you should try looking into your CrumbPathView.m see if the render function works correctly or not from what i see here you copied & pasted the code from Apple Docs right? In the file you should find this in method drawMapRect:zoomScale:inContext:
if (path != nil)
{
CGContextAddPath(context, path);
CGContextSetRGBStrokeColor(context, 0.0f, 0.0f, 1.0f, 0.6f);
CGContextSetLineJoin(context, kCGLineJoinRound);
CGContextSetLineCap(context, kCGLineCapRound);
CGContextSetLineWidth(context, lineWidth);
CGContextStrokePath(context);
CGPathRelease(path);
}
See if the path is nil or not if it's nil-ed nothing gets rendered. Have you debug into this view? The rendering happens in CrumbPathView so, you should see what happen in it.
If the path is nil then you should check the method which is called to assign its value, located above the if (path != nil)
I am using a single sprite again and again (requirement of program) for different animations on different touch location and keeping the final image after each animation on screen [sprite spriteWithSpriteFrameName:#"abc.png"] .
Now problem is that after completion of all animation i want to remove all done animation's final image from screen but due to using same sprite again and again sprite has only reference of last done animation and using [sprite removeFromParentAndCleanup:true] only remove last done animation.Please help me to remove all done animation spriteFrame.
here is sample code
-(void)draw1
{
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:#"a.plist"];
// Create a sprite sheet with the images
spritebatch= [CCSpriteBatchNode batchNodeWithFile:#"a.png"];
[self addChild:sprite z:12];
// Load up the frames of our animation
NSMutableArray *animFrames = [NSMutableArray array];
for(int i = 1; i <= 13; ++i) {
[animFrames addObject:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:[NSString stringWithFormat:#"crta%d.png", i]]];
}
CCAnimation *walkAnim = [CCAnimation animationWithFrames:animFrames delay:.1f];
sprite=[CCSprite spriteWithSpriteFrameName:#"crta13.png"];
[self addChild:sprite z:20];
drawaction = [CCRepeat actionWithAction:[CCAnimate actionWithAnimation:walkAnim] times:1];
//[CCRepeat actionWithAction: [CCAnimate actionWithAnimation:animation] times:1];
[sprite runAction:drawaction];
}
-(void)draw2
{
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:#"a.plist"];
// Create a sprite sheet with the images
spritebatch= [CCSpriteBatchNode batchNodeWithFile:#"a.png"];
[self addChild:sprite z:12];
// Load up the frames of our animation
NSMutableArray *animFrames = [NSMutableArray array];
for(int i = 1; i <= 13; ++i) {
[animFrames addObject:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:[NSString stringWithFormat:#"crta%d.png", i]]];
}
CCAnimation *walkAnim = [CCAnimation animationWithFrames:animFrames delay:.1f];
sprite=[CCSprite spriteWithSpriteFrameName:#"crta13.png"];
[self addChild:sprite z:20];
drawaction = [CCRepeat actionWithAction:[CCAnimate actionWithAnimation:walkAnim] times:1];
//[CCRepeat actionWithAction: [CCAnimate actionWithAnimation:animation] times:1];
[sprite runAction:drawaction];
}