using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class Follow : MonoBehaviour
{
public Transform targetToFollow;
public Text text;
public float lookAtRotationSpeed;
public float moveSpeed;
private float minMoveSpeed = 0f;
private bool moveInDistance = false;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void FixedUpdate()
{
Vector3 lTargetDir = targetToFollow.position - transform.position;
lTargetDir.y = 0.0f;
transform.rotation = Quaternion.RotateTowards(transform.rotation,
Quaternion.LookRotation(lTargetDir), Time.time * lookAtRotationSpeed);
var distance = Vector3.Distance(transform.position, targetToFollow.position);
text.text = distance.ToString();
if(distance > 5f && moveInDistance == false)
{
moveSpeed = moveSpeed + 0.5f * Time.deltaTime;
}
if (distance > 1.5f && distance < 5f)
{
moveInDistance = true;
moveSpeed = moveSpeed + 0.05f;
}
else if (distance < 1f)
{
moveSpeed = Mathf.Max(minMoveSpeed, moveSpeed - 0.3f);
}
transform.position = Vector3.MoveTowards(transform.position, targetToFollow.position, Time.deltaTime * moveSpeed);
}
}
The first time the game is running the transform will move faster to the target in this part :
if(distance > 5f && moveInDistance == false)
{
moveSpeed = moveSpeed + 0.5f * Time.deltaTime;
}
Then when the transform has reach close distance to the target he will keep following the target in slower speed in this part :
if (distance > 1.5f && distance < 5f)
{
moveInDistance = true;
moveSpeed = moveSpeed + 0.05f;
}
The problem is if in the future in the game I will want to change the speed again if the transform will be more then 5 distance from the target. Then where should I change the flag moveInDistance to false again ?
You can put moveInDistance before moving logical like
moveInDistance = distance > 1.5f && distance < 5f; // or other condition
if(distance > 5f && !moveInDistance)
{
moveSpeed = moveSpeed + 0.5f * Time.deltaTime;
}
if (distance > 1.5f && distance < 5f)
{
moveSpeed = moveSpeed + 0.05f;
}
else if (distance < 1f)
{
moveSpeed = Mathf.Max(minMoveSpeed, moveSpeed - 0.3f);
}
...
Add example if else for comment
...
float ms = moveSpeed;
if(distance > 5f)
{
ms = moveSpeed + 0.5f; //move faster
}
else if (distance < 1f)
{
ms = Mathf.Max(minMoveSpeed, moveSpeed - 0.3f); // move slower
}else {
//set your ms (movement speed) when follwer is move in distance range if you don’t need default value that equal moveSpeed here
}
// by default if distance in range of 1-5 ms is equal moveSpeed and IsInRange Case
// move object by using ms value
...
Ps. In my opinion I think moveInDistance is not important thing to use in my if-else condition you can check distance range in elseIf statement instead of set to moveInDistance
You could edit your existing if statement alittle to include what you are after:
if(distance > 5f)
{
if (moveInDistance == false) {
moveSpeed = moveSpeed + 0.5f * Time.deltaTime;
} else {
moveInDistance = false;
}
}
Related
I'm working on a 3D fps, and want a dash. The player uses a character controller, and no rigidbody. My original implementation:
x = Input.GetAxis("Horizontal"); //just gets ur wasd inputs by default
z = Input.GetAxis("Vertical");
Vector3 move = transform.right * x + transform.forward * z; //creates a movement vector
controller.Move(move * speed * Time.deltaTime); //moves the player
if (currentDashLength < dashLength ) //dashes for a set time period
{
currentDashLength += Time.deltaTime;
velocity.y = 0; //keeps you up in the air
controller.Move(move * Time.deltaTime * dashSpeed);
}
else
{
currentDashLength = dashLength;
isDashing = false;
}
if (Input.GetKeyDown(KeyCode.LeftShift) && dashTimer >= 1 && !isCrouching)
{
isDashing = true;
dashTimer -= 1;
currentDashLength = 0f;
health.GiveIFrames(dashLength);
}
This dash works fine, but I realized that if you had pressed shift right after starting to move, the dash would be significantly weaker. I assumed this was due to the fact that my current velocity was low, so the acceleration from the Move function didn't make me reach the speed I would when already moving at terminal velocity. I tried to fix this by multiplying the Move function inputs by 1/(the horizontal velocity of the player) but this didn't fix the issue. My full dashing code:
float dispX = transform.position.x - posX; //gets chnage in position since last frame, this is important to caluclate velocity, since the player isnt using a rigidbody
float dispY = transform.position.y - posY;
float dispZ = transform.position.z - posZ;
posX = transform.position.x;
posY = transform.position.y;
posZ = transform.position.z;
float horizontalVelocity = Mathf.Sqrt(dispX*dispX + dispZ*dispZ) / Time.deltaTime;
x = Input.GetAxis("Horizontal"); //just gets ur wasd inputs by default
z = Input.GetAxis("Vertical");
Vector3 move = transform.right * x + transform.forward * z; //creates a movement vector
controller.Move(move * speed * Time.deltaTime); //moves the player
if (currentDashLength < dashLength )
{
currentDashLength += Time.deltaTime;
velocity.y = 0;
if(horizontalVelocity == 0)
{
Debug.Log("Cannot dash while standing still");
}
else
{
controller.Move(move * Time.deltaTime * dashSpeed * (1 / horizontalVelocity));
}
}
else
{
currentDashLength = dashLength;
isDashing = false;
}
if (Input.GetKeyDown(KeyCode.LeftShift) && dashTimer >= 1 && !isCrouching)
{
isDashing = true;
dashTimer -= 1;
currentDashLength = 0f;
health.GiveIFrames(dashLength);
}
How would I go about ensuring that the dash speed is constant?
(I tried to post this on unity answers, but the website isn't responding to me)
To get the desired behaviour I have re-written your original script by quite a lot, hope that is okay! This code only give you movement and dash control, and nothing else. You'll have to add your crouch check and health stuff back in.
Essentially, we check if the player is dashing before applying any movement, this way we don't add the dash to the current movement. If the player is dashing, we ignore their forward input and apply forward movement based on the predetermined dash. If we aren't dashing then we apply the calculated movement as usual.
private CharacterController controller;
public float speed = 10.0f;
public float dashSpeed = 20.0f;
public float dashLength = 0.5f;
private float currentDashLength = 0;
private bool isDashing = false;
private void Start()
{
controller = GetComponent<CharacterController>();
}
private void Update()
{
float x = Input.GetAxis("Horizontal");
float z = Input.GetAxis("Vertical");
// Same as before
Vector3 move = new Vector3(x, 0, z);
// gets the move direction
// before we move, check if dashing
if (isDashing)
{
currentDashLength += Time.deltaTime;
move = new Vector3(move.x * dashSpeed, move.y, 1 * dashSpeed);
// this gets the current player movement, and replaces the forward velocity rather than adding to it. We add to the sideways velocity to allow for slight directional control.
// do this instead if you want the dash to only move the player in the forward direction, and prevent strafing: move = new Vector3(move.x, move.y, 1 * dashSpeed);
if (currentDashLength >= dashLength) // when we run out of time, set dash to false
{
isDashing = false;
}
}
else // if we are not dashing then move as normal
{
move *= speed;
if (Input.GetKeyDown(KeyCode.LeftShift) && move.magnitude > 0) // stops the dash ability being used when stationary
{
isDashing = true;
currentDashLength = 0;
}
}
controller.Move(move * Time.deltaTime);
}
float dispX = transform.position.x - posX;
float dispY = transform.position.y - posY;
float dispZ = transform.position.z - posZ;
posX = transform.position.x;
posY = transform.position.y;
posZ = transform.position.z;
float horizontalVelocity = Mathf.Sqrt(dispX*dispX + dispZ*dispZ) / Time.deltaTime;
x = Input.GetAxis("Horizontal");
z = Input.GetAxis("Vertical");
Vector3 move = transform.right * x + transform.forward * z;
controller.Move(move * speed * Time.deltaTime);
if (currentDashLength < dashLength )
{
currentDashLength += Time.deltaTime;
velocity.y = 0;
Vector3 dashMove = move * Time.deltaTime * dashSpeed; // Calculates the dash movement vector
// Move the player based on the dash movement vector
controller.Move(dashMove);
}
else
{
currentDashLength = dashLength;
isDashing = false;
}
if (Input.GetKeyDown(KeyCode.LeftShift) && dashTimer >= 1 && !isCrouching)
{
isDashing = true;
dashTimer -= 1;
currentDashLength = 0f;
health.GiveIFrames(dashLength);
}
By calculating the dash movement vector independently from the player's horizontal velocity, you can ensure that the dash speed remains constant regardless of whether the player is moving before dashing or not.
I think this is what you were trying to achieve? Hope it helps!
I make 3rd person game, where i have script for control model of my character.
Problem is that when i jump and don't push any button he stack in the air, but if i jump with pushing controlling button he worked normally.
Thanks in advance.
Player controller script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[RequireComponent(typeof(CharacterController))]
public class RelativeMovement : MonoBehaviour
{
[SerializeField] private Transform target;
public float rotSpeed = 5.0f;
public float moveSpeed = 6.0f;
public float jumpSpeed = 15.0f;
public float gravity = -9.8f;
public float terminalVelocity = -20.0f;
public float minFall = -1.5f;
private float _vertSpeed;
private CharacterController _charController;
private ControllerColliderHit _contact;
void Start()
{
_vertSpeed = minFall;
_charController = GetComponent<CharacterController>();
}
void Update()
{
Vector3 movement = Vector3.zero;
float horInput = Input.GetAxis("Horizontal");
float vertInput = Input.GetAxis("Vertical");
if(horInput != 0 || vertInput != 0)
{
movement.x = horInput * moveSpeed;
movement.z = vertInput * moveSpeed;
movement = Vector3.ClampMagnitude(movement, moveSpeed);
Quaternion tmp = target.rotation;
target.eulerAngles = new Vector3(0, target.eulerAngles.y, 0);
movement = target.TransformDirection(movement);
target.rotation = tmp;
Quaternion direction = Quaternion.LookRotation(movement);
transform.rotation = Quaternion.Lerp(transform.rotation,
direction, rotSpeed * Time.deltaTime);
bool hitGround = false;
RaycastHit hit;
if(_vertSpeed < 0 && Physics.Raycast(transform.position, Vector3.down, out hit))
{
float check =
(_charController.height + _charController.radius) / 1.9f;
hitGround = hit.distance <= check;
}
if(hitGround)
{
if (Input.GetButtonDown("Jump"))
{
_vertSpeed = jumpSpeed;
}
else
{
_vertSpeed = minFall;
}
}
else
{
_vertSpeed += gravity * 5 * Time.deltaTime;
if(_vertSpeed < terminalVelocity)
{
_vertSpeed = terminalVelocity;
}
if(_charController.isGrounded)
{
if(Vector3.Dot(movement, _contact.normal) < 0)
{
movement = _contact.normal * moveSpeed;
}
else
{
movement += _contact.normal * moveSpeed;
}
}
}
movement.y = _vertSpeed;
movement *= Time.deltaTime;
_charController.Move(movement);
}
}
void OnControllerColliderHit(ControllerColliderHit hit)
{
_contact = hit;
}
}
Jesus photo:
enter image description here
I didn't go through all of the code but I can immediately see what can cause the problem you describe:
your entire logic happen under this if statement:
if(horInput != 0 || vertInput != 0)
So everything inside this logic block will happen only when you actively press a movement button.
You can remove it (or better yet, close the if block at the right spot.. I think it would be 2 lines down)
The answer of this question is:
if((horInput == 0 || vertInput == 0) || (horInput != 0 && vertInput != 0))
{
movement.x = horInput * moveSpeed;
movement.z = vertInput * moveSpeed;
if(horInput != 0 || vertInput != 0)
{
movement = Vector3.ClampMagnitude(movement, moveSpeed);
Quaternion tmp = target.rotation;
target.eulerAngles = new Vector3(0, target.eulerAngles.y, 0);
movement = target.TransformDirection(movement);
target.rotation = tmp;
Quaternion direction = Quaternion.LookRotation(movement);
transform.rotation = Quaternion.Lerp(transform.rotation,
direction, rotSpeed * Time.deltaTime);
}
Thanks Joe
so I've been struggling with this for about 3 hours trying to make this work so excuse my spaghetti code.
this script is making the character move and also sprint. i cant see how to add more to this paragraph i belive ive made a pretty concise point and told helpers what my problem is
if (Input.GetKeyDown(KeyCode.LeftShift) && isGrounded)
{
moveSpeed = 10f;
}
if (Input.GetKeyUp(KeyCode.LeftShift) && isGrounded)
{
moveSpeed = 6f;
}
but it seem as after i added the script to slow down mid air sprinting ceased to work
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerMove : MonoBehaviour
{
public Rigidbody rb;
public Transform _camera;
public float moveSpeed = 6f;
float jumpForce = 5f;
public LayerMask Ground;
bool isGrounded;
bool Landed;
void Start()
{
}
void Update()
{
//grounding
isGrounded = Physics.CheckSphere(new Vector3(transform.position.x, transform.position.y - 1, transform.position.z), 0.4f, Ground);
Landed = isGrounded
//facing direction
Debug.DrawLine(_camera.position, transform.forward * 2.5f);
//SPRINTING
if (isGrounded == false)
{
moveSpeed = 2f;
}
else
{
if (Input.GetKeyUp(KeyCode.W) == false)
{
moveSpeed = 6f;
}
if (Input.GetKeyDown(KeyCode.LeftShift) && isGrounded)
{
moveSpeed = 10f;
}
if (Input.GetKeyUp(KeyCode.LeftShift) && isGrounded)
{
moveSpeed = 6f;
}
}
//moving
float x = Input.GetAxisRaw("Horizontal") * moveSpeed;
float y = Input.GetAxisRaw("Vertical") * moveSpeed;
//jumping
if (Input.GetKeyDown(KeyCode.Space) && isGrounded)
rb.velocity = new Vector3(rb.velocity.x, jumpForce, rb.velocity.z);
//setting movement
Vector3 move = transform.right * x + transform.forward * y;
rb.velocity = new Vector3(move.x, rb.velocity.y, move.z);
}
}
Maybe some pseudo code could be:
if(isGrounded)
{
moveSpeed = isSprinting
? 10f // sprinting on ground
: 6f; // walking on ground
}
else
{
moveSpeed = isSprinting
? 3f // sprinting in the air
: 2f; // moving in the air
}
Or reversely:
if(isSprinting)
{
moveSpeed = isGrounded
? 10f // sprinting on ground
: 3f; // sprinting in the air
}
else
{
moveSpeed = isGrounded
? 6f // walking on ground
: 2f; // moving in the air
}
I am a new unity developer that is trying to make a 3d game. So far I have managed to develop a pretty cool first-person character controller. It uses a character controller and it has sliding and jumping mechanics.
And the question is:
How can I make my player lose sliding speed when sliding up a slope and gain sliding speed when sliding down a slope?
Here is my code so far and please keep in mind that my code is messy and that I am a still beginner. If you need any further information please don't hesitate to ask. Thanks for your time! Also, any suggestions are more than welcome!
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerMovementScript : MonoBehaviour
{
//Character controller refrence
private CharacterController controller;
//Camera refrence
[SerializeField] Camera fpsCam;
//IsGrounded variables
private float groundDistance = 1.5f;
public bool isGrounded;
//SomethingAbove variables
private float cellingDistance = 1.5f;
public bool somethingAbove;
//Variables
//Speed that is modified
private float speed = 12f;
//Default speed
private float baseSpeed = 12f;
//Slide speed that is modified
private float slideSpeed = 15f;
//Default slide speed
private float slideSpeedStorage = 15f;
//Crouch speed
private float crouchSpeed = 3f;
//Jump height
private float jumpHeight = 2f;
private bool isJumping = false;
//Gravity
private float gravity = -14.715f;
//Air resistance
private float xAirResistance = 0.5f;
private float zAirResistance = 0.35f;
//Air multipiler
private float airMultiplierJump = 1.1f;
//Fov
private float fovWhenSliding = 100f;
private float defaultFov;
//Crouch variables
float originalHeight;
float reducedHeight = 0.5f;
//Player input
float x;
float z;
bool jump;
bool control;
//Directions
Vector3 velocity;
private Vector3 move = new Vector3();
private Vector3 slideDirection = new Vector3();
private Vector3 jumpDirection = new Vector3();
private void Awake()
{
controller = GetComponent<CharacterController>();
}
private void Start()
{
//Getting the original height
originalHeight = controller.height;
//Getting the default fov
defaultFov = fpsCam.fieldOfView;
}
private void Update()
{
MyInput();
Calculations();
PlayerMovement();
}
private void MyInput()
{
//Getting WASD input
x = Input.GetAxis("Horizontal");
z = Input.GetAxis("Vertical");
//Space bar input
jump = Input.GetButtonDown("Jump");
//Left control input
control = Input.GetKey(KeyCode.LeftControl);
}
private void Calculations()
{
//Checking if player is grounded
Debug.DrawRay(transform.position, Vector3.down * groundDistance, Color.red);
isGrounded = Physics.Raycast(transform.position, Vector3.down, groundDistance);
//Checking if player can get up
Debug.DrawRay(transform.position, Vector3.up * cellingDistance, Color.black);
somethingAbove = Physics.Raycast(transform.position, Vector3.up, cellingDistance);
/*//Getting the player has enough speed and getting jump direction
if (isGrounded && jump && z > 0)
{
//If pressing W the jump direction is forward
jumpDirection = transform.forward;
}*/
//Movement in air
if (isGrounded)
{
//On ground movement
move = transform.right * x + transform.forward * z;
}
else
{
//If pressing W
if (z > 0)
{
//Jump direction multiplied by airMultiplierJump
move = transform.forward * z * airMultiplierJump + transform.right * x * xAirResistance;
}
else
{
//Jump direction = Vector3.zero;
move = transform.forward * z * zAirResistance + transform.right * x * xAirResistance;
}
}
//Checking if the player has enough speed for sliding and getting direction
if (Input.GetKeyDown(KeyCode.LeftControl) && isGrounded)
{
if (z == 1 || x == 1 || x == -1)
{
//If pressing W || A || D slide direction is the direction u are moving towards
slideDirection = transform.forward * z + transform.right * x;
}
else
{
//If pressing S the slide direction is zero
slideDirection = Vector3.zero;
}
}
//Adding extra force so the player stays on ground
if (isGrounded && velocity.y < 0)
{
velocity.y = -2f;
}
//Calculating gravity
velocity.y += gravity * Time.deltaTime;
//Normalizing the move vector so the diagonal and normal movement are the same speed
if (move.sqrMagnitude > 1)
{
move.Normalize();
}
//Extra force on slope downwards so the player stays on it
if((x != 0 || z != 0) && OnSlope())
{
controller.Move(Vector3.down * controller.height / 2 * 2f);
}
}
private void PlayerMovement()
{
Jump();
Slide();
//Moving the player with WASD
controller.Move(move * speed * Time.deltaTime);
//Applying gravity
controller.Move(velocity * Time.deltaTime);
}
private void Slide()
{
//Checking for left control input and if the player is grounded
if(control && isGrounded && !OnSlope())
{
StartSlide();
}
//Checking if the player can uncrouch
else if(!somethingAbove && isGrounded)
{
StopSlide();
}
else if (!control)
{
controller.height = originalHeight;
speed = baseSpeed;
}
else if(control && !isGrounded)
{
controller.height = originalHeight;
speed = baseSpeed;
}
else
{
controller.height = reducedHeight;
speed = crouchSpeed;
}
}
//Starting to slide
private void StartSlide()
{
if (z != 1 || x != -1 || x != 1)
{
speed = crouchSpeed;
}
else
{
speed = 0f;
}
controller.height = reducedHeight;
controller.Move(slideDirection * Time.deltaTime * slideSpeed);
slideSpeed -= 10f * Time.deltaTime;
if (slideSpeed <= 0)
{
slideSpeed = 0f;
speed = crouchSpeed;
}
}
//Stopping the slide
private void StopSlide()
{
controller.height = originalHeight;
speed = baseSpeed;
//Slide speed recovery
slideSpeed = Mathf.Lerp(slideSpeed, slideSpeedStorage, Time.deltaTime * 2);
}
//Jumping mechanic
private void Jump()
{
if (jump && isGrounded)
{
isJumping = true;
velocity.y = Mathf.Sqrt(jumpHeight * -2f * gravity);
}
}
private bool OnSlope()
{
if (isJumping)
{
return false;
}
RaycastHit hit;
if(Physics.Raycast(transform.position, Vector3.down, out hit, controller.height / 2 * 5f))
if(hit.normal != Vector3.up)
{
return true;
}
return false;
}
//Stopping the slide if hitting object
private void OnControllerColliderHit(ControllerColliderHit hit)
{
if(hit.transform.tag == "SlideStop")
{
slideSpeed = 0f;
speed = crouchSpeed;
}
}
}```
private bool OnSlope()
{
if (isJumping)
{
return false;
}
RaycastHit hit;
if(Physics.Raycast(transform.position, Vector3.down, out hit, controller.height / 2 * 5f))
if(hit.normal != Vector3.up)
{
return true;
}
return false;
}
One issue is comparing Vector3s which is bad practice, since they are arrays of floats. Even if the plane is completely planar, it might still return true due to what's known as 'floating point errors'. A better practice is to use:
Vector3.distance(hit.normal, Vector3.up) > .1f // or some other small value;
I'm new to unity and I was following a youtube guide on scripting a 2D platformer, I have gotten to the point now where I am trying to add a flip to the sprite when I turn to go in the opposite direction.
I have added a code and now this is constantly flipping left to right. If I understand correctly its probably because I am constantly applying > 0.1f when moving resulting in the function to continuously run but honestly I am lost so any help would be appreciated.
using UnityEngine;
using System.Collections;
[RequireComponent (typeof (Controller2D))]
public class Player : MonoBehaviour {
public float jumpHeight = 4;
public float timeToJumpApex = .4f;
float accelerationTimeAirbourne = .2f;
float acceleratinTimeGrounded =.1f;
public float moveSpeed = 6;
float jumpVelocity;
float gravity;
Vector3 velocity;
float velocityXSmoothing;
Controller2D controller;
void Start(){
controller = GetComponent<Controller2D>();
gravity = -(2 * jumpHeight) / Mathf.Pow (timeToJumpApex, 2);
jumpVelocity = Mathf.Abs(gravity) * timeToJumpApex;
print ("gravity: " + gravity + " Jump Velocity: " + jumpVelocity);
}
void Update(){
if (controller.collisions.above || controller.collisions.above) {
velocity.y = 0;
}
Vector2 input = new Vector2 (Input.GetAxisRaw ("Horizontal"),
Input.GetAxisRaw ("Vertical"));
if (Input.GetKeyDown (KeyCode.Space) && controller.collisions.below) {
velocity.y = jumpVelocity;
}
float targetVelocityX = input.x * moveSpeed;
velocity.x = Mathf.SmoothDamp(velocity.x, targetVelocityX, ref
velocityXSmoothing, (controller.collisions.below)?
acceleratinTimeGrounded:accelerationTimeAirbourne);
velocity.y += gravity * Time.deltaTime;
controller.Move(velocity * Time.deltaTime);
}
void FixedUpdate(){
if ((Input.GetAxis("Horizontal") > 0.1f && this.transform.localScale.x != 1)
|| (Input.GetAxis("Horizontal") < -0.1f && this.transform.localScale.x !=
-1))
{
flip();
}
}
void flip()
{
this.transform.localScale = new Vector3(this.transform.localScale.x *
-1,
this.transform.localScale.y, this.transform.localScale.z);
}
}
Your error is here
void FixedUpdate(){
if ((Input.GetAxis("Horizontal") > 0.1f && this.transform.localScale.x != 1)|| (Input.GetAxis("Horizontal") < -0.1f && this.transform.localScale.x !=
-1)){
flip();
}
}
After you flip, the condition still evaluates to true. You will need a boolean hasflipped to check if you have already flipped, and if you have don't flip again :)
Fixed update is causing this check to happen once per frame, so you're going to get a bunch of flickering as your sprite flips every frame, unless you specify only to flip once