How best to give falling ball real world gravity
Asked Answered
B

3

16

I'm a fairly new app dev and very new to Sprite Kit. I am creating a game where I want a ball to always fall downward as if it were in gravity. This is just a 2d app so all I want is when the ball is falling, when you tilt the phone to the left, the ball travels down and left onscreen and if you tilt the phone to the right, the ball travels down and right onscreen. To give a better picture of what I want I made a quick diagram in photoshop:

Ball falling diagram

I would just like some opinions on which is the easiest and best way to do this. My first thought was record the yaw and use it to dictate how far the ball travels left or right. This would require some calculations to get the ball to fall perfectly at 90 degrees and although it sounds possible to me, I'm not sure if it is actually possible.

Does anybody think this is the way to do it or is there a better way to this?

A problem I came across when trying out my first idea was when I tilt the phone, the ball moved left and right (without any proper calculations, so it was entirely inaccurate), I would tilt the phone to the left (for example) and the further I tilted it, the less sensitive it became, so the ball would travel left less. This would prevent my idea from working and I'm not sure if there's a way around it.

A bigger problem I encountered was when I swivelled around in my seat, the yaw would change too! I assume this has something to do with the compass since neither the roll or pitch changed as I did this. I'm sure something can be done to correct this because I never have this problem with games I play that use the gyroscope. If somebody could point me in the right direction with that, I'd be grateful, or I might just ask it in a separate new question. Below is another diagram I quickly drew up in photoshop to help with explaining the problem. Diagram 1 is the way I want to be able to tilt the phone but when you turn around (diagram 2) the ball moves left or right depending on how far you turn. (Yes, that is meant to be a person holding the phone in diagram 2)

phone tilt diagram

Help would be greatly appreciated!

Bulla answered 27/3, 2014 at 20:39 Comment(0)
P
11

Here is some code I yanked from a demo project I built a while ago. It was a piece of rope created by connecting a bunch of views with UIKit Dynamics. Based on your description, I believe the behavior was the same as what you are looking for.

A caveat is that this code follows the true direction of gravity, so if you set your phone down flat, the gravity will be 0 or close to 0 in the plane parallel to the screen, because its full magnitude will be along the z axis. You may want to normalize it so the direction changes but the magnitude remains constant.

In this sample, self.gravity is a UIGravityBehavior, but you could use the same code to feed any kind of gravity simulation you want. Just log out the values inside the motion manager handler to see what kind of values you are likely to be getting.

self.motionManager = [[CMMotionManager alloc] init];
[self.motionManager startDeviceMotionUpdatesToQueue:self.motionQueue withHandler:^(CMDeviceMotion *motion, NSError *error) {
    CMAcceleration gravity = motion.gravity;
    dispatch_async(dispatch_get_main_queue(), ^{
        self.gravity.gravityDirection = CGVectorMake(gravity.x, -gravity.y);
    });
}];

I hope this helps!

Praedial answered 27/3, 2014 at 22:38 Comment(8)
Thank you very much for your help. I've got your code working in my project but I'm unsure of how to get it to move the ball, because I can't just multiply the screen width by the x value, so what would you suggest? I'm sure it's simpler than I think, but I can't think of a way to do it. I know you pulled the code from a project that sounds very different, but someone more experienced than myself might be able to work it out better than me! Thanks!Bulla
Perhaps the formula for position of a falling object will help? Displacement = 1/2 g t^2, where g is the gravity vector you just found and t is the time elapsed. More here.Praedial
Oh, I forgot you are using SpriteKit. I am not familiar with the API, but isn’t there a gravity behavior where you can plug in that vector?Praedial
Very sorry for the late reply. Very busy! Thank you so much for helping. I promise I'll mark your answer as 'answered'. Anyway, about the gravity behaviour you mentioned, do you have any idea where I can find out more about it? Googling hasn't shown me any results that I think are helpful. No worries if that's as much as you can help because I realise you're not familiar with SpriteKit. I'm sure I'll figure it out eventually, or I'll just give up and find an easier idea... (last resort)Bulla
It looks like you modify the gravity property of your SKPhysicsWorld. More info here.Praedial
Ah, OK. That's enough for me to figure out the rest. Thanks for all your help! Have a great day.Bulla
Just for future reference, after working with this solution for a while, I've realised that the gravity value can only be used for y axis movement because when using a gravity value for the x axis when you tilt the phone left (for example), there is a delay and acceleration of the ball to the left. This isn't realistic. I'm having to research and calculate how to move the ball along the x axis realistically. Your post, 'Zev Eisenberg', was still very helpful and got me further, but I just wanted to point out to others that there's more to this solution than you think.Bulla
That doesn’t sound right. If you have it set up right, tilting your phone to the left should make the ball fall straight towards the ground, which is to say, towards the left side of the phone. The only time when I would expect delays is when you tilt the phone forward or backward, and that can be fixed by normalizing the xy vector to a constant magnitude.Praedial
M
3

Here's a solution with SpriteKit:

1.Create a new SpriteKit Project enter image description here

2.Add the CoreMotion Framework

enter image description here

3.import the new framework in your 'MyScene' class:

#import <CoreMotion/CoreMotion.h>

4.Example is optimized for this orientation:

enter image description here

5.Add this code to the 'touchesBegan' method of the 'MyScene' class

    sprite.size = CGSizeMake(20, 20);
    sprite.physicsBody= [SKPhysicsBody bodyWithCircleOfRadius:sprite.size.width/2];

6.Add this code to the 'update' method of the 'MyScene' class

CMMotionManager *_motionManager;
-(void)update:(CFTimeInterval)currentTime {
    /* Called before each frame is rendered */
    if (_motionManager== nil) {
        _motionManager=[[CMMotionManager alloc]init];
        _motionManager.deviceMotionUpdateInterval=0.025;
        [_motionManager startDeviceMotionUpdates];
    }

    CMAcceleration gravity=  _motionManager.deviceMotion.gravity;
    self.physicsWorld.gravity = CGVectorMake(gravity.y*4, -gravity.x/4);

}

You have to play around to find the optimal factors for the gravity.

Hope that is usefull

Marylouisemaryly answered 7/4, 2014 at 19:23 Comment(2)
Thank you Stefan for your input. It's helpful seeing different solutions. I've realised though that using gravity to affect the sideways movement of the ball isn't realistic since when you turn the device there is a delay and then an acceleration. This isn't real world physics and I'm currently working on figuring out ways of controlling the sideways movement without using gravity.Bulla
Hi David. If you find a solution it would be great if you can share it here. I guess a lot of people (including me) tried the same.Marylouisemaryly
L
0

Swift Code:

 if motion.isDeviceMotionAvailable {
            self.motion.deviceMotionUpdateInterval = 1.0 / 60.0
            self.motion.showsDeviceMovementDisplay = true
            self.motion.startDeviceMotionUpdates(using: .xMagneticNorthZVertical)
            // Configure a timer to fetch the motion data.
            self.timer = Timer(fire: Date(), interval: (1.0/60.0), repeats: true,
                               block: { (timer) in
                                if let data = self.motion.deviceMotion {
                                  let gravityData = data.gravity
                                  scene.physicsWorld.gravity = CGVector(dx: 9.8 * (gravityData.x), dy: 9.8 * (gravityData.y))
                                }
            })
            // Add the timer to the current run loop.
            RunLoop.current.add(self.timer, forMode: .defaultRunLoopMode)
          }
Lute answered 12/10, 2018 at 10:55 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.