I'm trying to move my enemy mob back to it's starting location but it's going somewhere no even remotely close to it's starting position. Here is what I have
private Vector3 startingPosition;
void Start ()
{
startingPosition = transform.TransformPoint(Vector3.zero);
}
In the update position I do some checks to make sure when we lose chase range for the mob to run back to it's starting location with this. Well I'm trying to get it to run back to it's starting location
void Return2Location()
{
Debug.Log("Start" + startingPosition);
Debug.Log("Current " + transform.position);
if (transform.position != startingPosition)
{
Debug.Log("returning");
controller.Move(startingPosition);
animation.CrossFade(runClip.name);
}
}
If i set transform.position to startingPosition it's placed back to it's original location.
Update
Tried this also
Transform newLocation = new GameObject().transform;
newLocation.position = startingPosition;
newLocation.rotation = startingRotationPosition;
controller.SimpleMove(newLocation.forward * speed);
The problem there seems to be your use of Move(). Move takes a (scaled) direction as an argument. Not a position it needs to go to. So if you want to move it towards your starting position, you'll have to do something like the following instead:
Vector3 direction = startingPosition - Transform.position;
direction.Normalize(); //Make sure to have a pure "unit" direction
controller.Move(direction * speed * Time.deltaTime);
That should take you in the right direction at your desired speed. Of course you'll have to make sure you don't overshoot your target, but you get the idea.
Related
I've been told that Rigidbody.MoveRotation is the best way in Unity 3D to rotate the player between fixed positions while still detecting hits. However, while I can move smoothly from fixed position to position with:
if (Vector3.Distance(player.position, targetPos) > 0.0455f) //FIXES JITTER
{
var direction = targetPos - rb.transform.position;
rb.MovePosition(transform.position + direction.normalized * playerSpeed * Time.fixedDeltaTime);
}
I can't find out how to rotate smoothly between fixed positions. I can rotate to the angle I want instantly using Rigidbody.MoveRotation(Vector3 target);, but I can't seem to find a way to do the above as a rotation.
Note: Vector3.Distance is the only thing stopping jitter. Has anyone got any ideas?
First of all MoveRotation doesn't take a Vector3 but rather a Quaternion.
Then in general your jitter might come from overshooting - you might be moving further than the distance between your player and target actually is.
You can avoid that bit by using Vector3.MoveTowards which prevents any overshooting of the target position like e.g.
Rigidbody rb;
float playerSpeed;
Vector3 targetPos;
// in general ONLY g through the Rigidbody as soon as dealing wit Physics
// do NOT go through transform at all
var currentPosition = rb.position;
// This moves with linear speed towards the target WITHOUT overshooting
// Note: It is recommended to always use "Time.deltaTime". It is correct also during "FixedUpdate"
var newPosition = Vector3.MoveTowards(currentPosition, targetPos, playerSpeed * Time.deltaTime);
rb.MovePosition(newPosition);
// [optionally]
// Note: Vector3 == Vector3 uses approximation with a precision of 1e-5
if(rb.position == targetPos)
{
Debug.Log("Arrived at target!");
}
Then you can simply apply this same concept also to rotation by going through the equivalent Quaternion.RotateTowards basically just the same approach
Rigidbody rb;
float anglePerSecond;
Quaternion targetRotation;
var currentRotation = rb.rotation;
var newRotation = Quaternion.RotateTowards(currentRotation, targetRotation, anglePerSecond * Time.deltaTime);
rb.MoveRotation(newRotation);
// [optionally]
// tests whether dot product is close to 1
if(rb.rotation == targetRotation)
{
Debug.Log("Arrived at rotation!");
}
You can go one step further and use a tweeting library to tween between rotations.
DOTween
With that you can call it like this:
rigidbody.DoRotate(target, 1f) to rotate to target in 1 second.
Or even add callbacks.
rigidbody.DoRotate(target, 1f).OnComplete(//any method or lambda you want)
If at some point you want to cancel the tween yuou can save it on a variable and then call tween.Kill();
So, you want to animate the rotation value over time until it reaches a certain value.
Inside the Update method, you can use the Lerp method to keep rotating the object to a point, but you will never really reach this point if you use Lerp. It will keep rotating forever (always closer to the point).
You can use the following:
private bool rotating = true;
public void Update()
{
if (rotating)
{
Vector3 to = new Vector3(20, 20, 20);
if (Vector3.Distance(transform.eulerAngles, to) > 0.01f)
{
transform.eulerAngles = Vector3.Lerp(transform.rotation.eulerAngles, to, Time.deltaTime);
}
else
{
transform.eulerAngles = to;
rotating = false;
}
}
}
So, if the distance between the current object angle and the desired angle is greater than 0.01f, it jumps right to the desired position and stop executing the Lerp method.
I know this question is already asked a few times, but didn't find it in the way of my code.
In the movement of my playerObject I consider the direction the Camera is looking to atm.
What I want to do is to slow down the speed of the movement, but it doesn't work with the walkSpeed I use.
I also want to smooth the movement (start and end).
public void UpdateMovement()
{
Vector2 targetDir = new Vector2(Input.GetAxisRaw("Horizontal"), Input.GetAxisRaw("Vertical")); //In targetDir the direction the Player wants to move to is saved -> player presses W means forward
// targetDir = Vector2.ClampMagnitude(targetDir, 1);
Vector3 camForward = cam.forward; //camForward saves the direction, the cam is looking actually
Vector3 camRight = cam.right; //camRight saves the actual right side of the cam
camForward.y = 0.0f; //y=0 because we don't want to fly or go into the ground
camRight.y = 0.0f;
camForward = camForward.normalized;
camRight = camRight.normalized;
transform.position += (camForward * targetDir.y + camRight * targetDir.x) * walkSpeed * Time.deltaTime;
I'm not sure exactly about what the context of what you're trying to do here is, but I think you're looking for Vector2.Lerp().
The way you use it is to first calculate a target position as a Vector. So,
Vector2 target = new Vector2(targetX, targetY)
I'll let you figure out what targetX and targetY are, based on wherever you're trying to move the thing you're moving.
Then you would come up with the speed, which is what you're pretty much already doing. Like so:
float speed = walkSpeed * Time.deltatime;
Finally, instead of setting the position directly, you use the Lerp function instead.
transform.position = Vector2.Lerp(transform.position, target, speed);
I didn't test any of this, and just wrote it on the fly, but I'm pretty sure this is what you're looking for.
I wrote a script you can try to create a cube, I'm sorry because I'm also a unity3d newbie, but I hope it helps you
private Transform player;
Vector3 toposition;//interpolation
void Start(){
player = this.transform;
toposition = new Vector3(0,5,0);
}
// Start is called before the first frame update
void Update(){
if(Input.GetKey(KeyCode.W)){
UpdateMovement();
}
}
public void UpdateMovement(){
player.transform.position = Vector3.Lerp(transform.position, toposition, Time.deltaTime * 5f);
}
In mini game what I'm working on in Unity I want to loop movetowards. I want to move object and back to 1st position but not only 1 time. Now I have this code what give me working "move to postition and back system". How can I change this code to make loop in moving?
public class TestMovement : MonoBehaviour
{
public float speed = 3;
public Vector3 target = Vector3.zero;
private Vector3 origin;
void Start(){
origin = transform.position;
}
void Update(){
TestTransform ();
}
void TestTransform (){
transform.position = Vector3.MoveTowards (transform.position, target, speed * Time.deltaTime);
if (transform.position == target) target = origin;
}
}
TestTransform is getting called from Update, meaning it gets called once for every frame. So, you are essentially already in a loop (an endless loop as long as your game is running). Let's look at your existing code and your desired outcome:
Your existing code starts immediately moving toward the target. Upon reaching the target, it switches the target back to the origin, and starts moving back toward this new target.
Your code is changing the "target" to origin on the first pass. After that, once it has returned to the new "target" (which is now "origin"), it's just sitting there, testing that transform.position == target, and setting target = origin over and over on every frame.
If you want the object to bounce back and forth between origin and target, there are a couple of changes you need to make:
You need a third Vector3 (let's call it "currentTarget").
On start, set currentTarget = target.
In TestTransform, change where you're testing against and changing "target" to operate on "currentTarget" instead. When you change it, you'll need to consider whether currentTarget is set to target or origin, and pick the other as your next target.
End result should be "currentTarget" changes back and forth between "target" and "origin".
Side note: as someone else mentioned in comment, testing for exact equality of vectors won't always work -- you should be checking the distance between vectors and waiting for it to be less than some very small value.
this alow you to have as many positions to move as you want, just create empty GameObjects for each target and add them to list in inspector. Your transform will move in loop, of course if you want only 2 positions add 2 elements to list. (ps. i added everything in your script just to make it easy to understand, i would recomend to make another method CheckNextPosition() with the logic that manages targets)
public class TestMovement : MonoBehaviour
{
public float speed = 3;
public List<Transform> targets= new List<Transform();
public Transform target;
protected int currentTargetIndex=0;
void Start(){
origin = transform.position;
target= targets[currentTargetIndex];
}
void Update(){
TestTransform ();
}
void TestTransform (){
transform.position = Vector3.MoveTowards (transform.position, target.position, speed * Time.deltaTime);
if (transform.position == target.position)//check if you reached the target
{
if(currentTargetIndex >= targets.Count)//check if you reached the last position in your targets list
{
currentTargetIndex=0;//go to first target in your target list
}
else
{
currentTargetIndex++;// go to next target in your target list
}
target=targets[currentTargetIndex];// set the next target
}
}
}
Use Mathf.PingPong to easily do oscillation.
float distanceBetweenPosts = Vector3.Distance(origin, target);
float distanceTowardTarget = Mathf.PingPong(Time.time * speed, distanceBetweenPosts);
float percentageDistance = distanceTowardTarget / distanceBetweenPosts;
Vector3 currentOffset = (target-origin ) * percentageDistance;
Vector3 currentPosition = origin + currentOffset;
transform.position = currentPosition;
If you want to set a specific time startTime for the target starting at origin, then you can use :
float distanceTowardTarget = Mathf.PingPong((Time.time-startTime) * speed,
distanceBetweenPosts);
I have just started working on another project where one of the game mechanics is the ability to relocate a drone to the position of your mouse cursor.
Here is my code below where I have several problems(will specify)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class lightDrone : MonoBehaviour
{
Vector3 newPosition;
void Start()
{
newPosition = transform.position + (0,0,10); //***Problem A***
}
void Update()
{
if (Input.GetMouseButtonDown(0))
{
RaycastHit hit;
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out hit))
{
newPosition = hit.point;
transform.position = newPosition; // ***Problem B***
}
}
}
}
Problem A - I am trying to have the new position = wherever the cursorhas clicked + 10 on the y axis so the "Drone" is seemed to be flying and not in the ground. This isn't achieving anything and is just giving me compiling errors.
I want the exact position to be (cursor.x, (public int (y)), cursor.z) but I have a very vague idea to do it.
Problem B -
Currently when I click my mouse, the object moves to the cursor but it seems to be just teleporting instantly. I want it to move at a certain speed and I think I need a public float to do so and change transform.position to translate.position but this again doesn't work.
Thank you in advance for answering my questions, I am trying to learn these new mechanics and how to code them. :)
Problem A
If you want your new position to be where ever the cursor clicked + 10 on the y axis it makes no sense to put any of your code in Start(). That's for initialization only. It runs just once at the beginning of the scene. Just add the 10 to your newPosition in the Update() method.
Problem B
Setting transform.position to a certain value will make your transform instantly move to that position. If you want to do it over time you need to move your transform a few increments at a time. Since this introduces that "over time" element, you need to use a method that takes place over time, so you need coroutines, asyncs or you can use just the update method if you're a tiny bit crafty.
Also you need the ray to intersect and hit something. Like a flat plane, the ground, terrain, anything with a collider. If it doesn't hit anywhere then you won't have a new place to move to.
Code
using UnityEngine;
public class LightDrone : MonoBehaviour
{
public float speed = 60.0f;
// Our destination needs to be remembered outside a single iteration of
// Update. So we put it outside of the method in order to remember it
// across multiple frames.
private Vector3 currentDestination;
// We need to check if we're at the destination yet so we know when to stop.
private bool notAtDestinationYet;
// When we're closer to the destination than this tolerance, we decide that
// we have arrived there.
private float tolerance = 0.1f;
private void Update()
{
if (Input.GetMouseButtonDown(0))
{
RaycastHit hit;
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out hit))
{
var newPosition = hit.point;
// You can add whatever height you want here. I added
// just 2 instead of 10.
currentDestination = newPosition + new Vector3(0, 2.0f, 0);
notAtDestinationYet = true;
}
}
if (notAtDestinationYet)
{
// Use a bit of vector math to get the direction from our current
// position to the destination. The direction is a normalized Vector
// that we can just multiply with our speed to go in that direction
// at that specific speed!
var heading = currentDestination - transform.position;
var distance = heading.magnitude;
var direction = heading / distance;
// Check if we've arrived at our destination.
if (distance < tolerance)
{
notAtDestinationYet = false;
}
else
{
// If the remaining distance between us and the destination is
// smaller than our speed, then we'll go further than necessary!
// This is called overshoot. So we need to make our speed
// smaller than the remaining distance.
// We also multiply by deltaTime to account for the variable
// amount of time that has passed since the last Update() call.
// Without multiplying with the amount of time that has passed
// our object's speed will increase or decrease when the
// framerate changes! We really don't want that.
float currentSpeed = Mathf.Clamp(speed * Time.deltaTime,
Mathf.Epsilon, distance);
transform.position += direction * currentSpeed;
}
}
}
}
I have a simple waypoint system. It uses a transform of arrays that act as the bucket what holds the waypoint values.
I use this waypoint system to move a camera throughout a scene by moving towards one point to another. The scene is not big - so everything is close to each other.
The camera moves from one position to another by button click/press. This works fine.
void Start()
{
//Sets the Camera to the first point
Camera = GameObject.Find("Main Camera");
Camera.transform.position = patrolPoints[0].position
currentPoint = 0;
}
//Fixed Update seems to work better for LookAt
void FixedUpdate()
{
//Looks at initial Target
Camera.transform.LookAt(TargetPoints[0]);
if (click == true)
{
Camera.transform.position = Vector3.MoveTowards(Camera.transform.position, patrolPoints[currentPoint].position, moveSpeed * Time.deltaTime);
//Camera.transform.rotation = Quaternion.Slerp(Camera.transform.rotation, patrolPoints[currentPoint].transform.rotation, Time.deltaTime);
Camera.transform.LookAt(TargetPoints[currentPoint]);
}
}
public void onNextClick()
{
if (currentPoint >= patrolPoints.Length)
{
currentPoint = 0;
}
if (Camera.transform.position == patrolPoints[currentPoint].position)
{
currentPoint++;
click = true;
}
}
I am having difficulty with one aspect of the transform that I haven't talked about yet. That is the rotation.
I have used lookAt for setting up the target of the first look at point and that works. However when it runs to the next target in the look at array - the change is sudden.
I have tried an Slerp in the shot as well - and this works when the waypoint has been placed in the appropriate rotation value - and the value Slerps from one position to the next. However, the timing isn't quite aligning up yet. Some position transitions get there quicker - meaning the rotation is trying to get caught up / while others are lagging behind.
I have tried getting the distance between the current waypoint and the next waypoint and treating that as an overall percentage in the update relative to the current position of the camera - I believe this could help work out how far the rotation should be orientated given the position update.
However, I am somewhat lost on it. Many examples suggest looking at iTween - and I wouldn't imagine this would work - however, I want to get into the math a bit.
Can anyone put me in the right direction?
Looks like the Lerp for Position and a Slerp for Rotation done the trick.
MoveTowards wasn't playing ball with a Slerp on rotation - so I believe the timings weren't aligning.
if (click == true)
{
Camera.transform.position = Vector3.MoveTowards(Camera.transform.position, patrolPoints[currentPoint].position, moveSpeed * Time.deltaTime);
Camera.transform.rotation = Quaternion.Lerp(Camera.transform.rotation, patrolPoints[currentPoint].rotation, moveSpeed * Time.deltaTime);
I've been led to believe the lerp values work like a percentage of such - so the same input value works for it.
Finally I used a range on the distance between current position and update on the click - this helped speed up the button click.
if (Vector3.Distance(Camera.transform.position, patrolPoints[currentPoint].position) < PositionThreshold)
{
currentPoint++;
click = true;
}
Thank you for your time.