transform.InverseTransformDirection, not getting proper output - c#

I have a sphere as player and I want it to move relative to camera
and not world.
When I rotate camera along Y-axis by 90 or 270 degree and give input, it gives opposite output than expected.
Here is my script attached to sphere:
using System.Collections;
using UnityEngine;
public class mover : MonoBehaviour {
public Rigidbody sphere;
public GameObject cameraa;
void Start ()
{
sphere = GetComponent<Rigidbody> ();
}
void Update ()
{
// getting input in controlVector
Vector3 controlVector = new Vector3 (Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical"));
// transforming direction from world space to local space[to camera]
Vector3 localVectToCam = cameraa.transform.InverseTransformDirection (controlVector);
//applying inputs
sphere.AddForce (localVectToCam * 10);
}
}
when camera's direction is same as world and when camera is rotated along Y-axis by 180 degree, it works fine.
But on rotating camera along Y-axis by 90 and 270 degree is gives opposite output.
example:
camera rotation along Y-axis = 90 degree [ i.e (0,90,0) ]
input given = (0,0,1)
output got = (-1,0,0)
expected output = (1,0,0)
Can you help me to understand and correct it?

I think you want to go the other way round actually!
Transform.InverseTransformDirection converts a vector from world space into local space.
What you get as input however is a local vector on the XZ plane and Rigidbody.AddForce expects a world space vector.
You want to apply this direction according to your player objects orientation, if e.g. pressing right (your input is 1,0,0) the object shall move towards its transform.right vector.
So you rather want to convert in the opposite direction into world space to move the object in the Unity world space.
You should rather be using Transform.TransformDirection!
var worldMove = transform.TransformDirection(controlVector);
sphere.AddForce(worldMove * 10);
Or alternatively you can also just multiply by the rotation like
var worldMove = transform.rotation * controlVector;
sphere.AddForce(worldMove * 10);
However, since you are using a Rigidbody there is also Rigidbody.AddForceRelative which basically works the same way and expects a local space vector which is then internally converted into a world space force.
sphere.AddRelativeForce (controlVector * 10);

Related

Split a bullet in 3 different directions using Quaternion.AngleAxis

I'm trying to assign a skill to a projectile that, when used, divides the project into 3 (the original projectile and 2 more new ones).
However, when I instantiate these two clones, they keep following the same trajectory. The idea would be for them to take this route:
The green dotted curve indicating the motion of the original bullet, the blue vector indicating the instantaneous velocity of the original bullet at time of special activation, the red vectors indicating the two velocity vectors belonging to each of the newly spawned bullets, and the green angle indicating the direction of the new bullet relative to the original velocity direction
But at the moment, they spawn and continue following the same trajectory as the original. The sprites even rotate to the right angle, but that doesn't seem to make much difference in how the physics is applied.
Does anyone know how I can solve this?
This is my code so far
Ability Script:
public class AirSpecialSplit : MonoBehaviour, IAirSpecial
{
public float SplitAngleInDegrees = 10;
GameObject bird_down;
GameObject bird_up;
public void ExecuteAirSpecial()
{
{
//hold the velocity of the original bird
Vector2 original_velocity = this.gameObject.GetComponent<Rigidbody2D>().velocity;
//clone two new birds
bird_down = Birb.MakeBirbCopy(this.gameObject);
bird_up = Birb.MakeBirbCopy(this.gameObject);
//apply the angle to the clones
bird_down.transform.rotation = Quaternion.AngleAxis(-SplitAngleInDegrees, Vector2.up);
bird_up.transform.rotation = Quaternion.AngleAxis(SplitAngleInDegrees, Vector2.up);
//get the rigidboy from the clones
Rigidbody2D rb_bird_down = bird_down.GetComponent<Rigidbody2D>();
Rigidbody2D rb_bird_up = bird_up.GetComponent<Rigidbody2D>();
rb_bird_down.simulated = true;
rb_bird_up.simulated = true;
rb_bird_down.velocity = new Vector2(original_velocity.x, original_velocity.y);
rb_bird_up.velocity = new Vector2(original_velocity.x, original_velocity.y);
}
}
}
Well you apply the same velocity to both so of course they will move in the same direction. The velocity is in world space!
You probably wanted to rather add the rotation like e.g.
rb_bird_down.velocity = bird_down.transform.forward * original_velocity.magnitude;
And before that you probably should take the current bullet rotation into account like
bird_down.tranform.rotation = transform.rotstion * Quaternion.Euler(0,0, -SplitAngleInDegrees);

Character rotation depending on camera

I've spent my whole day trying to figure out a problem I'm having with my school project, so I'm off to my last resort: stackoverflow!
My problem:
I'm trying to rotate a character relative to a Camera you can rotate around the character.
input: xbox controller
relevant information:
The camera rotates horizontally around the character, using the right joystick
The character movement happens with the left joystick
The character already moves relative to the Camera, that's working as expected. I'm trying to rotate the character, which happens in a seperate method.
When the left joystick is pulled downwards, the character should always be facing (and moving towards) the camera.
When the left joystick is pulled upwards, the character should always be facing the opposite of (and moving away from) the camera.
I'm leaving a lot of code out, just to keep it readable for you guys. If you need something, just ask and I'll provide.
What I have so far: https://imgur.com/TERUXV6
Why it's wrong: The character rotation is perfect. However, I'm cheating here. The camera rotates according to the world coordinates. As soon as I rotate the camera, this is obvious.
The following script is attached to the Character GameObject.
public class CharacterBehaviour : MonoBehaviour
{
public GameObject HumanoidModel;
[SerializeField]private Transform _mainCameraTransform;
private void Update()
{
ApplyMovement();
RotateCharacter();
}
private void ApplyMovement()
{
//get input movement vector
Vector3 inputMovement = new Vector3(_inputMoveCharacterXAxis, 0, _inputMoveCharacterZAxis);
//make sure camera forward is player movement forward
Vector3 mainCameraForwardXz = Vector3.Scale(_mainCameraTransform.forward, new Vector3(1, 0, 1)); //multiplied by (1, 0, 1) to remove Y component
Vector3 mainCameraRightXz = Vector3.Scale(_mainCameraTransform.right, new Vector3(1, 0, 1)); //multiplied by (1, 0, 1) to remove Y component
Vector3 movementInCameraForwardDirection = mainCameraForwardXz * inputMovement.z;
Vector3 movementInCameraRightDirection = mainCameraRightXz * inputMovement.x;
Vector3 movementForward = movementInCameraForwardDirection + movementInCameraRightDirection;
_velocity = movementForward * MaximumSpeed;
}
private void RotateCharacter()
{
Vector3 inputDirection = new Vector3(_inputMoveCharacterXAxis, 0, _inputMoveCharacterZAxis);
HumanoidModel.transform.LookAt(HumanoidModel.transform.position +
HumanoidModel.transform.forward + inputDirection);
}
The following script is attached to the Main Camera GameObject
public class CameraBehaviour : MonoBehaviour
{
[SerializeField] private Transform _characterTransform;
[SerializeField] private Transform _mainCameraTransform;
private void Update ()
{
RotateCamera();
}
// Rotate camera horizontally
private void RotateCamera()
{
_mainCameraTransform.RotateAround(_characterTransform.position, Vector3.up, _inputRotateCameraHorizontal);
}
}
The source of the problem is in the RotateCharacter() function. I know I need to get some calculations in there to make the character rotation relative to the camera rotation, I just can't figure out what that calculation is, and why.
Thanks in advance!
Thrindil
so heres what you need...
camDefault, a Vector3 for the cameras initial position behind the char.
camCur, a Vector3 for the cameras current position(to track where it is in orbit around the character)
you need to set camDefault in Awake() to the its current position at that time, IE camDefault = cam.transform.position);
then in a fixed update,
camCur= cam.transform.position;
then,
if(Input//your horizontal axis here//==0){
if(camCur!=camDefault){
//translate camera to cam default
cam.tranform.translate(camDefault);
cam.lookat(player.transform.forward);
}
}
keep in mind that some of this is pseudocode, just a general direction. but the unity methods are there. if properly implemented this script will allow you to rotate around your char with right stick, than the camera will slide back behind you when you let go.
I believe, for your sanity, it would be easier to make the camera a direct child of ybot, so it rotates with it and you dont need to do it maually the camera will always stay behind the player. but thats just my opinion. as it sits now, the camera, and the model are children of player, if you want the camera to turn with the player, just make it a child of that model.
in this case, you could store the initial rotation, and current rotation as above, and us your stick to look left and right and then snap back to forward when you let the stick go.

Object Not Looking at Other Object at Right Angle in Unity3D

I'm working on a prototype of a tower defense game and I've encountered a problem with the rotation of a turret. I made it so that every turret must have a rotator part which rotates horizontally and holds the main turret body with the cannon which rotates vertically. I came up with a simple script to this but it only seems to work for the rotator and not for the cannon, at least not the way it should.
Here is the code from the script:
void Update () {
if (target != null) {
Vector3 tempRotatorRotation = rotator.transform.localEulerAngles;
rotator.transform.LookAt (target.transform);
rotator.transform.localEulerAngles = new Vector3 (tempRotatorRotation.x, rotator.transform.localEulerAngles.y, tempRotatorRotation.z);
Vector3 tempCannonRotation = cannon.transform.localEulerAngles;
cannon.transform.LookAt (target.transform);
cannon.transform.localEulerAngles = new Vector3 (cannon.transform.localEulerAngles.x, tempCannonRotation.y, tempCannonRotation.z);
}
}
And here is an image of how this turns out. The rotator is rotated perfectly, but as you can see the cannon is looking down for some reason.
(Blue is the pedestal which doesn't move. Green is rotator. Red is turret body. Light blue is cannon)
The origin of the cannon 3D model is set almost at the start of it.
Here is the screenshot of the canon selected showing it's axis and transform data
forward to unity is the blue line, which in your diagram is facing up. try this
crate empty, attach to turret so it rotates, make sure blue line(z axis) is facing your forward direction, you can do this manualy by rotating. then place your barrel as a child of that object, and point that object at target.
ive had to do this several times with blender models, since blender uses the z axis as its vertical axis not its depth axis like unity.
-turret_test
-turret_test_pedestal
-turret_test_rotater
-turret_test_turret
-AIM(new empty, orient the proper direction then add child)
-turret_test_cannon
Your cannon's barrel points in its forward direction, so all you need to use is cannon.transform.LookAt (target.transform, cannon.transform.up);
void Update () {
if (target != null) {
/* rotator code here */
// Remember which way the top of the cannon faces.
Vector3 cannonUpDirection = cannon.transform.up;
// point the cannon directly at the target
cannon.transform.LookAt (target.transform, cannonUpDirection);
}
}
If the rotator isn't pointing at/above/below the target, then you have to figure out how much to rotate the canon upwards/downwards from the horizontal, then point it in the same direction as the rotator and then do that:
void Update () {
if (target != null) {
/* rotator code here */
// Remember which way the top of the cannon faces.
Vector3 cannonUpDirection = cannon.transform.up;
// point the cannon directly at the target
cannon.transform.LookAt (target.transform, cannonUpDirection);
// Find global direction for looking straight ahead above/below the target
Vector3 sameYDirection = Vector3.Scale(
cannon.transform.forward,
new Vector3(1f,0f,1f)
);
// Convert that to local
Vector3 sameYDirectionLocal = cannon.transform
.InverseTransformDirection(sameYDirection);
// get rotation for moving from looking ahead to looking direct
Quaternion lookTowards = Quaternion.FromToRotation(
sameYDirectionLocal,
Vector3.forward
);
// lookTowards is a locally-vertical rotation from horizontal to the target,
// given some top side of the cannon.
// Clamp lookTowards here if you have a max rotation speed.
// Face cannon in same direction of rotator;
cannon.transform.rotation = Quaternion.LookRotation(
rotator.forward, cannonUpDirection);
// Use lookTowards to look down from that position.
cannon.transform.rotation *= lookTowards;
}
}
Turns out Unity is broken and when I re-imported the model all the axis have changed and instead of the canon turning in the X axis up and down now it was in the Y axis while everything else rotates horizontally in Y.
Also in the calculation I had to add + 90 to both equations. This will make no sense now since it shows both are changing in Y axis but one is rotating horizontally and the other vertically.
if (target != null) {
Vector3 tempRotatorRotation = rotator.transform.localEulerAngles;
rotator.transform.LookAt (target.transform);
rotator.transform.localEulerAngles = new Vector3 (tempRotatorRotation.x, rotator.transform.localEulerAngles.y + 90, tempRotatorRotation.z);
Vector3 tempCanonRotation = canon.transform.localEulerAngles;
canon.transform.LookAt (target.transform);
canon.transform.localEulerAngles = new Vector3 (tempCanonRotation.x, canon.transform.localEulerAngles.y + 90, tempCanonRotation.z);
}

Gyroscope with compass help needed

I need to have a game object point north AND I want to combine this with gyro.attitude input. I have tried, unsuccessfully, to do this in one step. That is, I couldn't make any gyro script, which I found on the net, work with the additional requirement of always pointing north. Trust me, I have tried every script I could find on the subject. I deduced that it's impossible and probably was stupid to think it could be done; at least not this way (i.e. all-in-one). I guess you could say I surmised that you can't do two things at once. Then I thought possibly I could get the same effect by breaking-up the duties. That is, a game object that always points north via the Y axis. Great, got that done like this:
_parentDummyRotationObject.transform.rotation = Quaternion.Slerp(_parentDummyRotationObject.transform.rotation, Quaternion.Euler(0, 360 - Input.compass.trueHeading, 0), Time.deltaTime * 5f);
And with the game object pointing north on the Y, I wanted to add the second game-object, a camera in this case, with rotation using gyro input on the X and Z axis. The reason I have to eliminate the Y axes on the camera is because I get double rotation. With two things rotating at once (i.e. camera and game-object), a 180 degree rotation yielded 360 in the scene. Remember I need the game object to always point north (IRL) based on the device compass. If my device is pointing towards the East, then my game-object would be rotated 90 degrees in the unity scene as it points (rotation) towards the north.
I have read a lot about gyro camera controllers and one thing I see mentioned a lot is you shouldn't try to do this (limit it) on just 1 or 2 axis, when using Quaternions it's impossible when you don't know what you're doing, which I clearly do not.
I have tried all 3 solutions from this solved question: Unity - Gyroscope - Rotation Around One Axis Only and each has failed to rotate my camera on 1 axis to satisfy my rotational needs. Figured I'd try getting 1 axis working before muddying the waters with the 2nd axis. BTW, my requirements are simply that the camera should only rotate on 1 axis (in any orientation) based on the X axis of my device. If I could solve for X, then I thought it'd be great to get Z gyro input to control the camera as well. So far I cannot get the camera controlled on just 1 axis (X). Anyway, here are my findings...
The first solution, which used Input.gyro.rotationRateUnbiased, was totally inaccurate. That is, if I rotated my device around a few times and then put my phone/device down on my desk, the camera would be in a different rotation/location each time. There was no consistency. Here's my code for the first attempt/solution:
<code>
private void Update()
{
Vector3 previousEulerAngles = transform.eulerAngles;
Vector3 gyroInput = Input.gyro.rotationRateUnbiased;
Vector3 targetEulerAngles = previousEulerAngles + gyroInput * Time.deltaTime * Mathf.Rad2Deg;
targetEulerAngles.y = 0.0f;
targetEulerAngles.z = 0.0f;
transform.eulerAngles = targetEulerAngles;
}
</code>
The second solution was very consistent in that I could rotate my device around and then put it down on the desk and the unity camera always ended up in the same location/rotation/state so-to-speak. The problem I had was the camera would rotate on the one axis (X in this case), but it did so when I rotated my device on either the y or x axis. Either type of rotation/movement of my phone caused the unity camera to move on the X. I don't understand why the y rotation of my phone caused the camera to rotate on X. Here is my code for solution #2:
private void Start()
{
Input.gyro.enabled = true;
startEulerAngles = transform.eulerAngles;
startGyroAttitudeToEuler = Input.gyro.attitude.eulerAngles;
}
private void Update()
{
Vector3 deltaEulerAngles = Input.gyro.attitude.eulerAngles - startGyroAttitudeToEuler;
deltaEulerAngles.y = 0.0f;
deltaEulerAngles.z = 0.0f;
transform.eulerAngles = startEulerAngles - deltaEulerAngles;
}
The 3rd solution: I wasn't sure how to complete this last solution, so it never really worked. With the 2 axis zeroed-out, the camera just flipped from facing left to right and back, or top to bottom and back; depending on which axis were commented out. If none of the axis were commented-out (like the original solution) the camera would gyro around on all axis. Here's my code for attempt #3:
private void Start()
{
_upVec = Vector3.zero;
Input.gyro.enabled = true;
startEulerAngles = transform.eulerAngles;
}
private void Update()
{
Vector3 gyroEuler = Input.gyro.attitude.eulerAngles;
phoneDummy.transform.eulerAngles = new Vector3(-1.0f * gyroEuler.x, -1.0f * gyroEuler.y, gyroEuler.z);
_upVec = phoneDummy.transform.InverseTransformDirection(-1f * Vector3.forward);
_upVec.z = 0;
// _upVec.x = 0;
_upVec.y = 0;
transform.LookAt(_upVec);
// transform.eulerAngles = _upVec;
}
Originally I thought it was my skills, but after spending a month on this I'm beginning to think that this is impossible to do. But that just can't be. I know it's a lot to absorb, but it's such a simple concept.
Any ideas?
EDIT: Thought I'd add my hierarchy:
CameraRotator (parent with script) -> MainCamera (child)
CompassRotator (parent) -> Compass (child with script which rotates parent)
I'd do this in the following way:
Camara with default 0, 0, 0 rotation
Screenshot
Object placed at the center of the default position of the camera.
Script for the Camera:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class NewBehaviourScript : MonoBehaviour
{
Camera m_MainCamera;
// Start is called before the first frame update
void Start()
{
// Disable the sleep timeout during gameplay.
// You can re-enable the timeout when menu screens are displayed as necessary.
Screen.sleepTimeout = SleepTimeout.NeverSleep;
// Enable the gyroscope.
if (SystemInfo.supportsGyroscope)
{
Input.gyro.enabled = true;
}
m_MainCamera = Camera.main;
m_MainCamera.enabled = true;
}
// Update is called once per frame
void Update()
{
if (m_MainCamera.enabled)
{
// First - Grab the Gyro's orientation.
Quaternion tAttitude = Input.gyro.attitude;
// The Device uses a 'left-hand' orientation, we need to transform it to 'right-hand'
Quaternion tGyro = new Quaternion(tAttitude.x, tAttitude.y, -tAttitude.z, -tAttitude.w);
// the gyro attitude is tilted towards the floor and upside-down reletive to what we want in unity.
// First Rotate the orientation up 90deg on the X Axis, then 180Deg on the Z to flip it right-side up.
Quaternion tRotation = Quaternion.Euler(-90f, 0, 0) * tGyro;
tRotation = Quaternion.Euler(0, 0, 180f) * tRotation;
// You can now apply this rotation to any unity camera!
m_MainCamera.transform.localRotation = tRotation;
}
}
}
With this script my Object always face SOUTH no matter what.
If you want the object to face NORTH you just have to turn the view 180ยบ on the Y axis as a last rotation:
Quaternion tRotation = Quaternion.Euler(-90f, 0, 0) * tGyro;
tRotation = Quaternion.Euler(0, 0, 180f) * tRotation;
//Face NORTH:
tRotation = Quaternion.Euler(0,180f, 0) * tRotation;
Hope this might help ;)

Rotate around an object to its direction of velocity

(For Unity 5.3.5f1)
Right now I am working on a 3D camera that is orbiting horizontally around the player. The player is a RigidBody rolling sphere. The player can freely rotate the axis horizontally but when there is no input I want the rotation to reset back to the direction of the velocity.
Right now all I need to know is how to situate the camera behind the player's direction of velocity by rotating the camera around the player from its previous rotation.
Here is a crude drawing of what I am looking for:
Currently to update the camera to orbit around the player I use this script on the camera (with comments):
using UnityEngine;
using System.Collections;
public class example : MonoBehaviour {
public GameObject Player;
//I assume you would use this PlayerRB to find data regarding the movement or velocity of the player.
//public Rigidbody PlayerRB;
private float moveHorizontalCamera;
private Vector3 offset;
// Use this for initialization
void Start ()
{ //the offset of the camera to the player
offset = new Vector3(Player.transform.position.x - transform.position.x, transform.position.y - Player.transform.position.y, transform.position.z - Player.transform.position.z);
}
// Update is called once per frame
void Update ()
{
moveHorizontalCamera = Input.GetAxis("Joy X"); //"Joy X" is my right joystick moving left, none, or right resulting in -1, 0, or 1 respectively
//Copied this part from the web, so I half understand what it does.
offset = Quaternion.AngleAxis(moveHorizontalCamera, Vector3.up) * offset;
transform.position = Player.transform.position + offset;
transform.LookAt(Player.transform.position);
}
}
Any help at all would be great!
I would suggest you use Vector3.RotateTowards (https://docs.unity3d.com/ScriptReference/Vector3.RotateTowards.html)
You take the Vector that is currently pointing from the Player to the camera (transform.position - Player.transform.position) and rotate it towards -Player.GetComponent<Rigidbody>().velocity.normalized * desiredDistance (the vector pointing in the opposite direction of the players velocity with magnitude corresponding the desired distance of the camera to the player). You can then use Vector3.RotateTowards to rotate the camera (smoothly or immediately) around the Player by setting the cameras position accordingly.
Something like:
transform.position = Player.transform.position + Vector3.RotateTowards(transform.position - Player.transform.position, -Player.GetComponent<Rigidbody>().velocity.normalized * desiredDistance, step, .0f);
where step if the angle update in radians. (Note: you should avoid calling GetComponent<Rigidbody>() every Update. You are better off storing it somewhere)
I hope I understood your question correctly and this helps.

Categories

Resources