Many thanks! I worked for hours on this problem, not being able to figure out how the rotation system in Unity works. Now with your help it works fine! Thank you so much. :)
the //UP/DOWN part of the code is broken. float upAngle = Vector3.Angle(target.position, barrel.transform.up); doesn't return the angle that we are looking for. we need float upAngle = Vector3.Angle( *transform.position - target.position* , barrel.transform.up); for that angle. also the -transform.up+90 trick is not optional. if you try to use transform.forward you will get jitter. the reason for that is that -transform.up+90 simulates (while pointing in the direction of the enemy) a signed angle. with transform.forward the barrel will frequently overshoot the target and then do a full 360 rotation. still a good video though.
Thanks for the great video! I adjusted the script according to my needs, since my condition is to have a static turret base. Here it is, there is an explanation after the script about what i changed: using UnityEngine; public class SimpleTurretRotator : MonoBehaviour { //public fireProjectile weapon; //not used public Transform target; private void Update() { Aim(); //weapon.fire(); } private void Aim() { Vector3 newRotation = Vector3.zero; // TURN float targetPlaneAngle = vector3AngleOnPlane(target.position, transform.position, -transform.up, transform.forward); newRotation.y = targetPlaneAngle; // UP/DOWN float upAngle = vector3AngleOnPlane(target.position, transform.position, -transform.right, transform.forward); newRotation.x = upAngle; transform.Rotate(newRotation, Space.Self); //Reset Z rotation Vector3 curRotation = transform.localRotation.eulerAngles; curRotation.z = 0f; transform.localRotation = Quaternion.Euler(curRotation); } float vector3AngleOnPlane(Vector3 from, Vector3 to, Vector3 planeNormal, Vector3 toZeroAngle) { Vector3 projectedVector = Vector3.ProjectOnPlane(from - to, planeNormal); float projectedVectorAngle = Vector3.SignedAngle(projectedVector, toZeroAngle, planeNormal); return projectedVectorAngle; } } ===== Explanation ===== - I applied the same method I used in the horizontal rotation to the vertical rotation; - I combined the corresponding vertical and horizontal angles into 1 single Vector3; - After applying the rotation, i had to reset the Z value of the barrel's local rotation to 0 to avoid strange rotations involving that value. ##### DISCLAIMER ##### There is no clamping yet.
Thanks for this elegant solution! My implementation relied on SignedAngle and loved to twitch at very small angles, as if the rotation over-rotated to the other side. This works perfectly! It also does perfectly fine on moving-while-firing entities, which is great for tank-related stuff. I wonder if it can be used for simple NPC aiming just as well.
Thank you for sharing this lesson, i don't fully understand it yet bit i'll try to pick it apart and learn from it. I've been working on a turret with quaternions and slerps.eulerangles clamps and lookrotations around the parent transform.up(the base), Works fine when stuff is spread out,but as soon as i try to put some restrictions, all the prior code goes out of the window. And the turret ignores the base rotation completely looking at wherever it wants. You said on this video that you talked about planes will definitely check more of your channel. Thank you again
In my case the turretBase is static and i'm calling the //up/down on my turret Barreltransform and it just spins uncontrolably, could you share some insights please? The project on plane works for the y rotation though
You'll want to make sure you're getting the "up" angle relative to the barrel by using (if ignoring left and right) Vector3.ProjectOnPlane(target.position - barrel.position, transform.right) I know it doesn't seem intuitive to use "right" if we're trying to get a value on "forward and above me", but it's actually the piece we want to ignore, so we're saying, "Find it's position if it were directly in front of or above me, so remove the left/right and make it effectively 0." The "up" position is always relative to something, so track what "up" is relative to. If you're script is on the barrel itself, and the barrel pitches back, the angle relative to the turret/barrel will change if the barrel moves even if the target does not. SO, instead of transform.right, you might have to use a global value of "Vector3.right", but this would fail if you rotate your turret.
@@bitgalaxis1147 Thank you so much for the reply! I actually got it to work after several err hundred times and i ended up managing to clamp the turret on its x axis without losing track of the target(unless it went under the x value or over it) regardless of the base rotation, meaning i now have a turret that i can fit on any ship. I have bought several courses on Udemy,mostly where i learned the basics of scripting, where i posted some doubts, and posted on unity forum , but you are the first one to care and reply, so sir i want to express you my deepest gratitude. Learning without feedback to our questions is a gruesome experience at times, so again i thank you man The content of your channel is superb,specially the video on torque, that really knocked my socks off and its making me think about how to design some of my ships or other objects. So again Thank You Thank You and Thank You!! God Bless You!
Hey im trying to smooth movement and limit max angles, i tried bellow code but it did not work Can you help me out? if (HorizontalPivot == null || Target == null) return; //Turn float targetPlaneAngle = vector3AngleOnPlane( aim_Predicted_Position, HorizontalPivot.transform.position, -HorizontalPivot.transform.up, HorizontalPivot.transform.forward ); Vector3 newRotation = new Vector3(0, targetPlaneAngle, 0); Vector3 temp = newRotation; if (HorizontalRotationLimit) { if (HorizontalPivot.transform.localEulerAngles.y > RightRotationLimit) { HorizontalPivot.transform.localEulerAngles = new Vector3(0, RightRotationLimit, 0); temp = new Vector3(0, 0, 0); } else if (HorizontalPivot.transform.localEulerAngles.y < -LeftRotationLimit) { HorizontalPivot.transform.localEulerAngles = new Vector3(0, -LeftRotationLimit, 0); temp = new Vector3(0, 0, 0); } else { } } float limiited_y = Mathf.Lerp(HorizontalPivot.transform.localEulerAngles.y,temp.y,rotate_Speed * Time.deltaTime); Vector3 lerp_V3 = new Vector3(0, limiited_y, 0); HorizontalPivot.transform.Rotate(lerp_V3, Space.Self);
In the video below, I link to where I got the textures, and the video explains how I was able to make the skybox with it. ua-cam.com/video/g38f-xxvEQ8/v-deo.html
Thanks for this tutorial the right left aim is nice, but by moving the target up and down the Projectile misses. Edit: I mean not that I move the target too fast
I don't get how it works for you, but doesn't work in my project. When I copy paste your code the turret and barrel rotate absolutely fine unless the turret's y-rotation is negative, then the barrel aims "negatively" and with an offset. Has Unity changed the angle calculations? i.e. does the same code work for you? Does it have something to do with project settings or similar?
I'm not able to replicate this on 2019 or 2020, but I did find one way to break it that might be related. The turret code assumes that it is attached to a parent object, and the code also assumes that the barrel is a child of the turret object. The turret base in my example is also a child object, but that shouldn't matter.
@@bitgalaxis1147 I've recreated your hierarchy exactly as in the video. Still doesn't work. My Unity version is 2020.1. Meanwhile I've solved the problem by using a second plane to calculate the X angle of the barrel. The adjustments compared to the videos are: "pitch" is the barrel (up/down). target is a Vector3. Local variables renamed. Aim function is: private void Aim() { // TURN float angleY = vector3AngleOnPlane(target, transform.position, -transform.up, transform.forward); Vector3 rotationY = new Vector3(0, angleY, 0); transform.Rotate(rotationY, Space.Self); // UP / DOWN if (pitch != null) { float angleX = vector3AngleOnPlane(target, pitch.position, -transform.right, transform.forward); Vector3 rotationX = new Vector3(angleX, 0, 0); pitch.localRotation = Quaternion.Euler(rotationX); } } Still. It is kinda scary that code functions differently sometimes and I'd love to find out why and when this happens.
@@CameronCheese Warning! I've later noticed that there a few specific angles that break the movement of my solution. I never solved it perfectly as it wasn't a problem in my game. But I'm curious. So if you find the problem and fix it, let me know, please.
I need to post the full solution on GitHub, so I'll work on that. But for now, here's what you need. A script called, "fireProjectile.cs". See below. A laser game object with a laser script attached. I'll post in the next comment. (fireProjectile.cs) ================================================================================ using System.Collections; using System.Collections.Generic; using UnityEngine; public class fireProjectile : MonoBehaviour { public GameObject projectile; public AudioEvent audioSound; [SerializeField] private float fireRatePerSecond = 1f; private float timeBetweenShots; private float nextFire; private AudioSource audioSource; void Start() { timeBetweenShots = 1f / fireRatePerSecond; nextFire = Time.time + timeBetweenShots; audioSource = GetComponent(); } void Update() { if(Input.GetButton("Fire1")) { fire(); } } public void fire() { if(Time.time >= nextFire) { nextFire = Time.time + timeBetweenShots; GameObject.Instantiate(projectile, transform.position, transform.rotation); audioSound.PlayOneShot((audioSource)); } } }
Attach this script to the laser gameobject (this should be made into a prefab which will behave like a laser. A stretched cylinder will work). (laserBehavior.cs) ============================================================================================================================= public class laserBehavior : MonoBehaviour { [SerializeField] private float speed = 100f; private float endOfLife; private float force = 1000f; void Start() { endOfLife = Time.time + (150f / speed); } void Update() { if(Time.time > endOfLife) Destroy(gameObject); RaycastHit hit; if(Physics.Raycast(transform.position, transform.TransformDirection(Vector3.forward), out hit, Time.deltaTime * speed)) { // If laser hits an emeny, damage it and knock it back hit.rigidbody.AddForceAtPosition(force * speed * transform.forward, hit.point); EnemyShip enemy = hit.collider.GetComponent(); if(enemy) enemy.damage(100f); // If laser hits the player, damage player and knock player back RigidBodyAircraft player = hit.collider.GetComponent(); if(player) player.damage(100f); Destroy(gameObject); } else transform.Translate(Vector3.forward * speed * Time.deltaTime, Space.Self); } }
My best regards ! Your script is great ! Have a good time for you !
I really appreciate this. Was working on this for 2 days and finally got it fixed!
Many thanks! I worked for hours on this problem, not being able to figure out how the rotation system in Unity works.
Now with your help it works fine! Thank you so much. :)
Nice work, especially float vector3AngleOnPlane is what I was looking for. thnx
Thanks for the vector3AngleOnPlane function - I needed it.
the //UP/DOWN part of the code is broken.
float upAngle = Vector3.Angle(target.position, barrel.transform.up); doesn't return the angle that we are looking for.
we need float upAngle = Vector3.Angle( *transform.position - target.position* , barrel.transform.up); for that angle.
also the -transform.up+90 trick is not optional.
if you try to use transform.forward you will get jitter.
the reason for that is that -transform.up+90 simulates (while pointing in the direction of the enemy) a signed angle.
with transform.forward the barrel will frequently overshoot the target and then do a full 360 rotation.
still a good video though.
TYSM I was wondering why is was like inverting itself hahah you've saved me :D
Thanks for the great video! I adjusted the script according to my needs, since my condition is to have a static turret base.
Here it is, there is an explanation after the script about what i changed:
using UnityEngine;
public class SimpleTurretRotator : MonoBehaviour
{
//public fireProjectile weapon; //not used
public Transform target;
private void Update()
{
Aim();
//weapon.fire();
}
private void Aim()
{
Vector3 newRotation = Vector3.zero;
// TURN
float targetPlaneAngle = vector3AngleOnPlane(target.position, transform.position, -transform.up, transform.forward);
newRotation.y = targetPlaneAngle;
// UP/DOWN
float upAngle = vector3AngleOnPlane(target.position, transform.position, -transform.right, transform.forward);
newRotation.x = upAngle;
transform.Rotate(newRotation, Space.Self);
//Reset Z rotation
Vector3 curRotation = transform.localRotation.eulerAngles;
curRotation.z = 0f;
transform.localRotation = Quaternion.Euler(curRotation);
}
float vector3AngleOnPlane(Vector3 from, Vector3 to, Vector3 planeNormal, Vector3 toZeroAngle)
{
Vector3 projectedVector = Vector3.ProjectOnPlane(from - to, planeNormal);
float projectedVectorAngle = Vector3.SignedAngle(projectedVector, toZeroAngle, planeNormal);
return projectedVectorAngle;
}
}
===== Explanation =====
- I applied the same method I used in the horizontal rotation to the vertical rotation;
- I combined the corresponding vertical and horizontal angles into 1 single Vector3;
- After applying the rotation, i had to reset the Z value of the barrel's local rotation to 0 to avoid strange rotations involving that value.
##### DISCLAIMER #####
There is no clamping yet.
Thanks for this elegant solution! My implementation relied on SignedAngle and loved to twitch at very small angles, as if the rotation over-rotated to the other side. This works perfectly!
It also does perfectly fine on moving-while-firing entities, which is great for tank-related stuff. I wonder if it can be used for simple NPC aiming just as well.
Thank you for sharing this lesson, i don't fully understand it yet bit i'll try to pick it apart and learn from it. I've been working on a turret with quaternions and slerps.eulerangles clamps and lookrotations around the parent transform.up(the base),
Works fine when stuff is spread out,but as soon as i try to put some restrictions, all the prior code goes out of the window. And the turret ignores the base rotation completely looking at wherever it wants. You said on this video that you talked about planes will definitely check more of your channel. Thank you again
You are the BEST !!!! 😍
In my case the turretBase is static and i'm calling the //up/down on my turret Barreltransform and it just spins uncontrolably, could you share some insights please? The project on plane works for the y rotation though
You'll want to make sure you're getting the "up" angle relative to the barrel by using (if ignoring left and right)
Vector3.ProjectOnPlane(target.position - barrel.position, transform.right)
I know it doesn't seem intuitive to use "right" if we're trying to get a value on "forward and above me", but it's actually the piece we want to ignore, so we're saying, "Find it's position if it were directly in front of or above me, so remove the left/right and make it effectively 0."
The "up" position is always relative to something, so track what "up" is relative to. If you're script is on the barrel itself, and the barrel pitches back, the angle relative to the turret/barrel will change if the barrel moves even if the target does not. SO, instead of transform.right, you might have to use a global value of "Vector3.right", but this would fail if you rotate your turret.
@@bitgalaxis1147 Thank you so much for the reply! I actually got it to work after several err hundred times and i ended up managing to clamp the turret on its x axis without losing track of the target(unless it went under the x value or over it) regardless of the base rotation, meaning i now have a turret that i can fit on any ship.
I have bought several courses on Udemy,mostly where i learned the basics of scripting, where i posted some doubts, and posted on unity forum , but you are the first one to care and reply, so sir i want to express you my deepest gratitude.
Learning without feedback to our questions is a gruesome experience at times, so again i thank you man
The content of your channel is superb,specially the video on torque, that really knocked my socks off and its making me think about how to design some of my ships or other objects. So again Thank You Thank You and Thank You!! God Bless You!
Hey im trying to smooth movement and limit max angles, i tried bellow code but it did not work
Can you help me out?
if (HorizontalPivot == null || Target == null) return;
//Turn
float targetPlaneAngle =
vector3AngleOnPlane(
aim_Predicted_Position,
HorizontalPivot.transform.position,
-HorizontalPivot.transform.up,
HorizontalPivot.transform.forward
);
Vector3 newRotation = new Vector3(0, targetPlaneAngle, 0);
Vector3 temp = newRotation;
if (HorizontalRotationLimit)
{
if (HorizontalPivot.transform.localEulerAngles.y > RightRotationLimit)
{
HorizontalPivot.transform.localEulerAngles = new Vector3(0, RightRotationLimit, 0);
temp = new Vector3(0, 0, 0);
}
else if (HorizontalPivot.transform.localEulerAngles.y < -LeftRotationLimit)
{
HorizontalPivot.transform.localEulerAngles = new Vector3(0, -LeftRotationLimit, 0);
temp = new Vector3(0, 0, 0);
}
else
{
}
}
float limiited_y = Mathf.Lerp(HorizontalPivot.transform.localEulerAngles.y,temp.y,rotate_Speed * Time.deltaTime);
Vector3 lerp_V3 = new Vector3(0, limiited_y, 0);
HorizontalPivot.transform.Rotate(lerp_V3, Space.Self);
What is that cool looking skybox you're using?
In the video below, I link to where I got the textures, and the video explains how I was able to make the skybox with it.
ua-cam.com/video/g38f-xxvEQ8/v-deo.html
@@bitgalaxis1147 Thanks for explaining!
Thanks for this tutorial the right left aim is nice, but by moving the target up and down the Projectile misses.
Edit: I mean not that I move the target too fast
I don't get how it works for you, but doesn't work in my project.
When I copy paste your code the turret and barrel rotate absolutely fine unless the turret's y-rotation is negative, then the barrel aims "negatively" and with an offset. Has Unity changed the angle calculations? i.e. does the same code work for you?
Does it have something to do with project settings or similar?
I'm not able to replicate this on 2019 or 2020, but I did find one way to break it that might be related. The turret code assumes that it is attached to a parent object, and the code also assumes that the barrel is a child of the turret object. The turret base in my example is also a child object, but that shouldn't matter.
@@bitgalaxis1147 I've recreated your hierarchy exactly as in the video. Still doesn't work. My Unity version is 2020.1. Meanwhile I've solved the problem by using a second plane to calculate the X angle of the barrel. The adjustments compared to the videos are:
"pitch" is the barrel (up/down). target is a Vector3. Local variables renamed.
Aim function is:
private void Aim()
{
// TURN
float angleY = vector3AngleOnPlane(target, transform.position, -transform.up, transform.forward);
Vector3 rotationY = new Vector3(0, angleY, 0);
transform.Rotate(rotationY, Space.Self);
// UP / DOWN
if (pitch != null)
{
float angleX = vector3AngleOnPlane(target, pitch.position, -transform.right, transform.forward);
Vector3 rotationX = new Vector3(angleX, 0, 0);
pitch.localRotation = Quaternion.Euler(rotationX);
}
}
Still. It is kinda scary that code functions differently sometimes and I'd love to find out why and when this happens.
@@DoubleBob Thank you sensei, I had the same problem and this worked.
@@CameronCheese Warning! I've later noticed that there a few specific angles that break the movement of my solution.
I never solved it perfectly as it wasn't a problem in my game. But I'm curious. So if you find the problem and fix it, let me know, please.
firePorjectile not exist
I need to post the full solution on GitHub, so I'll work on that. But for now, here's what you need.
A script called, "fireProjectile.cs". See below.
A laser game object with a laser script attached. I'll post in the next comment.
(fireProjectile.cs)
================================================================================
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class fireProjectile : MonoBehaviour
{
public GameObject projectile;
public AudioEvent audioSound;
[SerializeField]
private float fireRatePerSecond = 1f;
private float timeBetweenShots;
private float nextFire;
private AudioSource audioSource;
void Start()
{
timeBetweenShots = 1f / fireRatePerSecond;
nextFire = Time.time + timeBetweenShots;
audioSource = GetComponent();
}
void Update()
{
if(Input.GetButton("Fire1"))
{
fire();
}
}
public void fire()
{
if(Time.time >= nextFire)
{
nextFire = Time.time + timeBetweenShots;
GameObject.Instantiate(projectile, transform.position, transform.rotation);
audioSound.PlayOneShot((audioSource));
}
}
}
Attach this script to the laser gameobject (this should be made into a prefab which will behave like a laser. A stretched cylinder will work).
(laserBehavior.cs)
=============================================================================================================================
public class laserBehavior : MonoBehaviour
{
[SerializeField] private float speed = 100f;
private float endOfLife;
private float force = 1000f;
void Start()
{
endOfLife = Time.time + (150f / speed);
}
void Update()
{
if(Time.time > endOfLife) Destroy(gameObject);
RaycastHit hit;
if(Physics.Raycast(transform.position, transform.TransformDirection(Vector3.forward), out hit, Time.deltaTime * speed))
{
// If laser hits an emeny, damage it and knock it back
hit.rigidbody.AddForceAtPosition(force * speed * transform.forward, hit.point);
EnemyShip enemy = hit.collider.GetComponent();
if(enemy) enemy.damage(100f);
// If laser hits the player, damage player and knock player back
RigidBodyAircraft player = hit.collider.GetComponent();
if(player) player.damage(100f);
Destroy(gameObject);
}
else transform.Translate(Vector3.forward * speed * Time.deltaTime, Space.Self);
}
}
Mate you need to make more tutorials.... quite easily could make a living of UA-cam. Is your code available?