using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class RotateToTarget : MonoBehaviour
{
public Transform target;
public Transform character;
private const float FAC_SPEED = 10f;
private const float FAC_LERP = 0.9f;
private const float ANG_MAX = 80f;
// Start is called before the first frame update
void Start()
{
/* ... */
}
// Update is called once per frame
void Update()
{
float targetAngleFromForward = Vector3.Angle(character.transform.forward, target.position - transform.position);
Vector3 lerpPoint;
if (targetAngleFromForward < ANG_MAX)
{
// Lerp towards the target's direction
// This is not a very good or elegant solution but it demonstrates the idea
lerpPoint = Vector3.Lerp(transform.forward, (target.position - transform.position).normalized * FAC_SPEED, FAC_LERP * Time.deltaTime);
}
else
{
// Lerp towards the forward direction
// Same idea, but to character.transform.forward instead
lerpPoint = Vector3.Lerp(transform.forward, character.transform.forward * FAC_SPEED, FAC_LERP * Time.deltaTime);
}
transform.rotation = Quaternion.LookRotation(lerpPoint);
}
}
I want to add a global public variable to control the lerpPoint in the IF and in the ELSE.
To control the speed of the Lerp towards the target's direction and the Lerp towards the forward direction.
I tried to play with the const variables values but didn't figure it how to control this speeds.
Lerp gives you a single point part of the way between the two inputs, based on the final parameter which should be between 0 and 1. So you need to smoothly increase the final parameter of Lerp from 0 to 1 over time.
Probably the easiest way would be something like this (pseudocode stripping down your example for clarity):
public class RotateToTarget : MonoBehaviour
{
public float secondsToRotate = 2.0f;
private float secondsSoFar = 0.0f;
void Update()
{
secondsSoFar += Time.deltaTime;
float t = secondsSoFar / secondsToRotate;
Vector3 lerpPoint = Vector3.Lerp(start, end, t);
transform.rotation = Quaternion.LookRotation(lerpPoint);
}
}
Related
I am creating a third person player movement script. The movement adds force to the rigidbody relative to the direction of the camera. I want to have a max speed limit in the forward direction (cForward), and a separate max speed limit for the horizontal/right direction (cRight). Normally I would be fine with setting the velocity directly, however this screws up gravity for the player. Is there any way to achieve this by using addforce OR is there a way to get gravity working properly when setting velocity directly? Here is what I have so far(some of my other attempts at a solution are commented out):
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerMovement : MonoBehaviour
{
public Camera mainCamera;
public float horizontalWalkSpeed = 500.0f;
public float verticalWalkSpeed = 500.0f;
public float horizontalSprintSpeed = 1000.0f;
public float verticalSprintSpeed = 1000.0f;
public bool isSprinting = false;
public bool cannotMove = false;
public float accelerationSpeed = 0.2f;
private Rigidbody pRigidBody;
private Vector2 currentInputVector;
private Vector2 smoothInputVelocity;
// Start is called before the first frame update
void Start()
{
pRigidBody = GetComponent<Rigidbody>();
}
// Update is called once per frame
void Update()
{
HandleInput();
}
private void FixedUpdate()
{
Vector3 cForward = mainCamera.transform.forward;
Vector3 cRight = mainCamera.transform.right;
cForward.y = 0.0f;
cRight.y = 0.0f;
cForward.Normalize();
cRight.Normalize();
if (!cannotMove && !isSprinting)
{
Vector3 vForce = cForward * currentInputVector.y * verticalWalkSpeed;
Vector3 hForce = cRight * currentInputVector.x * horizontalWalkSpeed;
//Vector3 gravity = Vector3.up * -9.8f;
Vector3 force = vForce + hForce;
//float verSpeed = Vector3.Dot(pRigidBody.velocity, cForward);
//float horSpeed = Vector3.Dot(pRigidBody.velocity, cRight);
//if (verSpeed >= 0 && verSpeed <= verticalWalkSpeed)
//{
// pRigidBody.AddForce(vForce, ForceMode.VelocityChange);
//}
//if(horSpeed < horizontalWalkSpeed)
//{
// pRigidBody.AddForce(hForce, ForceMode.VelocityChange);
//}
//float velocityInDirection = Vector3.Dot(pRigidBody.velocity, cForward);
//if(velocityInDirection > verticalWalkSpeed)
//{
// pRigidBody.AddForce(-vForce, ForceMode.VelocityChange);
//}
//pRigidBody.velocity = force;
pRigidBody.AddForce(force, ForceMode.VelocityChange);
}
else if (!cannotMove && isSprinting)
{
pRigidBody.velocity = cForward * currentInputVector.y * verticalSprintSpeed * Time.fixedDeltaTime + cRight * currentInputVector.x * horizontalSprintSpeed * Time.fixedDeltaTime;
}
}
private void HandleInput()
{
isSprinting = Input.GetButton("Sprint");
Vector2 input = new Vector2(Input.GetAxis("Horizontal"), Input.GetAxis("Vertical"));
currentInputVector = Vector2.SmoothDamp(currentInputVector, input, ref smoothInputVelocity, accelerationSpeed);
}
}
I think this thread may help --https://answers.unity.com/questions/9985/limiting-rigidbody-velocity.html.
Basically, there are two methods.
Add force in the opposite direction and increase with the extent to which the object exceeds the limit. This requires additional calculations and tests.
simply normalize the velocity value when it exceeds the limit.
I have a script to jump, but it jumps depending on the value of the JumpFactor constant. I would like the jump to happen using the JumpToPositionX (float target) method. I think I need to somehow calculate the JumpFactor depending on the value of the target in the JumpToPositionX method. I found an article on Wikipedia, but I don't even know where to start. I have not found examples of how to do this with the CharacterController.
public class ExampleClass : MonoBehaviour
{
const float Speed = 6f;
const float JumpFactor = 14f;
const float Gravity = 20f;
private Vector3 moveDirection = Vector3.zero;
CharacterController controller;
void Start()
{
controller = GetComponent<CharacterController>();
}
void Update()
{
if (controller.isGrounded)
{
moveDirection = new Vector3(1f, 0f, 0f); // Move X Direction
moveDirection *= Speed;
if (Input.GetButton("Jump"))
moveDirection.y = JumpFactor;
}
moveDirection.y -= Gravity * Time.deltaTime;
controller.Move(moveDirection * Time.deltaTime);
}
// Method for jump, calling from other script.
void JumpToPositionX(float target)
{
}
}
Theory
I assume you want to use the ballistic physic.
Let's see what formula we can use
Where :
V : Velocity
a : Acceleration
P : Position
t : time since start
0 : Initial
is known it's (0f,-9.81f) or (0f, Gravity) in your case.
is known too, it's your initial position before the jump.
is the end target position.
Resolution
You have to find the end time and the initial Velocity . So here's the position equation:
We can simplify the process by looking only for the x axis because there's no acceleration.
is simplified to :
Let's isolate the from the x position formula :
Now you have the total time of the jump .
You just need to find the initial y velocity by using the y position formula :
Isolate the Vyo :
So now you have all informations for computing the jump : Initial and end position, Total time, Initial velocity and acceleration.
Implementation
Here's an example of the MonoBehaviour you can create :
public class JumpBehaviour : MonoBehaviour
{
public CharacterController CharacterController;
public Transform TargetPosition;
private bool jumpStarted = false;
private float totalTime = 0f;
private float t = 0f;
private Vector2 V0;
private Vector2 initialPosition;
private bool jumpKeyDown;
private const float xSpeed = 6f;
private const float gravity = -9.81f;
void Update()
{
jumpKeyDown = Input.GetKeyDown(KeyCode.Space);
}
void FixedUpdate()
{
if (jumpStarted)
{
UpdateJumpPosition(Time.fixedDeltaTime);
}
else if (jumpKeyDown )
{
StartJump();
}
}
private void UpdateJumpPosition(float deltaTime)
{
t += deltaTime;
if(t > totalTime)
{
//End of jump
transform.position = TargetPosition.position;
jumpStarted = false;
return;
}
Vector2 newPosition = new Vector2(0f, gravity) * t * t + V0 * t + initialPosition;
CharacterController.Move(newPosition);
}
private void StartJump()
{
jumpStarted = true;
initialPosition = transform.position;
// Absolute because xSpeed could be positive or negative. We dont want negative time
float delta = TargetPosition.position.x - transform.position.x;
totalTime = Mathf.Abs(delta / xSpeed);
t = 0f;
float yInitialSpeed = (TargetPosition.position.y - transform.position.y - gravity * totalTime * totalTime) / totalTime;
// Using Sign to set the direction of horizontal movement
V0 = new Vector2(xSpeed * Mathf.Sign(delta), yInitialSpeed);
}
}
And this is the result
Conclusion
This is just an quick example to figure out how to compute the initial value of the jump and movement in 2D.
As derHugo said on comment you should not use transform.position direct attribution with Physic.
For 3D it's the same idea but you should compute the speed x,z from target direction and speed magnitude.
Vector3 delta = TargetPosition - transform.position;
delta.y = 0f;
float direction = delta.normalized;
Vector3 initialSpeed = direction * movementSpeed;
i got a runner game and cube is my player, the problem is i can't stop cube from rolling. The ground is slippery(friction = 0) but it is still rolling. When i freeze rotation of y axis it seems like lagging so it doesn't work either. Please help me. There is my movement code
I changed values of mass and drag but it didn't help.
public Rigidbody rb;
public float forwardForce = 2000f;
public float sidewaysForce = 500f;
public float acceleration;
public PlayerMovement movement;
void FixedUpdate()
{
rb.AddForce(0, 0, forwardForce * Time.deltaTime);
forwardForce += Time.deltaTime * acceleration;
if (Input.GetKey("d"))
{
rb.AddForce(sidewaysForce * Time.deltaTime, 0, 0, ForceMode.VelocityChange);
}
if (Input.GetKey("a"))
{
rb.AddForce(-sidewaysForce * Time.deltaTime, 0, 0, ForceMode.VelocityChange);
}
There are no error messages.
you could try and replace Time.deltaTime with Time.fixedDeltaTime since it's in FixedUpdate.
Okay so after trying to help you in the comments I have written a small demo of what you are trying to achieve so you can use it to try and find where you went wrong before and how I would go about doing what you are trying to do.
Cube Controls
using UnityEngine;
public class CubeControl : MonoBehaviour
{
public Rigidbody rb;
public float forwardForce = 2000f;
public float sidewaysForce = 500f;
public float acceleration = 1;
void FixedUpdate()
{
rb.AddForce(0, 0, forwardForce * Time.deltaTime);
forwardForce += Time.deltaTime * acceleration;
//Using the in-built methods uses the keys you were but also the arrow keys
float inputX = Input.GetAxis("Horizontal");
//Check there is input
if (Mathf.Abs(inputX) > float.Epsilon)
{
//set which force direction to use by comparing the inputX value
float force = inputX > 0 ? sidewaysForce : -sidewaysForce;
rb.AddForce(force * Time.deltaTime, 0, 0, ForceMode.VelocityChange);
}
}
}
There are a couple of changes there but they are commented to explain.
Camera Tracking
using UnityEngine;
public class CameraTracking : MonoBehaviour
{
#pragma warning disable 0649
[SerializeField] private GameObject _cube;
#pragma warning restore 0649
private Vector3 offset;
void Awake()
{
offset = _cube.transform.position + transform.position;
}
void LateUpdate() {
transform.position = _cube.transform.position + offset;
}
}
It uses the initial offset between the cube and Camera that is setup before starting the pressing play.
Tip I recommend not using this but making the camera a child of the cube as this is something that you are calcualting each frame which you don't need too!
Path Generator
using UnityEngine;
using System.Collections;
public class PathGenerator : MonoBehaviour
{
#pragma warning disable 0649
[SerializeField] private GameObject _path1;
[SerializeField] private GameObject _path2;
[SerializeField] private GameObject _cube;
#pragma warning restore 0649
private float _cubeRepositionZDistance;
private float _pathPositionX;
private float _pathPositionY;
//Distance center of path should be behind the cube;
private float _resetDistanceFromCube = 25f;
private void Awake()
{
// You could hard code this but this way you can change the length of each path segment and it will be updated here automatically.
_cubeRepositionZDistance += _path1.transform.localScale.z / 2;
_cubeRepositionZDistance += _path2.transform.localScale.z / 2;
_pathPositionX = _path1.transform.position.x;
_pathPositionY = _path1.transform.position.y;
// Position path2 relative to path1.transform.position
_path2.transform.position = new Vector3(_pathPositionX, _pathPositionY, _path1.transform.position.z + _cubeRepositionZDistance);
StartCoroutine(PathRepositioner());
}
private IEnumerator PathRepositioner()
{
//Can change bool to something like !GameOver
while (true)
{
if (_path1.transform.position.z < _cube.transform.position.z - _resetDistanceFromCube)
{
_path1.transform.position = new Vector3(_pathPositionX, _pathPositionY, _path2.transform.position.z + _cubeRepositionZDistance);
}
if (_path2.transform.position.z < _cube.transform.position.z - _resetDistanceFromCube)
{
_path2.transform.position = new Vector3(_pathPositionX, _pathPositionY, _path1.transform.position.z + _cubeRepositionZDistance);
}
yield return null;
}
}
}
Doing it this way you are reusing the same 2 path segements and not creating clones all the time, you can change it to use 3 or more if needed.
Scene Setup
Position the cube and then position the path1 segement under the Cube.
Assign all required GameObjects to the scripts in the inspector.
Press Play!
Sidenote: It is recommended to move the path segements not the Cube(Player) when doing an endless runner like this but as you are new to this I suggest reading up on that when you get a chance.
I want to move a cube using some device and periodically (every 3 seconds) print those coordinates to a file. I am not sure how to accomplish this with my code below. Does anybody have ideas as to how this can be done?
Thank you!
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
[RequireComponent(typeof(MeshCollider))]
public class UserController : MonoBehaviour {
public int speed = 20;
// Update is called once per frame
void Update()
{
// get input data from keyboard or controller
float moveHorizontal = Input.GetAxis("Horizontal");
float moveVertical = Input.GetAxis("Vertical");
// update player position based on input
Vector3 position = transform.position;
position.x += moveHorizontal * speed * Time.deltaTime;
position.z += moveVertical * speed * Time.deltaTime;
transform.position = position;
}
void OnMouseDrag()
{
if(Input.GetMouseButton(0))
{
float distance_to_screen = Camera.main.WorldToScreenPoint(gameObject.transform.position).z;
transform.position = Camera.main.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, distance_to_screen));
}
}
}
I would suggest creating a separate script and attach it to your cube.
public class CubeTracker : MonoBehaviour
{
private bool logging = true;
void Awake()
{
StartCoroutine(LogPosition());
}
private IEnumerator LogPosition()
{
while (logging)
{
Debug.Log(transform.position);
yield return new WaitForSeconds(3f);
}
}
}
This will start a coroutine as soon as the cube is created and should log your desired result into console. If that's what you desire, you can then go ahead and replace the Debug.Log with a write-to-file implementation.
Use a global variable, and feed it inside Update() method.
for example:
private Vector3 LastCoordinate{get; set;}
and the use a periodic timer like :
private System.Threading.Timer timer;
timer = new System.Threading.Timer(GetLastCoordinate, null, 3000, 0);
private void GetLastCoordinate()
{
lock(this)
{
Vector3 lastCoordEachThreeSecs = LastCoordinate;
}
}
I'm using the standard camera2DFollow script that comes with Unity 5. But I have a problem with the position of the camera. I've rotated my main camera and it looks like this now.
You see that my player is on top of the screen instead of the middle.
This is the default script in C# for the people who don't have it.
using System;
using UnityEngine;
namespace UnityStandardAssets._2D
{
public class Camera2DFollow : MonoBehaviour
{
public Transform target;
public float damping = 1;
public float lookAheadFactor = 3;
public float lookAheadReturnSpeed = 0.5f;
public float lookAheadMoveThreshold = 0.1f;
private float m_OffsetZ;
private Vector3 m_LastTargetPosition;
private Vector3 m_CurrentVelocity;
private Vector3 m_LookAheadPos;
// Use this for initialization
private void Start()
{
m_LastTargetPosition = target.position;
m_OffsetZ = (transform.position - target.position).z;
transform.parent = null;
}
// Update is called once per frame
private void Update()
{
// only update lookahead pos if accelerating or changed direction
float xMoveDelta = (target.position - m_LastTargetPosition).x;
bool updateLookAheadTarget = Mathf.Abs(xMoveDelta) > lookAheadMoveThreshold;
if (updateLookAheadTarget)
{
m_LookAheadPos = lookAheadFactor*Vector3.right*Mathf.Sign(xMoveDelta);
}
else
{
m_LookAheadPos = Vector3.MoveTowards(m_LookAheadPos, Vector3.zero, Time.deltaTime*lookAheadReturnSpeed);
}
Vector3 aheadTargetPos = target.position + m_LookAheadPos + Vector3.forward*m_OffsetZ;
Vector3 newPos = Vector3.SmoothDamp(transform.position, aheadTargetPos, ref m_CurrentVelocity, damping);
transform.position = newPos;
m_LastTargetPosition = target.position;
}
}
}
I want to change the Y to a +3 of the current position. So if my camera is on Y 2 than put it on Y 5. (This makes it so the player is in the middle and not on the top).
Thanks for the help!
You can do this by adding 3 to the camera's position at the end of each frame but I recommend against it.
What I would do, is create an empty object, name it "PlayerCameraCenter" and make the player parent to this object; then place the camera center wherever you want relative to the player, like y = 3, and make the camera follow this object instead of the player.
This way you can easily change the position of the camera, through the editor without fiddling with code.