Unity ScreenToWorldPoint returning behind camera - c#

In unity, I am using ScreenToWorldPoint to move my object with my mouse, but the DrawRay from my transform to the ScreenPoint returns behind my camera for some reason. I've tried testing and finding out the reason but I just have no idea; here is my code:
using UnityEngine;
using System.Collections;
public class ScreenToWorldPointTest : MonoBehaviour {
public GameObject obj;
public Vector3 objDist;
public Vector3 objDistFwd;
public float moveSpeed = .1f;
void Start() {
obj = GameObject.Find("ChessKnightWhite");
}
void Update() {
Vector3 objPos = obj.transform.position;
objDist = objPos - transform.position;
objDistFwd = new Vector3(objDist.x * transform.forward.x, objDist.y * transform.forward.y, objDist.z * transform.forward.z);
Vector3 screenPoint = Camera.main.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, objDistFwd.magnitude)
Vector3 move = Vector3.lerp(screenPoint, transform.position, moveSpeed);
Debug.DrawRay(transform.position, screenPoint, Color.Green);
if(Input.GetMouseButton(0)) {
obj.transform.position = move - new Vector3(0, .5f, 0);
}
}
}
and usually, this works fine; the ray ends at the object (well, not at the object if it's being moved, but if it (the mouse) is still it would be at the object) and the object moves whenever and wherever I want it to, keeping the same distance from me (forwardly, where it's not the same distance but, say, the transform is normal (0, 0, 1), it will stay at 1 on the z-axis). But sometimes (observed at camera/player is at 0, 15, -10 -- with a rotation of 60 on the x -- and the object is at 0, 1, 0) the ray is behind me! and what's even weirder, is instead of going to where the Ray ends, it stays a little bit in front of the camera?! I thought, "maybe the z on ScreenToWorldPoint is negative?", but it cant be, because the x, z, and y are squared and added together to get magnitude. and printing it out even confirms this, but for some reason, it changes when I move the mouse sometimes. I have no idea what's causing this, so any insight would be very helpful (please also, if you can, include an explanation or any information as to why this might happen).

so after a while I realized that Pluto was right, I needed to use dot product instead of the magnitude of (xa * xb, ya * yb, za * zb). Sorry Pluto, thanks for your help and your patience with me

Related

Using MoveRotation in Unity 3D to turn player towards a certain angle

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.

Unity Simple Character Controller Script not working as expected

Im scratching my head over this one, really cant figure it out.
Im trying to implement the player movement from this example:
https://www.youtube.com/watch?v=LNidsMesxSE
Starts at minute 4:25 and ends at minute 5:20
This script tries translating all that to unity.So I started all the way over from scratch and just want to make a simple movement script.
You can plug this into any Unity version, throw the script onto and object with a CharacterController component, add a child object with a mesh that will tilt, the main object will rotate around its Y axis and move.
I do recommend using a simple T pose character or atleast a long capsule so you can better see what is happening than when you would be using a cube to test this.
The weird glitches im having is that the object randomly spasms out even though im always only adding extremely small rotations and movements every frame. And 95% of the time it doesnt happen, so I havent been able to pinpoint exactly what is causing this.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CharacterMove : MonoBehaviour
{
public float speed = 200f;
public float rotationSpeed = 180f;
public float tiltFactor = 8f;
public bool normalizeVelocity = false;
CharacterController cc;
Vector3 velocity;
Transform armatureBody;
void Start()
{
cc = GetComponent<CharacterController>();
armatureBody = transform.GetChild(0);
}
void Update()
{
velocity = new Vector3(Input.GetAxis("Horizontal"), 0,
Input.GetAxis("Vertical"));
}
void FixedUpdate()
{
if ((velocity != Vector3.zero))
{
var ccVelocity = cc.velocity; // CharacterController velocity. This might be the problem, but without it I cannot rotate the object towards the actual forward velocity.
Quaternion toRotation;
toRotation = Quaternion.LookRotation(ccVelocity, Vector3.up);
transform.rotation = Quaternion.RotateTowards(transform.rotation, toRotation, rotationSpeed * Time.deltaTime);
if (Input.GetKey(KeyCode.S)) print("new toRotation: " + toRotation);
armatureBody.localEulerAngles = new Vector3(velocity.z * tiltFactor, 0, -velocity.x * tiltFactor); // Tilt Body towards velocity This causes the weirdest and most consistest twitching bugs when the tilt is backwards so the object is moving backwards, maybe because im using LookRotation to look backwards?
}
velocity = Quaternion.Euler(0, transform.eulerAngles.y, 0) * velocity; // forward is always forward
cc.SimpleMove(velocity * Time.deltaTime * speed);
}
}
This script does 4 things,
get the velocity from player input.
rotate the main Object(this script) towards the velocity
add tilt towards the velocity, to the child armatureBody, this is a t Pose character in my case.
move the main Object this script is on.
Any help would be appreciated.
I tried removing the vector3 != Zero check, I tried normalizing the vectors, and I tried using a different Quaternion rotation method, but all with the same faulty results.
Check in Update() if Input.forward > threshold if so rotate to velocity. Dont rotate to velocity when only going sideways or backwards, so we can now strafe sideways and walk backwards when standing still or run and rotate to our velocity with the AD keys when running.
void Update()
{
velocity = new Vector3(Input.GetAxis("Horizontal"), 0,
Input.GetAxis("Vertical"));
if (velocity.z > 0.2f)
{
forwardVelocityThreshold = true;
}
else forwardVelocityThreshold = false;
}
Then in our FixedUpdate() we check for that bool and apply the rotateToVelocity if true.
Adjust the following if statement:
if (ccVelocity != Vector3.zero && forwardVelocityThreshold)
{
toRotation = Quaternion.LookRotation(ccVelocity, Vector3.up);
transform.rotation = Quaternion.RotateTowards(transform.rotation, toRotation, rotationSpeed * Time.fixedDeltaTime);
}
Now it works as intended, I also changed the deltaTime to FixedDeltaTime as suggested by Voidsay in the comments, although I have heard conflicting things about this. Some say FixedUpdate always uses fixedDeltaTime even if you are using deltaTime, other say no, always use fixedDeltaTime in FixedUpdate.

