Set a physicsbody to a constant velocity - sprite-kit

Moving a ball with constant velocity
I tried to make a box of balls where the balls move with constant velocity. The shouldn't slow down when they collide with each other. I think I have set all properties right but it didn't work and after 30s all of the balls stopped to move.
The box is set like this:
self.physicsBody = SKPhysicsBody(edgeLoopFromRect: self.frame)
self.physicsBody.dynamic = false
self.physicsBody.restitution = 1
self.physicsBody.friction = 0
The balls are set like this:
Is this a bug of the physics engine or am I missing something?

If you want them to have a constant velocity all the time, no change at all, you're going to have to set their velocity to a fixed-length vector in SKScene update. Physics engines aren't designed to adhere strictly to the conservation of energy law ... or one could argue that some energy is dissipated through heating the device. ;)
General principle for keeping the same direction but adjusting vector length/speed to a fixed value (pseudo-code):
CGPoint velocity = somePhysicsBody.velocity;
velocity = normalized(velocity);
velocity = multiply(velocity, desiredSpeed);
somePhysicsBody.velocity = velocity;

In addition to Steffen's (LearnCocos2D) answer, the methods mentioned in the pseudo-code can be found in this very good Ray Wenderlich tutorial.
static inline CGPoint rwAdd(CGPoint a, CGPoint b) {
return CGPointMake(a.x + b.x, a.y + b.y);
}
static inline CGPoint rwSub(CGPoint a, CGPoint b) {
return CGPointMake(a.x - b.x, a.y - b.y);
}
static inline CGPoint rwMult(CGPoint a, float b) {
return CGPointMake(a.x * b, a.y * b);
}
static inline float rwLength(CGPoint a) {
return sqrtf(a.x * a.x + a.y * a.y);
}
// Makes a vector have a length of 1
static inline CGPoint rwNormalize(CGPoint a) {
float length = rwLength(a);
return CGPointMake(a.x / length, a.y / length);
}
They use CGPoint as the parameters and return value, but this can be easily converted to use CGVector.
These methods are very useful for physics calculations, and you will find use for them at many points. It is best to keep these methods in a separate header file, and use them in the code throughout your project.

Related

Ball Mechanics - Is this the best approach?

Good day,
I'd like to program a constantly moving ball (object3) being passed between two stationary objects (object1, object2), with the ability to set the max height Y of the pass trajectory dynamically.
What would you argue is the best way to program the ball physics for this concept?
I've looked at using addForce on a default sphere w/ a rigidbody. It seems like there should be an equation that expresses the trajectory of a pass of object3 from object1's x to object2's x... at a known, given speed, with a known, set mass, and a known gravity environment.
However, currently I have a Vector3.Lerp interpolating the ball between the two objects on each FixedUpdate() with t expressed as:
`(Mathf.Sin(speed * Time.time) + 1.0f) / 2.0f;`
It works and all, but with this approach, it seems there's no clear way to add height to the trajectory of the ball path. I've considered adding the height to the Y value in object2 until the ball is half way there, and then setting it back to the original Y position... but it just feels wrong! Thoughts?
Thanks!
Okey so if I understand you correctly currently you are doing
privte void FixedUpdate()
{
var factor = (Mathf.Sin(speed * Time.time) + 1.0f) / 2.0f;
object1.MovePosition(Vector3.Lerp(object2.position, object3.position, factor));
}
which moves the ball pingpong between object1 and object2 position but only planar.
Assuming for now the objects will only be moving within the XZ plane and never have different Y position in order to rather get a curve with height you could treat the separatly:
- Interpolate between both positions as before
- Separately calculate the Y position with sinus or any other mathematical curve function - for realistic physics probably rather a parabola actually
Could look somhow like
public class Example : MonoBehaviour
{
public Rigidbody object1;
public Transform object2;
public Transform object3;
// adjust in the Inspector
public float speed = 1;
public float Amplitude = 0;
// Just for debug
[Range(0, 1)] [SerializeField] private float linearFactor;
[SerializeField] private float yPosition;
private void FixedUpdate()
{
// This always returns a value between 0 and 1
// and linearly pingpongs forth and back
linearFactor = Mathf.PingPong(Time.time * speed, 1);
// * Mathf.PI => gives now a value 0 - PI
// so sinus returns correctly 0 - 1 (no need for +1 and /2 anymore)
// then simply multiply by the desired amplitude
var sinus = Mathf.Sin(linearFactor * Mathf.PI);
yPosition = sinus * Amplitude;
// As before interpolate between the positions
// later we will ignore/replace the Y component
var position = Vector3.Lerp(object2.position, object3.position, linearFactor);
object1.MovePosition(new Vector3(position.x, yPosition, position.z));
}
}
You could optionally also try and add some dumping in the Y direction in order to make the vertical movement more realistic (slow down when reaching the peak). I tried a bit using inverted SmoothStep like
// just for debug
[Range(0, 1)] [SerializeField] private float dampedSinusFactor;
[Range(0, 1)] [SerializeField] private float linearFactor;
[SerializeField] private float yPosition;
private void FixedUpdate()
{
// Use two different factros:
// - a linear one for movement in XZ
// - a smoothed one for movement in Y (in order to slow down when reaching the peak ;) )
linearFactor = Mathf.PingPong(Time.time * speed, 1);
dampedSinusFactor = InvertSmoothStep(linearFactor);
// * Mathf.PI => gives now a value 0 - PI
// so sinus returns correctly 0 - 1 ()
// then simply multiply by the desired amplitude
var sinus = Mathf.Sin(dampedSinusFactor * Mathf.PI);
yPosition = sinus * Amplitude;
// later we will ignore/replace the Y component
var position = Vector3.Lerp(object2.position, object3.position, linearFactor);
object1.position = new Vector3(position.x, yPosition, position.z);
}
// source: https://stackoverflow.com/a/34576808/7111561
private float InvertSmoothStep(float x)
{
return x + (x - (x * x * (3.0f - 2.0f * x)));
}
However for slow movements this looks a bit strange yet. But you can come up with any other maths curve that results in the expected behavior for x=[0,1] ;)

