I am trying to move my camera to a clicked Gameobjects position with a specified offset. So the script should calculate the resulting position by the clicked gameobjects position, the actual camera position and a offset.
I have tried it with this code:
Vector3 distanceVector = transform.position - target.transform.position;
Vector3 distanceVectorNormalized = distanceVector.normalized;
targetPosition = (distanceVectorNormalized * preferredDistance);
But I am getting some really weird values. Here is the code I made for this:
public float moveSpeed = 0.1f;
private bool moving = false;
private GameObject target;
// The distance between the camera and the targets position
private float preferredDistance = 3;
// The position the camera will move to
private Vector3 targetPosition;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
if (Input.GetMouseButtonDown(0))
{
var ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out var hit, 100) == false) return;
Debug.Log(hit.transform.gameObject.name);
target = hit.transform.gameObject;
... Here should be the calculations
moving = true;
}
if (moving)
{
transform.position = Vector3.Lerp(transform.position, targetPosition, moveSpeed);
transform.LookAt(target.transform.position);
var offsetX = Math.Abs(transform.position.x - targetPosition.x);
var offsetZ = Math.Abs(transform.position.z - targetPosition.z);
if (offsetX < .01
&& offsetZ < .01) moving = false;
}
}
Your normalized distanceVector acts as the direction of the camera to the object.
This direction should be normalized, which you do, so it can be multiplied by your preferredDistance. It then becomes the offset of the camera from the target.
The part where it goes wrong is that you set this normalized offset as the new camera position, while it should be added to it:
Vector3 distanceVector = transform.position - target.transform.position;
Vector3 distanceVectorNormalized = distanceVector.normalized;
targetPosition = target.transform.position + (distanceVectorNormalized * preferredDistance);
Note the difference in the last line.
Related
So, Hey guys I am new to unity. I have a small doubt. I have a player who is a child of the topcube in a 3 stackcube placed upon eachother.
These cubes have a target position to move once the user clicks on them.
Like imagine there are 3 points in my world. POINT A with location coordinates as(0,0,1),POINT B with (0,0,2),POINT C with (0,0,3) and the 3 stack cube is place on (0,0,0) with the player attached as a child to topcube in that 3stackcube.
All these points(A,B,C) has a script called targetpoint with a variable bool isFilled(default as false) in them which becomes true when one of the cube reaches to its target position.
Further im checking whenever the cubes reaches their target position make isFilled true and check to see if there is a child attached if yes get the animator of the child and trigger jump animation. The jump animation is an inplace animation.
So I want to programmatically move my character +1 towards the direction he is facing (if he is facing z move + 1 in z, if x move +1 in x like this)when the cube he is attached reached its target position while also playing jump animation.
I did a code. it doesnt seem to be working. And sorry for huge paragraphs. Im totally new to coding and asking doubts. Any help will be helpful thanks.
[SerializeField] public List<targetpoint> waypoints;
[SerializeField] float moveSpeed = 2f;
[SerializeField] AudioClip[] surferSounds;
[SerializeField] GameObject particleToPlay;
int waypointIndex = 0;
float t;
//Cached Reference
AudioSource audioSource;
//State
public bool move = false;
void Start()
{
transform.position = this.transform.position;
t = 0f;
}
void FixedUpdate()
{
if (move == true)
{
MoveTarget();
}
}
void MoveTarget()
{
//Time.timeScale = 0.1f;
if (waypointIndex <= waypoints.Count - 1)
{
var targetPosition = waypoints[waypointIndex].transform.position;
transform.position = Vector3.MoveTowards(transform.position, targetPosition, moveSpeed * Time.deltaTime);
if (transform.position == targetPosition)
{
//Debug.Log(t);
if (waypoints[waypointIndex].isFilled == false)
{
waypoints[waypointIndex].isFilled = true;
AudioClip clip = surferSounds[UnityEngine.Random.Range(0, surferSounds.Length)];
var storeToDestroy = Instantiate(particleToPlay, targetPosition, Quaternion.identity);
Destroy(storeToDestroy , 5f);
audioSource.PlayOneShot(clip);
move = false;
}
else if(waypoints[waypointIndex].isFilled == true)
{
waypointIndex++;
targetPosition = waypoints[waypointIndex].transform.position;
transform.position = Vector3.MoveTowards(transform.position, targetPosition, moveSpeed * Time.deltaTime);
}
if (this.gameObject.transform.childCount > 0)
{
var storeChild = gameObject.transform.GetChild(1).gameObject;
StartCoroutine(GravityAndJump(storeChild,storeChild.transform.position+1*transform.forward,1f));
}
else
{
return;
}
}
}
}
IEnumerator GravityAndJump(GameObject child, Vector3 newPosition , float time)
{
var elapsedTime = 0f;
var startingPosition = child.transform.position;
while(elapsedTime < time)
{
child.GetComponent<Animator>().SetTrigger("shouldJump?");
child.transform.position = Vector3.Lerp(startingPosition, newPosition, (elapsedTime / time));
elapsedTime += Time.deltaTime;
yield return null;
}
//storeChild.GetComponent<Animator>().SetFloat("JumpSpeed", 1f);
//yield return new WaitForSeconds(1f);
//gameObject.GetComponentInChildren<Rigidbody>().useGravity = true;
}
}
So I want to programmatically move my character +1 towards the direction he is facing (if he is facing z move + 1 in z, if x move +1 in x like this)
You can get the forward direction for a GameObject using with transform.forward you can use this to calculate target position in front of the GameObject.
Set target position some distance in front of the transform
targetPosition = transform.position + (transform.forward * distance);
Move towards target position at some speed.
transform.position = Vector3.Lerp(transform.position, targetPosition, Time.deltaTime * followSpeed);
Determining arrival to targetPosition
When it comes to determining if transform has arrive to target destination you should measure the distance instead of comparing that the vectors are the same.
So replace this:
if (transform.position == targetPosition){}
With something like this:
if(Vector3.Distance(transform.position, targetPosition) < .001f){
transform.position = targetPosition;
}
Unless you strictly set two vectors to same values it's likely that they will never be considered equal due to how floating point numbers work.
I have a game in a 3D world space. The gameplay is in 2D. So, the player has position frozen on 1 axis and rotation frozen on 2 axes. Position freeze works fine. However, some of the players experience unwanted object rotation during the gameplay even with frozen constraints (I have never encountered such an issue during BETA-test)
Constraints while I test it for myself:
My code (Player movement script)
public class Movement : MonoBehaviour
{
public Rigidbody rb;
public int clickForce = 500;
private Plane plane = new Plane(Vector3.up, Vector3.zero);
public float speed;
public Slider staminaBar;
private void Start()
{
Rigidbody rb = GetComponent<Rigidbody>();
rb.centerOfMass = Vector3.zero;
rb.inertiaTensorRotation = Quaternion.identity;
}
void FixedUpdate()
{
Cursor.visible = false;
Cursor.lockState = CursorLockMode.Confined;
if (Input.GetMouseButton(0))
{
if (staminaBar.value > 0.25)
{
var ray = Camera.main.ScreenPointToRay(Input.mousePosition);
float enter;
if (plane.Raycast(ray, out enter))
{
var hitPoint = ray.GetPoint(enter);
var mouseDir = hitPoint - gameObject.transform.position;
mouseDir = mouseDir.normalized;
rb.AddForce(mouseDir * clickForce);
}
Plane playerPlane = new Plane(Vector3.up, transform.position);
Ray rayy = Camera.main.ScreenPointToRay(Input.mousePosition);
float hitdist = 0.0f;
if (playerPlane.Raycast(rayy, out hitdist))
{
Vector3 targetPoint = rayy.GetPoint(hitdist);
Quaternion targetRotation = Quaternion.LookRotation(targetPoint - transform.position);
transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, speed * Time.deltaTime);
}
}
}
}
}
Thanks in advance!
As #Iggy indicated, assigning to the transform ignores and overrides rigidbody constraints. To fix this, you can restrict the local up axis to be in its current direction using some vector math and the 2nd parameter of Quaternion.LookRotation. Explanation in comments:
if (playerPlane.Raycast(rayy, out hitdist))
{
Vector3 targetPoint = rayy.GetPoint(hitdist);
Vector3 targetDir = targetPoint - transform.position;
// Find direction orthogonal to both targetDir and current local up.
// this will become local right.
Vector3 newLocalRight = Vector3.Cross(transform.up, targetDir);
// If local up and target dir are colinear, do something sensible
// like not changing the rotation
if (newLocalRight == Vector3.zero)
{
/* something sensible */
/* doing nothing may be the sensible choice */
}
else
{
// If they are not colinear, determine the direction perpendicular to local up
// and local right. This will be the fixed forward direction.
Vector3 newLocalForward = Vector3.Cross(newLocalRight, transform.up);
// Assign a rotation using the fixed forward direction, and the current local up
Quaternion targetRotation = Quaternion.LookRotation(newLocalForward,
transform.up);
transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation,
speed * Time.deltaTime);
}
}
I'm having trouble with my Camera script. The Camera rotates around the player by using a pivot that's assigned as a child of the camera. This works with 2 arrows (I'm developing this game for mobiles, so they're touch arrows) that allow the camera to rotate left and right.
The problem is when the Camera goes behind a wall or a huge object and can't see anything. I looked for a solution and I see that many developers used the RaycastHit or something similar.
Here's my code, the goal is to get the Camera to go closer to the pivot when near a wall in order to avoid blocking the Camera view. Can anyone help me?
public Transform target1;
public Transform pivot;
protected ButtonLeft buttonLeft;
protected ButtonRight buttonRight;
public Vector3 offset;
public bool useOffsetValues;
public float rotateSpeed;
private void Start()
{
buttonLeft = FindObjectOfType<ButtonLeft>();
buttonRight = FindObjectOfType<ButtonRight>();
if (!useOffsetValues)
{
offset = target1.position - transform.position;
}
pivot.transform.position = target1.transform.position;
//pivot.transform.parent = target.transform;
//USE IF U WANT TO DISAPPEAR THE CURSOR
//Cursor.lockState = CursorLockMode.Locked;
//pivot.transform.parent = target.transform;
pivot.transform.parent = null;
// usa questa dopo la costruzione del livello1
//pivot.transform.position = target.transform.position;
}
private void Update()
{
pivot.transform.position = target1.transform.position;
if (buttonLeft.Pressed)
{
pivot.Rotate(0, -90 * Time.deltaTime, 0);
Debug.Log("rotate left");
}
if (buttonRight.Pressed)
{
pivot.Rotate(0, 90 * Time.deltaTime, 0);
Debug.Log("rotate left");
}
Ray ray = new Ray(pivot.transform.position, pivot.transform.position - transform.position);
RaycastHit hit;
/*float horizontal = Input.GetAxis("Mouse X") * rotateSpeed;
float horizontal = Input.GetAxis("Mouse X") * rotateSpeed;
pivot.Rotate(0, horizontal, 0);
pivot.Rotate(0, horizontal, 0);
Use this to make the camera rotate on Mouse Y axes*/
/*float vertical = Input.GetAxis("Mouse Y") * rotateSpeed;
target.Rotate(vertical, 0, 0); */
//move camera based on the current rotation of the target and the original offset
float desiredYAngle = pivot.eulerAngles.y;
//Use this float to set the x angle of player
float desiredXAngle = pivot.eulerAngles.x;
//Use this rotation only if you want to rotate on Y
//Quaternion rotation = Quaternion.Euler(0, desiredYAngle, 0);
//Use this if u want to rotate up&down on x axes
Quaternion rotation = Quaternion.Euler(desiredXAngle, desiredYAngle, 0);
transform.position = target1.position - (rotation * offset);
//transform.position = target.position - offset;
transform.LookAt(target1);
}
Good approach is when you do raycast from pivot to camera, and, if some obstacle found, put camera on a ray, a bit closer to pivot, than hit point. Like this: (pseudocode, not tested):
Vector3 origin = pivot.transform.position;
Vector3 direction = transform.position - pivot.transform.position;
float maxCameraDistance = 10;
Ray ray = new Ray(origin, direction);
RaycastHit hit;
if (Physics.Raycast(ray, maxCameraDistance))
{
Vector3 offsetFromObstacle = -direction.normalized * 0.1f;
transform.position = hit.point + offsetFromObstacle;
}
I have a problem with moving from one place to another in Unity over time. I would like my character to move from current position to current + 1 on Y. Unfortunately, looks like it does not get the current position properly or something, since if I debug what I wrote, it says that the magnitude is always 1, so the point is moving with me. Shouldn't it just check the current position and add 1 to Y, move to that position and then check again? I have no idea what's wrong with this code, or if it's strictly connected with how Unity checks positions and things in real time?
public bool moving = false;
private Vector3 dir;
void FrontMovement()
{
Vector3 movement = new Vector3(-Mathf.Sin(transform.eulerAngles.z * Mathf.PI / 180), Mathf.Cos(transform.eulerAngles.z * Mathf.PI / 180), 0f); // always moving front, even after rotation
if (moving == false)
{
dir = movement - transform.position;
moving = true;
return;
}
transform.Translate(dir.normalized * Time.deltaTime);
if(dir.magnitude <= Time.deltaTime)
{
Debug.Log("Finished movement");
moving = false;
}
}
void FixedUpdate()
{
Debug.Log(dir.magnitude);
FrontMovement();
}
I would also like to know how to do rotations over time.
https://docs.unity3d.com/ScriptReference/Vector3.Lerp.html
lerp also works for rotations
// Movement speed in units per second.
public float speed = 1.0F;
// Time when the movement started.
private float startTime;
// Total distance between the markers.
private float journeyLength;
void StartMoving() {
// Keep a note of the time the movement started.
startTime = Time.time;
Vector3 modifiedPosition = transform.position;
transform.position.y += 1.0f;
// Calculate the journey length.
journeyLength = Vector3.Distance(transform.position, modifiedPosition.position);
moving = true;
}
// Move to the target end position.
void Update()
{
if (moving) {
// Distance moved equals elapsed time times speed..
float distCovered = (Time.time - startTime) * speed;
// Fraction of journey completed equals current distance divided by total distance.
float fractionOfJourney = distCovered / journeyLength;
// Set our position as a fraction of the distance between the markers.
transform.position = Vector3.Lerp(startMarker.position, endMarker.position, fractionOfJourney);
if (fractionOfJourney >= 1.0f) {
moving = false;
}
}
}
You could use Coroutine + Vector3.Lerp to move with a specified amount of time:
public IEnumerator MoveToPosition(Transform transform, Vector3 position, float timeToMove)
{
var currentPos = transform.position;
var t = 0f;
while(t <= 1f)
{
t += Time.deltaTime / timeToMove;
transform.position = Vector3.Lerp(currentPos, position, t);
yield return null;
}
transform.position = position;
}
you call the coroutine in Start Method
StartCoroutine(MoveToPosition(transform, newposition, timeToMove))
You could use the same logic for Rotating, with Quaternion.Lerp or Slerp and Quaternion.LookRotation, of course you have lot of sample with rotation over time on WEB!! google is your friend...
public IEnumerator RotateToDirection(Transform transform, Vector3 position, float timeToRotate)
{
var startRotation = transform.rotation;
var direction = position - transform.position;
var finalRotation = Quaternion.LookRotation(direction);
var t = 0f;
while (t <= 1f)
{
t += Time.deltaTime / timeToRotate;
transform.rotation = Quaternion.Lerp(startRotation, finalRotation, t);
yield return null;
}
transform.rotation = finalRotation;
}
I have an object moving forward constantly, I want to make it move on the X axis by mouse dragging any part of the screen (swiping), so I tried this code but when first clicking on the screen the object move to the mouse X position (without dragging)! do you have any suggestions on how to make it move only when dragging?
The Script:
private bool dragging = false;
private float distance;
public Rigidbody Ball;
public float Speed = 100;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void FixedUpdate () {
Ball.velocity = transform.forward * Speed * Time.deltaTime;
}
void Update()
{
if (Input.GetMouseButtonDown(0))
{
distance = Vector3.Distance(transform.position, Camera.main.transform.position);
dragging = true;
}
if(Input.GetMouseButtonUp(0))
{
dragging = false;
}
if (dragging)
{
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
Vector3 rayPoint = ray.GetPoint(distance);
transform.position = new Vector3(rayPoint.x, transform.position.y, transform.position.z);
}
}
For the continues movement you can use GetMouseButton which is true while the button stays pressed (in contrary to GetMouseButtonDown which is true only the first frame the button is pressed)
You said the script is attached to the same as the RigiBody component. In such a case you should never place an object using
transform.position = ...
but instead use RigidBody.MovePosition like
Ball.MovePosition(new Vector3( ... ));
Finally store the initial position on mouse down and than use it as a reference point:
Vector3 initialPosition;
void Update()
{
// called the first time mouse button is pressed
if (Input.GetMouseButtonDown(0))
{
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
initialPosition = transform.position;
Vector3 rayPoint = ray.GetPoint(0);
// Not sure but this might get you a slightly better value for distance
distance = Vector3.Distance(transform.position, rayPoint );
}
// called while button stays pressed
if (Input.GetMouseButton(0))
{
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
Vector3 rayPoint = ray.GetPoint(distance);
Ball.MovePosition(initialPosition + new Vector3(rayPoint.x, 0, 0));
}
}
Also note that I'm not sure that
// Update is called once per frame
void FixedUpdate () {
Ball.velocity = transform.forward * Speed * Time.deltaTime;
}
is what you want to do. The usage of Time.deltaTime makes not much sense here in my eyes. You either want to set the velocity to a certain speed like
Ball.velocity = transform.forward * Speed;
or you want to change a position using Time.deltaTime to have a smooth movement like
Ball.MovePosition(transform.position + transform.forward * Speed * Time.deltaTime);