I'm having a problem when I try to move the content of a UIScrollView with CoreMotion.
The thing is, I have a background with approximately 5000px, inside a container, which is inside my scrollView (is a landscape app); I already set the self.scrollView.bounces = FALSE;, my value for motion is attitude.pitch * 10.0; and when I move the content inside and it reaches the edge of the pointZero or the self.scrollView.contentSize.width it doesn't respect the bounds and keep moving in a white screen like it has no limit.
So, I set a verification (the code below), but when it reaches the pointZero it was supposed to stop but still give me a little white border. I set a NSLog and I saw that the contentOffset was still going till x =-14, like the bounce was active. The pitch value is controlling that because when the pitch value is zero the content stays at pointZero, when I raise the value of the pitch ir keep going till -14.
I think that is something wrong with my verification, if anyone can help I will be really grateful!!
self.accel = attitude.pitch *10.0;
//"pointZERO"----------------
if (self.gameArea.contentOffset.x <= self.gameArea.frame.origin.x) {
NSLog(#"%2.f",self.gameArea.contentOffset.x);
self.gameArea.contentOffset = CGPointZero;
} else {
self.gameArea.contentOffset = CGPointMake(self.gameArea.contentOffset.x + self.accel,0.0);
}
//"END OF SCREEN"------------------------
if (self.gameArea.contentOffset.x + self.gameArea.frame.size.width >= self.gameArea.contentSize.width) {
NSLog(#"%2.f",self.gameArea.contentOffset.x);
self.gameArea.contentOffset = CGPointMake(self.gameArea.contentSize.width - self.gameArea.frame.size.width ,0.0);
} else {
self.gameArea.contentOffset = CGPointMake(self.gameArea.contentOffset.x + self.accel,0.0);
}
Related
I have a SKSpriteNode that moves with the accelerometer by using the following code:
-(void)processUserMotionForUpdate:(NSTimeInterval)currentTime {
SKSpriteNode* ship = (SKSpriteNode*)[self childNodeWithName:#"fishderp"];
CMAccelerometerData* data = self.motionManager.accelerometerData;
if (fabs(data.acceleration.y) > 0.2) {
[gameFish.physicsBody applyForce:CGVectorMake(0, data.acceleration.y)];
}
}
This works well however, the node (gamefish) moves off the screen. How can I prevent this and have it stay on the screen?
Try using an SKConstraint which was designed exactly for this purpose and introduced in iOS8:
Just add this to the setup method of the gameFish node. The game engine will apply the constraint after the physics has run. You won't have to worry about it. Cool huh?
// get the screensize
CGSize scr = self.scene.frame.size;
// setup a position constraint
SKConstraint *c = [SKConstraint
positionX:[SKRange rangeWithLowerLimit:0 upperLimit:scr.width]
Y:[SKRange rangeWithLowerLimit:0 upperLimit:scr.width]];
gameFish.constraints = #[c]; // can take an array of constraints
The code depends on whether you have added the gameFish node to self or to another node (something like a "worldNode"). If you have added it to self, look at the code below:
// get the screen height as you are only changing your node's y
float myHeight = self.view.frame.size.height;
// next check your node's y coordinate against the screen y range
// and adjust y if required
if(gameFish.position.y > myHeight) {
gameFish.position = CGPointMake(gameFish.position.x, myHeight);
}
For the bottom you can do a check of < 0 or whatever value you need.
I'm trying to write a basic game using Apple's Sprite Kit framework. So far, I have a ship flying around the screen, using SKPhysicsBody. I want to keep the ship from flying off the screen, so I edited my update method to make the ship's velocity zero. This works most of the time, but every now and then, the ship will fly off the screen.
Here's my update method.
// const int X_MIN = 60;
// const int X_MAX = 853;
// const int Y_MAX = 660;
// const int Y_MIN = 60;
// const float SHIP_SPEED = 50.0;
- (void)update:(CFTimeInterval)currentTime {
if (self.keysPressed & DOWN_ARROW_PRESSED) {
if (self.ship.position.y > Y_MIN) {
[self.ship.physicsBody applyForce:CGVectorMake(0, -SHIP_SPEED)];
} else {
self.ship.physicsBody.velocity = CGVectorMake(self.ship.physicsBody.velocity.dx, 0);
}
}
if (self.keysPressed & UP_ARROW_PRESSED) {
if (self.ship.position.y < Y_MAX) {
[self.ship.physicsBody applyForce:CGVectorMake(0, SHIP_SPEED)];
} else {
self.ship.physicsBody.velocity = CGVectorMake(self.ship.physicsBody.velocity.dx, 0);
}
}
if (self.keysPressed & RIGHT_ARROW_PRESSED) {
if (self.ship.position.x < X_MAX) {
[self.ship.physicsBody applyForce:CGVectorMake(SHIP_SPEED, 0)];
} else {
self.ship.physicsBody.velocity = CGVectorMake(0, self.ship.physicsBody.velocity.dy);
}
}
if (self.keysPressed & LEFT_ARROW_PRESSED) {
if (self.ship.position.x > X_MIN) {
[self.ship.physicsBody applyForce:CGVectorMake(-SHIP_SPEED, 0)];
} else {
self.ship.physicsBody.velocity = CGVectorMake(0, self.ship.physicsBody.velocity.dy);
}
}
}
At first, I used applyImpulse in didBeginContact to push the ship back. This made the ship bounce, but I don't want the ship to bounce. I just want it to stop at the edge.
What is the right way to make the ship stop once it reaches the edge? The code above works most of the time, but every now and then the ship shoots off screen. This is for OS X—not iOS—in case that matters.
Check out this link...
iOS7 SKScene how to make a sprite bounce off the edge of the screen?
[self setPhysicsBody:[SKPhysicsBody bodyWithEdgeLoopFromRect:self.frame]]; //Physics body of Scene
This should set up a barrier around the edge of your scene.
EDIT:
This example project from Apple might also be useful
https://developer.apple.com/library/mac/samplecode/SpriteKit_Physics_Collisions/Introduction/Intro.html
Your code is not clear in what the velocity variables represent. Keep in mind that if the velocity is too high your ship will have travelled multiple points between updates. For example, your ship's X/Y is at (500,500) at the current update. Given a high enough velocity, your ship could be at (500,700) at the very next update. If you had your boundary set at (500,650) your ship would already be past it.
I suggest you do a max check on velocity BEFORE applying it to your ship. This should avoid the problem I outlined above.
As for bouncy, bouncy... did you try setting your ship's self.physicsBody.restitution = 0; ? The restitution is the bounciness of the physics body. If you use your own screen boundaries, then I would recommend setting those to restitution = 0 as well.
Your best bet would be to add a rectangle physics body around the screen (boundary). Set the collision and contact categories of the boundary and player to interact with each other. In the didBeginContact method you can check if the bodies have touched and, if they have, you can call a method to redirect the ship.
Your problem is that your update method may not be checking the location frequently enough before the ship gets off screen.
This will help you to define you screen edges in Swift.
self.physicsBody = SKPhysicsBody ( edgeLoopFromRect: self.frame )
I am struggling in solving this problem.
On my scene, I have a camera which looks at the center of mass of an object. I have a some buttons that enable to set camera position on particular view (front view, back view,...) along a invisible sphere that surroung the object (constant radius).
When I click on the button, i would like the camera to move from its start position to the end position along the sphere surface. When camera moves I would like it to keep fixing center of mass of the object.
Has anyone have a clue on how to achieve this?
Thanks for help!
If you are happy/prefer to use basic trigonometry then in your initialisation section you could do this:
var cameraAngle = 0;
var orbitRange = 100;
var orbitSpeed = 2 * Math.PI/180;
var desiredAngle = 90 * Math.PI/180;
...
camera.position.set(orbitRange,0,0);
camera.lookAt(myObject.position);
Then in your render/animate section you could do this:
if (cameraAngle == desiredAngle) { orbitSpeed = 0; }
else {
cameraAngle += orbitSpeed;
camera.position.x = Math.cos(cameraAngle) * orbitRange;
camera.position.y = Math.sin(cameraAngle) * orbitRange;
}
Of course, your buttons would modify what the desiredAngle was (0°, 90°, 180° or 270° presumably), you need to rotate around the correct plane (I am rotating around the XY plane above), and you can play with the orbitRange and orbitSpeed until you hare happy.
You can also modify orbitSpeed as it moves along the orbit path, speeding up and slowing down at various cameraAngles for a smoother ride. This process is called 'tweening' and you could search on 'tween' or 'tweening' if you want to know more. I think Three.js has tweening support but have never looked into it.
Oh, also remember to set your camera's far property to be greater than orbitRadius or you will only see the front half of your object and, depending on what it is, that might look weird.
I am trying to imitate what Apple has when showing the search result in the App Store. (reference: http://searchengineland.com/apple-app-search-shows-only-one-result-at-a-time-133818)
It shows like the detailed-application-info in a cards and it is paged. I am stuck at how to make the previous-and-next card shows when one active card in the middle and the scroll view's paging behaviour is still intact.
I have tried using the UICollectionView and set the clipSubviews to NO, hoping that it will show the previous page and the next page, but as soon as the cell goes off-screen, the cell gets hidden (removed from the view hierarchy) and not displayed. I think thats the flyweight pattern of the UICollectionView (the behavior of UICollectionView). Any ideas of what would be possible?
Cheers,
Rendy Pranata
The problem: UICollectionView as a subclass of UIScrollView essentially animates its bounds by a stride of bounds.size. Although this could mean that all you had to do is decrease the bounds while keeping the frame bigger, unfortunately UICollectionView will not render any cells outside its current bounds... destroying your preview effect.
The Solution:
Create a UICollectionView with paging set to NO and with the desired frame.
Create UICollectionViewCells that are smaller than the UICollectionView's frame/bounds. At this stage, a part of the next cell should show in the frame. This should be visible before implementing the other steps below.
Add a collectionView.contentInset.left and right (I assume your layout is horizontal) equal to the contentOffsetValue method (as shown below for simplicity) so as to align the first and last cells to the middle.
Create a UICollectionViewFlowLayout which overrides the method that gives the stopping point like so:
Like so:
-(CGFloat)contentOffsetValue
{
return self.collectionView.bounds.size.width * 0.5f - self.itemSize.width * 0.5f;
}
- (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity
{
static float EscapeVelocity = 0.5f; // otherwise snap back to the middle
NSArray* layoutAttributesArray = [self layoutAttributesForElementsInRect:self.collectionView.bounds];
if(layoutAttributesArray.count == 0)
return proposedContentOffset;
CGFloat currentBoundsCenterX = self.collectionView.contentOffset.x + self.collectionView.bounds.size.width * 0.5f;
UICollectionViewLayoutAttributes* candidateNextLayoutAttributes = layoutAttributesArray.firstObject;
for (UICollectionViewLayoutAttributes* layoutAttributes in layoutAttributesArray)
{
if ((layoutAttributes.representedElementCategory != UICollectionElementCategoryCell) ||
(layoutAttributes == candidateNextLayoutAttributes)) // skip the first comparison
continue;
if(velocity.x > EscapeVelocity || velocity.x < -(EscapeVelocity))
{
if(velocity.x > EscapeVelocity && layoutAttributes.center.x > candidateNextLayoutAttributes.center.x)
{
candidateNextLayoutAttributes = layoutAttributes;
}
else if (velocity.x < -(EscapeVelocity) && layoutAttributes.center.x < candidateNextLayoutAttributes.center.x)
{
candidateNextLayoutAttributes = layoutAttributes;
}
}
else
{
if(fabsf(currentBoundsCenterX - layoutAttributes.center.x) < fabsf(currentBoundsCenterX - candidateNextLayoutAttributes.center.x))
{
candidateNextLayoutAttributes = layoutAttributes;
}
}
}
return CGPointMake(candidateNextLayoutAttributes.center.x - self.collectionView.bounds.size.width * 0.5f, proposedContentOffset.y);
}
I just put together a sample project which shows how you could do this. I created a container view which is 100 points wider than the 320 points for the screen. Then I put a UICollectionView into that container. This offsets everything by 50 points on both sides of the screen.
Then there is a content cell which simply has a background and a label so you can visually identify what is happening. On the left and right there are empty cells. In the viewDidLoad method the content inset is set to negative values on the left and right to make the empty cells now scroll into view. You can adjust the inset to your preference.
This mimics the behavior fairly closely. To get the label below, like in the example you can simply check the contentOffset value to determine which cell is in focus. To do that you'd use the UIScrollViewDelegate which is a part of UICollectionView.
https://github.com/brennanMKE/Interfaces/tree/master/ListView
You'll notice this sample project has 2 collection views. One is a normal horizontal flow layout while the other one which has larger cells is the one which mimics the example you mentioned.
i want a game that scrolls the background in a similar way to a UItableView. I solved it with a timer that moves the background up and brings another copy of the same picture up
if (bg1.center.y <= - self.view.bounds.size.height/2 ) {
bg1.center = CGPointMake(bg1.center.x, 690);
}
if (bg2.center.y <= - self.view.bounds.size.height/2 ) {
bg2.center = CGPointMake(bg2.center.x, 690);
bg1.center = CGPointMake(bg1.center.x, bg1.center.y - movement);
bg2.center = CGPointMake(bg2.center.x, bg2.center.y - movement);
But the faster i move the pictures the more problems occur: There are appearing gaps between the backgrounds and they are getting biggiger the faster i move them! movement is defined by the speed of swiping over the screen
Any idea to solve that?
You could try using the canvas element