iOS scrolling background placement issue - ios

I got a little issue i'm trying to figure out. I want to scroll a background image from top to bottom, but it just won't go the right way. Here's what I got so far:
for (int i = 0; i < 2; i++) {
SKSpriteNode * bg = [SKSpriteNode spriteNodeWithImageNamed:#"Background"];
bg.anchorPoint = CGPointZero;
bg.position = CGPointMake(0, bg.size.height * i);
bg.name = #"bg";
[_bgLayer addChild:bg];
}
The image shows up about 2/3 to the left on the simulator and the right side of the screen is just black. Now doesn't bg.anchorPoint = CGPointZero; move the reference point from center to bottom left? If I get rid of that line and do self.frame.size.width / 2, it centers it. Also the height and width of my *bg are only showing 768 and the Background image is 864x1536 which is another thing that doesn't make sense. Anyone see the problem?

Related

Sticking nodes to SkSpriteNode after enlargement

Problem
As of right now, I've just realised a pretty major design flaw in my application..
So, the problem is:
A rifle shot is fired, and lands based on no trajectory as of right now, I'm toying with the idea. However, the bullets land and their marks are left as nodes.
-(void)applyShot:(int) posX with:(int) posY {
SKSpriteNode *impact = [[SKSpriteNode alloc] initWithColor:[UIColor grayColor] size:CGSizeMake(2, 2)];
impact.zPosition = 1;
impact.userInteractionEnabled = NO;
impact.position = CGPointMake(posX , posY);
[backgroundImage addChild:impact];
}
And posX / posY are sent this way.
//Find difference between centre and background moved distance.
CGPoint positionNow = CGPointMake(backgroundImage.position.x, backgroundImage.position.y);
CGPoint positionPrev = CGPointMake(0.5, 0.5);
float xdiff = positionNow.x - positionPrev.x;
float ydiff = positionNow.y - positionPrev.y;
//Calculate ydrop
int newYDrop = yDrop * 10;
//
CGPoint newPositionOne = CGPointMake(0.5 - xdiff, 0.5 - ydiff);
newPositionOne = CGPointMake((newPositionOne.x + [self myRandom:15 withFieldLower:-15]), (newPositionOne.y - newYDrop));
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(secondsDelay * NSEC_PER_SEC)), dispatch_get_main_queue(), ^(void){
[self applyShot:newPositionOne.x with:newPositionOne.y];
});
Which seems to work fine, until I started toying with zoom.
Now, this application basically is a 1080p image for the reticle, and the background enlarges to give the (zoom) effect, then the background is touch-dragged/inverted to give the reticle moving effect.
Then I managed to get it to fire exactly where the crosshair was, which was good.. Then I started toying with zoom and noticed this.
So if it's not too easy to notice, the (bullet hits) are the grey marks, however, when you enlarge the backgroundImage they aren't tied to it. so they remain the same spread.
Solution
Now what I need is to either,
A: tie the nodes to the backgroundImage
B: enlarge the spread to the same factor as the enlargement.
But I'm pretty confused at how to achieve this.
Additional problem
When calibrating the drop, the drop will remain a burden no matter of the zoom as of right now. so if it's a 1 mil-dot drop on 6x zoom, it's also a 1 mil-dot drop at 10x zoom or 1x zoom ( I keep saying zoom when I mean enlargement )
How can I achieve a calibration for drop intensity no matter what enlargement the background image is at?
Thanks for reading and confusing yourself with my dire problems, it's appreciated!
What I've Tried
saving the children to a mutable array, then recreating them after a zoom change;
//recreate children
[backgroundImage removeAllChildren];
for (int i=0; i < [shotsHitX count]; i++) {
double posX = ([shotsHitX[i] doubleValue] / 5) * rifleZoom;
double posY = ([shotsHitY[i] doubleValue] / 5) * rifleZoom;
SKSpriteNode *impact = [[SKSpriteNode alloc] initWithColor:[UIColor grayColor] size:CGSizeMake(2, 2)];
impact.zPosition = 1;
impact.userInteractionEnabled = NO;
impact.position = CGPointMake(posX , posY);
[shotsHitX addObject:[NSNumber numberWithDouble:posX]];
[shotsHitY addObject:[NSNumber numberWithDouble:posY]];
[backgroundImage addChild:impact];
}
//end recreate children
Crash with memory error.
not so good at this!
[backgroundImage removeAllChildren];
for (int i=0; i < [shotsHitX count]; i++) {
double posX = ([shotsHitX[i] doubleValue] / 5) * rifleZoom;
double posY = ([shotsHitY[i] doubleValue] / 5) * rifleZoom;
SKSpriteNode *impact = [[SKSpriteNode alloc] initWithColor:[UIColor grayColor] size:CGSizeMake(2, 2)];
impact.zPosition = 1;
impact.userInteractionEnabled = NO;
impact.position = CGPointMake(posX , posY);
[backgroundImage addChild:impact];
}
//end recreate children
Works, however, now It doesn't just seem quite right..
I think the problem is when the initial zoom goes in it works, then when it reverts it's mixing zoom shots in the array with old shots.. Here we go again, MORE ARRAYS.
//recreate children
[backgroundImage removeAllChildren];
for (int i=0; i < [shotsHitRem count]; i+= 2) {
double posX = ([shotsHitRem[i] doubleValue] / 5) * rifleZoom;
double posY = ([shotsHitRem[i+1] doubleValue] / 5) * rifleZoom;
SKSpriteNode *impact = [[SKSpriteNode alloc] initWithColor:[UIColor grayColor] size:CGSizeMake(2, 2)];
impact.zPosition = 1;
impact.userInteractionEnabled = NO;
impact.position = CGPointMake(posX , posY);
[backgroundImage addChild:impact];
//add olds m4
if ([shotsHitM4 count] > 0) {
posX = ([shotsHitM4[i] doubleValue] / 2) * rifleZoom;
posY = ([shotsHitM4[i+1] doubleValue] / 2) * rifleZoom;
impact.position = CGPointMake(posX, posY);
[backgroundImage addChild:impact];
}
}
//end recreate children
Now I crash attempting to add a sknode which already has a parent
Confusing as it should removeAllChildren before looping
Well after some major messing around
[backgroundImage setScale:rifleZoom];
Programming is my favourite, oh yeah.. Five hours, Oh yeah.. For one line, oh yeah!
I wasn't scaling before, I was creating a new cgsize. and that was my problem.
I now have the issue of scaling and trying to render a new centrepoint as it still remembers the centrepoint of scale = 1 and scale = 2, nightmare.

