turning towards an object in Unity - unity3d

Good times. How do I implement the NPC rotation towards the selected object?
public GameObject BufferObject;
public float MoveSpeed = 1f;
void Update()
{
float step = MoveSpeed * Time.deltaTime;
transform.position = Vector2.MoveTowards(transform.position, BufferObject.transform.position, step);
}
Here is the script for moving the NPC to the selected object (Buffer Object) and the movement works perfectly, but the implementation of the rotation causes Me difficulties, please advise.
For Unity2D.

Simply get the desired direction
var direction = (BufferObject.transform.position - transform.position).normalized;
and then the desired rotation using Quaternion.LookRotation like e.g.
var targetRotation = Quaternion.LookDirection(direction);
Then either apply it directly if you want it immediately
transform.rotation = targetRotation;
Or if you want it smooth you could use e.g. Quaternion.RotateTowards like
transform.rotation = Quaternion.RotateTowards(transform.rotation, targetRotation, anglePerSecond * Time.deltatime);
Have in mind though that it might look awkward if the rotation is too slow since your NPC could move back/sidewards already while still not fully looking towards the target ;)
So you might want to wait until
if(Quaternion.Angle(targetRotation, transform.rotation) <= certainThreshold)
{
... your movement code
}

So the answer and the solution from Me, albeit stupid, but working. In order to reflect the sprite, you need to get a variable, either 1 or -1 (For Scale). This code will show the distance from one NPC to the object:
BufferObject.transform.position.x - transform.position.x
And here I get a lot of values as if to the left of the NPC, then -x... , and if to the right, then x... thereby it is possible to determine which side of the object, so also level the value from here (Optional) to 1 or -1 and apply this result to transform.localScale and thereby solve the problem of how to reflect (Rotate the sprite) towards the object. Use it for your health :)
Complete code:
float localPositionAmount = BufferObject.transform.position.x - transform.position.x;
if (localPositionAmount >= 1)
{
gameObject.transform.localScale = new Vector3(1, transform.localScale.y, transform.localScale.z);
}
if (localPositionAmount <= -1)
{
gameObject.transform.localScale = new Vector3(-1, transform.localScale.y, transform.localScale.z);
}
if (localPositionAmount == 0)
{
gameObject.transform.localScale = new Vector3(transform.localScale.x, transform.localScale.y, transform.localScale.z);
}
Yes, the code is the simplest and without enumerations and other things, but it works perfectly. I still had to figure it out myself...

Related

applying velocity to rigidbody makes it float instead of dashing

Im trying to code so that my Character dashes to the right when pressing the Left mouse button, but instead of dashing it just starts slowly glieding or lets say floating.
This is the code i´ve used;
if (Input.GetMouseButton(0))
{
rb.velocity = Vector2.right * DashSpeed;
}
Im not sure but a other part of my code might be the reason for this problem but if so i would like to know how i could solve it. Thats the part im talking about
rb.velocity = new Vector2(moveInput * speed, rb.velocity.y);
thats the code im using for movement.
void Start()
{
cam = Camera.main;
rb = GetComponent<Rigidbody2D>();
}
private void Update()
{
horizontal = Input.GetAxisRaw("Horizontal");
animator.SetFloat("Horizontal", Input.GetAxis("Horizontal"));
if (Input.GetKeyDown(KeyCode.Space) && isGrounded == true)
{
float jumpVelocity = 7f;
rb.velocity = Vector2.up * jumpVelocity;
jumpsound.Play();
}
Vector3 worldPos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
if (Input.GetKey(KeyCode.RightAlt))
{
Dashing();
}
}
void FixedUpdate()
{
isGrounded = Physics2D.OverlapCircle(groundCheck.position, CheckRadius, whatisGround);
moveInput = Input.GetAxisRaw("Horizontal");
rb.velocity = new Vector2(moveInput * speed, rb.velocity.y);
if (Input.GetKeyDown(KeyCode.Escape))
{
SceneManager.LoadScene("Main menu");
}
}
void Dashing()
{
rb.AddForce(Vector2.right * DashSpeed, ForceMode2D.Impulse);
}
The issue with your current code is you are directly changing velocity in a few places. Some basic physics, the integral of position vs. time graph is velocity, and the integral of velocity is acceleration vs. time. To get a more realistic movement, it is better to apply a Force to objects. When doing this, the physics engine Unity uses can add a new force at a given time, then using acceleration can accelerate the object in that direction over time, then can change the velocity over time which will result in the position changing over time.
The example code you posted, you are directly setting velocity in a few places.
rb.velocity = Vector2.up * jumpVelocity; (Jump)
rb.velocity = new Vector2(moveInput * speed, rb.velocity.y); (Movement)
rb.velocity = Vector2.right * DashSpeed; (Dash)
When directly setting these values, the new values overwrite the old ones. As your movement code is not in any sort of if conditional it will continually write to the velocity causing the dash to never change anything regardless if you use add-force or change velocity directly.
I would consider making both your jump and dash use AddForce, and if you like the feel of your movement by applying velocity directly, then add the velocity do not set it.
Your previous line rb.velocity = new Vector2(moveInput * speed, rb.velocity.y); would then become rb.AddForce(new Vector2(moveInput * speed, 0), ForceMode2D.Impulse);. Similarly you can update your jump and dash to match this. Let me know if you get this working or have more questions.
It could be a problem with your animation. Link to a thread on unity answers:
https://answers.unity.com/questions/674516/rigidbody-character-problems-constant-floating-jum.html
You should go over to the animation place and hit bake into pose.
You should use the Rigidbody2D.AddForce(Vector2, ForceMode2D). What this does is moving the GameObject in the direction of the Vector2, with the force mode of ForceMode2D. What is different about this from just translating it, is that it interacts with physics and improves the quality of your game. Here is a link, and the script:
https://docs.unity3d.com/ScriptReference/Rigidbody2D.AddForce.html
Rigidbody2D rb;
float dashSpeed;
void Update()
{
if (Input.GetMouseButton(0))
{
rb.AddForce(Vector2.right * dashSpeed);
}
}
And if the other part of the code you were talking about, if that is glitching, then do the same trick.

