- Home /
problems with quaternion rotations
hi there my friends i have a trouble in making the following:
1-i have a spaceship that is controlled by using (rigidbody.AddRelativeTorque) thats is getting info from the position of the mouse on screen.
2-i have a turret on top of the ship.
3-the turret uses the following script to lead the targets:
#pragma strict
#pragma implicit
#pragma downcast
var damping = 5.0;
var Target : GameObject;
var projSpeed : int;
var fireHitRatio : float;
var targetLead : GameObject;
var enableFire : boolean;
var rotation : Quaternion;
function Start(){
fireHitRatio = 1.15;
}
function Update () {
if (Target != null){
TargetLead();
}else if (Target == null){
NoTargets();
}
}
function TargetLead(){
if (Target != null) {
var TGV = Target.rigidbody.velocity;
projectileSpeed = projSpeed;
var distTarget = Vector3.Distance(Target.transform.position, transform.position);
velocityPosition1 = Target.transform.position + ((TGV * fireHitRatio) * (distTarget/projectileSpeed));
velocityDist1 = Vector3.Distance(velocityPosition1, transform.position);
velocityPosition2 = Target.transform.position + ((TGV * fireHitRatio) * (velocityDist1/projectileSpeed));
velocityDist2 = Vector3.Distance(velocityPosition2, transform.position);
TargetInterceptPosition = Target.transform.position + ((TGV * fireHitRatio) * (velocityDist2/projectileSpeed));
rotation = Quaternion.LookRotation((TargetInterceptPosition) - transform.position);
transform.rotation = Quaternion.Slerp(transform.rotation, rotation, Time.deltaTime * damping);
}
targetLead.transform.position = camera.main.WorldToViewportPoint(TargetInterceptPosition);
}
function NoTargets(){
targetLead.transform.position = Vector3(0,0,0);
transform.rotation = Quaternion.Slerp(transform.rotation, transform.parent.rotation, Time.deltaTime * damping / 5);
}
4-the turret must be alinged with the ship at all times , but must also try to look at the target lead with the following:
the above script works the best if used on a turret made with 1 piece of geometry.
the turret is made up with 2 parts ,(turret) which moves only on Y Axis , (barrel) which moves only on X Axis but also follows the turret Y Axis.
the troubles i am having:
1-used many scripts , cant list them here but it always mess the rotation of the turret by a really weird rotations.
2-the following script is the best but it does not aim well , it aims little higher than it must be:
var rotationInfo : ProcessingTargets;
var displayedInfo : Quaternion;
function Update (){
displayedInfo = Quaternion.Slerp(transform.rotation, rotationInfo.rotation, Time.deltaTime * rotationInfo.damping);
if (rotationInfo.Target != null){
transform.rotation = Quaternion(displayedInfo.x,displayedInfo.y,displayedInfo.z,displayedInfo.w);
Debug.DrawLine(transform.position,rotationInfo.Target.transform.position,Color(1,1,1,1));
}
}
var turretRotationsInfo : TurretTargeter;
var angle = 0.0;
var axis : Vector3;
var isBarrel : boolean;
function Update (){
axis = turretRotationsInfo.displayedInfo.eulerAngles;
if(isBarrel == false){
transform.localEulerAngles.y = axis.y - 90;
}else if(isBarrel == true){
transform.localEulerAngles.y = axis.x + 1.5;
}
}
please i wish someone really helps me with this because it have taken a long time in debugging , trying , scripting ....etc
and also i am having lot of troubles with (quaternions) i have read the documents on it but still didnt understand it fully and i think thats why i failed at fixing this.
thx in adv
anyone , i worked more on it but still failing to get at least a good results , hope i get help soon.
thx
Answer by aldonaletto · Jul 09, 2012 at 06:51 PM
I didn't understand exactly what your script should do, but you should not mix eulerAngles with localEulerAngles - localEulerAngles are the angles relative to the parent transform, and you should take the parent rotation into account before setting them. Furthermore, eulerAngles aren't too reliable - eulerAngles are actually the transform.rotation converted to 3-axes representation on the fly; since there are several XYZ combinations equivalent to any possible rotation, Unity sometimes selects a weird one to return, driving us crazy.
I think the simplest solution would be just to use transform.LookAt(target) in the turret script:
var target: Transform;
function Update(){
transform.LookAt(target);
}
If you want to follow the target with some delay, use this:
var target: Transform;
function Update(){
var dir = target.position - transform.position;
var rot = Quaternion.LookRotation(dir); // LookRotation takes a direction as argument
transform.rotation = Quaternion.Slerp(transform.rotation, rot, damping * Time.deltaTime);
}
EDITED: If you want to rotate the turret and the barrel around their local Y and X axes, respectively, there's a "dirty trick" that can split movements: for the turret, transform the target position in local space with InverseTransformPoint, zero its Y coordinate and convert it back to world space with TransformPoint - this will project the target position in the turret's local horizontal plane; you can then use Quaternion.LookRotation to find the desired rotation, and Slerp to it. You could also repeat the trick for the barrel (zeroing its X coordinate instead, so that the point would be projected in the barrel's vertical plane), but it's not advisable: errors accumulated over time will cause a deviation. It's better to just use LookRotation to find the necessary rotation to the target position and Slerp the barrel to it - the result is the same, but you'll not get accumulated errors:
EDITED2: You're right: the turret was using its local space to find the point, and gradually this reference was being lost, giving very weird rotations. I changed the things a little: created an empty object (let's call it Ref) at the turret position, added the script below to it, then childed the turret and the barrel to the Ref object - the hierarchy is as follows:
Ship <- ship is parent of the Ref object
Ref <- control script goes here
Barrel <- barrel is childed to Ref
Turret <- turret is childed to Ref
The script caches references to Barrel and Turret, and control both:
var target: Transform;
var damping: float = 5.0;
private var turret: Transform;
private var barrel: Transform;
private var trf: Transform;
function Start () { // the barrel is a turret's child:
trf = transform;
barrel = trf.Find("Barrel");
turret = trf.Find("Turret");
}
function Update () {
// convert target position to local space:
var pos = trf.InverseTransformPoint(target.position);
pos.y = 0; // project it in the horizontal plane
// convert pos back to world space:
pos = trf.TransformPoint(pos);
// find the desired turret rotation and Slerp to it:
var rot = Quaternion.LookRotation(pos-trf.position, trf.up);
turret.rotation = Quaternion.Slerp(turret.rotation, rot, damping*Time.deltaTime);
// just point the barrel to the target:
rot = Quaternion.LookRotation(target.position-barrel.position, trf.up);
barrel.rotation = Quaternion.Slerp(barrel.rotation, rot, damping*Time.deltaTime);
}
ok thx for ur comment , but if u looked in my first post , u will find that i have a script that also leads the target depending on the speed or the bullet and the speed and range of the target. so if i used transform.LookAt(target) it will just look at the target without leading it.
the only problem i am facing now , is to align the turret and the barrel to the ship without ruining the targeting script or the angles of the turret.
thx for ur help anyway , anymore hints?
Ok, I suppose to have understood your question now: your first script is good for a single piece turret, but you're having problems with a combination turret+barrel, where you want the turret to rotate around its local Y axis, and the barrel to rotate around its X axis - is it correct?
thats right good sir , but also i want them to align with the moving ship that they are on it.
as u said , i want them to rotate around their local axis only.
thx a lot for ur time
Take a look at my answer: I made the turret rotate around its local Y axis in direction to the target, while the barrel follows it and rotate also around its X axis to point the target - and all of this with some delay. It doesn't matter if the turret is childed to the ship: it will rotate around its local Y axis.
i tried ur script but.... i still get the same problem, + another one:
1-the turret still gets really strange angle if the ship rotates to down or left for Ex...
2-the turret aims right at the target with no leading for the target (that means when i fire the bullet will just miss bcz the target moves)
i was thinking , can i take the rotation of my first script posted and split it in to angles so i can use whatever i want but convert them in to local angles.
as i said i am really bad with Quaternion , thats why i failed.
and sry for bad english or anything wrong , its 2:30A$$anonymous$$ here and i am really tired.
still i really thx u for ur time in helping me.
Answer by captaincrunch80 · Jul 09, 2012 at 11:52 PM
I had the same freaking problem and worked around it like this.
Here the corpus and barrel are handled. Both transformations are reduced to a 1 or 2 dimensional rotation. I use a temp look-at vector to work around quaternions.
I stripped all the code for shooting particle control and some other stuff.
!! For this to work, the barrel must be an individual model and has to have it's origin at it's rotation point
If you only want to rotate the whole turret (corpus) then just stip the barrel code.
// C#
public Transform corpus;
public float corpusTurnSpeed = 10.0f;
public Transform barrel;
public float barrelTurnSpeed = 10.0f;
private GameObject currentTarget = null;
void Update ()
{
// Stripped ....
if (currentTarget != null) {
Vector3 targetPoint = currentTarget.collider.bounds.center;
// Calculate Corpus X-Y-Z Rotation and LERP it
// then set Corpus Y Rotation to 0.0
Vector3 newCorpusFwd
= Vector3.Lerp (corpus.forward,
(targetPoint - corpus.position).normalized,
Time.deltaTime * corpusTurnSpeed);
newCorpusFwd.y = 0.0f;
corpus.transform.forward = newCorpusFwd;
// Calculate Barrel Y Rotation and smoothStep it
// Then align Barrel x-z rotation to the corpus again
barrel.transform.LookAt (targetPoint);
Vector3 newBarrelFwd = barrel.forward;
float newBarrelY
= Mathf.SmoothStep (barrel.forward.y,
(targetPoint - barrel.position).normalized.y,
Time.deltaTime * barrelTurnSpeed);
newBarrelFwd.x = newCorpusFwd.x;
newBarrelFwd.y = newBarrelY;
newBarrelFwd.z = newCorpusFwd.z;
barrel.forward = newBarrelFwd;
// Stripped ...
} else {
// Stripped ...
}
}
As requested, here the javascript version, but not tested:
// javascript
var corpus : Transform;
var corpusTurnSpeed : float = 10.0f;
var barrel : Transform ;
var barrelTurnSpeed : float = 10.0f;
private var currentTarget : GameObject;
void Update ()
{
// Stripped ....
if (currentTarget != null) {
var targetPoint : Vector3 = currentTarget.collider.bounds.center;
// Calculate Corpus X-Y-Z Rotation and LERP it
// then set Corpus Y Rotation to 0.0
var newCorpusFwd : Vector3
= Vector3.Lerp (corpus.forward,
(targetPoint - corpus.position).normalized,
Time.deltaTime * corpusTurnSpeed);
newCorpusFwd.y = 0.0f;
corpus.transform.forward = newCorpusFwd;
// Calculate Barrel Y Rotation and smoothStep it
// Then align Barrel x-z rotation to the corpus again
barrel.transform.LookAt (targetPoint);
var newBarrelFwd : Vector3 = barrel.forward;
var newBarrelY : float
= Mathf.SmoothStep (barrel.forward.y,
(targetPoint - barrel.position).normalized.y,
Time.deltaTime * barrelTurnSpeed);
newBarrelFwd.x = newCorpusFwd.x;
newBarrelFwd.y = newBarrelY;
newBarrelFwd.z = newCorpusFwd.z;
barrel.forward = newBarrelFwd;
// Stripped ...
} else {
// Stripped ...
}
}
can u good sir if u please , convert it to js , i am nothing in C#
Hi there,
I tried to convert it to js, even if I am not often working with it. As you can see in this case only the variable declarations are a problem. (See above in my original post)
C# and js are not so different. The idea behind the code stays the same, it are mostly some syntax issues. I am not sure about using .NET in js though.
Anyways - learn C#, I can recommend it. It is very handy to have all that classes, inheritance (And all the other object oriented stuff) stuff on your side.
Best of luck!
Answer by MigLeader · Jul 09, 2012 at 05:30 PM
ok this Script works semi fine:
var turretRotationsInfo : TurretTargeter;
var angle = 0.0;
var axis : Vector3;
var isBarrel : boolean;
function Update (){
axis = turretRotationsInfo.displayedInfo.eulerAngles;
if(isBarrel == false){
transform.localEulerAngles.y = axis.y - 90;
}else if(isBarrel == true){
transform.localEulerAngles.y = axis.x + 1.5;
}
}
but as soon as i move the ship , the turrets starts to inherit some added rotation from the ship so it starts missing the right point to aim to.
any fix for this?
please help me at least with a hint only.
Answer by MigLeader · Jul 24, 2012 at 10:07 PM
hi again , sigh , i tried everything above and trying to combine it with my code of lead targeting but nothing worked probably, so here i am asking a fix for it as the following:
i will have to write it in the answers cuz i didnt find how i can format the code using comments , if its wrong to do it like that then i am sorry , i will remove it.
i have this script:
#pragma strict
#pragma implicit
#pragma downcast
var damping = 5.0;
var Target : GameObject;
var projSpeed : int;
var fireHitRatio : float;
var targetLead : GameObject;
var targetLead2 : GameObject;
var targetLead3 : GameObject;
var shiproty : Transform;
function Start(){
fireHitRatio = 1.15;
}
function Update () {
if (Target != null){
TargetLead();
}else if (Target == null){
NoTargets();
}
}
function TargetLead(){
if (Target != null) {
var TGV = Target.rigidbody.velocity;
projectileSpeed = projSpeed;
var distTarget = Vector3.Distance(Target.transform.position, transform.position);
var velocityPosition1 = Target.transform.position + ((TGV * fireHitRatio) * (distTarget/projectileSpeed));
var velocityDist1 = Vector3.Distance(velocityPosition1, transform.position);
var velocityPosition2 = Target.transform.position + ((TGV * fireHitRatio) * (velocityDist1/projectileSpeed));
var velocityDist2 = Vector3.Distance(velocityPosition2, transform.position);
var TargetInterceptPosition = Target.transform.position + ((TGV * fireHitRatio) * (velocityDist2/projectileSpeed));
var rotation = Quaternion.LookRotation((TargetInterceptPosition) - transform.position);
transform.rotation = Quaternion.Slerp(transform.rotation, rotation, Time.deltaTime * damping);
}
targetLead.transform.position = camera.main.WorldToViewportPoint(velocityPosition1);
targetLead2.transform.position = camera.main.WorldToViewportPoint(velocityPosition2);
targetLead3.transform.position = camera.main.WorldToViewportPoint(TargetInterceptPosition);
}
all i want is to split the rotations from it in to X , Y , Z and store them in Vars , then convert them in to local rotations.
i prefer to not change the code of leading targets cuz its working the best and i couldn't find another way to do it.
please this problem have taken from me most of my Time , still i thank everyone who tried to help me with it.
thx in adv