SKSpriteNode ScaleUpFrom?

Problem
Scaling up a 2D image, scales up from the image centre point, however. I need it to scale up from a specific co-ordinate?
[backgroundImage setScale:rifleZoom];
My current technique to scaling up the image.
I need to scale up from centre screen as opposed to centre image
Now my way to place a rifle shot on the screen # centre is this way:
CGPoint positionNow = CGPointMake(backgroundImage.position.x, backgroundImage.position.y);
CGPoint positionPrev = CGPointMake(0.5, 0.5);
float xdiff = positionNow.x - positionPrev.x;
float ydiff = positionNow.y - positionPrev.y;
CGPoint newPositionOne = CGPointMake(0.5- xdiff, 0.5 - ydiff);
newPositionOne = CGPointMake(newPositionOne.x/rifleZoom, newPositionOne.y/rifleZoom);
Now this works perfectly no matter how much the image is scaled, however. I cannot seem to implement it into scaling the image up from the centre of the screen opposed to centre of the image.
What I've Tried
I've tried to change the position of the image before scaling it up. To the same same point make as newPositionOne However, this does not work or not being done right.
EDIT
This scales and brings centre to screen centrepoint, or messes up completely. It's a little too off the cuff to make a decision exactly what it's doing.
CGPoint positionNow = CGPointMake(backgroundImage.position.x, backgroundImage.position.y);
CGPoint positionPrev = CGPointMake(0.5, 0.5);
float xdiff = positionNow.x - positionPrev.x;
float ydiff = positionNow.y - positionPrev.y;
CGPoint newPositionOne = CGPointMake(0.5- xdiff, 0.5 - ydiff);
newPositionOne = CGPointMake(newPositionOne.x/rifleZoom, newPositionOne.y/rifleZoom);
double xPosition = newPositionOne.x / backgroundImage.size.width;
double yPosition = newPositionOne.y / backgroundImage.size.height;
CGPoint prevAnchorPoint = backgroundImage.anchorPoint;
backgroundImage.anchorPoint = CGPointMake(xPosition,yPosition);
double positionX = backgroundImage.position.x + (backgroundImage.anchorPoint.x - prevAnchorPoint.x) * backgroundImage.size.width;
double positionY = backgroundImage.position.y + (backgroundImage.anchorPoint.y - prevAnchorPoint.y) * backgroundImage.size.height;
backgroundImage.position = CGPointMake(positionX,positionY);
[backgroundImage runAction:[SKAction repeatAction:[SKAction scaleTo:rifleZoom duration:1.0] count:1]];
You can use the anchor point property of the background node to change the point from which image scales. By default the anchorPoint is at (0.5,0.5). This indicates the center of the node. If you make the anchorPoint (0,0), then its moved to the bottom left corner.
anchorPoint : Defines the point in the sprite that corresponds to the
node’s position. You specify the value for this property in the unit
coordinate space. The default value is (0.5,0.5), which means that the
sprite is centered on its position.
When you shift the anchorPoint, you have to adjust the background position again to counteract the movement due to changing the anchorPoint.
So you can use,
CGPoint xPosition = convertedPoint.x / background.size.width
CGPoint yPosition = convertedPoint.y / background.size.height
CGPoint prevAnchorPoint = background.anchorPoint
background.anchorPoint = CGPointMake(xPosition,yPosition)
CGFloat positionX = background.position.x + (background.anchorPoint.x - prevAnchorPoint.x) * background.size.width
CGFloat positionY = background.position.y + (background.anchorPoint.y - prevAnchorPoint.y) * background.size.height
background.position = CGPointMake(positionX,positionY)

