I'm creating a simple movement/following script in C#. I have a prefab (ball) spawn randomly within the Awake() method, at a random area in my scene. I then have another script attached to a different game object (robot) that at the start of the game shall move towards the ball.
Now the movement works but when the game object has come into the the minDistanceToTarget it begins to "jitter" and move around.
Here's the movement script:
public class RobotMovement : MonoBehaviour {
// Movement
private Transform target;
private float speed = 2f;
private float distance;
private float minDistanceToTarget = 1f;
// Rotation
private Vector3 targetDirection;
private Quaternion lookAtRotation;
private float RotationSpeed = 2f;
void Start() {
target = GameObject.FindGameObjectWithTag("Ball").transform;
}
void Update() {
targetDirection = target.position - transform.position;
lookAtRotation = Quaternion.LookRotation(targetDirection);
transform.rotation = Quaternion.Slerp(transform.rotation, lookAtRotation, RotationSpeed * Time.deltaTime);
distance = Vector3.Distance(transform.position, target.position);
if (distance >= minDistanceToTarget) {
transform.position = Vector3.MoveTowards(transform.position, target.position, speed * Time.deltaTime);
}
}
}
Edit I tried to change the affect to use FixedUpdate and affect the RigidBodies instead.
public class RobotMovement : MonoBehaviour {
private Rigidbody robot;
public Rigidbody target;
// Movement
private float speed = 2f;
private float minDistanceToTarget = 2f;
void Start() {
robot = GetComponent<Rigidbody>();
target = target.GetComponent<Rigidbody>();
}
void FixedUpdate() {
if (Vector3.Distance(robot.position, target.position) > minDistanceToTarget) {
robot.MovePosition(target.position * speed * Time.deltaTime);
}
}
}
Edit I now use a new movement method and include a rotation calculation. But it still jumps and jitters
public class RobotMovement : MonoBehaviour {
private Rigidbody robot;
private Transform ball;
// Movement
private float robotMovSpeed = 2f;
private float distanceToTarget;
private float distanceToStop = 2f;
// Rotation
private Vector3 targetDirection;
private Quaternion lookAtTarget;
private float rotationSpeed = 2f;
void Start() {
robot = GetComponent<Rigidbody>();
ball = GameObject.FindGameObjectWithTag("Ball").transform;
}
void FollowTarget(Transform target, float distanceToStop, float speed) {
distanceToTarget = Vector3.Distance(robot.position, target.position);
// Rotation
targetDirection = target.position - robot.position;
lookAtTarget = Quaternion.LookRotation(targetDirection);
if (distanceToTarget >= distanceToStop) {
transform.rotation = Quaternion.Slerp(transform.rotation, lookAtTarget, rotationSpeed * Time.deltaTime);
robot.MovePosition(robot.position + (target.position - robot.position).normalized * speed * Time.deltaTime);
}
}
void FixedUpdate() {
FollowTarget(ball, distanceToStop, robotMovSpeed);
}
}
Related
As you may know, the mode for the configurable spring has been removed, and I've discovered this following a Brackeys tutorial on multiplayer fps (part 5). There was a comment under the video saying that you should ignore this as Brackeys sets it manually in the code later. However, when I did this, there was no errors, but the jump button did nothing (yes I checked the inputs)
My code is below:
using UnityEngine;
[RequireComponent(typeof(PlayerMotor))]
[RequireComponent(typeof(ConfigurableJoint))]
public class PlayerController : MonoBehaviour
{
private Vector3 _movHorizontal;
private Vector3 _movVertical;
private Vector3 _velocity;
[SerializeField]
private float speed = 5f;
[SerializeField]
private float lookSensitivity = 5f;
[SerializeField]
private float thrusterForce = 1000f;
[Header("Spring Settings:")]
[SerializeField]
private JointDriveMode jointMode = JointDriveMode.Position;
[SerializeField]
private float jointSpring = 20f;
[SerializeField]
private float joinMaxForce = 40f;
private PlayerMotor motor;
private ConfigurableJoint joint;
void Start ()
{
motor = GetComponent<PlayerMotor>();
joint = GetComponent<ConfigurableJoint>();
SetJointSettings(jointSpring);
}
void Update ()
{
//Calc movement velocity as Vector 3D
float _xMov = Input.GetAxisRaw("Horizontal");
float _zMov = Input.GetAxisRaw("Vertical");
_movHorizontal = transform.right * _xMov;
_movVertical = transform.forward * _zMov;
//Final movement vector
_velocity = (_movHorizontal + _movVertical).normalized * speed;
//Apply movement
motor.Move(_velocity);
//Calculate rotation as a 3d vector (turning around)
float _yrot = Input.GetAxisRaw("Mouse X");
Vector3 _rotation = new Vector3 (0f, _yrot, 0f) * lookSensitivity;
//Apply rotation
motor.Rotate(_rotation);
//Calculate rotation as a 3d vector (turning around)
float _xrot = Input.GetAxisRaw("Mouse Y");
Vector3 _cameraRotation = new Vector3 (_xrot, 0f, 0f) * lookSensitivity;
//Apply rotation
motor.RotateCamera(_cameraRotation);
//Calculate thruster force based on player input
Vector3 _thrusterForce = new Vector3 (0, 0, 0);
if (Input.GetButton("Jump"))
{
_thrusterForce = Vector3.up * thrusterForce;
SetJointSettings(0f);
} else
{
SetJointSettings(jointSpring);
}
//Apply thruster force
motor.ApplyThruster(_thrusterForce);
}
private void SetJointSettings(float _jointSpring)
{
joint.yDrive = new JointDrive
{
mode = jointMode,
positionSpring = _jointSpring,
maximumForce = joinMaxForce
};
}
}
I am learning Unity, 3d free look camera and no errors, the only problem is the face and hands of the character doesn't rotate, only the armor of the character is rotating when I go to a different direction. I am using a free viking prefab from unity asset store as game character, the prefab has two child the face&hands and the armor. On the line "child = transform.GetChild(0).transform;" when I replace the 0 with 1 only the face and hands rotate. How can I make the whole prefab rotate when moving around?
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerController : MonoBehaviour
{
private Player playerInput;
private CharacterController controller;
private Vector3 playerVelocity;
private bool groundedPlayer;
[SerializeField]
private float playerSpeed = 2.0f;
[SerializeField]
private float jumpHeight = 1.0f;
[SerializeField]
private float gravityValue = -9.81f;
[SerializeField]
private float rotationSpeed = 4f;
private Transform cameraMain;
private Transform child;
private void Awake()
{
playerInput= new Player();
controller= GetComponent<CharacterController>();
}
private void OnEnable()
{
playerInput.Enable();
}
private void OnDisable()
{
playerInput.Disable();
}
private void Start()
{
cameraMain = Camera.main.transform;
child = transform.GetChild(0).transform;
}
void Update()
{
groundedPlayer = controller.isGrounded;
if (groundedPlayer && playerVelocity.y < 0)
{
playerVelocity.y = 0f;
}
Vector2 movementInput= playerInput.PlayerMain.Move.ReadValue<Vector2>();
Vector3 move = (cameraMain.forward * movementInput.y + cameraMain.right *
movementInput.x);
move.y = 0f;
controller.Move(move * Time.deltaTime * playerSpeed);
// Changes the height position of the player..
if (playerInput.PlayerMain.Jump.triggered && groundedPlayer)
{
playerVelocity.y += Mathf.Sqrt(jumpHeight * -3.0f * gravityValue);
}
playerVelocity.y += gravityValue * Time.deltaTime;
controller.Move(playerVelocity * Time.deltaTime);
if (movementInput != Vector2.zero)
{
Quaternion rotation = Quaternion.Euler(new Vector3(child.localEulerAngles.x,
cameraMain.localEulerAngles.y, child.localEulerAngles.z));
child.rotation = Quaternion.Lerp(child.rotation, rotation, Time.deltaTime *
rotationSpeed);
}
}
}
Hi I am very new to Unity and only got basic knowledge. I created a player who can move and also a camera which follows the player and according to the mouseX and mouseY acis the camera rotates.
Now I want the camera rotation angle to be the forward position.
If the player press w he moves along x acis but if the camera angle change and the player press w it should move into this direction.
player.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DoublePlayer : MonoBehaviour
{
private CharacterController _controller;
public float _jumpHeight = 3f;
[SerializeField]
private float _moveSpeed = 5f;
[SerializeField]
private float _gravity = 9.81f;
private float _directionY;
void Start()
{
_controller = GetComponent<CharacterController>();
}
// Update is called once per frame
void Update()
{
float horizontalInput = Input.GetAxis("Horizontal");
float verticalInput = Input.GetAxis("Vertical");
Vector3 direction = new Vector3(horizontalInput, 0, verticalInput);
// set gravity
_directionY -= _gravity * Time.deltaTime;
direction.y = _directionY;
_controller.Move(direction * _moveSpeed * Time.deltaTime);
}
}
camera.cs
public class CameraController : MonoBehaviour
{
public float RotationSpeed = 1;
public Transform Target, Player;
float mouseX, mouseY;
void Start()
{
Cursor.visible = false;
Cursor.lockState = CursorLockMode.Locked;
}
// Update is called once per frame
void LateUpdate()
{
CamControl();
}
void CamControl()
{
mouseX += Input.GetAxis("Mouse X") * RotationSpeed;
mouseY += Input.GetAxis("Mouse Y") * RotationSpeed;
mouseY = Mathf.Clamp(mouseY, -35, 60);
transform.LookAt(Target);
Target.rotation = Quaternion.Euler(mouseY, mouseX, 0);
Player.rotation = Quaternion.Euler(0, mouseX, 0);
}
}
For Target and Player variable I selected the player object.
Do someone has a simple solution I can understand?
You could simply take the direction and rotate it along with the object that component is attached to using Quaternion * Vector3 operator
var rotatedDirection = transform.rotation * direction;
Or if you rather need the orientation of the other script attached to a different object then do e.g.
// link your camera or whatever shall be used for the orientation via the Inspector
[SerializeField] private Transform directionProvider;
and then
var rotatedDirection = directionProvider.rotation * direction;
Since you're getting the rotation directly from a Transform, you could use Transform.TransformDirection:
Vector3 rotatedDirection = transform.TransformDirection(direction);
I'm currently working on a fps game in which you can travel to asteroids and walk on them, currently I have gravity for those asteroids and I have a fps controller that works for all my other movement systems (swimming, floating) but translating it to the gravity of the asteroid is giving me a lot of trouble. I can get the controller to work perfectly on flat surfaces using Unity's gravity but not on the asteroid.
The current fps controller moves in the direction of the camera which means the character floats up when looking up, all my other fps controllers didn't work at all with the planet and this one is the closet I've gotten to getting it to work.
Is there anyway to fix this? I'm quite new to coding so any explanations would help me heaps!
Here's the code I'm using for the controller:
public Transform targetCamera;
public Rigidbody targetRigidbody;
public Transform targetBody;
public float walkSpeed = 9f;
public float runSpeed = 14f;
public float maxSpeed = 20f;
public float jumpPower = 30f;
public float extraGravity = 0;
float bodyRotationX;
float camRotationY;
Vector3 directionIntentX;
Vector3 directionIntentY;
float speed;
public bool grounded;
public float thrust = 20f;
public float forwardSwimSpeed = 45f;
private float xRotation;
private const float Sensitivity = 50f;
private const float SensitivityMultiplier = 1f;
private float desiredX;
public float dragMult = 2f;
public float rotationSmoothSpeed = 10f;
void Update()
{
Look();
targetRigidbody.drag = dragMult;
}
private void FixedUpdate()
{
if (targetRigidbody.velocity.magnitude > maxSpeed)
{
targetRigidbody.velocity = targetRigidbody.velocity.normalized * maxSpeed;
}
if (Input.GetKey(KeyCode.W))
AddForce(Vector3.forward);
if (Input.GetKey(KeyCode.S))
AddForce(Vector3.back);
if (Input.GetKey(KeyCode.A))
AddForce(Vector3.left);
if (Input.GetKey(KeyCode.D))
AddForce(Vector3.right);
if (Input.GetKey(KeyCode.Space))
AddForce(Vector3.up);
if (Input.GetKey(KeyCode.LeftControl))
AddForce(Vector3.down);
}
private void AddForce(Vector3 direction)
{
float scaledForwardSwimSpeed = Time.deltaTime * forwardSwimSpeed;
targetRigidbody.AddForce(targetCamera.transform.TransformDirection(
direction * thrust) * scaledForwardSwimSpeed,
ForceMode.Acceleration);
}
private void Look()
{
float mouseX = Input.GetAxis("Mouse X") * Sensitivity * Time.fixedDeltaTime * SensitivityMultiplier;
float mouseY = Input.GetAxis("Mouse Y") * Sensitivity * Time.fixedDeltaTime * SensitivityMultiplier;
Vector3 rotation = targetCamera.transform.localRotation.eulerAngles;
desiredX = rotation.y + mouseX;
xRotation -= mouseY;
xRotation = Mathf.Clamp(xRotation, -90f, 90f);
targetCamera.transform.localRotation = Quaternion.Euler(xRotation, desiredX, 0);
}
And here's the first of two bits of code for the asteroid: GravityCtrl
public float gravity = 10f;
private void OnTriggerEnter(Collider other)
{
if (other.GetComponent<GravityCtrl>())
{
other.GetComponent<GravityCtrl>().gravity = this.GetComponent<GravityOrbit>();
}
Here's the second: GravityOrbit
public GravityOrbit gravity;
private Rigidbody rb;
public float rotationSpeed = 20f;
// Start is called before the first frame update
void Start()
{
rb = GetComponent<Rigidbody>();
}
// Update is called once per frame
void FixedUpdate()
{
if (gravity)
{
Vector3 gravityUp = Vector3.zero;
gravityUp = (transform.position - gravity.transform.position).normalized;
Vector3 localUp = transform.up;
Quaternion targetrotation = Quaternion.FromToRotation(localUp, gravityUp) * transform.rotation;
transform.up = Vector3.Slerp(transform.up, gravityUp, rotationSpeed * Time.deltaTime);
rb.AddForce((-gravityUp * gravity.gravity) * rb.mass);
}
I have code in C# in a game in Unity that needs to rotate the player based on the slope of the terrain but the RotateTowards function(calculates the slope and angle) doesn't allow the object to be rotated sideway to move it in different directions. If I take out the rotateTowards function, rotations sideways work. If I dont, the correct slope rotation works but player won'r rotate sideways when buttons are pressed.
How could I fix this so the player could rotate in both ways?
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerController1 : MonoBehaviour
{
[System.Serializable]
public class MoveSettings
{
public float forwardVel = 10f; // walk speed
public float rotateVel = 100; // character rotation speed, character can walk 360 degree
public float jumpVel = 25f;
public LayerMask ground;
public Transform backLeft; // back left feet
public Transform backRight; // back right feet
public Transform frontLeft; // front left feet
public Transform frontRight; // front left feet
}
[System.Serializable]
public class PhysicsSettings
{
public float downAccel = 0.75f; // down speed when not grounded
}
public GameObject Model;
public GameObject Origin;
public MoveSettings moveSettings = new MoveSettings();
public PhysicsSettings physicsSettings = new PhysicsSettings();
private Vector3 velocity = Vector3.zero;
private Quaternion targetRotation;
private CharacterController cc;
private float forwardInput, turnInput, jumpInput = 0;
private RaycastHit lr;
private RaycastHit rr;
private RaycastHit lf;
private RaycastHit rf;
private Vector3 upDir;
private Animator Anim; // global private variable
private void Start()
{
Anim = GetComponent<Animator>(); // in the Start function
targetRotation = transform.rotation;
cc = GetComponent<CharacterController>();
}
public bool Grounded()
{
return cc.isGrounded;
}
private void FixedUpdate()
{
Run(); // calculate the velocity to be applied on character controller, stored in the velocity variable
Jump(); // code for jumping
GetInput(); // movement input keys
Turn(); // character movement direction input
cc.Move(transform.TransformDirection(velocity) * Time.deltaTime);
RotateTowardsGround();
}
private void GetInput()
{
Anim.SetFloat("vSpeed", forwardInput); // in the GetInput() function
Anim.SetFloat("Direction", 1f);
forwardInput = Input.GetAxis("Vertical");
turnInput = Input.GetAxis("Horizontal");
jumpInput = Input.GetAxisRaw("Jump");
}
private void Turn()
{
targetRotation *= Quaternion.AngleAxis(moveSettings.rotateVel * turnInput * Time.deltaTime, Vector3.up);
transform.rotation = targetRotation;
}
public void Jump()
{
if (jumpInput > 0 && Grounded())
{
velocity.y = moveSettings.jumpVel;
}
else if (jumpInput == 0 && Grounded())
{
velocity.y = 0;
}
else
{
velocity.y -= physicsSettings.downAccel;
}
}
private void Run()
{
velocity.z = moveSettings.forwardVel * forwardInput;
}
public void RotateTowardsGround()
{
// we have four feet
Physics.Raycast(moveSettings.backLeft.position + Vector3.up, Vector3.down, out lr);
Physics.Raycast(moveSettings.backRight.position + Vector3.up, Vector3.down, out rr);
Physics.Raycast(moveSettings.frontLeft.position + Vector3.up, Vector3.down, out lf);
Physics.Raycast(moveSettings.frontRight.position + Vector3.up, Vector3.down, out rf);
upDir = (Vector3.Cross(rr.point - Vector3.up, lr.point - Vector3.up) +
Vector3.Cross(lr.point - Vector3.up, lf.point - Vector3.up) +
Vector3.Cross(lf.point - Vector3.up, rf.point - Vector3.up) +
Vector3.Cross(rf.point - Vector3.up, rr.point - Vector3.up)
).normalized;
Debug.DrawRay(rr.point, Vector3.up);
Debug.DrawRay(lr.point, Vector3.up);
Debug.DrawRay(lf.point, Vector3.up);
Debug.DrawRay(rf.point, Vector3.up);
Model.transform.up = upDir;
}
}
The proper way to rotate object based on Terrain slope/curvature is to first throw raycast then obtain the returned RaycastHit.normal value and assign it your to the object's transform.up. It's better to use Lerp or Slerp to do this form smooth ration.
As for the position of the object, you can calculate that with Terrain.activeTerrain.SampleHeight as described in this post or you can use RaycastHit.point like you did in the code from your question.
Below is an example of what I described above. It is a minimal code to move/rotate object over a terrain. You can modify it to fit your four character legs scenario.
public class Hover : MonoBehaviour
{
public Transform objectToMove;
public float maxSpeed = 10f;
public float angleSpeed = 5f;
public float groundDistOffset = 2f;
private Vector3 toUpPos = Vector3.zero;
void Update()
{
float hInput = Input.GetAxis("Horizontal");
float vInput = Input.GetAxis("Vertical");
Vector3 objPos = objectToMove.position;
objPos += objectToMove.forward * vInput * maxSpeed * Time.deltaTime;
objPos += objectToMove.right * hInput * maxSpeed * Time.deltaTime;
RaycastHit hit;
if (Physics.Raycast(objectToMove.position, -Vector3.up, out hit))
{
//Get y position
objPos.y = (hit.point + Vector3.up * groundDistOffset).y;
//Get rotation
toUpPos = hit.normal;
}
//Assign position of the Object
objectToMove.position = objPos;
//Assign rotation/axis of the Object
objectToMove.up = Vector3.Slerp(objectToMove.up, toUpPos, angleSpeed * Time.deltaTime);
}
}