Box2D Vehicle

Monkey Forums/Monkey Programming/Box2D Vehicle

Hezkore(Posted 2014) [#1]
I've tried for some time now to convert vehicle examples for Box2D I've found online.
Somehow they never quite work as they should. (probably my fault)
But anyways, here is one example that I have converted to some success - http://www.emanueleferonato.com/2011/09/18/creation-of-a-box2d-car-with-10-customizable-parameters/

And here is the Monkey X code and a playable example.
The problem is that it doesn't feel like a proper car, it's like the suspension is all messed up. (and pointing the wrong way?)
Hold "Space" for a second to make the car fly straight up, you'll see the suspension isn't really working.
What am I doing wrong, how can I improve this?
Strict
Import mojo
Import box2d.flash.flashtypes
Import box2d.dynamics
Import box2d.collision
Import box2d.collision.shapes
Import box2d.dynamics.joints
Import box2d.dynamics.contacts
Import box2d.common
Import box2d.common.math

Class MyApp Extends App
	Field world:b2World = New b2World(New b2Vec2(0, 10), True)
	Field worldScale:int = 30
	Field car:b2Body
	Field leftWheelRevoluteJoint:b2RevoluteJoint
	Field rightWheelRevoluteJoint:b2RevoluteJoint
	Field motorSpeed:Float = 0
	Field leftAxlePrismaticJoint:b2PrismaticJoint
	Field rightAxlePrismaticJoint:b2PrismaticJoint
	
	Field carPosX:Float = 320
	Field carPosY:Float = 0
	Field carWidth:Float = 45
	Field carHeight:Float = 10
	Field axleContainerDistance:Float = 30
	Field axleContainerWidth:Float = 5
	Field axleContainerHeight:Float = 20
	Field axleContainerDepth:Float = 10
	Field axleAngle:Float = 20
	Field wheelRadius:Float = 25
	Field leftWheel:b2Body
	Field rightWheel:b2Body
	
	Method OnCreate:Int()
		debugDraw()
		
		'/ / * * * * * * * * * * * * * * * * * * * * * * * * THE FLOOR * * * * * * * * * * * * * * * * * * * * * * * * / /
		Local floorShape:b2PolygonShape = New b2PolygonShape()
		floorShape.SetAsBox(640 / worldScale, 10 / worldScale)
		Local floorFixture:b2FixtureDef = New b2FixtureDef()
		floorFixture.density = 0
		floorFixture.friction = 10
		floorFixture.restitution = 0
		floorFixture.shape = floorShape
		Local floorBodyDef:b2BodyDef = New b2BodyDef()
		floorBodyDef.position.Set(320 / worldScale, 480 / worldScale)
		Local floor:b2Body = world.CreateBody(floorBodyDef)
		floor.CreateFixture(floorFixture)
		
		'/ / * * * * * * * * * * * * * * * * * * * * * * * * THE CAR * * * * * * * * * * * * * * * * * * * * * * * * / /
		Local carShape:b2PolygonShape = New b2PolygonShape()
		carShape.SetAsBox(carWidth / worldScale, carHeight / worldScale)
		Local carFixture:b2FixtureDef = New b2FixtureDef()
		carFixture.density = 5
		carFixture.friction = 3
		carFixture.restitution = 0.3
		carFixture.filter.groupIndex = -1
		carFixture.shape = carShape
		Local carBodyDef:b2BodyDef = New b2BodyDef()
		carBodyDef.position.Set(carPosX / worldScale, carPosY / worldScale)
		carBodyDef.type = b2Body.b2_Body
		
		'/ / * * * * * * * * * * * * * * * * * * * * * * * * LEFT AXLE CONTAINER * * * * * * * * * * * * * * * * * * * * * * * * / /
		Local leftAxleContainerShape:b2PolygonShape = New b2PolygonShape()
		leftAxleContainerShape.SetAsOrientedBox(axleContainerWidth / worldScale, axleContainerHeight / worldScale, New b2Vec2(-axleContainerDistance / worldScale, axleContainerDepth / worldScale), axleAngle)
		Local leftAxleContainerFixture:b2FixtureDef = New b2FixtureDef()
		leftAxleContainerFixture.density = 3
		leftAxleContainerFixture.friction = 3
		leftAxleContainerFixture.restitution = 0.3
		leftAxleContainerFixture.filter.groupIndex = -1
		leftAxleContainerFixture.shape = leftAxleContainerShape
		
		'/ / * * * * * * * * * * * * * * * * * * * * * * * * RIGHT AXLE CONTAINER * * * * * * * * * * * * * * * * * * * * * * * * / /
		Local rightAxleContainerShape:b2PolygonShape = New b2PolygonShape()
		rightAxleContainerShape.SetAsOrientedBox(axleContainerWidth / worldScale, axleContainerHeight / worldScale, New b2Vec2(axleContainerDistance / worldScale, axleContainerDepth / worldScale), -axleAngle)
		Local rightAxleContainerFixture:b2FixtureDef = New b2FixtureDef()
		rightAxleContainerFixture.density = 3
		rightAxleContainerFixture.friction = 3
		rightAxleContainerFixture.restitution = 0.3
		rightAxleContainerFixture.filter.groupIndex = -1
		rightAxleContainerFixture.shape = rightAxleContainerShape
		
		'/ / * * * * * * * * * * * * * * * * * * * * * * * * MERGING ALL TOGETHER * * * * * * * * * * * * * * * * * * * * * * * * / /
		car = world.CreateBody(carBodyDef)
		car.CreateFixture(carFixture)
		car.CreateFixture(leftAxleContainerFixture)
		car.CreateFixture(rightAxleContainerFixture)
		
		'/ / * * * * * * * * * * * * * * * * * * * * * * * * THE AXLES * * * * * * * * * * * * * * * * * * * * * * * * / /
		Local leftAxleShape:b2PolygonShape = New b2PolygonShape()
		leftAxleShape.SetAsOrientedBox(axleContainerWidth / worldScale / 2, axleContainerHeight / worldScale, New b2Vec2(0, 0), axleAngle)
		Local leftAxleFixture:b2FixtureDef = New b2FixtureDef()
		leftAxleFixture.density = 0.5
		leftAxleFixture.friction = 3
		leftAxleFixture.restitution = 0
		leftAxleFixture.shape = leftAxleShape
		leftAxleFixture.filter.groupIndex = -1
		Local leftAxleBodyDef:b2BodyDef = New b2BodyDef()
		leftAxleBodyDef.type = b2Body.b2_Body
		Local leftAxle:b2Body = world.CreateBody(leftAxleBodyDef)
		leftAxle.CreateFixture(leftAxleFixture)
		leftAxle.SetPosition(New b2Vec2(carPosX / worldScale - axleContainerDistance / worldScale - axleContainerHeight / worldScale * Cos( (90 - axleAngle)), carPosY / worldScale + axleContainerDepth / worldScale + axleContainerHeight / worldScale * Sin( (90 - axleAngle))))
		Local rightAxleShape:b2PolygonShape = New b2PolygonShape()
		rightAxleShape.SetAsOrientedBox(axleContainerWidth / worldScale / 2, axleContainerHeight / worldScale, New b2Vec2(0, 0), -axleAngle)
		Local rightAxleFixture:b2FixtureDef = New b2FixtureDef()
		rightAxleFixture.density = 0.5
		rightAxleFixture.friction = 3
		rightAxleFixture.restitution = 0
		rightAxleFixture.shape = rightAxleShape
		rightAxleFixture.filter.groupIndex = -1
		Local rightAxleBodyDef:b2BodyDef = New b2BodyDef()
		rightAxleBodyDef.type = b2Body.b2_Body
		Local rightAxle:b2Body = world.CreateBody(rightAxleBodyDef)
		rightAxle.CreateFixture(rightAxleFixture)
		rightAxle.SetPosition(New b2Vec2(carPosX / worldScale + axleContainerDistance / worldScale + axleContainerHeight / worldScale * Cos( (90 - axleAngle)), carPosY / worldScale + axleContainerDepth / worldScale + axleContainerHeight / worldScale * Sin( (90 - axleAngle))))
		
		'/ / * * * * * * * * * * * * * * * * * * * * * * * * THE WHEELS * * * * * * * * * * * * * * * * * * * * * * * * / /
		Local wheelShape:b2CircleShape = New b2CircleShape(wheelRadius / worldScale)
		Local wheelFixture:b2FixtureDef = New b2FixtureDef()
		wheelFixture.density = 1
		wheelFixture.friction = 15
		wheelFixture.restitution = 0.2
		wheelFixture.filter.groupIndex = -1
		wheelFixture.shape = wheelShape
		Local wheelBodyDef:b2BodyDef = New b2BodyDef()
		wheelBodyDef.type = b2Body.b2_Body
		wheelBodyDef.position.Set(carPosX / worldScale - axleContainerDistance / worldScale - 2 * axleContainerHeight / worldScale * Cos( (90 - axleAngle)), carPosY / worldScale + axleContainerDepth / worldScale + 2 * axleContainerHeight / worldScale * Sin( (90 - axleAngle)))
		leftWheel = world.CreateBody(wheelBodyDef)
		leftWheel.CreateFixture(wheelFixture)
		wheelBodyDef.position.Set(carPosX / worldScale + axleContainerDistance / worldScale + 2 * axleContainerHeight / worldScale * Cos( (90 - axleAngle)), carPosY / worldScale + axleContainerDepth / worldScale + 2 * axleContainerHeight / worldScale * Sin( (90 - axleAngle)))
		rightWheel = world.CreateBody(wheelBodyDef)
		rightWheel.CreateFixture(wheelFixture)
		
		'/ / * * * * * * * * * * * * * * * * * * * * * * * * REVOLUTE JOINTS * * * * * * * * * * * * * * * * * * * * * * * * / /
		Local leftWheelRevoluteJointDef:b2RevoluteJointDef = New b2RevoluteJointDef()
		leftWheelRevoluteJointDef.Initialize(leftWheel, leftAxle, leftWheel.GetWorldCenter())
		leftWheelRevoluteJointDef.enableMotor = True
		leftWheelRevoluteJoint = b2RevoluteJoint(world.CreateJoint(leftWheelRevoluteJointDef))
		leftWheelRevoluteJoint.SetMaxMotorTorque(10)
		Local rightWheelRevoluteJointDef:b2RevoluteJointDef = New b2RevoluteJointDef()
		rightWheelRevoluteJointDef.Initialize(rightWheel, rightAxle, rightWheel.GetWorldCenter())
		rightWheelRevoluteJointDef.enableMotor = True
		rightWheelRevoluteJoint = b2RevoluteJoint(world.CreateJoint(rightWheelRevoluteJointDef))
		rightWheelRevoluteJoint.SetMaxMotorTorque(10)
		
		'/ / * * * * * * * * * * * * * * * * * * * * * * * * PRISMATIC JOINTS * * * * * * * * * * * * * * * * * * * * * * * * / /
		Local leftAxlePrismaticJointDef:b2PrismaticJointDef = New b2PrismaticJointDef()
		leftAxlePrismaticJointDef.lowerTranslation = 0
		leftAxlePrismaticJointDef.upperTranslation = axleContainerDepth / worldScale
		leftAxlePrismaticJointDef.enableLimit = True
		leftAxlePrismaticJointDef.enableMotor = True
		leftAxlePrismaticJointDef.Initialize(car, leftAxle, leftAxle.GetWorldCenter(), New b2Vec2(-Cos( (90 - axleAngle)), Sin( (90 - axleAngle))))
		leftAxlePrismaticJoint = b2PrismaticJoint(world.CreateJoint(leftAxlePrismaticJointDef))
		leftAxlePrismaticJoint.SetMaxMotorForce(10)
		leftAxlePrismaticJoint.SetMotorSpeed(10)
		Local rightAxlePrismaticJointDef:b2PrismaticJointDef = New b2PrismaticJointDef()
		rightAxlePrismaticJointDef.lowerTranslation = 0
		rightAxlePrismaticJointDef.upperTranslation = axleContainerDepth / worldScale
		rightAxlePrismaticJointDef.enableLimit = True
		rightAxlePrismaticJointDef.enableMotor = True
		rightAxlePrismaticJointDef.Initialize(car, rightAxle, rightAxle.GetWorldCenter(), New b2Vec2(Cos( (90 - axleAngle)), Sin( (90 - axleAngle))))
		rightAxlePrismaticJoint = b2PrismaticJoint(world.CreateJoint(rightAxlePrismaticJointDef))
		rightAxlePrismaticJoint.SetMaxMotorForce(10)
		rightAxlePrismaticJoint.SetMotorSpeed(10)
	
		Return (True)
	End
	
	Method OnUpdate:Int()
		If KeyDown(KEY_LEFT) Then motorSpeed += 0.1
		If KeyDown(KEY_RIGHT) Then motorSpeed -= 0.1
		motorSpeed *= 0.99
		'If motorSpeed > 10 Then motorSpeed = 10
		
		If KeyDown(KEY_SPACE) Then
			leftWheel.ApplyForce(New b2Vec2(0, -150), leftWheel.GetWorldCenter())
			rightWheel.ApplyForce(New b2Vec2(0, -150), rightWheel.GetWorldCenter())
		EndIf
		
		leftWheelRevoluteJoint.SetMotorSpeed(motorSpeed)
		rightWheelRevoluteJoint.SetMotorSpeed(motorSpeed)
		
		world.TimeStep(1.0 / 30.0, 10, 10)
		world.ClearForces()
		Return(True)
	End
	
	Method debugDraw:int()
		Local worldDebugDraw:b2DebugDraw = New b2DebugDraw()
		'Local debugSprite:Sprite = new Sprite()
		'addChild(debugSprite)
		'worldDebugDraw.SetSprite(debugSprite)
		worldDebugDraw.SetDrawScale(worldScale)
		worldDebugDraw.SetFlags(b2DebugDraw.e_shapeBit | b2DebugDraw.e_jointBit)
		worldDebugDraw.SetFillAlpha(0.5)
		world.SetDebugDraw(worldDebugDraw)
		Return (True)
	End
	
	Method OnRender:Int()
		world.DrawDebugData()
		Return(True)
	End
End

Function Main:Int()
	New MyApp
	Return(True)
End



navyRod(Posted 2014) [#2]
nice vehicle you have there

I am no physics major and you obviously are 10 steps ahead of me on box2d but to me looks like you don't have enough bounce in the
suspension based on the mass of the wheels .

When I reduced the radius of wheels to like 10 you can see a lot more of what is going on and suspension seemed better

From what I have read so far restitution is the parameter that gives objects their bounce but I am sure you will be getting a lot better
advice from others.


Trez(Posted 2014) [#3]
Hi, I use rube to do most stuff with box2d but anyway your vehicle is not far off.

Add back in the Const for degreesToRadians and then apply it back to the angle calculations as per the original code. Replace all the Sin and Cos Functions with Sinr and Cosr.

Const degreesToRadians:Float = 0.0174532925


Cosr( (90 - axleAngle) * degreesToRadians)
Sinr( (90 - axleAngle) * degreesToRadians)


That should fix it and give you the same results as the original example.

@navyRod you are correct in that restitution basically sets how bouncy an object is but in this case the bounce force in the suspension is controlled by the prismatic joint motors. The motor is pushing the suspension down with a force of 10 at a speed of 10 changing these values will effect how bouncy the suspension is, too much force and speed will probably eject the vehicle off the screen. The limits on the prismatic joint limit the travel of the suspension in both directions.

Stuart


Hezkore(Posted 2014) [#4]
Using Sinr and Cosr combined with degreesToRadians works very well!
The suspension is still very much NOT like a cars suspension though heh. It slowly "goes up" instead of bounces like it should.
Is there something else I should use for the joints? I've heard talk about line joints (later renamed wheel joints) being the best for vehicles.


Trez(Posted 2014) [#5]
To make a wheel joint you will need to combine a distance joint and a line joint which will give you a more realistic suspension. This demo uses a wheel joint from rube and is then converted to a distance joint and line joint with the rube importer ( wheel joints are not supported in the current box2d port available for monkey X ).

http://playniax.com/showcase/trucker/demo.html

You can get a trial version of rube https://www.iforce2d.net/rube/ it will not allow you to export but you can play about and simulate the physics so at least you have a reference.