iOS gradient animating with uiscrollview paging

I have an horizontal scrollview with 3 pages.
I would like to have one background color different for each page and draw gradient between the 3 colors.
Page 1 with green, page 2 with blue and page 3 with red.
How can I do that?
I was thinking about ScrollViewDidScroll delegate method:
- (void)scrollViewDidScroll:(UIScrollView *)sender
{
CGFloat pageWidth = self.statisticScrollView.frame.size.width;
float fractionalPage = self.statisticScrollView.contentOffset.x / pageWidth;
NSLog(#"fractional Page: %f", fractionalPage);
NSInteger lowerNumber = floor(fractionalPage);
NSLog(#"lower Page: %i", lowerNumber);
NSInteger upperNumber = lowerNumber + 1;
NSLog(#"upper Page: %i", upperNumber);
if (self.lastContentOffset > sender.contentOffset.x){
//RIGHT --->
if (lowerNumber == 0) {
//gradient green to blue
}else if (lowerNumber == 1){
//gradient blue to red
}else if (lowerNumber == 2){
//end pages
}
}else if (self.lastContentOffset < sender.contentOffset.x){
//LEFT <----
if (lowerNumber == 0) {
//gradient blue to green
}else if (lowerNumber == 1){
//gradient red to blue
}else if (lowerNumber == 2){
//end pages
}
}
}
Can I use fractionalPage to set the percentage of the gradient?
I read about startPoint and endPoint properties of the CAGradientLayer, but It seems not working correctly.
Any suggestions will be appreciated!
Thank you in advance
The startPoint and endPoint properties are measured from 0 to 1 and NOT in the coordinate system of the view which may be an issue you are encountering. So to run the gradient from side to side you need to declare it like this.
gradlayer.startPoint = CGPointMake(0, 0.5);
gradlayer.endPoint = CGPointMake(1, 0.5);
A simple way of achieving what you want to do is to add a sublayer to the scroll view with the gradient pre-configured.
CGFloat width = CGRectGetWidth(self.view.frame);
CGFloat height = CGRectGetHeight(self.view.frame);
self.scroller.contentSize = CGSizeMake(3*width, height);
self.gradlayer = [CAGradientLayer layer];
self.gradlayer.frame = CGRectMake(0, 0, width*3, height);
[self.scroller.layer addSublayer:self.gradlayer];
self.gradlayer.colors = #[((id)[UIColor redColor].CGColor),((id)[UIColor greenColor].CGColor),((id)[UIColor blueColor].CGColor)];
self.gradlayer.startPoint = CGPointMake(0, 0.5);
self.gradlayer.endPoint = CGPointMake(1, 0.5);
However this may cause memory issues if your content size gets much larger.
I had a look on the Sim and it didn't seem grow with more pages. I went to 10 and it seemed to stay about the same as 3 pages but it is a consideration so you may have to go back to your original theory of adjusting the color array depending on the horizontal offset.
I think the scrollviews internal paging handles its sublayers effectively to avoid this.
Sweet color scheme though. Jerry Garcia would be proud.

Sprite Kit: Rope with SKPhysicsJointLimit breaks under excessive force?

The following code works great. It creates a dangling rope made of square links, and then applies a force to the bottom link to swing it around. It is executed in the SKScene object when the SKScene is created.
However, if you change the force on the last line from 2000 to 4000, the physics go haywire, and the links sometimes break. Sometimes the links seem to be vibrating or they’ll just submit entirely to gravity as if there is no joint. Additionally, if you have other physics bodies, they no longer collide with the haywire links, even if they did before the force was applied.
Although 4000 seems excessive, I’ve encountered this issue with much less force on lighter objects, or smaller forces on ropes with less of a gap between links.
Am I doing something wrong? Is there a bug in Sprite Kit or some other explanation? I couldn’t find any answers online.
It also doesn't matter if the force is applied during SKScene creation or delayed until later.
self.scaleMode = SKSceneScaleModeAspectFit;
self.anchorPoint = CGPointMake(0.5, 0.5);
CGFloat length = 20;
CGFloat gapMult = 1.8;
int numBoxes = 10;
SKSpriteNode *prevBox = nil;
for (int i = 0; i < numBoxes; i++) {
SKSpriteNode *box = [SKSpriteNode spriteNodeWithColor:[UIColor yellowColor] size:CGSizeMake(length, length)];
box.position = CGPointMake(0, -i * length * gapMult);
[self addChild:box];
box.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:CGSizeMake(length, length)];
if (i == 0) {
box.physicsBody.dynamic = NO;
} else {
CGPoint pt1 = CGPointMake(0, prevBox.position.y - length / 2);
CGPoint pt2 = CGPointMake(0, box.position.y + length / 2);
SKPhysicsJointLimit *joint = [SKPhysicsJointLimit jointWithBodyA:prevBox.physicsBody bodyB:box.physicsBody anchorA:pt1 anchorB:pt2];
[self.physicsWorld addJoint:joint];
}
prevBox = box;
}
[prevBox.physicsBody applyForce:CGVectorMake(2000, 0)];

Rotate CAEmitterCell content based on Animation Direction

Using CAEmitterLayer and Cell to show particles that have direction sensitive content image (an arrow).
Want the content (arrow image) to point in the direction the cell is moving.
Here is code for having all the arrows move from outside edge toward center. How to rotate image so the image points in direction of movement:
emitterLayer = [CAEmitterLayer layer];
emitterLayer.emitterPosition = self.view.center;
emitterLayer.emitterSize = self.view.bounds.size;
emitterLayer.emitterMode = kCAEmitterLayerOutline;
emitterLayer.emitterShape = kCAEmitterLayerRectangle;
CAEmitterCell* arrow = [CAEmitterCell emitterCell];
arrow.birthRate = 10;
arrow.velocity = 100;
arrow.emissionLatitude = M_PI;
arrow.scale = 0.5;
arrow.lifetime = 2;
arrow.contents = (id) [[UIImage imageNamed:#"arrowOutline.png"] CGImage];
emitterLayer.emitterCells = #[arrow];
[self.view.layer addSublayer:emitterLayer];
How to get the content image to adjust based on direction of cell movement?
Ended up using 4 different CAEmitterLayers one for each direction where emitterPosition is each edge of the screen and emitterSize is length of given edge.
If there is better solution please share.

Resources