0
\$\begingroup\$

I am making a simple space simulator, where the player can control a ship using 2 controls, one for pitch and one for bank. The ship is properly rotated but I cannot figure out how to make the ship move forward based on that angle. Here's what the code looks like right now:

func renderer(renderer: SCNSceneRenderer, updateAtTime time: NSTimeInterval) {

    let t=CGFloat(time - lastTime)

    ship!.transform = CATransform3DRotate(CATransform3DRotate(CATransform3DRotate(ship!.transform, ud*t, -1, 0, 0), 0.00, 0, 1, 0), lr*t, 0, 0, 1);

    ship!.position = ?

}

I tried every combination of vector, euler angles, matrices, quaternions that I could think of/read about with no success. I am probably missing something stupid but cannot find what it is.

The code is in Swift using Scenekit, but language doesn't matter that much to me and I can translate any algorithm. It's also worth noting it does not work like Unity and Quarternions/Vectors don't have properties like .forward() etc.

Thank you for your help!

\$\endgroup\$
  • \$\begingroup\$ Please avoid using variable names like t, ud, lr when you ask, this makes your code hard to understand. transform is misleading, because generally, a transform includes the rotation and the translation. Is your transform really a transform or is it a rotation only? \$\endgroup\$ – Vaillancourt Jan 28 '16 at 17:52
  • \$\begingroup\$ Scaling also included in transform :) \$\endgroup\$ – Hamza Hasan Jan 28 '16 at 18:34
  • \$\begingroup\$ @AlexandreVaillancourt: Thanks for the tip, I was just prototyping so I forgot to name them properly before posting... \$\endgroup\$ – Shameful Guest Jan 28 '16 at 19:49
  • 1
    \$\begingroup\$ @HamzaHasan you're right, the scaling (and shearing) are also included in a transformation matrix, I forgot about that. \$\endgroup\$ – Vaillancourt Jan 28 '16 at 21:24
  • \$\begingroup\$ I have updated my answer, if you're still interested. In your situation, you just add a 3rd dimension. \$\endgroup\$ – Vaillancourt Feb 3 '16 at 2:22
2
\$\begingroup\$

You will need a forward vector which represent the forward direction of your ship, and the velocity of your ship.

// Stuff that you need (or already have)
vec3 shipForwardVector; // This is according to your game infrastructure 
                        // and ship model; make it a unit vector. 
mat4 worldSpaceRotationMat; // assuming ship!.transform is only the rotation.
float maxVeolicityPerSecond; // Your game settings
float requestedVelocityRatio; // User request; from a keyboard, it's 0 or 1, 
                              // from a joystick, 0..1

// Computation
vec3 rotatedForward = worldSpaceRotationMat * shipForwardVector;
float requestedVelocity = maxVeolicityPerSecond * requestedVelocityRatio;
// scale the movement according to the time that has passed
vec3 displacementThisFrame = rotatedForward * (t * requestedVelocity) 

ship.position = ship.position + displacementThisFrame

Based on Jon's comment, here is an addendum.

This is an implementation with the suggestion I have already made:

// Assuming the following:
float mSideThrustersRadPerSec;// Used to compute how much a ship can turn per second
Matrix mTransform;            // Used to track the orientation of the ship
Vector2 mFrontVector;         // Used to determine where your ship model is pointing in its 
                              // local space
float mRearThrustersKmPerSec; // How much are your thrusters able to push the ship
Vector2 mPosition;            // Position of the ship. 

if ( sf::Keyboard::isKeyPressed( sf::Keyboard::Left ) )
  mTransform = mTransform.rotate( sfUtil::radiansToDegrees( 
    -mSideThrustersRadPerSec * Global::GetInstance().getFrameTime() ) );

if ( sf::Keyboard::isKeyPressed( sf::Keyboard::Right ) )
  mTransform = mTransform.rotate( sfUtil::radiansToDegrees( 
    mSideThrustersRadPerSec * Global::GetInstance().getFrameTime() ) );

if ( sf::Keyboard::isKeyPressed( sf::Keyboard::Up ) )
{
  sf::Vector2f offset = mFrontVector * 
    ( mRearThrustersKmPerSec * Global::GetInstance().getFrameTime() );
  sf::Vector2f orientedOffset = mTransform * offset;

  mPosition = mPosition + orientedOffset;
}

Here is the result:

enter image description here

And this is based on if your ship has a velocity and no external resistance.

// Assuming the following:
float mSideThrustersRadPerSec; // Used to compute how much a ship can turn per second
Matrix mTransform;             // Used to track the orientation of the ship
Vector2 mFrontVector;          // Used to determine where your ship model is pointing in 
                               // its local space
float mRearThrustersKmPerSec;  // How much are your thrusters able to push the ship
float mFrontThrustersKmPerSec; // How much are your thrusters able to slow the ship (and 
                               // eventually make it go reverse)