Is there a better way to write this top down car mdoel

I Am writing a top down car drift game, where i want to make the car move in a circle and user controls the radius of the circle with the steering, basically straight steering is a straight line and turned left is a small circle to the left of the car which it will follow. as the car turns more (either side) i try to make the cars front turn more and more towards the centre, as if its drifting.
Am using unity and this is how i though i should design the model.
This is for a mobile game. so i have written a steering controller for the car, to control its left and right, the speed is constant.
There are two rigid bodies.
The front white coloured box and the big grey coloured box both are rigid bodies. connected with a hinge joint.
The front box on every frame rotate a certain amount of degrees on it own z axis and moves a little forward. Thus follows a circle shape. how much is rotates is determined my the steering.
I Can make both the bodies kinematic and make sure they follow a circle shape and move that way, but i also want appropriate behaviour during collisions, In the final plan there can be all kinds of collisions. So i made them kinematic and added velocities using add force methods. On it's own the front box is perfect, It rotates in circle as i change the steering the circle gets bigger everything is fine, But when i add the Other chassis box, the forces are all messed up. centrifugal forces, and the inertia messes everything.
Basically i need the car to drift is perfect circles and user should be able to control the radius of the circle. What is the best way to model this.
Here is the code for both the rigid bodies.
the front box code. This is just the fixed update method. Please ignore the controls related code. It'e unnecessary so i left it out.
{
//-------------------------
// I N I T.
//-------------------------
current_steering_angle = controls_i.ToSignedRotation(controls_i.getSteeringAngle());
current_steering_angle = (current_steering_angle < 0) ? (current_steering_angle + 90): current_steering_angle;
current_steering_angle -= 45;
//-------------------------
// S P E E D.
//-------------------------
current_speed = Utils.curve_map_linear(0, 45, min_speed, max_speed, Mathf.Abs(current_steering_angle), -1);
_rigid_body.AddRelativeForce(acceleration * Vector3.up);
velocity = transform.InverseTransformVector(_rigid_body.velocity);
if(velocity.y > current_speed){
velocity.y = current_speed;
}
// to prevent reverse movement due to centrifugal force.
velocity.x = 0;
//-------------------------
// R O T A T I O N.
//-------------------------
current_radius = Mathf.Sign(current_steering_angle) * Utils.exp_range_3(0, 45, min_radius, max_radius, Mathf.Abs(current_steering_angle), steering_tension, -1);
current_rot_rate = (velocity.y / (2 * Mathf.PI * current_radius)) * 360;
Vector3 angle = transform.localEulerAngles;
angle.z += (current_rot_rate * Time.fixedDeltaTime);
transform.localEulerAngles = angle;
//-------------------------
// A P P L Y.
//-------------------------
_rigid_body.velocity = transform.TransformVector(velocity);
}
The Utils.exp_linear and exp_range functions are just range to range mappers with some tension, to control the sensitivity of the steering on the controls.
Code for the chassis.
{
//-------------------------
// I N I T.
//-------------------------
current_steering_angle = controls_i.ToSignedRotation(controls_i.getSteeringAngle());
current_steering_angle = (current_steering_angle < 0) ? (current_steering_angle + 90): current_steering_angle;
current_steering_angle = current_steering_angle - 45;
current_steering_angle = ((float)((int)(current_steering_angle * Mathf.Pow(10, 5))))/Mathf.Pow(10,5);
//-------------------------
// R O T A T I O N.
//-------------------------
current_angle = front_transform.eulerAngles.z - _rigid_body.transform.eulerAngles.z;
current_angle = (Mathf.Abs(current_angle) > 180) ? ((Mathf.Abs(current_angle) - 360) * Mathf.Sign(current_angle)) : current_angle;
current_angle += Mathf.Sign(current_steering_angle) * Utils.curve_map_linear(0, 45, min_angle, max_angle, Mathf.Abs(current_steering_angle), 1);
_rigid_body.transform.RotateAround(front_transform.position, Vector3.forward, current_angle);
//-------------------------
// S P E E D.
//-------------------------
Vector3 velocity = _rigid_body.transform.InverseTransformVector(_rigid_body.velocity);
if (velocity.y < 0){
velocity.y = 0;
}
_rigid_body.velocity = _rigid_body.transform.TransformVector(velocity);
}
I would like to know how to actually implement something like this in unity. I am new to this unity stuff. I looked all over and there was a lot of stuff for 3d cars but not much on top down ones. Appreciate any kind of feedback.
Make it non kinematic, remove the rigidbody of the front tires and make the front tires a child of the chassis.
Handling position:
Make a method that looks at the car's current position, how much the player is turning, and returns the tangential velocity the car needs to go to drive in the appropriate circle:
Vector2 GetTangentialVelocity(...) { }
Then, find the difference between that velocity and the current velocity of the chassis:
Vector2 velDiff = GetTangentialVelocity(...) - chassis_rigidbody.velocity;
Then, apply that difference using AddForce with ForceType.VelocityChange:
chassis_rigidbody.AddForce(velDiff, ForceType.VelocityChange);
Handling rotation:
Move the center of mass forward. You can do this by making the rigidbody long and put the collider at the rear of the object, or by setting RigidBody.centerOfMass:
chassis_rigidbody.centerOfMass = Vector2.up * 0.5;
Make a function similar to GetTangentialVelocity that returns the desired angular velocity, find the difference from chassis_rigidbody.angularVelocity, then use AddTorque with ForceType.VelocityChange:
Vector2 angVelDiff = GetAngularVelocity(...) - chassis_rigidbody.angularVelocity;
chassis_rigidbody.AddTorque(angVelDiff , ForceType.VelocityChange);

