I am currently using a Taxi model from as an enemy with AI that will drive to different waypoints. Whenever the car moves to a waypoint it auto rotates 90 degrees to the right, right away but keeps moving from waypoint to waypoint.
How do I fix a NavMeshAgent that auto turns 90 degrees when moving to a waypoint? The commented out code fixes the auto rotate but does not rotate enough when moving to the waypoint upon setDestination.
The uncommented code first rotates 90 degrees and then rotates a little after each waypoint (from the 90 degree position). (Unity API script from Vector3.RotateTowards)
_agent.UpdateRotation = false , stops initial rotation but I then have to control rotation manually (which I am having a hard time with)
private void Start()
{
_agent = GetComponent<NavMeshAgent>();
// Almost works doesn't rotate enough
//_agent.updateRotation = false;
_isStopped = false;
}
private void Update()
{
//Almost works, doesn't rotate enough
//transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.Euler(0, (_angleToRotate) * 8, 0), 1f);
//Rotates but turns 90 degrees first
Vector3 targetDirection = _wayPoints[_currentWayPoint].transform.position - transform.position;
float singleStep = _speed * Time.deltaTime;
Vector3 newDirection = Vector3.RotateTowards(transform.forward, targetDirection, singleStep, 0.0f);
Debug.DrawRay(transform.position, newDirection, Color.red);
transform.rotation = Quaternion.LookRotation(newDirection);
switch (_currentState)
{
case AIState.NonAlert:
//Debug.Log("Not Alert...");
if (_isStopped == true)
{
return;
}
else
{
if (_wayPoints.Count > 0)
{
_agent.SetDestination(_wayPoints[_currentWayPoint].transform.position);
//Gets distance between two Vector3s
float distanceToWayPoint = Vector3.Distance(_wayPoints[_currentWayPoint].transform.position, transform.position);
if (distanceToWayPoint < 1.0f)
{
_currentWayPoint++;
//Almost works, doesnt rotate enough
//_angleToRotate = Vector3.SignedAngle(transform.position, _wayPoints[_currentWayPoint].transform.position, Vector3.up);
}
}
}
This is due to the fact that the taxi model itself has an 90° offset.
You can change that with creating a prefab for the taxi model with an empty gameobject as parent and give the taxi the needed 90° offset on the prefab. After that you use the parent object for movement and rotation
Or
Change the direction of the model in blender or any other 3d modeling tool
Related
I have made an endless runner game. There, a cube is sliding on a slippery plane. Therefore I created multiple prefabs of planes with obstacles on it which are random placed one after another. Now, when I start the game and my player (a cube) slides on the planes, it sometimes seems to collide with the edges of a plane, which causes the cube to fly away.
private void Update(){
if (rb != null){
if (rb.position.y > 0.513f){
pos = rb.transform.position;
pos.y = 0.51f;
rb.transform.position = pos;
}
rot = rb.transform.rotation;
rot.y = 0;
rot.x = 0;
rot.z = 0;
rb.transform.rotation = rot;
if (rb.position.y > 1.01f)
forwardForce = 2500f;
else
forwardForce = 5300f;
if (Input.GetKey("d"))
rb.AddForce(sidewayForce * Time.deltaTime, 0, 0, ForceMode.VelocityChange);
if (Input.GetKey("a"))
rb.AddForce(-sidewayForce * Time.deltaTime, 0, 0, ForceMode.VelocityChange);
}
}
Here, rb is the Rigidbody of the cube. And as you can see, I have tried to solve the problem by detecting if the player position is too high, and then putting it just slightly above the y-position of the plane, so that caused by possible bad floating point precision, it doesn't get stuck inside the plane. Now the cube doesn't fly away, but it kind of bounces very aggressively instead. And while writing this question I found out, that you can freeze the y-coordinate of the player, which helps a bit, but instead of bouncing it slows down now, which is also bad but better.
Edit: I 'fixed' it now by just letting the cube fly slightly above the platform. By that it doesn't collide with the edges or whatever the reason was
Instead of relying on the physics engine you can calculate only the essential things and gain more control.
Vector3 velocity;
float initialForwardSpeed;
LayerMask obstacleLayer;
void Start(){
velocity = Vector3.forward*initialForwardSpeed;
}
private void Update(){
if (Input.GetKey("d"))
velocity += new Vectro3(sidewayForce * Time.deltaTime, 0, 0);
if (Input.GetKey("a"))
velocity += new Vectro3(-sidewayForce * Time.deltaTime, 0, 0);
tranform.position += velocity*Time.deltaTime;
Collider[] hitColliders = Physics.OverlapBox(transform.position, Vector3.One*0.5f, transform.rotation, obstacleLayer);
if(hitColliders.Length > 0){
CollisionOccured();
}
}
void CollisionOccured(){
Debug.Log("Collided with obstacle");//You'll probably need this for your game logic
}
This code should move a cube of size 1 forward with the speed of initialForwardSpeed, allow for exactly the same lateral movement and will give you a message when you collide with an obstacle (object with a collider on the obstacle layer)
In this case you may interest : https://docs.unity3d.com/ScriptReference/RigidbodyConstraints.html
You can freeze rigidbody position Y Axis.
if (rb.position.y > 0.513f) {
pos = rb.transform.position;
pos.y = 0.51f;
rb.transform.position = pos;
}
The rigidbody position is already equal to the position of the gameobject it's on, so you just need to get to the gameobject.transform.position here.
if (transform.position.y > yMaxValue) {
transform.position = new
Vector3(transform.position.x, targetYValue, transform.position.z);
I am trying to recreate a "GTA-Style" third person camera in Unity. I created code that rotates the player in the direction the camera is pointing. However, the camera is a child of the player, so when I rotate the player, I also rotate the camera. So I have to reset the camera's Y rotation. which in "turn" causes a frame or two to have the camera looking in a different direction.
Hierarchy of Player:
Script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ThirdPersonCamera : MonoBehaviour
{
public float rotationSpeed = 2f;
public Transform target, player;
float mouseX;
float mouseY;
float h;
float v;
// Start is called before the first frame update
void Start()
{
Cursor.lockState = CursorLockMode.Locked;
Cursor.visible = false;
}
void LateUpdate()
{
h = Input.GetAxis("Horizontal");
v = Input.GetAxis("Vertical");
mouseX += Input.GetAxis("Mouse X") * rotationSpeed;
mouseY -= Input.GetAxis("Mouse Y") * rotationSpeed;
mouseY = Mathf.Clamp(mouseY, -35, 60);
CamControl();
}
void CamControl()
{
transform.LookAt(target);
if (h != 0 || v != 0)
{
target.rotation = Quaternion.Euler(mouseY, mouseX, 0);
player.rotation = Quaternion.Euler(0, mouseX, 0);
}
else
{
target.rotation = Quaternion.Euler(mouseY, mouseX, 0);
while (h != 0 || v != 0)
{
target.rotation = Quaternion.Euler(target.rotation.x, 0, target.rotation.z);
player.rotation = Quaternion.Euler(player.rotation.x, player.rotation.y - target.rotation.y + 180, player.rotation.z);
}
}
}
}
How this script works:
How I have it set up on Main Camera. (Notice hierarchy setup)
Note: The "Player" and "Target" variables have the same name as the GameObjects assigned to them.
If there is no input on Horizontal and Vertical axes, the camera will rotate around the player like normal.
If there is input on the Horizontal and Vertical axes, the player will face the direction the camera is facing. Then it has to reset the camera's Y rotation.
What I have tried:
Using slerp to smooth out the camera angles.
Note: I have TRIED slerping, I'm not sure that I did it correctly.
Unchilding the Target (Camera)
Result: This just made the camera not follow the player, but it shows that my code to turn the player is somewhat functional.
Setting the Player (in script) to the graphics.
Result: This gave me an odd view of the player, and made the player rotate up and down as well.
Sorry for the long and complicated post!
If your code to rotate the player and the camera works when unchilding the target from the player, then don't child the target of camera in the player.
Instead, make the target follow the player.
For instance:
void CamControl()
{
target.position = player.position;
transform.LookAt(target);
...
}
You can then do a smooth follow using a SmoothDamp for example, instead of copying directly the player position.
Moreover, if your player is controlled by physics (using the rigidbody for the movement), you should update your camera in the fixed update.
Unity using C#
In an top down game where the camera rotation is locked, I have made a character controller which is restricted to 8 axis and will only rotate in 45 degree increments. Think Links awakening. Everything works except when moving in a diagonal direction(using WASD).
When I let go of W & D for example, whichever key I let go of last even if by a fraction of a second, the player will end up facing in that direction rather than the intended up and to the right diagonal direction.
I am looking to modify the following code to add some sort of buffer so that the player remains facing the intended direction when I let go of the 2 keys. I'd like to keep it as simple as possible.
public float defaultSpeed = 6f;
CharacterController controller;
private Vector3 playerMovement;
Vector2 input;
float angle;
Quaternion targetRotation;
Transform cam;
// Start is called before the first frame update
void Start()
{
controller = GetComponent<CharacterController>();
cam = Camera.main.transform;
}
// Update is called once per frame
void Update()
{
Movement();
}
public void Movement()
{
//Get inputs
input.x = Input.GetAxisRaw("Horizontal");
input.y = Input.GetAxisRaw("Vertical");
//Only update if keys are pressed
if (Mathf.Abs(input.x) < deadZone && Mathf.Abs(input.y) < deadZone) return;
//Get camera angle: input to radians - radians to degrees - degrees plus the camera angle - limit angle to 45 degree increments
angle = Mathf.Atan2(input.x, input.y);
angle = Mathf.Rad2Deg * angle;
angle += cam.eulerAngles.y;
angle = Mathf.Round(angle / 45) * 45;
//Rotate Character
targetRotation = Quaternion.Euler(0, angle, 0 );
transform.rotation = targetRotation;
//Move Character
transform.position += transform.forward * defaultSpeed * Time.deltaTime;
}
Make a courotine that checks if a key is released, and waits, say 0.5 seconds for another key to be released. If another key is released, then stay in that direction. Hope it works. Good luck.
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'm totally new to coding and I'm trying to create a ping pong game.
I scripted that the enemy paddle position to move with the ball position. (very simple)
public float speed = 6f;
Vector2 player2;
Vector2 player2position;
GameObject ballpos;
void Start ()
{
ballpos = GameObject.FindGameObjectWithTag ("ball");
}
void Update()
{
player2 = Vector2.Lerp (gameObject.transform.position , ballpos.transform.position , Time.deltaTime * speed );
player2position = new Vector2 ( 3.1f , Mathf.Clamp ( player2.y , -2.4f, 2.4f)) ;
gameObject.transform.position = new Vector2 ( 3.1f , player2position.y );
}
What is code do is that the ""center"" of the enemy paddle will move with respect to the ""center"" of the ball.
Now what I'm trying to do is to let the enemy hit the ball randomly to different directions, so that collision between the enemy paddle and the ball happened on the edges of the enemy paddle. (not always with the center).
Also, I have set a distance between the ball and the enemy paddle, so that when if the distance > 3 the enemy paddle will stop.
if (Mathf.Abs (ballpos.transform.position.x - paddle.transform.position.x) > 3)
{
speed = 0f;
}
if (Mathf.Abs (ballpos.transform.position.x - paddle.transform.position.x) < 3)
{
speed = 6f;
}
can you please help me with this. I have been trying to do this about two days.
Thanks :)
You can add Physics Material component to your ball for bounciness effect, and to move both players use simple movement script.
Now to know ball position you can use OnCollisionEnter2D function and another function that calculates the ball's speed depending on where it hit.
void OnCollisionEnter2D(Collision2D col) {
if (col.gameObject.name == "PlayerLeft") // collide with left player
{
float y = AFactor(transform.position,
col.transform.position,
col.collider.bounds.size.y);
// Calculate direction, make length=1 via .normalized
Vector2 dir = new Vector2(1, y).normalized;
// Set Velocity with dir * speed
GetComponent<Rigidbody2D>().velocity = dir * speed;
}
if (col.gameObject.name == "PlayerRight")// hit right player
{
float y = AFactor(transform.position,
col.transform.position,
col.collider.bounds.size.y);
// Calculate direction, make length=1 via .normalized
Vector2 dir = new Vector2(-1, y).normalized;
// Set Velocity with dir * speed
GetComponent<Rigidbody2D>().velocity = dir * speed;
}