0
\$\begingroup\$

I am trying to program TopDown Car using python and Box2D (similar to this tutorial: http://www.iforce2d.net/b2dtut/top-down-car ) The steering is so far limited to thres states of wheel rotation, that I will need to solve later, now I have problem with controlling the car velocity.

The problem is that the vehicle moves rather uncontrollably, I press "W" to move forward, "A" or "D" to steer, but then I ofttimes need to press "S" key to keep going in forward direction (which is of course counter-intuitive).

EDIT: I tried to convert world/object coordinates, it helped partially because now my car is moving from the beginning in straight line (it was always going slightly obliquely to the right, but now it has problems with turning, it turns uncontrollably and there is still the problem with messed-up directions (when I need the car to go forward after turning i have to press S

This is the main game loop: it should be updating PyGame frame 60 times per second, in Every frame I am cancelling lateral velocity of a body and then I am trying to controll the body

while running:
# Check the event queue
    screen.fill((255, 255, 255,0))
    for body in world.bodies:
        for fixture in body.fixtures:
            fixture.shape.draw(body, fixture)

        # Cancelling lateral velocity
        for wheel in Wheels:

            if wheel in range(2):
                continue # Rear wheels are not powered
            else:
                Current_Normal = wheel.GetWorldVector((0,1))
                Current_N_Squared =  b2Dot(Current_Normal, wheel.linearVelocity) * Current_Normal

                Right_Normal = wheel.GetWorldVector((1,0))
                Right_Normal_Squared = b2Dot(Right_Normal, wheel.linearVelocity) * Right_Normal

                Impulse = -Right_Normal_Squared * wheel.mass
                if Impulse.length >max_lateral_impulse:
                    Impulse *= max_lateral_impulse/Impulse.length

                wheel.ApplyLinearImpulse(Impulse, wheel.worldCenter, True)

                # Allowing skidding

                aimp = 0.05 * wheel.inertia * -wheel.angularVelocity
                wheel.ApplyAngularImpulse(aimp, True)

                current_forward_speed = Current_N_Squared.Normalize()

                drag_force_magnitude = -2 * current_forward_speed
                wheel.ApplyForce(drag_force_magnitude * Current_N_Squared,
                                        wheel.worldCenter, True)

        # Moving forward/backward       
    if event.type == KEYDOWN and event.key == K_w:
        Left_Wheel.ApplyLinearImpulse(impulse=Left_Wheel.GetWorldVector((0, 10)), point =Left_Wheel.GetWorldVector((Left_Wheel.position)),wake = True)
        Right_Wheel.ApplyLinearImpulse(impulse=Right_Wheel.GetWorldVector((0, 10)),point = Right_Wheel.GetWorldVector((Right_Wheel.position)),wake = True)

    elif event.type == KEYDOWN and event.key == K_s:
        Left_Wheel.ApplyLinearImpulse(impulse=Left_Wheel.GetWorldVector((0, -10)), point =Left_Wheel.GetWorldVector((Left_Wheel.position)),wake = True)
        Right_Wheel.ApplyLinearImpulse(impulse=Right_Wheel.GetWorldVector((0, -10)),point = Right_Wheel.GetWorldVector((Right_Wheel.position)),wake = True)

If you want to play with my code in order to better see where the problem is I can give you the whole script (270 lines in total)

I know that to move more realistically I need to play with the aimp or max_lateral_velocity values. But first I need the car to be controlled better

EDIT 2: I am sorry, I was probably able to solve this problem by applying this:

Left_Wheel.ApplyLinearImpulse(b2Vec2(Left_Wheel.GetWorldVector((0, 10))), Left_Wheel.position,True)
Right_Wheel.ApplyLinearImpulse(b2Vec2(Right_Wheel.GetWorldVector((0, 10))), Right_Wheel.position,True)

This seems to be working, many thanks to Aleš Koblížek for patience Now my problem seems to be solved

\$\endgroup\$
0
\$\begingroup\$

The KEYDOWN event is fired every time the key is pressed down, so the impulse is applied only then. Instead, you should remember the state of the key and apply force in every iteration of the main loop if the key is pressed down.

See this article for example code: https://www.iforce2d.net/b2dtut/forces

Also to make sure the speed is framerate-independent, you should multiply the applied force by frame time.

EDIT: The error with the force directions is that the function ApplyLinearImpulse takes direction in world coordinates, not in object coordinates.

EDIT2: You can use getWorldVector method to convert a vector from object coordinates to world coordinates, or getWorldPoint to convert points. There are also inverse methods getLocalVector and getLocalPoint.

\$\endgroup\$
  • \$\begingroup\$ By multiplying the applied force by frame time you mean something like this? if event.type == KEYDOWN and event.key == K_w: Left_Wheel.ApplyLinearImpulse(TIME_STEP*b2Vec2(10,0), Left_Wheel.position,False) Thank you. (For the first comment about keydown please see edit in my question \$\endgroup\$ – Mechatrnk Jun 25 at 7:49
  • \$\begingroup\$ Yes, but instead of impulse you should use force. (impulse = force * time) Impulse is a sudden push - applied instantly. By applying force continually over time the acceleration should be more gradual. Regarding the processing of events: what is in the variable "event"? I suppose it is a single event that was processed as last during that iteration of the main loop. So the main loop repeatedly responds only to that single event. And for the force directions: see edit. \$\endgroup\$ – Aleš Koblížek Jun 25 at 11:42
  • \$\begingroup\$ Ok, thanks I think I can accept this answer, but could you please give me at least a hint how to convert world coordinates to object coordinates (Like do I have to do some complex projections or is there any function which would do it for me)? Does for example applying force (as you recommended) take object coordinates? Thank you very much \$\endgroup\$ – Mechatrnk Jun 25 at 12:08
  • \$\begingroup\$ I think you want the opposite - convert object coordinates to world coordinates. You know the direction in which the car should turn relative to the car (in object coordinates). I have added the names of the suitable methods to the answer. \$\endgroup\$ – Aleš Koblížek Jun 25 at 16:10
  • \$\begingroup\$ I tried to applyForce as you suggested, but after reading this: iforce2d.net/b2dtut/constant-speed I decided that Impulse would be better option, because I am using linear impulse for canceling lateral velocity, but after I tried to convert object coordinates to world it helped partially but not completely see edit \$\endgroup\$ – Mechatrnk Jun 28 at 8:10

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.