What is happening in the following code?

This code is used in the accelerometer
method.
It uses a CGPoint variable called playerVelocity.
-(void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration
{
//controls how quickly the velocity decelerates
float deceleration = 0.4f;
//determines how sensitive the accelerometer reacts
float sensitivity = 6.0f;
//how fast the velocity can be at most
float maxVelocity = 100;
playerVelocity.x = playerVelocity.x *deceleration + acceleration.x *sensitivity;
if (playerVelocity.x < -maxVelocity)
{
playerVelocity.x = -maxVelocity;
}
else if (playerVelocity.x > maxVelocity)
{
playerVelocity.x = maxVelocity;
}
}
Now I know that the playerVelocity variable is a CGPoint so I imagine it as a X,Y Graph.
I'm assuming that wherever the playerVelocity variable is resting (let's say 150,0), it first multiplies whatever coordinates by 0.4 whenever the accelerometer input is received (which is by the iPhone being tilted)and it then add's the accelerometer.x multiplied by 6.0 to the playerVelocity variable. Is this correct?
Later on in another method, this is added to my other objects position via
CGPoint pos = playerObject.position;
pos.x+= playerVelocity.x;
playerObject.position = pos;
What I'm confused about is what exactly is happening behind the scenes here. Is my assumption above correct?
When the playerVelocity is at 150,0 and is multiplied by 0.4, does the X coordinate of the playerVelocity variable gradually reduce, i.e. 150,0 , 145,0 , 130,0 etc.. ?
If I figure this out I'll then know how my playerObject is moving.
It looks like you have a constant deceleration (.4) that is opposing motion in whatever direction you are currently traveling subtracted from the acceleration as received via the accelerometer, which is multiplied by a constant. This value is then added to your current velocity. So you are essentially adding the difference of (acceleration from accelerometer - constant deceleration) to your current velocity for each calculation.

