Joints and rocket propulsion
by Joe Williams · in Torque 2D Beginner · 03/21/2013 (11:17 pm) · 17 replies
I want to make a ship of some kind and put two rocket engines on the back of it. Then I want to apply impulse force to the rockets to make the ship go. From what I can tell, it seems like I should be able to do this with a joint, but I don't know what kind to use. I'm trying weld right now, but it isn't doing what I want it to do. The way I want it to work is to have the two engines on the back of the ship, about 0.25 to the left and right. If I apply force to both rockets, the ship goes forward. If I apply force to the right rocket, the ship moves forward but also rotates counter-clockwise. And it rotates clockwise and moves forward if I apply force to the left rocket.
Can I do it this way, or do I just need to apply the impulse force to the ship itself based on the rockets' local points on the ship? If someone has a suggestion and can post some sample code, that would be great. I'll post my own code for correction/critique tomorrow if I can get something close to what I'm looking for.
Can I do it this way, or do I just need to apply the impulse force to the ship itself based on the rockets' local points on the ship? If someone has a suggestion and can post some sample code, that would be great. I'll post my own code for correction/critique tomorrow if I can get something close to what I'm looking for.
About the author
Recent Threads
#2
Spots to fill in that code are to store the scene you are using in %yourScene, adding graphics to your Sprite declarations, and adjusting any of the joint properties you might need to customize. The application of impulse kind of depends on your exact behavior. You start by toying with applyLinearImpulse:
Now, some things have to change in the above code based on questions:
1. What is your game's perspective? Top down?
2. Do you want the thrusters themselves to twist during the impulse?
03/22/2013 (5:22 am)
Examples:%thrusterOne = new Sprite(); %thrusterTwo = new Sprite(); %ship = new Sprite(); %yourScene.add(%thrusterOne); %yourScene.add(%thrusterTwo); %yourScene.add(%ship); %jointId = %yourScene.createDistanceJoint( %ship, %thrusterOne, 0.25 ); %jointId = %yourScene.createDistanceJoint( %ship, %thrusterTwo, -0.25 );
Spots to fill in that code are to store the scene you are using in %yourScene, adding graphics to your Sprite declarations, and adjusting any of the joint properties you might need to customize. The application of impulse kind of depends on your exact behavior. You start by toying with applyLinearImpulse:
if (%direction $= "left")
{
%thrusterOne.applyLinearImpulse(%impulse, "1 0");
}
else if (%direction $= "right)
{
%thrusterTwo.applyLinearImpulse(%impulse, "1 0");
}
else
{
%thrusterOne.applyLinearImpulse(%impulse, "1 0");
%thrusterTwo.applyLinearImpulse(%impulse, "1 0");
}Now, some things have to change in the above code based on questions:
1. What is your game's perspective? Top down?
2. Do you want the thrusters themselves to twist during the impulse?
#4
This is what the composite sprite is for! Don't forget to turn-off sprite-culling on it though for better performance.
03/22/2013 (7:08 am)
... or use a composite sprite that has three sprites, has no joints and you can apply a force to any position on it causing torque as well as translation if you so wish.This is what the composite sprite is for! Don't forget to turn-off sprite-culling on it though for better performance.
#5
03/22/2013 (7:34 am)
That will certainly reduce the amount of code you have to right, less object management, less physics calculations and joints.
#6

To declare Weld Joints :
You find the impulse direction of both rockets by getting the thruster's angle and giving it a magnitude that will determine the thrusting power.
You then apply equal impulses to both rockets to go forward, and apply different impulses to each rocket to turn left and right.
03/22/2013 (7:57 am)
While Melv's solution is by far the most elegant, I've used Weld joints to test them out, yielding pretty awesome results.
To declare Weld Joints :
%joint1 = myScene.createWeldJoint(SpaceShip,LeftThruster,"-0.25 -0.25"); %joint1 = myScene.createWeldJoint(SpaceShip,RightThruster,"0.25 -0.25");
You find the impulse direction of both rockets by getting the thruster's angle and giving it a magnitude that will determine the thrusting power.
%ThrustVector = Vector2Direction(LeftThruster.Angle,%thrust_force);
You then apply equal impulses to both rockets to go forward, and apply different impulses to each rocket to turn left and right.
#7
I did try the distance joints first, but I'm not getting any torque on the ship when I apply force to the rockets that are attached to them. Do I have to declare something special about the ship's sprite to get torque when applying force to the right or left of the center of mass?
03/23/2013 (11:25 am)
Thanks for the help guys. Simon's suggestion might be the one I'm going for. Melv's suggestion would be good for a regular game, but mine involves building your ship on the fly, too (a la Captain Forever). I'm not sure how I would allow the user to drag-and-drop components while using the composite-sprite idea.I did try the distance joints first, but I'm not getting any torque on the ship when I apply force to the rockets that are attached to them. Do I have to declare something special about the ship's sprite to get torque when applying force to the right or left of the center of mass?
#8
You will have to play with the anchor points location as well as the extra parameters, i.e. distance, frequency and damping to achieve the desired results.
Also, remember to always define valid collision shapes to your shapes if you want them to react to physical forces!
03/23/2013 (1:08 pm)
@Joe : I did try DistanceJoints first as well but they just kept spinning around my spaceship; not the expected result.You will have to play with the anchor points location as well as the extra parameters, i.e. distance, frequency and damping to achieve the desired results.
Also, remember to always define valid collision shapes to your shapes if you want them to react to physical forces!
#9
Take a look at the CompositeSpriteToy and you can see that it's adding sprites of all kinds over time.
We need to get some info on the wiki about this deceptively powerful object.
03/24/2013 (12:31 am)
@Joe: The composite sprite can be modified on the fly using "addSprite()", "removeSprite()", "setSpriteImage()" etc. You'll also be dealing with a single object and it'll be much faster.Take a look at the CompositeSpriteToy and you can see that it's adding sprites of all kinds over time.
We need to get some info on the wiki about this deceptively powerful object.
#10
03/24/2013 (5:28 pm)
@Melv: Okay, I see what you're saying with that now. That actually looks like it might be easier once I understand more about the composite sprite class.
#11
Whilst the CompositeSprite has a lot of methods you can call on it, 90% of them are just the configuration options for a sprite i.e. its position, size, angle, color etc.
The CompositeSprite itself has three main things you need to configure: layout and culling/batching. Each of these is a single call/field.
03/25/2013 (1:02 am)
I've added a "CompoundObjectsToy" into the development branch to demonstrate the CompositeSprite when used as a simpler multi-sprite object.Whilst the CompositeSprite has a lot of methods you can call on it, 90% of them are just the configuration options for a sprite i.e. its position, size, angle, color etc.
The CompositeSprite itself has three main things you need to configure: layout and culling/batching. Each of these is a single call/field.
#12
Anyway, I have the weld joints working, but I'm not getting any kind of torque from firing individual rockets, or trans-aligned rockets on either side of the ship sprite. Here's the relevant code:
Am I missing something to make the playerChar (ship) rotate when just one rocket fires?
03/26/2013 (9:28 pm)
Okay, going with the weld joints because adding and removing components as part of a composite sprite seems to difficult. If I use individual sprites, then they'll individually respond to touch. I can't figure out how to do that with the composite sprite.Anyway, I have the weld joints working, but I'm not getting any kind of torque from firing individual rockets, or trans-aligned rockets on either side of the ship sprite. Here's the relevant code:
function CFD::createPlayer( %this, %posX, %posY )
{
CFD.playerChar = new Sprite() { class = "mainPlayer"; };
CFD.playerChar.setImage( "CFD:modernBasic" );
CFD.playerChar.setPosition( %posX, %posY );
CFD.playerChar.setSize( 1, 1 );
CFD.playerChar.LinearDamping = 1;
CFD.playerChar.setUpdateCallback( true );
CFD.playerChar.setDefaultDensity( CFD.rocketDensity );
CFD.playerChar.createPolygonCollisionShape( "-1 -1 -1 1 1 1 1 -1" );
SandboxScene.add( CFD.playerChar );
%flame1 = CFD.createFlame();
%flame2 = CFD.createFlame();
%flame3 = CFD.createFlame();
%flame4 = CFD.createFlame();
%flame3.setAngle( 180 );
%flame4.setAngle( 180 );
CFD.rockets[2].add( %flame1 ); //add flame to right-rotating group
CFD.rockets[3].add( %flame2 ); //add flame to left-rotating group
CFD.rockets[0].add( %flame1 ); //add flame to forward group
CFD.rockets[0].add( %flame2 ); //add flame to forward group
CFD.rockets[2].add( %flame3 ); //add flame to right-rotating group
CFD.rockets[3].add( %flame4 ); //add flame to left-rotating group
CFD.rockets[1].add( %flame3 ); //add flame to reverse group
CFD.rockets[1].add( %flame4 ); //add flame to reverse group
SandboxScene.createWeldJoint( CFD.playerChar, %flame1, "-0.5 -0.5", "0 0", 0 );
SandboxScene.createWeldJoint( CFD.playerChar, %flame2, "0.5 -0.5", "0 0", 0 );
SandboxScene.createWeldJoint( CFD.playerChar, %flame3, "0.5 0.5", "0 0", 0 );
SandboxScene.createWeldJoint( CFD.playerChar, %flame4, "-0.5 0.5", "0 0", 0 );
}
function CFD::createFlame( %this )
{
%obj = new Sprite();
%obj.setImage( "CFD:flame" );
%obj.setSize( 0.25, 0.25 );
%obj.setDefaultDensity( CFD.rocketDensity );
SandboxScene.add( %obj );
return %obj;
}
function mainPlayer::onUpdate( %this )
{
CFD.cycleRockets();
}
function CFD::cycleRockets( %this )
{
if( CFD.rocketGroup1 > -1 )
{
//echo( "RG1" );
%size = getRandom( CFD.minFlameHeight, CFD.maxFlameHeight );
for( %i = 0; %i < CFD.rockets[CFD.rocketGroup1].getCount(); %i++ )
{
//echo( "RG1 Counting" );
%obj = CFD.rockets[CFD.rocketGroup1].getObject( %i );
%obj.setSizeY( %size );
%obj.myOwner.applyLinearImpulse( mSin(mDegToRad(%obj.getAngle())) * CFD.baseRocketPower
SPC mCos(mDegToRad(%obj.getAngle())) * CFD.baseRocketPower, %obj.getPosition() ); //%obj.myOwner.getLocalPoint( %obj.getPosition() ) );
}
}
//same code exists here for rocketgroup2 and rocketgroup3 to handle multiple inputs.
}Am I missing something to make the playerChar (ship) rotate when just one rocket fires?
#13
03/26/2013 (9:33 pm)
Okay, never mind, I just figured out that the flames needed collision shapes, too. Working pretty okay, now. I should be able to figure out the rest.
#14
Right now it uses center, which might not be where you want the physics body to really represent the sprite.
Consider a composite sprite where you basically display some game information above your animated sprite (health/etc), but still want something like rotation to happen around the center of just your animated sprite and not consider the extra height you gave it for a health bar. You'd also ant positioning to be based on the animated sprite, and not take into account the extra size added.
Or, am I just crazy? :)
03/27/2013 (11:32 am)
Hey Melv, not to hijack the thread but since you are CompSprites. It would be awesome if a CompSprite could change the anchor point for the b2body. Right now it uses center, which might not be where you want the physics body to really represent the sprite.
Consider a composite sprite where you basically display some game information above your animated sprite (health/etc), but still want something like rotation to happen around the center of just your animated sprite and not consider the extra height you gave it for a health bar. You'd also ant positioning to be based on the animated sprite, and not take into account the extra size added.
Or, am I just crazy? :)
#16
I just created a CompositeSprite and do not see what you describe. I can position/rotate the CompositeSprite based on its World center. Any sprites I've added just follow suite (i.e they do not change the size of the parent Composite (from my experiments).
What I'm having issue with is that, when I set an angular velocity on the CompositeSprite, then add a Sprite to it, and use setAngularVelocity on the Sprite, it actually changes the angular velocity on the CompositeSprite instead.
I'm hoping this is just a lack of understanding on my part but, from what I read, any Sprite added to a CompositeSprite gets focus until you specifically deselect it or add another Sprite. This does not seem to be the case with setAngularVelocity().
03/27/2013 (1:06 pm)
@ William - While I agree that setting the anchor position for SceneObjects should be optional (with [0,0] as the default), I am not seeing the same scenario you describe.I just created a CompositeSprite and do not see what you describe. I can position/rotate the CompositeSprite based on its World center. Any sprites I've added just follow suite (i.e they do not change the size of the parent Composite (from my experiments).
What I'm having issue with is that, when I set an angular velocity on the CompositeSprite, then add a Sprite to it, and use setAngularVelocity on the Sprite, it actually changes the angular velocity on the CompositeSprite instead.
I'm hoping this is just a lack of understanding on my part but, from what I read, any Sprite added to a CompositeSprite gets focus until you specifically deselect it or add another Sprite. This does not seem to be the case with setAngularVelocity().
#17
@Brian: Physics operations apply to scene objects, which is the CompositeSprite. Sprites added to a scene object just become part of the whole composite.
setAngularVelocity() can only be applied the CompositeSprite object, not to a sprite within the composite.
03/27/2013 (1:22 pm)
So, of course right after posting this I figured out how to make it work. CompositeSprite's local position [0,0] will map to the physics body, and is the physics anchor point. You just have to put your character/animated sprite at local position [0,0] and you're done. Offset any other sprites in the composite from there.@Brian: Physics operations apply to scene objects, which is the CompositeSprite. Sprites added to a scene object just become part of the whole composite.
setAngularVelocity() can only be applied the CompositeSprite object, not to a sprite within the composite.
Employee Michael Perry
ZombieShortbus
2. You will not apply impulse to a joint. You will apply impulse to the bodies that make up the thrusters. That should provide you with the movement you are seeking
To find sample code, have a look through the Sandbox. You will want to search for createDistanceJoint. That should give you a good example.