2D collision detection when using transform.position. How to enable?

I just started with unity and followed thair 2D UFO example project.
In an effort to extend it, I came up with a quirky way of contolling my Player.
It always moves in circular paths and once I click a button, the circle's direction changes and the imaginary circle center is tanslated as shown in the picture below. That allows you to move in a figure 8 or S shape pattern and is quite fun.
However, once I figured out how to do this motion, the player object did not have any collision detection anymore.
In the original example the whole movemet handling is done within FixedUpdate(). I, however, use Update() since the former does not seem to work at all with my code (i.e. no movement at all).
This is my code for the movement so far:
public class ControlledRotation : MonoBehaviour
{
public float radius = 3f;
public float speed = 3f;
private float timeCounter = 0;
private float direction = 1f;
private Vector3 offset;
private Rigidbody2D rb2d;
void Start()
{
offset = transform.position;
}
void Update()
{
if (Input.GetKeyDown(KeyCode.RightArrow))
{
//change the offset from the circle center and the rotation direction on keypress
offset += new Vector3(Mathf.Cos(timeCounter), Mathf.Sin(timeCounter), 0) * radius * direction * 2;
direction *= -1;
}
timeCounter += Time.deltaTime * direction * speed;
transform.position = new Vector3(Mathf.Cos(timeCounter), Mathf.Sin(timeCounter)) * radius * direction + offset;
}
}
The Plyer object has a Rigidbody 2D and a Circle Collider 2D. The walles it should collide with have Box Collider 2D. Yet, the UFO can simply pass the walls.
I assume a probable cause in the fact that I simply change transform.position or since im using Update/FixedUpdate wrong.
If you happen to have any advice on how I can keep my chosen movement control mechanism and still be able to collide with objects, I'd highly appreciate it :)
Edit:
I feel I need to go with using the rigidbody and applying some force... but I haven't figured out how to reproduce this movement with forces and also forces seem to not be super crisp in response
When you need to move an object that has a Rigidbody, you need to move it using forces, you cannot do it with just transform.position, it ignores physics. that's why you cannot detect the collision.. I suggest you move it like that. I have some examples when I had to move a character that needed to interact with physics.
gameObject.GetComponent<Rigidbody>().velocity = Vector3.zero;
And this one is for moving it in certain directions:
//Keys for controlling the player (move and shoot) only when he's alive
if (Input.GetKey(KeyCode.UpArrow) && alive)
{
GetComponent<Rigidbody>().MovePosition(transform.position + Vector3.forward * Time.deltaTime * 4);
}
if (Input.GetKey(KeyCode.DownArrow) && alive)
{
GetComponent<Rigidbody>().MovePosition(transform.position + Vector3.back * Time.deltaTime * 4);
}
I hope it helps.. greetings :)

Unity Move rotating object

I have a ball which rotates around the point 0,0,0 in the Z-axis. When the space button is pressed, the ball has to go inside the large circle. Now my code looks like this. When you press space, the ball does not behave as they should. I want to know how to make a balloon down exactly down
that's how the ball should behave ->
behavior image
my code:
void Update () {
if (Input.GetKeyDown (KeyCode.Space)) {
transform.position = new Vector3 (transform.position.x - 1, transform.position.y - 1, 0);
} else {
transform.RotateAround(new Vector3(0,0,0), new Vector3(0,0,1), 2);
}
}
Your code to 'jump' the orbit doesn't do what you want because Transform.RotateAround modifies both the rotation and the position of the object's transform.
So jumping to (position - 1,1,0) in the world is going to return wildly different results every time.
What you want to do instead is calculate the (Vector) direction from the object to the centre of orbit (the difference), then scale that down to how far you want it to move, then apply it to the position.
private Vector3 _orbitPos = Vector3.zero;
private float _orbitAngle = 2f;
private float _distanceToJump = 2f;
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
var difference = (_orbitPos - transform.position).normalized * _distanceToJump;
transform.Translate(difference);
}
transform.RotateAround(_orbitPos, Vector3.forward, _orbitAngle);
}
This will move the object to be orbiting 2 units closer when space is pressed immediately.
If you wanted to have a smooth transition instead of a jump, look into using Mathf.Lerp, Vector3.Lerp and the routines involved.