GameObject is vibrating throughout the movement

I have a GameObject that takes position coordinates from a file. This GameObject moves with vibration rather than smoothly. (as if moving back and forth.)
Here is the code that provides the movement:
int counter = 0;
void Update()
{
var maxDistance = speed_* Time.deltaTime;
var oldPosition = transform.position;
var newPosition = Vector3.MoveTowards(transform.position, positions[counter], maxDistance);
var actualDistance = Vector3.Distance(newPosition, oldPosition);
var distanceRemainder = maxDistance - actualDistance;
if (distanceRemainder > 0.0001)
{
newPosition = Vector3.MoveTowards(newPosition, positions[counter], distanceRemainder);
counter++;
}
transform.localPosition = newPosition;
}
NOTE: The data read from the file is in the "positions" array (x,y,z coordinates).
When I lower the 300f value in the variable maxDistance, the vibration stops and the motion becomes more fluid. However, Gameobject speed is also slowing down. How can I ensure a fast and smooth movement?
SOLUTION:
While looking for a solution of the problem, I came across the this topic. It helped me learn the source of the problem.
I have observed that the GameObject is not vibrating in Scene view, it was moving smoothly. But the object seemed to be vibrating in GameView. The problem is not the moving object, it's the camera function I write to follow it.
The camera function that was written to follow the object has been updated and the problem has disappeared.
One can see more of the solution by going to Post #13 in the link above.
Thanks to everyone trying to help.
This is caused cause you are using your distanceRemainder as your maxDistanceDelta, which I think is incorrect, if you want a smooth movement, you should multiply it for the Time.deltaTime, try:
newPosition = Vector3.MoveTowards(newPosition, positions[counter], distanceRemainder*Time.deltaTime);
Or simply declare speed variable and do:
newPosition = Vector3.MoveTowards(newPosition, positions[counter], speed*Time.deltaTime);
I assume what you want is your object moving with a constant speed to the first position. Once it reaches it, move to the next one.
I would do it in a simple Coroutine which is better to understand and maintain than doing stuff in Update:
private void Start()
{
// todo assign positions
StartCoroutine(RunPositions());
}
private IEnumerator RunPositions()
{
foreach(var position in positions)
{
while(!Mathf.Approximately(Vector3.Distance(transform.position, position), 0))
{
var maxDistance = speed_* Time.deltaTime;
transform.position = Vector3.MoveTowards(transform.position, positions[counter], maxDistance);
// render and continue in the next frame
yield return null;
}
}
}
If you are fine with a precision of 0.00001 you can also simply use
while(transform.position != position)
instead.

Remove delay between lerps