Throw ball using cocos2d but without any physic engine

I want to throw a ball with swipe speed but do not want to use any physic engine. so please can any one suggest me how i do this.
thanks
A simple yet effective approach without having to explicitly use any physics engine is to step the velocity and position of the ball sprite manually in your update loop, Euler-style.
In the typical case (with downward gravity), you will have non-constant velocity in the y-direction and constant velocity in the x-direction, hence the following code:
-(void) update: (ccTime) dt
{
// Step only the y-velocity
velocity_y += GRAVITY * dt;
// Step the position values and update the ball sprite position accordingly
ball.position.x += velocity_x * dt;
ball.position.y += velocity_y * dt;
}
Then when a swipe event is detected,
Capture the swipe velocity (you will have to compute the change in position of the finger touch in the current and last frame)
Multiply the velocity with a scaling factor if necessary.
Set *velocity_x* and *velocity_y* to these initial values.
I am using projectile motion formulas to do this, http://en.wikipedia.org/wiki/Projectile_motion, along with cocos actions. Here you have a possible approach:
First
Implement a CCActionInterval subclass that receives the projectile formula parameters and updates the projectile position. In this case, ProjectileAction. These are the key methods you need to over-ride:
-(void) startWithTarget:(id)target
{
self.initialPosition = [target position];
self.elapsedTime = 0;
[super startWithTarget:target];
}
-(void) update: (ccTime) tt
{
self.elapsedTime += tt;
float t = self.elapsedTime;
float theta = CC_DEGREES_TO_RADIANS(self.angle);
float v0 = self.velocity;
float g = self.gravitationalAcceleration;
double x = v0 * cos(theta) * t;
double y = v0 * sin(theta) * t - 0.5 * g * t * t ;
[self.target setPosition: ccp(self.initialPosition.x + (float)x, self.initialPosition.y + (float)y)];
}
Second
Use your swipe gesture to recognize the speed, that will translate to the ball's initial velocity in the equation above and to the theVelocity parameter bellow. I leave that part to you.
Third
Run the action. i.e.
ProjectileLaunch* action = [ProjectileLaunch actionWithDuration:10
angle:45
initialVelocity:theVelocity
g:9.8];
[sprite runAction:action];
I hope this helps you.
Regards.

Box2d:apply velocity in a direction

I apply impulse in an object in box2d iPhone app and now want to increase its speed in particuler direction....i mean i need two thing
1.through the object in a direction
2.increase speed
plz help..
b2Vec2 force = b2Vec2(xAcceleration, yAcceleration);
force *= dt; // Use this if your game engine uses an explicit time step
b2Vec2 p = myObjectBody->GetWorldPoint(b2Vec2(0.0f, 0.0f));
body->ApplyForce(force, p);
By modifying xAcceleration and yAcceleration, you can make the object move with various speeds in different directions. (If you calculate angles, you might want to use force.Normalize(); and then multiply by a velocity.)
b2Vec2 vector = self.speed * b2Vec2(cos(angle), sin(angle));
self.yourbodyBody->SetLinearVelocity(vector);
[self schedule:#selector(increaseSpeed) interval:0.1];
- (void)increaseSpeed
{
self.speed += 0.01;
float angle = self.yourbodyBody->GetAngle();
b2Vec2 vector = self.speed * b2Vec2(cos(angle), sin(angle));
self.yourbodyBody->SetLinearVelocity(vector);
}