Moving perpendicular to an object

im trying to move between a path made from different objects. I apply a constant speed with Translate() and Rotating with the perpendicular vector from the object on the right using Raycasting
Although it turns , is it does not rotate fast enough to fully turn and moves out of the path.
Any ideas how to fix this? Or some other way to implement it?
any help will be appreciated
image to help visualize:
Raycast and Rotation image
void Update()
{
RaycastHit hit;
if (!Physics.Raycast(transform.position, Vector3.right, out hit))
return;
MeshCollider meshCollider = hit.collider as MeshCollider;
if (meshCollider == null || meshCollider.sharedMesh == null)
return;
Mesh mesh = meshCollider.sharedMesh;
Vector3[] normals = mesh.normals;
int[] triangles = mesh.triangles;
Vector3 n0 = normals[triangles[hit.triangleIndex * 3 + 0]];
Vector3 n1 = normals[triangles[hit.triangleIndex * 3 + 1]];
Vector3 n2 = normals[triangles[hit.triangleIndex * 3 + 2]];
Vector3 baryCenter = hit.barycentricCoordinate;
Vector3 interpolatedNormal = n0 * baryCenter.x + n1 * baryCenter.y + n2 * baryCenter.z;
interpolatedNormal = interpolatedNormal.normalized;
Transform hitTransform = hit.collider.transform;
interpolatedNormal = hitTransform.TransformDirection(interpolatedNormal);
Vector3 targetDir = Vector3.Cross(interpolatedNormal, Vector3.up); // Get the perpendicular vector
Vector3 newDir = Vector3.RotateTowards(transform.forward, targetDir, 20f, 0f);
transform.rotation = Quaternion.LookRotation(newDir); // Rotate Object
transform.Translate(0,0,0.2f); // Constant Speed
Debug.DrawRay(transform.position, perp,Color.red);
}
I don't think this is a good method, but it works for me. Maybe this can help you.
public float fixedDist = 2.0f;
void WallDetect() {
RaycastHit hit;
if (!Physics.Raycast(transform.position, transform.TransformPoint(Vector3.right) - transform.position, out hit))
return;
Vector3 perp = Vector3.Cross(hit.normal, Vector3.up);
Vector3 targetDir = Vector3.Project(transform.forward, perp).normalized;
Vector3 currentDir = transform.TransformPoint (Vector3.forward) - transform.position;
RaycastHit hit2;
if (Physics.Raycast (transform.position, -hit.normal, out hit2)) {
Vector3 fixedPos = hit2.point + hit.normal * fixedDist;
Vector3 predictPos = fixedPos + targetDir;
transform.position = Vector3.MoveTowards (transform.position, predictPos, 0.01f);
transform.rotation = Quaternion.Lerp(transform.rotation, Quaternion.LookRotation (predictPos - transform.position), 0.05f);
}
}
First thing: you seem not to be using data from HitInfo struct - and believe it or not it already contains a .normal Vector3 member, calculated during the raycast (or lazily calculated when requested, I am not sure but it makes no difference), it's best to use it rather than roll your own, simpler and less error prone (your manual finding of the normal looks correct neverless, I haven't tried it though)
Second thing: your last line has a Quaternion.Lerp with t=0.05 which means for each new rotation, you are still taking 95% of the original rotation, which is pretty darn slow rotation indeed. Try something in the range of Time.deltaTime (which is rougly an equivalent of getting close within a second)
Third thing: for rotation its better to use Slerp rather than Lerp, unless you are realt concerned about performance, which doesn't seem to be an issue considering the rest of the code.
Fourt thing: instead of hardcoding linear and rotation speed, try to use multiplies of Time.deltaTime, this way they won't be framerate dependend (as they currently are)
Fifth thing: I have a feeling you shouldn't be setting your target rotation based on normal at current position. The way you are doing it now your rotation lags behind - you should be raycasting from a position one step in the future from your current position, so you know what rotation to take so it is correct by the time you make that step. Currently you set target a future rotation to a rotation correct now, which will lag a frame. Alternatively you could just move the translate step to the top of the loop, the transform will update and the rest should flow as it does.
Finally, your image link doesn't work.
I hope that helps at all

Categories

Resources