I made a simple script that goes to one waypoint and then to the next.
My problem is that it seems to be a delay while going from waypoint1 to waypoint2 and i don't know why:
¿Why is that delay happening and how can i remove it?
using UnityEngine;
using System.Collections;
public class Missile : MonoBehaviour
{
public Vector3 finalTarget;
public Transform forwardObject;
public GameObject impactAreaPrefab;
float smoothingDelay = 0.1f;
bool fired = false;
bool powerPhase = true;
Vector3 currentTarget;
private void OnEnable() {
fire(new Vector3(-25.29f, 0.5f, -10.638f));
}
void fire(Vector3 coords) {
currentTarget = forwardObject.position;
finalTarget = coords;
Instantiate(impactAreaPrefab, finalTarget, Quaternion.identity);
fired = true;
}
void Update() {
if (!fired) {
return;
}
if (powerPhase && transform.position == currentTarget) {
powerPhase = false;
currentTarget = finalTarget;
smoothingDelay = 0.05f;
}
transform.position = Vector3.Lerp(transform.position, currentTarget, Time.deltaTime / smoothingDelay);
transform.rotation = Quaternion.Lerp(transform.rotation, Quaternion.LookRotation(Vector3.RotateTowards(transform.forward, currentTarget, 1, 0.0f)), Time.deltaTime / smoothingDelay);
}
}
That's happening, because you're using lerp not exactly properly. If you want to get linear movement you should cache your first argument (position/rotation on beginning) and provide increasing third parameter. This delay is happening because if your bullet is very close to final position and it's still trying to get there, but your current distance |finalPos - transform.position| is so small that your step Time.deltaTime/smoothingDelay is almost not moving it.
Vector3 startPos;
Vector3 finalPos;
float currentT = 0.0f;
void Update()
{
currentT += Time.deltaTime;
transform.position = Vector3.Lerp(startPos, finalPos, currentT);
}
Checking if Vector3 == Vector3 is also not a good idea. Use pattern from above and check if currentT is larger or equal to 1. If it's true then you're on final position. You get also some control over movement duration by dividing currentT.
So First thing read these post to get better understanding of Lerp function-
https://chicounity3d.wordpress.com/2014/05/23/how-to-lerp-like-a-pro/
http://www.kinematicsoup.com/news/2016/8/9/rrypp5tkubynjwxhxjzd42s3o034o8
You should have a better understanding of lerp now.
In summary lerp does a really simple thing. Say u have two values X and Y. For this example let us give them some value, X = 0, Y = 1, Now you want to get a value some percent between them, like u want to get a value which is 50% from X and Y. You can guess the answer is 0.5. The lerp equation for this would be
Mathf.Lerp(0, 1, 0.5f);
So simply- given two values, x and y, Mathf.Lerp returns a value that is t percent between them.
Now to properly use Lerp you need to cache the position before starting the lerp. Most times I use a coroutine to get this effect works pretty well and then u can use animation curve to change the third parameter to create some crazy good effects. For example on using a animation curve just comment i will write it.
For this problem of yours you have two options-
1) Lerp like a pro using Animation curve to manipulate the speed. Remember u can create animation curves in runtime too.
IENumerator Move(Transform toMove, Vector3 end, float duration){
Vector3 startPos = toMove.position;
float elapsed = 0f;
while(elapsed < duration){
elapsed += Time.deltaTime;
toMove.position = Vector3.Lerp(startPos, end, elapsed / duration);//manipulate the last parameter to move the object linearly
yield return null;//basically wait for next frame
}
toMove.position = end;//after lerp ends
}
Now you can instead of duration use speed and then with it you calculate the time required and change the speed to make it faster
float distance = Vector3.Distance(startPos, end);
toMove.position = Vector3.Lerp(startPos, end, elapsed / (distance/(speed * multiplier)));
2) Use Vector3.MoveTowards - This function moves a point to a end point with a given maximum step, requires three paramters, (currentPosition, end, step), u can multiply step with variable to control the speed, both work really good.
Using this is much easier in most cases
Example-
float step = speed * Time.deltaTime;//to make it framerate independent
transform.position = Vector3.MoveTowards(transform.position, end, step * multiplier);
Hope this helps. I am sorry I was unable to format my answer properly, hopefully will get better at answering. Any edits to improve the answer are welcomed :)
I recommend using iTween for smooth movement.
I modified iTween at some point for me to be able to do anything I want. like this:
public static void Rotate (Transform transform, Vector3 target, float transitionTime, Action onEnd = null, bool ignoreTimescale = false, iTween.EaseType ease = iTween.EaseType.easeInOutQuad, float delay = 0)
{
Vector3 from, to;
from = transform.localEulerAngles;
to = target;
Action <object> onUpdateAction = (rotation =>
{
transform.localEulerAngles = (Vector3) rotation;
});
Action <object> onCompleteAction = (data =>
{
if (onEnd != null)
onEnd ();
});
Hashtable hash = new Hashtable ();
hash.Add ("from", from);
hash.Add ("to", to);
hash.Add ("time", transitionTime);
hash.Add ("delay", delay);
hash.Add ("easetype", iTween.EaseType.easeInOutQuad);
hash.Add ("ignoretimescale", ignoreTimescale);
hash.Add ("onupdate", onUpdateAction);
hash.Add ("oncomplete", onCompleteAction);
iTween.ValueTo (transform.gameObject, hash);
}
That gives me full control in a variety of scenarios.
Here is the code if you want to implement it.
https://drive.google.com/open?id=1nLEEYTp-q4Kfh2n3nWQJcMXmPNtVPLLP

