I am trying to make an enemy in my game that can see the light coming from the flashlight of the player. I am doing this by raycasting from the origin of the flashlight towards 5 directions: the middle of the light beam, and the top, bottom, left and right end of the light. Something like this:
I use the following update function in the flashlight of the player:
void Update () {
float angle = GetComponent<Light>().spotAngle;
if (Physics.Raycast (transform.position, transform.forward, out hitMiddle)){
hitMiddlePosition = hitMiddle.transform.position;
}
if (Physics.Raycast (transform.position, Quaternion.AngleAxis(-angle, transform.up) * transform.forward, out hitDown)){
hitDownPosition = hitDown.transform.position;
}
if (Physics.Raycast (transform.position, Quaternion.AngleAxis(angle,transform.up) * transform.forward, out hitUp)){
hitUpPosition = hitUp.transform.position;
}
if (Physics.Raycast (transform.position, Quaternion.AngleAxis (angle,transform.right) * transform.forward, out hitRight)){
hitRightPosition = hitRight.transform.position;
}
if (Physics.Raycast (transform.position, Quaternion.AngleAxis (-angle, transform.right) * transform.forward, out hitLeft)){
hitLeftPosition = hitLeft.transform.position;
}
}
This code is supposed to track the location of each of the hits. However, the locations never seem to correspond to the locations the light beam is hitting. The locations usually are identical in all directions (Vector3(-87.77855, 0, 87.83269)) and do not change when the player is moving. Looking in different directions sometimes changes one of the values.
I tried a lot of different ways of the direction for the raycasts, but I am still not very confident the rays are being cast in the direction I intended.
Any tips?
Many thanks!
I found the answer, and it was (of course) very trivial.
The problem was that hit.transform.position gave the position of the object that was hit, not the position of the actual intersect location.
Changing this to hit.point resolved the problem. It now works as designed.
Related
How to create collisions for a flying camera in Unity 3d? to prevent the camera from falling into objects on the scene. My camera script snippet:
public class FlyCamera : MonoBehaviour
{
void FixedUpdate()
{
if (!isAlternative)
SetCameraMovement();
}
private void SetCameraMovement()
{
lastMouse = Input.mousePosition - lastMouse;
lastMouse = new Vector3(-lastMouse.y * camSens, lastMouse.x * camSens, 0);
lastMouse = new Vector3(transform.eulerAngles.x + lastMouse.x, transform.eulerAngles.y + lastMouse.y, 0);
if (Input.GetMouseButton(1))
transform.eulerAngles = lastMouse;
lastMouse = Input.mousePosition;
GetBaseInput();
transform.position = Vector3.Lerp(transform.position, NewPosition, Time.deltaTime * movementTime);
}
private void GetBaseInput()
{
Speed = Input.GetKey(KeyCode.LeftShift) ? superSpeed : mainSpeed;
if (Input.GetKey(KeyCode.W))
NewPosition += transform.forward * Speed;
if (Input.GetKey(KeyCode.S))
NewPosition += transform.forward * -Speed;
if (Input.GetKey(KeyCode.A))
NewPosition += transform.right * -Speed;
if (Input.GetKey(KeyCode.D))
NewPosition += transform.right * Speed;
}
}
If you want to achieve a RTS-like flying camera, you can set the camera to a parent dolly-like object and keep it looking at that object:
Object (Camera Dolly) -> this is moved by wasd and is rotated to the desired view angle
Child object: Camera -> only the camera's position.y is ever changed
Then, on a custom script on the dolly in Update(), send a LineCast from the dolly to the camera. When the cast hits a collider (important to use e.g. tags here so it does not collide with anything, like e.g. units) in the world, gradually (over time) reduce the camera's position.y and if nothing is hit, gradually increase it to the desired height. It will not entirely prevent the camera from clipping trough bigger objects but move it so it doesn't stay "inside" a collider. Something like that is also typically used in RPG third person cameras.
Please let me know if more clarification is needed!
Edit: I'd do that only for really big view-blocking objects though, not for the terrain or smaller buildings/units - at least if your goal is more of an RTS or sim game.
Edit2: In a solar system setting, it is a bit more complicated. A few options:
Before translating the camera, do a SphereCast (in the desired (final) movement direction with sphere radius of minimum allowed distance to any object) against all celestial object SphereColliders and set your "NewPosition" to the nearest hitPoint minus the desired minimum allowed distance to a celestial object. Also add some margin here, so the camera doesn't get stuck. This would be a continuous collision detection approach.
Everytime after translating the camera , do a CheckSphere (sphere radius is allowed minium distance to any object) against all celestial objects and as long as there is any hitpoint, lerp the camera position out of it over time. This is a "softer" looking approach, but if you fly very fast, it can still clip into something.
You can savely do both in LateUpdate().
Better put you camera movement in the LateUpdate() as adviced in the docs.
With a rigidbody and a collider properly set for the collision detection. Check docs so that you can detect the collisions properly.
Check that " Notes: Collision events are only sent if one of the colliders also has a non-kinematic rigidbody attached", so your camera could be one gameObject with no rigidbody, so that you can fly free and collide.
I case you might need physics behaviour for yout camera and you want it also to behave as a rigidbody I would set the camera movement with rigidbody.SetPosition instead of with transform.position = new Vector3(), as you need to choose to set the transform in the geometric or the physics world.
Also you would need to uncheck the gravity option not to fall.
The problem is that i cannot make a script that makes the enemy rotate so that "up" is pointing against the player, (with that i mean so that it will come to the player with transform.up) And i have not found any working solution yet, (from what i can tell)
I am trying to make a small 2D game, just for my friends to play. What i have tried is making the enemy look at the player and then set the "z" rotation to the same as the "x" rotation and after that reset the "x" and "y" rotation (so that "up" is pointing at the player). But the enemy just goes straight up.
using UnityEngine;
public class Enemy : MonoBehaviour
{
public Transform Player; // The Transform of the Player that you can controll
public float MoveSpeed = 4; // How fast the enemy should go
void FixedUpdate()
{
transform.LookAt(Player);
transform.rotation = Quaternion.Euler(transform.rotation.x, 0, transform.rotation.x);
transform.rotation = Quaternion.Euler(0, 0, transform.rotation.z);
transform.position += transform.up * MoveSpeed * Time.deltaTime;
}
}
So what i want to happen is that the Enemy will move to the Player but the rotation part is not working and it just goes straight up in the air instead of following the player. I might just be stupid and have missed something simple...
By using
transform.rotation = Quaternion.Euler(transform.rotation.x, 0, transform.rotation.x);
transform.rotation = Quaternion.Euler(0, 0, transform.rotation.z);
both times you overwrite the rotation with new values additionally mixing Euler (Vector3) with Quaternion fields ... transform.rotation.x is not the pure rotation around the X-axis you see in the Unity inspector.
It depends a bit on how exactly you want the enemy rotated in the end but you could simply use Transform.RotateAround like
transform.LookAt(Player);
transform.RotateAround(transform.position, transform.right, 90);
LookAt by default results in a rotation such that transform.forward is pointing at the target, transform.right remains pointing right(ish) and transform.up remains pointing up(ish). So all you have to do is rotating around that local X-axis transform.right exactly 90° so now transform.forward points down instead and transform.up points at the target.
which results in
If you than also want to change the other axis you can still additionally rotate it around the local transform.up axis as well e.g. to flip the forward vector "upwards":
transform.LookAt(Player);
transform.RotateAround(transform.position, transform.right, 90);
transform.RotateAround(transform.position, transform.up, 180);
Is there actually a special reason why you are doing that in FixedUpdate and not rather in Update? FixedUpdate should only be used for Physics related stuff.
I am trying to move (fire) a sphere in the direction of a mouse click. But when I click, the sphere moves in unpredictable directions.
My code for adding the force to move the sphere:
if (Input.GetMouseButtonDown(0)){
RaycastHit hit;
/**
* We r using raycasting to detect mouse click on plane
* */
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out hit)){
newPosition = hit.point;
this.GetComponent<Rigidbody>().AddForce( (newPosition).normalized * 25, ForceMode.Impulse );
}
}
You've supplied an incorrect argument for Rigidbody.AddForce(). The vector you supply for the first argument should not be the position you want the force to point towards - it should be the direction of the force, multiplied by the magnitude.
In this case, you can calculate the direction of the force by subtracting the current position of the object from the target position:
Vector3 forceDir = newPosition - transform.position;
this.GetComponent<Rigidbody>().AddForce( forceDir.normalized * 25, ForceMode.Impulse );
Hope this helps! Let me know if you have any questions.
I'm making a basic 2D space shooter game in unity. I have the movements working fine and the camera follows the player. I've been scratching my head over how to give the camera a slight delay from the player moving to the camera moving to catch up to it without it teleporting. I was told to use a Vector3.Lerp and tried a few things from stackoverflow answers but none seemed to work with the way my code is set up. Any suggestions?
(myTarget is linked to the player)
public class cameraFollower : MonoBehaviour {
public Transform myTarget;
void Update () {
if(myTarget != null){
Vector3 targPos = myTarget.position;
targPos.z = transform.position.z;
transform.position = targPos;
}
}
}
If you linear interpolate (Lerp) you risk that Time.deltaTime * speed > 1 in which case the camera will start to extrapolate. That is, instead of following it will get in front if your target.
An alternative is to use pow in your linear interpolation.
float speed = 2.5f;
float smooth = 1.0f - Mathf.Pow(0.5f, Time.deltaTime * speed);
transform.position = Vector3.Lerp(transform.position, targetPos, smooth);
Mathf.Pow(0.5, time) means that after 1/speed second, half of the distance to the target point will be travelled.
The idea with Lerping camera movement is to gradually and smoothly have the camera make its way to the target position.
The further away the camera is, the bigger the distance it will travel per frame, but the closer the camera is, the distance per frame becomes smaller, making the camera ease into its target position.
As an example, try replacing your transform.position = targPos; line with:
float speed = 2.5f; // Set speed to whatever you'd like
transform.position = Vector3.Lerp(transform.position, targPos, Time.deltaTime * speed);
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