Vector2 mPosition;             // Position of the ship. 
Vector2 mVelocity;             // Speed and direction of the ship
float mSpeedMax;               // Maximum speed of the ship


if ( sf::Keyboard::isKeyPressed( sf::Keyboard::Left ) )
  mTransform = mTransform.rotate( sfUtil::radiansToDegrees( 
    -mSideThrustersRadPerSec * Global::GetInstance().getFrameTime() ) );

if ( sf::Keyboard::isKeyPressed( sf::Keyboard::Right ) )
  mTransform = mTransform.rotate( sfUtil::radiansToDegrees( 
    mSideThrustersRadPerSec * Global::GetInstance().getFrameTime() ) );

bool shouldApplyOffset = false;

sf::Vector2f offset;

if (
     sf::Keyboard::isKeyPressed( sf::Keyboard::Up )
  && sf::Keyboard::isKeyPressed( sf::Keyboard::Down ) )
{
  offset =
      (  mFrontVector * ( mRearThrustersKmPerSec  * Global::GetInstance().getFrameTime()))
    + ( -mFrontVector * ( mFrontThrustersKmPerSec * Global::GetInstance().getFrameTime()));
  shouldApplyOffset = true;
}
else if ( sf::Keyboard::isKeyPressed( sf::Keyboard::Up ) )
{
  offset = mFrontVector * (mRearThrustersKmPerSec  * Global::GetInstance().getFrameTime());
  shouldApplyOffset = true;
}
else if ( sf::Keyboard::isKeyPressed( sf::Keyboard::Down ) )
{
  offset = -mFrontVector * (mFrontThrustersKmPerSec * Global::GetInstance().getFrameTime());
  shouldApplyOffset = true;
}

if ( shouldApplyOffset )
{
  sf::Vector2f orientedOffset = mTransform * offset;

  mVelocity += orientedOffset;

  float currentSpeed = sfUtil::lenght( mVelocity );

  if ( currentSpeed > mSpeedMax )
  {
    sf::Vector2f direction = mVelocity / currentSpeed;
    mVelocity = direction * mSpeedMax;
  }
}

mPosition = mPosition + mVelocity;

With the result:

enter image description here

You can get the working VC2013 code here (it uses SFML).

\$\endgroup\$
  • \$\begingroup\$ What if the ship turns towards a direction that is not its current velocity? \$\endgroup\$ – jgallant Jan 28 '16 at 18:11
  • \$\begingroup\$ @Jon that's a bit more complex you have to modify the velocity vector according to thrusters strength and use the velocity vector instead of the ships orientation (rotation) to change the position of the ship; I'll be able to expand on this later tonight. \$\endgroup\$ – Vaillancourt Jan 28 '16 at 18:16
  • \$\begingroup\$ Yeah I am just giving you a hard time. :P \$\endgroup\$ – jgallant Jan 28 '16 at 18:32
  • \$\begingroup\$ @Jon I know :P Because I know that you know very well how to achieve this hehe :P \$\endgroup\$ – Vaillancourt Jan 28 '16 at 18:34
  • \$\begingroup\$ Thank you for the code! I see that you are multiplying a Matrix by a Vec3, which is I'm afraid impossible in the engine I'm using... I'll try to make it work and get back to you! \$\endgroup\$ – Shameful Guest Jan 28 '16 at 19:51
0
\$\begingroup\$

Well, I generally use to ask this questions in interviews :)

Although I don't know Swift but I can give you a general idea about to achieve this.

You have to play with some trigonometry to get current direction vector according to your current angle.

Here is the pseudo code.

float speed = 0.01

void GameLoop()
{
    // I don't know that how Vector2 works at your end, but I hope you will get this.
    Vector2 currentPosition = currentPositionOfObject;
    float currentAngle = currentAngleOfObject;
    float directionX = Cos (currentAngle); // check if your Cos method takes angle in radians of degrees.
    float directionY = Sin (currentAngle); // check if your Sin method takes angle in radians of degrees.

    Vector2 newPosition = (currentPosition.x + (directionX * speed),currentPosition.y + (directionY * speed));
    currentPositionOfObject = newPosition;
}

I have already answered the same question but in Unity (C#) here. Just for reference if you want more details.

\$\endgroup\$
  • \$\begingroup\$ Thank you for the tip! It works great, but sadly I need it in 3D... My fault for not being precise about it... Sorry! \$\endgroup\$ – Shameful Guest Jan 28 '16 at 19:52
  • \$\begingroup\$ oww.. Would your object move in open space (ant-where) or x-z plane? \$\endgroup\$ – Hamza Hasan Jan 28 '16 at 20:11
  • \$\begingroup\$ Then I will turn my gun in that direction :) \$\endgroup\$ – Hamza Hasan Jan 28 '16 at 20:21

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.