I'm using this code at the moment to rotate a tank turret object towards the mouse position:
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
Physics.Raycast(ray, out hit, range);
Vector3 dir = hit.point - transform.position;
Quaternion lookRotation = Quaternion.LookRotation(dir);
Vector3 rotation = lookRotation.eulerAngles;
transform.rotation = Quaternion.Euler(0f, rotation.y, 0f);
Is there any way to create a rotation even when the ray doesn't hit any object, (pointing the mouse where there is no object initiated) (hit is false)? Also is there a better approach for a better solution?
Thanks.
Basically, I just get the position of the mouse on screen, compare it to the target object, convert that to degrees, and rotate the object around the wanted axis (Y for topDown 3D space, Z for 2D).
void RotateTowards()
{
var mouse = Input.mousePosition;
var screenPoint = Camera.main.WorldToScreenPoint(tankTransform.localPosition);
var offset = new Vector2(mouse.x - screenPoint.x, mouse.y - screenPoint.y);
var angle = Mathf.Atan2(offset.y, offset.x) * Mathf.Rad2Deg;
transform.rotation = Quaternion.Euler(0, angle, 0);
}
Related
I made a circle and attach a lazer box on top of it.
The lazer will fire a raycast to its upper y axis (straight up). I also add a line renderer to view it.
I want the raycast to rotate 90 degrees back and forth. Sort of like its scanning everything on top. My problem is that its not working properly. It does rotate back and forth but If I move the x position of the lazer object, the raycast will rotate in a weird angle.
Script for lazer object
public LineRenderer lineRenderer;
public LayerMask layerMask;
public float laserSpeed;
Vector3 pointA;
Vector3 pointB;
Vector3 castPosition;
RaycastHit2D rayCast;
float time;
void Start()
{
pointA = transform.eulerAngles + new Vector3(0f, 0f, 90f);
pointB = transform.eulerAngles + new Vector3(0f, 0f, -90f);
}
void Update()
{
time = Mathf.PingPong(Time.time * laserSpeed, 1);
transform.eulerAngles = Vector3.Lerp(pointA, pointB, time);
castPosition = new Vector3(transform.position.x, transform.position.y, transform.position.z);
rayCast = Physics2D.Raycast(castPosition, transform.TransformDirection(Vector2.up), 10f, layerMask);
lineRenderer.SetPosition(0, castPosition);
lineRenderer.SetPosition(1, transform.TransformDirection(Vector2.up) * 10f);
}
Using eulerAngles for continuous animations is quite "dangerous". Unity stores the rotations as Quaternion and there are multiple ways of how to represent these in euler space!
When you read the .eulerAngles property, Unity converts the Quaternion's internal representation of the rotation to Euler angles. Because, there is more than one way to represent any given rotation using Euler angles, the values you read back out may be quite different from the values you assigned. This can cause confusion if you are trying to gradually increment the values to produce animation.
To avoid these kinds of problems, the recommended way to work with rotations is to avoid relying on consistent results when reading .eulerAngles particularly when attempting to gradually increment a rotation to produce animation. For better ways to achieve this, see the Quaternion * operator.
so you should rather go for Quaternion and do e.g.
And then you are using transform.TransformDirection(Vector2.up) which is a direction and pass it to your line renderer as a position.
What you want there is rather the position combined from
transform.position + transform.up
So together it should probably rather be
public LineRenderer lineRenderer;
public LayerMask layerMask;
public float laserSpeed;
private Quaternion originalRotation;
private Quaternion minRotation;
private Quaternion maxRotation;
void Start()
{
originalRotation = transform.rotation;
minRotation = originalRotation * Quaternion.Euler(0, 0, -90);
maxRotation = originalRotation * Quaternion.Euler(0, 0, 90);
}
void Update()
{
// Note that Vector3 is a "struct" -> there is no need to manually use "new Vector3(transform.position.x, ...)"
var startPosition = transform.position;
lineRenderer.SetPosition(0, startPosition);
var factor = Mathf.PingPong(Time.time * laserSpeed, 1);
// instead of the eulers rather use Quaternion
transform.rotation = Quaternion.Lerp(minRotation, maxRotation, factor);
// "transform.up" basically equals using "transform.TransformDirection(Vector3.up)"
var rayCast = Physics2D.Raycast(startPosition, transform.up, 10f, layerMask);
if(rayCast.collider)
{
// when you hit something actually use this hit position as the end point for the line
lineRenderer.SetPosition(1, rayCast.point);
}
else
{
// otherwise from the start position go 10 units in the up direction of your rotated object
lineRenderer.SetPosition(1, startPosition + transform.up * 10f);
}
}
I'm trying to implement obstacle avoidance within steering behaviors for a university project.
When I run the project, the AI agent seems to get confused and 'spaz' out moving from left to right very quickly and still doesn't avoid obstacles correctly.
I'm trying to cast a box in front of the agent so that when that box collides with an obstacle, it'll steer away from it and continue to move wander.
Here is the code for the Obstacle Avoidance
public Vector3 ObstalceAvoidance()
{
//Cast a ray from the centre of the agent, in it's forward direction
Ray ray = new Ray(transform.position, transform.forward);
//Name a raycastHit
RaycastHit hitInfo;
//Set avoidanceForce to ZERO for all axis
Vector3 avoidanceForce = Vector3.zero;
//Calculate the 'Avoidance Force'
if(Physics.BoxCast(transform.forward, new Vector3(2.5f, 2.5f, 20.0f), transform.forward, out hitInfo, transform.rotation, maxDistance))
{
if(Vector3.Angle(hitInfo.normal, transform.up) > floorAngle)
{
//Reflect the Vector
avoidanceForce = Vector3.Reflect(agent.velocity, hitInfo.normal);
//Calculate the dot product between the Force and Velocity
if (Vector3.Dot(avoidanceForce, agent.velocity) < -0.9f)
{
//Transform Right
avoidanceForce = transform.right;
}
}
}
if(avoidanceForce != Vector3.zero)
{
//Calculate desired velocity
desiredVelocity = (avoidanceForce).normalized * agent.maxSpeed;
//Calculate the steering Force
steeringForce = desiredVelocity - agent.velocity;
oldSteeringForce = steeringForce;
forceTimer = 0;
}
else
{
steeringForce = Vector3.zero;
}
return steeringForce;
}
I've been trying for a while to get a 2D player to work kind of like a bullet that is always moving forward (forward being in this case the local X axis for the GameObject, as that's the way that the character is facing) and only changes direction when you touch a point on the screen, in which case it should smoothly start turning towards that point.
One problem I have is that I can't manage to keep the character moving smoothly at a constant speed in the last direction it was facing before, and the other problem that I'm finding is that the character is turning around the wrong axis and instead of rotating based on the Z axis, it's always rotating on the Y axis, which makes the sprite become invisible to the camera.
Here's the code that I have right now:
Vector3 lastTouchPoint;
private void Start()
{
lastTouchPoint = transform.position;
}
void Update()
{
if (Input.touchCount > 0)
{
// The screen has been touched so store the touch
Touch touch = Input.GetTouch(0);
if (touch.phase == TouchPhase.Stationary || touch.phase == TouchPhase.Moved)
{
// If the finger is on the screen, move the object smoothly to the touch position
lastTouchPoint = Camera.main.ScreenToWorldPoint(new Vector3(touch.position.x, touch.position.y, 10));
}
}
transform.position = Vector3.Lerp(transform.position, lastTouchPoint, Time.deltaTime);
//Rotate towards point
Vector3 targetDir = lastTouchPoint - transform.position;
transform.LookAt(lastTouchPoint);
}
Thanks in advance!
keep the character moving smoothly at a constant speed
You probably didn't understand what Lerp actually is: This interpolates between the two positions on the given factor where 0 means fully the first position, 1 means fully the last position and e.g. 0.5f would mean in the center between both positions.
This results in faster speeds if the positions are further apart and becomes slower and slower the smaller the distance between both positions becomes. In some cases especially with a factor that small as in your case the object might even never actually reach the target position.
Using this with a dynamic factor of Time.deltaTime makes no sense as this value changes every frame and jitters somewhere around 0,017 (assumin 60 FPS).
You could rather use Vector3.MoveTowards with a fixed constant speed
// set via the Inspector
public float speedInUnitsPerSecond = 1;
...
transform.position = Vector3.MoveTowards(transform.position, lastTouchPoint, Time.deltaTime * speedInUnitsPerSecond);
if you want to keep moving but stop once the touched position is reached.
If you rather wanted to continue moving in the according direction no matter what you could rather store the direction instead of a position and use a straight forward Transform.Translate
// set via the Inspector
public float speedInUnitsPerSecond = 1;
private Vector2 lastDirection;
privtae void Update()
{
...
// If the finger is on the screen, move the object smoothly to the touch position
var touchPosition = Camera.main.ScreenToWorldPoint(new Vector3(touch.position.x, touch.position.y, 10));
lastDirection = (touchPosition - transform.position).normalized;
...
// move with constant speed in the last direction
transform.Translate(lastDirection * Time.deltaTime * speedInUnitsPerSecond);
...
}
the character is turning around the wrong axis and instead of rotating based on the Z axis, it's always rotating on the Y axis
Note that Transform.LookAt has an optional second parameterworldUp which by default is Vector3.up so a rotation around the global Y axis!
Since you rather want a rotation around the Z axis you should pass
transform.LookAt(lastTouchPoint, Vector3.forward);
I don't know your setup ofcourse but also note that
LookAt
Rotates the transform so the forward vector points at worldPosition.
As you describe it it is also possible that you don't want the objects forward vector to point towards the target position but actually rather the objects right (X) vector!
You can do this by rather simply directly setting the transform.right like e.g.
transform.right = (lastTouchPoint - transform.position).normalized;
or
transform.right = lastDirection;
Btw it would actually be enough to set this rotation only once, namely the moment it changes so in
if (touch.phase == TouchPhase.Stationary || touch.phase == TouchPhase.Moved)
{
// If the finger is on the screen, move the object smoothly to the touch position
lastTouchPoint = Camera.main.ScreenToWorldPoint(new Vector3(touch.position.x, touch.position.y, 10));
transform.right = (lastTouchPoint - transform.position).normalized;
}
or
if (touch.phase == TouchPhase.Stationary || touch.phase == TouchPhase.Moved)
{
// If the finger is on the screen, move the object smoothly to the touch position
var touchPosition = Camera.main.ScreenToWorldPoint(new Vector3(touch.position.x, touch.position.y, 10));
lastDirection = (touchPosition - transform.position).normalized;
transform.right = lastDirection;
}
I ended up finding the answer to my own problem using code to rotate smoothly from another post. Here's the code:
Vector3 lastTouchPoint;
Vector3 direction;
Vector3 vectorToTarget;
//Character controller variables
public float moveSpeed = 5f;
public float angularSpeed = 3f;
private void Start()
{
lastTouchPoint = transform.position;
}
void Update()
{
if (Input.touchCount > 0)
{
// The screen has been touched so store the touch
Touch touch = Input.GetTouch(0);
if (touch.phase == TouchPhase.Began)
{
// If the finger is on the screen, move the object smoothly to the touch position
lastTouchPoint = Camera.main.ScreenToWorldPoint(new Vector3(touch.position.x, touch.position.y, 10));
direction = lastTouchPoint - transform.position;
vectorToTarget = lastTouchPoint - transform.position;
}
}
transform.position += direction.normalized * moveSpeed * Time.deltaTime;
float angle = Mathf.Atan2(vectorToTarget.y, vectorToTarget.x) * Mathf.Rad2Deg;
Quaternion q = Quaternion.AngleAxis(angle, Vector3.forward);
transform.rotation = Quaternion.Slerp(transform.rotation, q, Time.deltaTime * angularSpeed);
}
I have a little problem with this code:
private void Update()
{
if (Input.GetMouseButtonDown(0) && canClick)
{
Vector3 mouseWorldPosition = Camera.main.ScreenToWorldPoint(Input.mousePosition);
mouseWorldPosition.z = player.transform.position.z;
directionVector = player.transform.position - mouseWorldPosition;
player.GetComponent<Rigidbody2D>().AddForce(directionVector.normalized * speed);
Quaternion InstanceRotation = Quaternion.Euler(mouseWorldPosition.x - 90f, 0f, 0f);
GameObject effect = Instantiate(SteamFx, mouseWorldPosition, InstanceRotation);
effect.transform.LookAt(directionVector);
}
}
The problem is that when instantiating the object, it has a z rotation. So I have an effect that when I click on the screen, it should instantiate, and blow in the player direction. It has different rotation, how to make the rotation correct.
Thks!!!
void OnMouseDrag() {
float distance = transform.position.z - Camera.main.transform.position.z;
Vector3 pos = Input.mousePosition;
pos.z = distance;
Vector3 mousePosition = new Vector3(pos.x, pos.y, pos.z);
Vector3 objPosition = Camera.main.ScreenToWorldPoint(mousePosition);
transform.position = objPosition;
}
This is the code snippet help me to move the object on mouse drag. It is moving object on mouse drag in x axis while z axis movement is not working correctly using mouse. I basically want to move the object on x and z-Axis using mouse Input.
What is wrong how can i get z position from the mouse input in order to move the object on z axis correctly.
When you casting ray to your object it is being calculated multiple times so it returns your ball position when its not being move a real deal you can try something like this
void OnMouseDrag() {
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
Debug.DrawRay(ray.origin, ray.direction * 10, Color.yellow);
Debug.Log(ray);
RaycastHit hit;
if (Physics.Raycast(ray, out hit, Camera.main.farClipPlane))
{
if (hit.transform.gameObject.name == "CameraElasticPoint")
{
return;
}
else{
transform.position = new Vector3(hit.point.x,hit.transform.position.y+1, hit.point.z);
hitPoint = Input.mousePosition;
}
}
}
what it will do it will ignore your objects and works only on other hit info which would be your floor or any other surface you are trying to drag your object along and it will drag your object on X and Z axis taking the Y position of the Floor so it will always remain on top of the floor or any other surface collider on
let me know if it works
Good day
I would recommend going through the Unity Tutorial here:
http://unity3d.com/learn/tutorials/projects/survival-shooter-project
It might give you some ideas on how to solve the problem of x/z plane movement with the mouse.
No. screenpointtoray is very unnecessary here, you just need to say that pos.z is equal to pos.x....
like so...
Vector3 pos = Camera.main.ScreenToViewportPoint(Input.mousePosition - dragOrigin);
pos.z = pos.x;
Vector3 move = new Vector3(0, pos.y * dragSpeed, pos.z * -dragSpeed);
always a pleasure x