I have been struggling with this for a while now and cant seem to find the issue.
I have an SKScene which i will refer to as self, and an SKNode that is called chapterScene that gets added to self. I have a boundary set up that contains a movable character. Here is how I have set this up
ViewController.m (the controller that presents the SKScene subclass OLevel
- (void)viewDidLoad {
[super viewDidLoad];
// Configure the view.
SKView *skView = (SKView *)self.view;
skView.showsFPS = YES;
skView.showsNodeCount = YES;
// Create and configure the scene.
scene = [OLevel sceneWithSize:self.view.frame.size];
scene.scaleMode = SKSceneScaleModeAspectFit;
// Present the scene.
[skView presentScene:scene];
// Do things after here pertaining to initial loading of view.
}
Here is my OLevel.m
- (id)initWithSize:(CGSize)size {
if (self = [super initWithSize:size]) {
NSLog(#"Creating scene");
[self setUpScene];
}
return self;
}
- (void)setUpScene {
NSLog(#"Setting up scene");
//self.speed = 0.9f;
#pragma 1 Set up scene
// Set up main chapter scene
self.anchorPoint = CGPointMake(0.5, 0.5); //0,0 to 1,1
chapterScene = [SKNode node];
chapterScene.position = CGPointZero;
chapterScene.name = #"chapterScene";
[self addChild:chapterScene];
// Set up physics boundary
self.physicsWorld.gravity = CGVectorMake(0.0, 0.0);
self.physicsWorld.contactDelegate = self;
.
.
.
}
The main point here, is that ultimately I have set up my scene and its child nodes correctly (as I expected until of late). When I run on the simulator (iPhone 6), I am using the - (void)didBeginContact:(SKPhysicsContact *)contact method to monitor and collisions. Whenever contact does begin, I am logging the following
CGPoint contactPoint = contact.contactPoint;
NSLog(#"non conv: %f, %f", contactPoint.x, contactPoint.y);
CGPoint sceneContactPoint = [self convertPoint:contactPoint toNode:chapterScene];
NSLog(#"1 conv pt: %f, %f", sceneContactPoint.x, sceneContactPoint.y);
I am also logging the characters position as well to make sure that this converted point is correct.
When I run this on the simulator, and the moving node character hits the wall, I get this:
2016-02-25 20:02:31.102 testGame[43851:14374676] non converted point: 0.143219, 29.747963
2016-02-25 20:02:31.102 testGame[43851:14374676] 1 conv pt: -140.206223, 615.699341
2016-02-25 20:02:31.102 testGame[43851:14374676] Player hit the wall
2016-02-25 20:02:31.103 testGame[43851:14374676] player pos: -140.206238, 590.749268
which is quite correct.
HOWEVER, for whatever reason I can not seem to find, this is the exact same code running on my iPhone 5C...
2016-02-25 20:04:50.062 testGame[2907:1259447] non converted point: 160.337631, 310.808350
2016-02-25 20:04:50.063 testGame[2907:1259447] 1 conv pt: 70.996162, 900.004272
2016-02-25 20:04:50.064 testGame[2907:1259447] Player hit the wall
2016-02-25 20:04:50.065 testGame[2907:1259447] player pos: -89.003845, 593.984009
I really hope this is a simple fix that I am missing. If anyone can help me I would greatly appreciate it. thanks
UPDATE
It appears that all that is happening is that when I run it on the simulator, the point is being referenced from the center of the screen (0,0) whereas on the device, the reference point is the true origin, the top left corner being (0,0), with the center being, in the iPhone 5c's case, (160, 284). Still not sure how to correct this...or why it is even happening.
So far this is the only solution I can think of...
if (!TARGET_OS_SIMULATOR) {
contactPoint = CGPointMake(sceneContactPoint.x - screenBounds.size.width/2.0f, sceneContactPoint.y - screenBounds.size.height/2.0);
}
else {
contactPoint = CGPointMake(sceneContactPoint.x, sceneContactPoint.y);
}
But this is embarrassing. Either this is a bug with Xcode or Apple, or there is reason this is happening and a different solution.
Related
I am very new to Sprite Kit game development. I am currently developing my first game - a simple game where the player must navigate a simple object past obstacles. If the object collides with an obstacle - GAME OVER.
I got that to work fine but I have been stuck for over a week on an animation problem. I've literally searched for an answer for days now ... without success. So basically ... when my object is being navigated past the obstacles I have one animation pattern (that works fine). Once it hits an obstacle however, I want the animation pattern to change (into an explosion). And for some reason that is NOT HAPPENING! :( It is basically completely IGNORING the two lines of code ([self stopBeeAnimation]; and [self defeatedAnimation];) I added and the game is over right away.
Any help I might get on this, is really appreciated (this is driving me nuts) :)
Thank you so much.
Here my code:
-(void)didBeginContact:(SKPhysicsContact *)contact {
SKSpriteNode *firstSprite;
SKSpriteNode *secondSprite;
firstSprite = (SKSpriteNode *)contact.bodyA.node;
secondSprite = (SKSpriteNode *)contact.bodyB.node;
if ((contact.bodyA.categoryBitMask == beeCategory) && (contact.bodyB.categoryBitMask = obstacleCategory)) {
[self stopBeeAnimation];
[self defeatedAnimation];
[obstacleArray removeAllObjects];
[bee.physicsBody setAffectedByGravity:NO];
[timer invalidate];
scoreLabel.fontSize = 30;
scoreLabel.text = [NSString stringWithFormat:#"GAME OVER %d", score/2];
[self removeFromParent];
SKTransition *transition = [SKTransition fadeWithDuration:5];
[self.scene.view presentScene:[[NPMyScene alloc]initWithSize:self.size] transition:transition];
}}
and:
-(void) defeatedAnimation {
SKAction *defeatedAnimation;
NSMutableArray *textures2 =[NSMutableArray arrayWithCapacity:5];
for (int a = 1; a < 6; a++) {
NSString *textureName2 = [NSString stringWithFormat:#"defeated%d", a];
SKTexture *texture2 = [SKTexture textureWithImageNamed:textureName2];
[textures2 addObject:texture2];
}
defeatedAnimation = [SKAction animateWithTextures:textures2 timePerFrame:0.2];
}
Look here:
SKTransition *transition = [SKTransition fadeWithDuration:5];
[self.scene.view presentScene:[[NPMyScene alloc]initWithSize:self.size] transition:transition];
You're presenting a different scene (or maybe another instance of the same scene class?) in the view after trying to run your animations. Any animation changes in the current scene won't be visible because the new scene replaces the current scene.
But even before that...
- (void)defeatedAnimation {
// ...
defeatedAnimation = [SKAction animateWithTextures:textures2 timePerFrame:0.2];
}
You're creating an SKAction but not doing anything with it. For an action to take effect you need to run it on a node with runAction:.
I am implementing a CCLayer subclass that houses 2 UIImageViews. The views and layer are all the same size: I initialized the UIImageViews with the same frame, and set the contentSize of the layer to be the frame as well. Everything is working fine, but it seems as though something is going haywire with the first point when drawing. When the image is just tapped there is no line jump, but when I attempt to draw a stroke, as soon as I move my finger, the start of the line jumps down randomly(so in this screenshot I am drawing from left to right except for bottom-left line). I am not sure where I am going wrong in my code:
//_drawImageView is the UIImageView I am drawing in.
#pragma mark - Touch Methods
- (BOOL)ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event
{
penStroked = NO;
_prevPoint = [touch locationInView:[[CCDirector sharedDirector]view]];
CGRect rect = self.boundingBox;
if (CGRectContainsPoint(rect, _prevPoint)) {
return YES;
}
return NO;
}
- (void)ccTouchMoved:(UITouch *)touch withEvent:(UIEvent *)event
{
penStroked = YES;
_currPoint = [touch locationInView:_drawImageView];
UIGraphicsBeginImageContext(self.contentSize);
[_drawImageView.image drawInRect:CGRectMake(0, 0, _drawImageView.frame.size.width, _drawImageView.frame.size.height)];
CGContextMoveToPoint(UIGraphicsGetCurrentContext(), _prevPoint.x, _prevPoint.y);
CGContextAddLineToPoint(UIGraphicsGetCurrentContext(), _currPoint.x, _currPoint.y);
CGContextSetLineCap(UIGraphicsGetCurrentContext(), kCGLineCapRound);
CGContextSetLineWidth(UIGraphicsGetCurrentContext(), penSize);
CGContextSetRGBStrokeColor(UIGraphicsGetCurrentContext(), penColor.r, penColor.g, penColor.b, 1.0);
CGContextSetBlendMode(UIGraphicsGetCurrentContext(), kCGBlendModeNormal);
CGContextStrokePath(UIGraphicsGetCurrentContext());
_drawImageView.image = UIGraphicsGetImageFromCurrentImageContext();
[_drawImageView setAlpha: penOpacity];
UIGraphicsEndImageContext();
_prevPoint = _currPoint;
}
I was having a really hard time initially aligning the point at which the line would be draw n and the actual position of my finger, so that is why in the ccTouchBegan method the touch is taken from the CCDirector and in ccTouchMoved it is taken from the _drawImageView. This is the only way it seems to draw perfectly besides the initial wonky behavior.
The problem was definitely caused by the differing references for the touches. As I had said before though, it was the only way to have the line match the position my finger was in. I figured that the line jump was due to the line of code:
CGContextMoveToPoint(UIGraphicsGetCurrentContext(), _prevPoint.x, _prevPoint.y);
The problem was that on the first go around, or the first time that the pen/finger is stroked, the previous point was referenced from the CCDirectors view while the current point was from the imageView. I created an int variable, that was incremented every time the pen was stroked and when penStrokedInt == 1 (first stroke), I corrected the _prevPoint to the WorldSpace using the current point coordinates:
_prevPoint = [self convertToWorldSpace:_currPoint];
which basically got rid of the very first point in the line. It's not the best solution, but now it works! Make sure to reset the penStrokedInt back to 0 when the ccTouchEnded.
I have a subclass of NSView that re-implements a number of the mouse event functions. For instance in mouseDown to get the point from the NSEvent I use:
NSEvent *theEvent; // <- argument to function
NSPoint p = [theEvent locationInWindow];
p = [self convertPoint:p fromView:nil];
However the coordinates seem to be flipped, (0, 0) is in the bottom left of the window?
EDIT: I have already overridden the isFlipped method to return TRUE, but it has only affected drawing. Sorry, can't believe I forgot to put that straight away.
What do you mean by flipped? Mac uses a LLO (lower-left-origin) coordinate system for everything.
EDIT I can't reproduce this with a simple project. I created a single NSView implemented like this:
#implementation FlipView
- (BOOL)isFlipped {
return YES;
}
- (void)mouseDown:(NSEvent *)theEvent {
NSPoint p = [theEvent locationInWindow];
p = [self convertPoint:p fromView:nil];
NSLog(#"%#", NSStringFromPoint(p));
}
#end
I received the coordinates I would expect. Removing the isFlipped switched the orientation as expected. Do you have a simple project that demonstrates your problmem?
I found this so obnoxious - until one day I just sat down and refused to get up until I had something that worked perfectly . Here it is.. called via...
-(void) mouseDown:(NSEvent *)click{
NSPoint mD = [NSScreen wtfIsTheMouse:click
relativeToView:self];
}
invokes a Category on NSScreen....
#implementation NSScreen (FlippingOut)
+ (NSPoint)wtfIsTheMouse:(NSEvent*)anyEevent
relativeToView:(NSView *)view {
NSScreen *now = [NSScreen currentScreenForMouseLocation];
return [now flipPoint:[now convertToScreenFromLocalPoint:event.locationInWindow relativeToView:view]];
}
- (NSPoint)flipPoint:(NSPoint)aPoint {
return (NSPoint) { aPoint.x,
self.frame.size.height - aPoint.y };
}
- (NSPoint)convertToScreenFromLocalPoint:(NSPoint)point
relativeToView:(NSView *)view {
NSPoint winP, scrnP, flipScrnP;
if(self) {
winP = [view convertPoint:point toView:nil];
scrnP = [[view window] convertBaseToScreen:winP];
flipScrnP = [self flipPoint:scrnP];
flipScrnP.y += [self frame].origin.y;
return flipScrnP;
} return NSZeroPoint;
}
#end
Hope this can prevent just one minor freakout.. somewhere, someday. For the children, damnit. I beg of you.. for the children.
This code worked for me:
NSPoint location = [self convertPoint:theEvent.locationInWindow fromView:nil];
location.y = self.frame.size.height - location.y;
This isn't "flipped", necessarily, that's just how Quartz does coordinates. An excerpt from the documentation on Quartz 2D:
A point in user space is represented by a coordinate pair (x,y), where x represents the location along the horizontal axis (left and right) and y represents the vertical axis (up and down). The origin of the user coordinate space is the point (0,0). The origin is located at the lower-left corner of the page, as shown in Figure 1-4. In the default coordinate system for Quartz, the x-axis increases as it moves from the left toward the right of the page. The y-axis increases in value as it moves from the bottom toward the top of the page.
I'm not sure what your question is, though. Are you looking for a way to get the "flipped" coordinates? If so, you can subclass your NSView, overriding the -(BOOL)isFlipped method to return YES.
I am working on a Mac app for Mac OS X 10.6+ and need to redraw the contents of a CAOpenGLLayer while an animation is occurring. I think I've read up on all the necessary parts, but it is just not working for me. I set up the animation as follows:
[CATransaction setAnimationDuration:SLIDE_DURATION];
CABasicAnimation *drawAnim = [CABasicAnimation animationWithKeyPath:#"drawIt"];
[drawAnim setFromValue:[NSNumber numberWithFloat:0]];
[drawAnim setToValue:[NSNumber numberWithFloat:1]];
[drawAnim setDuration:SLIDE_DURATION];
[chartView.chartViewLayer addAnimation:drawAnim forKey:#"drawIt"];
chartFrame.origin.x += controlFrame.size.width;
chartFrame.size.width -= controlFrame.size.width;
chartView.chartViewLayer.frame = CGRectMake(chartFrame.origin.x,
chartFrame.origin.y,
chartFrame.size.width,
chartFrame.size.height);
The drawIt property is a custom property whose only purpose is to be called to draw the layer during the successive animation frames. It order to get this to work, you have to add this to the chartViewLayer's class:
+ (BOOL)needsDisplayForKey:(NSString *)key
{
if ([key isEqualToString:#"drawIt"])
{
return YES;
}
else
{
return [super needsDisplayForKey:key];
}
}
So this seems to all be working fine. However, I need to get the current (animated) size of the layer before drawing it. I've found various conflicting information on how to get this out of the presentation layer. Here is what I've tried when the layer's:
drawInCGLContext:(CGLContextObj)glContext
pixelFormat:(CGLPixelFormatObj)pixelFormat
forLayerTime:(CFTimeInterval)timeInterval
displayTime:(const CVTimeStamp *)timeStamp
method is called during the animation. I've tried getting the size using KVC, and by querying either the frame or the bounds.
CALayer *presentationLayer = [chartViewLayer presentationLayer];
//bounds.size.width = [[[chartViewLayer presentationLayer]
// valueForKeyPath:#"frame.size.width"] intValue];
//bounds.size.height = [[[chartViewLayer presentationLayer]
// valueForKeyPath:#"frame.size.height"] intValue];
//bounds.size = presentationLayer.bounds.size;
bounds.size = presentationLayer.frame.size;
NSLog(#"Size during animation: %f, %f", bounds.size.width, bounds.size.height);
In all cases, the returned value is the end result of the animation rather than the transitional values. My understanding is that using the presentationLayer should give the transitional values.
So is this just broken or am I missing some critical step? Thanks for any help.
I have a CAShapeLayer and it has to do a simple task of moving on the screen, guided by the user's finger.
The problem is that the movement is too slow. The layer does move, but there is a lag and it feels slow.
I have another test app where an UIImage is moved and there is no lag at all and the image moves instantly.
What can I do to overcome this?
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
currentPoint = [[touches anyObject] locationInView:self];
}
- (void) touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event
{
CGPoint activePoint = [[touches anyObject] locationInView:self];
CGPoint newPoint = CGPointMake(activePoint.x - currentPoint.x,activePoint.y - currentPoint.y);
curLayer.position = CGPointMake(shapeLayer.position.x+newPoint.x,shapeLayer.position.y+newPoint.y);
currentPoint = activePoint;
}
Thanks!
Keep in mind that when you set the position on a layer (assuming it's not the root layer of a UIView on which actions are disabled by default), it implicitly animates to the new position, which takes 0.25 seconds. If you want to make it snappier, temporarily disable actions on the layer like this:
- (void) touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event
{
CGPoint activePoint = [[touches anyObject] locationInView:self];
CGPoint newPoint = CGPointMake(activePoint.x -
currentPoint.x,activePoint.y - currentPoint.y);
[CATransaction begin];
[CATransaction setDisableActions:YES];
curLayer.position = CGPointMake(shapeLayer.position.x +
newPoint.x, shapeLayer.position.y + newPoint.y);
[CATransaction commit];
currentPoint = activePoint;
}
This should cause it to jump to the new position rather than animate. If that doesn't help, then let me take a look at your layer init code so I can see what properties and dimensions it has. Properties such as cornerRadius, for example, can affect performance.
Try setting shouldRasterize to YES on your CAShapeLayer, particularly if it is usually drawn at the same scale. If your app runs on high-DPI devices, you may also need to set rasterizationScale to match the layer’s contentsScale.
While rasterizing your shape can make it faster to move the shape around, you’ll probably want to temporarily disable rasterization while you’re animating the layer’s path or size.