- Home /
Set Velocity of Object According to Rotation
I am writing a program such that there is a rotating object and the player can attach to that rotating object and also rotate accordingly. After the click of a button the player is supposed to move in the direction where his head is. I've looked online and found some answers but none worked for me. Here are some example pictures:
Below is the code for my rotating platform:
void Update()
{
transform.Rotate(0, 0, rotationSpeed*Time.deltaTime);
}
Below is the code for my player to attach and release to the platform: to attach:
void OnTriggerEnter2D(Collider2D col)
{
if (col.gameObject.name == "head"&&canCollide)
{
canMove = false;
rb.velocity = new Vector2(0, 0);
transform.rotation = col.gameObject.transform.parent.rotation;
transform.parent = col.gameObject.transform.parent;
float difY = transform.position.y - transform.parent.position.y;
float difX = transform.position.x - transform.parent.position.x;
Vector3 temp = new Vector3(difX, difY,0);
transform.position = new Vector3(transform.position.x,transform.position.y,transform.position.z)+temp.normalized;
rb.gravityScale = 0;
canCollide = false;
}
}
to release(this is the part where I need to set my velocity according to my rotation I've got this code from another post but didn't work in my case):
void blastFromSkyHook()
{
if (transform.parent != null && Input.GetKeyDown("m"))
{
transform.rotation = transform.parent.rotation;
transform.parent = null;
float theta = transform.localEulerAngles.z +90;
rb.gravityScale = 0.3f;
rb.AddForce(new Vector2(1,1) * 500);
float newDirX = Mathf.Cos(theta * Mathf.Deg2Rad);
float newDirY = Mathf.Sin(theta * Mathf.Deg2Rad);
rb.AddRelativeForce(new Vector2(newDirX, newDirY));
Debug.Log(rb.velocity);
canMove = true;
}
}
Answer by Eno-Khaon · Aug 20, 2020 at 08:11 PM
It looks like you're trying to overengineer something that can be handled in much simpler(-looking) terms.
For now, let's take a look at your blastFromSkyHook() function.
To start, this line doesn't really make much sense and is probably the source of the erroneous results you're seeing:
rb.AddForce(new Vector2(1,1) * 500);
That will launch your character at (default) ~14.14 units/second (sqrt(2) * 500 / 50)
. I say "default" because you're applying a per-frame force as an immediate one; I'll go into detail on that later.
Then, you apply a directional force extremely weakly after that (it will have almost no visible effect on the trajectory you had just set prior).
rb.AddRelativeForce(new Vector2(newDirX, newDirY));
You can easily approach this from two different angles (at least). You can either:
A) use AddForce(global direction)
or B) use AddRelativeForce(local direction) for the same effect
But before that, let's go back to what I mentioned before. You're applying a per-frame force right now. That's because AddForce() (and similar) use the ForceMode2D of Force by default.
rb.AddForce(Vector2.one);
// is the same as...
rb.AddForce(Vector2.one, ForceMode2D.Force);
However, the way you're trying to use it indicates you're applying the intended force a single time. Well, the default physics rate is 50 times per second. If you change the physics update rate (from 0.2), then you'll suddenly apply more or less force to your character! Clearly, that isn't what you had in mind, so let's look at the alternative: ForceMode2D.Impulse, which applies the force immediately instead:
// This assumes the intended "forward" direction is "up"
// based on 90-degree offset
rb.AddForce(transform.up * launchSpeed * rb.mass, ForceMode2D.Impulse);
// or...
rb.AddRelativeForce(Vector3.up * launchSpeed * rb.mass, ForceMode2D.Impulse);
With either of those, you can also greatly simplify the function:
void BlastFromSkyHook()
{
transform.rotation = transform.parent.rotation;
transform.parent = null;
rb.AddRelativeForce(Vector3.up * launchSpeed * rb.mass, ForceMode2D.Impulse);
rb.gravityScale = 0.3f;
canMove = true;
}
You might notice that I'm also factoring the Rigidbody's mass into the calculations. This ensures that changing the character's mass will have no bearing on these launches.
Thank you very much I've been struggling with it whole day. One thing I still can't wrap my head around is Impulse versus normal AddForce. I understand that I should use impulse for like instant stuff like explosions, but I can't understand from a computer science perspective whats different. Sınce my function was in an if statement and lets say I practically made it impossible for the user to access that function more than once every second, would there still be a different between impulse and regular AddForce? Since I am applying both of these forces only once. Or even though i am adding force once in the physics update is that force being used multiple times?
Finally this is kind of unrelated but is this logical as a player run method? I want my player to be able move by input upto a certain velocity but be able pass this threshold using other world objects (such as this rotating platform above) and this was the only way I could accomplish it. (I couldn't format the code here as a comment so I will submit it below also) //ter$$anonymous$$alVelocity is how fast player can go by user input void run() { float direction = Input.GetAxisRaw("Horizontal"); if ((rb.velocity.x > ter$$anonymous$$alVelocity && direction > 0) || (rb.velocity.x < -ter$$anonymous$$alVelocity && direction < 0)) return; rb.AddForce(new Vector2(direction * xSpeed, 0)); }
(For reference, formatting code on Unity Answers can be done in one of two ways. You can surround your text using accent "`" characters (shares a key with tilde "~" on common QWERTY layout keyboards) ` for a same-line example
` or you can put it beyond an empty line with four leading spaces to get a full code block)
General body text `with in-line example`
(this line $$anonymous$$UST be left empty to produce code block)
_ _ _ _ Text starting after 4 spaces becomes code block formatted
Regular text can begin again on the line immediately following a code block.
<hr> (horizontal dividing line), <br> (line break, I often use in pairs)
Unity's physics (3D or 2D) cycle at their own pace, regardless of framerate. By default, that rate is 50 times per second, so when I talk about "frames" after this, I'm referring to physics updates.
When you use Force$$anonymous$$ode(2D).Force (by choice or when left as default), this is a force intended specifically to be applied EVERY FRA$$anonymous$$E. Using player input as an example:
Vector2 playerInput;
void Update()
{
playerInput = new Vector2(Input.GetAxis("Horizontal"), Input.GetAxis("Vertical"));
}
void FixedUpdate()
{
rb.AddForce(playerInput * playerAcceleration * rb.mass); // Force$$anonymous$$ode.Force is implied
}
When applied in this way, the applied force is automatically scaled by the (physics) framerate. This would effectively be the same as using:
rb.AddForce(playerInput * playerAcceleration * rb.mass * Time.fixedDeltaTime, Force$$anonymous$$ode.Impulse);
In this second case, I manually factored in the frame time, but it's silly/wasteful to do so when it's not necessary (and could lead to habits of doubling up framerate scaling if you're not careful).
Since you can move in all directions, comparing only X-axis velocity won't be adequate for your limitations. You'll want to get the Dot product of your tangential speed relative to your locally-transformed-input * ter$$anonymous$$alVelocity. Then, you'll also be able to use both directions in a single calculation:
Vector2 playerInput = transform.right * Input.GetAxisRaw("Horizontal");
if(Vector2.Dot(rb.velocity, playerInput.normalized) < ter$$anonymous$$alVelocity)
{
rb.AddForce(playerInput * xSpeed * rb.mass);
}
Answer by ogito123 · Aug 20, 2020 at 09:13 PM
Here is my run method @Eno-khan : //terminalVelocity is how fast player can go by user input
void run()
{
float direction = Input.GetAxisRaw("Horizontal");
if ((rb.velocity.x > terminalVelocity && direction > 0) || (rb.velocity.x < -terminalVelocity && direction < 0))
return;
rb.AddForce(new Vector2(direction * xSpeed, 0));
}