Most correct way to move an object straight to hit a specific point of a huge Gameobject

The code could look something like this:
void Update () {
if(Vector3.Distance(transform.position, specificPositionOfHugeGameobject) < dist)
{
transform.position = Vector3.MoveTowards(transform.position,
specificPositionOfHugeGameobject, Time.deltaTime * speed);
}
}
Realise I do NOT want to move the object to hugeGameobject.position, but a specific point of that huge game object.
Best solution I have thought is creating an empty game object which is child of the huge game object. Then, move the object towards that empty child game object.
The solution you propose is the common way to do it in Unity.
As an alternative to go to a point, you could either hardcode it or let the user enter a Vector3 in a public field.
public Vector3 targetPosition;
void Update () {
if(Vector3.Distance(transform.position, specificPositionOfHugeGameobject) < dist)
{
transform.position = Vector3.MoveTowards(transform.position,
targetPosition, Time.deltaTime * speed);
}
}
This of course means that the target position is fixed. Since your huge game object is moving, the target point must be moving relatively to this. You could code yourself the computation of the targetPosition at each frame using the HugeGameObject's information (position and rotation). However this is exactly what Unity does for you when you parent objects: It computes the position of children elements regarding the parent.
So making use of Unity's hierarchy features is acctually the most correct way to do this.
Another simple way is add a simple Vector3 offset depend on Huge position:
public Vector3 offset;
void Update ()
{
if(Vector3.Distance(transform.position, specificPositionOfHugeGameobject) < dist)
{
transform.position = Vector3.MoveTowards(transform.position, hugePosition + offset, Time.deltaTime * speed);
}
}
Now play with offset in inspector.

IsGrounded in CharacterController not stable

I have following simple scene: scaled box at (0, 0, 0) for floor and 1 size box at (0, 2, 0). I try to do simple gravity and use following code:
private float gravity = 1.0f;
private Vector3 moveVector;
void Update()
{
if (characterController.IsGrounded)
{
Debug.Log("is grounded");
verticalVelocity = 0;
}
else
{
Debug.Log("not grounded");
verticalVelocity -= gravity;
}
Debug.Log("vertical velocity:" + verticalVelocity);
moveVector.x = 0;
moveVector.y = verticalVelocity;
moveVector.z = 0;
characterController.Move(moveVector * Time.deltaTime);
}
I see in log that event when object is on the floor I have grounded/not grounded messages. Visually object is on the floor and not oscillating.
Why I don't have constant "is grounded" when object is on the floor?
Maybe it is how CharacterController works but I can't find anything about it in documentation.
You have to set Character Controller Min Move Distance to '0' (zero) or else you will always get this behavior, it is trying to limit how many times it checks if it's grounded, but at the end of the day gamers don't care, they will mash the jump button and call it buggy
Had the same issue
Just had some gravity when the character controller is grounded
Replace verticalVelocity = 0;
By
verticalVelocity = -gravity * Time.deltaTime;
Setting "Min Move Distance" to 0 in the Character Controller Inspector solved a very similar problem for me.
Strubble is right. You have to add some small gravity even if character is grounded.
if (characterController.IsGrounded)
{
// Press the character down to the floor to avoid jitter "true-false"
// of the isGrounded property.
// To do it, add some small gravity (or velocity in your terms).
verticalVelocity = -gravity * 0.1f;
}
just use a Raycast with Vector3.down because I've tired of fixing it and it's really unreliable to use even while prototyping.