transform.position teleporting. Not smoothly moving - c#

Simple question but hard time finding an answer.
I have a hook mechanic where on the press of key.B, a hook will fire for 5 seconds then should come back.
This code is working fine, just that when the code allocated to recall the object, it does not comeback smoothly, it teleports.
Here is the code, the problem-specific line in BOLD
public class Hook : MonoBehaviour
{ //Remember Couroutine is pretty much update()
public Transform Target;
private float Thrust; // Int for motion
public Rigidbody rb;
public float HookTravelTime; //Define float for seconds
bool isHookActive = false; //Are we currently moving?
public float timeHookTraveling = 0f;
// Use this for initialization
void Start()
{
Thrust = 75f;
rb = GetComponent<Rigidbody>();
float walkspeed = Thrust * Time.deltaTime;
}
void OnCollisionEnter(Collision col)
{
if (col.gameObject.name == "mob")
{
Destroy(col.gameObject);
print("Other code negated");
rb.velocity = Vector3.zero;
transform.position = Vector3.MoveTowards(transform.position, Target.position, Thrust);
isHookActive = false;
timeHookTraveling = 0f;
}
}
void ThrowHook()
{
if (Input.GetKeyDown(KeyCode.B))
{
isHookActive = true;
rb.AddForce(Vector3.forward * Thrust);
}
if (isHookActive )
{
if (timeHookTraveling >= HookTravelTime) //if the hook traveled for more than hookTravelTime(5 seconds in your case)
{
print("hehemeth");
rb.velocity = Vector3.zero; //negate addforce from before
**HERE** transform.position = Vector3.MoveTowards(transform.position, Target.position, Thrust);
isHookActive = false;//reset this bool so your Update will not check this script until you don't activate it in your ThrowHook
timeHookTraveling = 0f;//reset the travel time for your next hook activation
}
else//if you havent hit 5 keep increasing
{
timeHookTraveling += Time.deltaTime;//increase your travel time by last frame's time
}
}
}
// Update is called once per frame
void Update()
{
ThrowHook();
}
}
What am I doing wrong? It should work as intended, right?

Vector3.MoveTowards is needed to be run everyframe, that's why I mentioned * Time.deltaTime in comment.
At 5s, rb's velocity becomes zero and isHookActive becomes false, and thus Vector3.MoveTowards is not called everyframe.
if (Input.GetKeyDown(KeyCode.B))
{
isHookActive = true;
rb.AddForce(Vector3.forward * Thrust);
}
if (isHookActive )
{
if (timeHookTraveling >= HookTravelTime) //if the hook traveled for more than hookTravelTime(5 seconds in your case)
{
print("hehemeth");
rb.velocity = Vector3.zero; //negate addforce from before
isHookActive = false;//reset this bool so your Update will not check this script until you don't activate it in your ThrowHook
timeHookTraveling = 0f;//reset the travel time for your next hook activation
}
else//if you havent hit 5 keep increasing
{
timeHookTraveling += Time.deltaTime;//increase your travel time by last frame's time
}
}
else if (!isHookActive && transform.position != Target.position)
{
transform.position = Vector3.MoveTowards(transform.position, Target.position, Thrust * Time.deltaTime);
}
And a even better way is putting
if (Input.GetKeyDown(KeyCode.B))
{
isHookActive = true;
rb.AddForce(Vector3.forward * Thrust);
}
into FixedUpdate() but not Update() while
if (isHookActive )
{... }
remains in Update().

Related

Unity3D - Why my object doesnt move every frame per second

Im struggling with very weird and propably simple problem,I have created a robot and his task is to move towards target I mean Im using Vector3.MoveTowards function in void update to move my robot per every frame but the problem is that he is moving only one time and stopping, he is making one step instead of for example 100. Im working with Unity3D. Here is source code;
public class Test01 : MonoBehaviour
{
public float speed, stopDist, rotationSpeed, moveSpeed, minSpeed, maxBackSpeed, maxFrontSpeed, turnSpeed, riseSpeed;
public Transform target;
private Rigidbody rb;
private float currentSpeed;
public bool isFinding = false;
private PlaySound signal;
void Start()
{
signal = GameObject.FindGameObjectWithTag("Signal").GetComponent<PlaySound>();
rb = GetComponent<Rigidbody>();
}
private void Update()
{
// If space key button is pressed the robot's isFinding bool is becaming true and robot is starting searching for target.
if (Input.GetKeyDown(KeyCode.Space))
{
isFinding = true;
if (Vector3.Distance(transform.position, target.position) > stopDist)
{
transform.position = Vector3.MoveTowards(transform.position, target.position, speed * Time.deltaTime);
signal.isAlarming = true;
signal.Sound();
}
else if (Vector3.Distance(transform.position, target.position) < stopDist)
{
isFinding = false;
signal.isAlarming = false;
SoundManager.instance.StopSound();
StopChasing();
}
}
}
public void StopChasing()
{
transform.position = this.transform.position;
}
Well currently you have everything nested under the GetKeyDown so it is executed only exactly once.
You could change that doing
if (Input.GetKeyDown(KeyCode.Space))
{
isFinding = true;
}
if(isFinding)
{
if (Vector3.Distance(transform.position, target.position) > stopDist)
{
transform.position = Vector3.MoveTowards(transform.position, target.position, speed * Time.deltaTime);
signal.isAlarming = true;
signal.Sound();
}
else
{
isFinding = false;
signal.isAlarming = false;
SoundManager.instance.StopSound();
StopChasing();
}
}
So pressing Space once activates the isFinding mode until it arrives at Vector3.Distance(transform.position, target.position) < stopDist

Unity3D & C# - Can't Jump Properly in my Game

I dont know what to do, when I click play, I click Space for Jump and sometime it jump but sometime it not. What do I do wrong?
i got it from https://www.youtube.com/watch?v=I_mjYhwSsS8
this is my PlayerController Script.
// Start is called before the first frame update
void Start()
{
this.rb = GetComponent<Rigidbody>();
}
// Update is called once per frame
private void FixedUpdate()
{
Grounded();
Jump();
Move();
}
private void Jump()
{
if(Input.GetKeyDown(KeyCode.Space) && this.grounded)
{
this.rb.AddForce(Vector3.up * 4, ForceMode.Impulse);
}
}
private void Grounded()
{
if(Physics.CheckSphere(this.transform.position + Vector3.down, 0.2f, layerMask))
{
this.grounded = true;
}
else
{
this.grounded = false;
}
this.anim.SetBool("jump", !this.grounded);
}
private void Move()
{
float verticalAxis = Input.GetAxis("Vertical");
float horizontalAxis = Input.GetAxis("Horizontal");
Vector3 movement = this.transform.forward * verticalAxis + this.transform.right * horizontalAxis;
movement.Normalize();
this.transform.position += movement * 0.04f;
this.anim.SetFloat("vertical", verticalAxis);
this.anim.SetFloat("horizontal", horizontalAxis);
}
In general you want to get single time event user input within Update!
FixedUpdate is called in fixed time intervals, not every frame which might cause that if you press a button down exactly in a frame where FixedUpdate is not called you miss that event.
Secondly as soon as there is a rigidbody involved you should not use Transform.position to move it but rather go through the Rigidbody component either setting velocity or using MovePosition
private bool isJumpPressed;
private void Start()
{
if(!rb) rb = GetComponent<Rigidbody>();
}
private void Udpate()
{
GetJumpInput();
}
private void GetJumpInput()
{
// it is slightly more efficient to first check the simple bool flag
if(grounded && (Input.GetKeyDown(KeyCode.Space))
{
// set the jump flag that is later handled in FixedUpdate
isJumpPressed = true;
}
}
private void FixedUpdate()
{
Grounded();
HandleJump();
Move();
}
private void HandleJump()
{
// since the grounded might have changed in the meantime check it again
// then check and handle the jump flag
if(grounded && isJumpPressed)
{
rb.AddForce(Vector3.up * 4, ForceMode.Impulse);
}
// after having handled the input always reset it
isJumpPressed = false;
}
private void Grounded()
{
grounded = Physics.CheckSphere(this.transform.position + Vector3.down, 0.2f, layerMask);
anim.SetBool("jump", !this.grounded);
}
private void Move()
{
// you might want to consider to only allow a change of this while on the ground
// so midair you can't change the direction
//if(!grounded) return;
var verticalAxis = Input.GetAxis("Vertical");
var horizontalAxis = Input.GetAxis("Horizontal");
// store the current velocity
var velocity = rb.velocity;
// most importantly store the Y velocity in order to maintain that one
var y = velocity.y;
// instead of normalize you rather want to Clamp the magnitude
// otherwise you also get magnitude 1 even though the input is actually smaller
velocity = Vector3.ClampMagnitude(rb.rotation * Vector3.forward * verticalAxis + rb.rotation * Vector3.right * horizontalAxis, 1) * 0.04f;
// finally maintain the Y velocity so you can't fly ;)
velocity.y = y;
// and assign the velocity back to the rigidbody
rb.velocity = velocity;
this.anim.SetFloat("vertical", verticalAxis);
this.anim.SetFloat("horizontal", horizontalAxis);
}

Unity: raycast groundcheck 2d doesn't work

So I want to check if there is ground under my character
here is the code
public class move2d : MonoBehaviour
{
public float moveSpeed = 7f;
public float distanceGround;
public bool isGrounded = false;
// Start is called before the first frame update
void Start()
{
distanceGround = GetComponent<Collider2D>().bounds.extents.y;
}
// Update is called once per frame
void Update()
{
if (Physics2D.Raycast(transform.position, -Vector2.up, distanceGround + 0.1f))
{
}
else
{
isGrounded = true;
Jump();
}
Vector3 movement = new Vector3(Input.GetAxis("Horizontal"), 0f, 0f);
transform.position += movement * Time.deltaTime * moveSpeed;
}
void Jump()
{
if (Input.GetButtonDown("Jump") )
{
gameObject.GetComponent<Rigidbody2D>().AddForce(new Vector2(0f, 8f), ForceMode2D.Impulse);
}
}
}
but it doesn't work and I don't understand why it never enter else statement even there my character is on ground.
When dealing with rigidbody you shouldn't use transform.position not for getting and not for setting values. Rather use Rigidbody2D.position for getting and Rigidbody2D.MovePosition in FixedUpdate for setting.
Also shouldn't it be the other way round? You most probably are grounded if the raycast hits something .. not if it doesn't?
// Already reference these via the Inspector
[SerializeField] private RigidBody2D rigidBody2D;
[SerializeField] private Collider2D _collider2D;
public float moveSpeed = 7f;
public float distanceGround;
private Vector2 movement;
private bool jumped;
private void Awake ()
{
// Alternately get it once on runtime
if(! rigidBody2D) rigidBody2D = GetComponent<RigidBody2D>();
if(!_collider2D) _collider2D = GetComponent<Collider2D>();
}
private void Start()
{
// Micro performance improvement by calculating this only once ;)
distanceGround = _collider2D.bounds.extents.y + 0.1f;
}
// Get user Input every frame
private void Update()
{
// Directly check for the button
if (Input.GetButtonDown("Jump"))
{
// if button presed set the jump flag
jumped = true;
}
// store user input
movement = Vector3.right * Input.GetAxis("Horizontal");
}
// Apply physics in the physics update
private void FixedUpdate()
{
// If jump flag is set
if(jumped)
{
// You are grounded when you hit something, not the other way round
if (Physics2D.Raycast(rigidBody2D.position, Vector2.down, distanceGround))
{
rigidbody2D.AddForce(Vector2.up * 8f, ForceMode2D.Impulse);
}
// reset the flag
jumped = false;
}
// apply the normal movement without breaking the physics
rigidBody2D.MovePosition(rigidBody2D.position + movement * Time.deltaTime * moveSpeed);
}

Character doesn't jump sometimes - Unity

I am new to Unity and I am using the following CharacterController for my character. Everything is working well, except that sometimes the character jumps and sometimes it doesn't when I hit the spacebar. I used Debog.Log using Raycast to check if my character is grounded, and the result was True. So what is preventing my character from jumping whenever I hit the key?
using UnityEngine;
using System.Collections;
[RequireComponent(typeof(CharacterController))]
public class RPGMovement : MonoBehaviour
{
public float ForwardSpeed = 8f;
public float BackwardSpeed = 4f;
public float StrafeSpeed = 5f;
public float RotateSpeed = 110f;
CharacterController m_CharacterController;
Vector3 m_LastPosition;
Animator m_Animator;
PhotonView m_PhotonView;
PhotonTransformView m_TransformView;
float m_AnimatorSpeed;
Vector3 m_CurrentMovement;
float m_CurrentTurnSpeed;
Vector3 playerVelocity;
private bool groundedPlayer;
private float jumpHeight = 0.9f;
private float gravityValue = -20.81f;
void Start()
{
m_CharacterController = GetComponent<CharacterController>();
m_Animator = GetComponent<Animator>();
m_PhotonView = GetComponent<PhotonView>();
m_TransformView = GetComponent<PhotonTransformView>();
}
void Update()
{
if (m_PhotonView.isMine == true)
{
ResetSpeedValues();
UpdateRotateMovement();
UpdateForwardMovement();
UpdateBackwardMovement();
UpdateStrafeMovement();
MoveCharacterController();
UpdateJump();
ApplySynchronizedValues();
}
UpdateAnimation();
}
void UpdateAnimation()
{
Vector3 movementVector = transform.position - m_LastPosition;
float speed = Vector3.Dot(movementVector.normalized, transform.forward);
float direction = Vector3.Dot(movementVector.normalized, transform.right);
if (Mathf.Abs(speed) < 0.2f)
{
speed = 0f;
}
if (speed > 0.6f)
{
speed = 1f;
direction = 0f;
}
if (speed >= 0f)
{
if (Mathf.Abs(direction) > 0.7f)
{
speed = 1f;
}
}
m_AnimatorSpeed = Mathf.MoveTowards(m_AnimatorSpeed, speed, Time.deltaTime * 5f);
m_Animator.SetFloat("Speed", m_AnimatorSpeed);
m_Animator.SetFloat("Direction", direction);
m_LastPosition = transform.position;
}
void ResetSpeedValues()
{
m_CurrentMovement = Vector3.zero;
m_CurrentTurnSpeed = 0;
}
void ApplySynchronizedValues()
{
m_TransformView.SetSynchronizedValues(m_CurrentMovement, m_CurrentTurnSpeed);
}
void MoveCharacterController()
{
m_CharacterController.Move(m_CurrentMovement * Time.deltaTime);
}
void UpdateForwardMovement()
{
if (Input.GetKey(KeyCode.W) || Input.GetAxisRaw("Vertical") > 0.1f)
{
m_CurrentMovement = transform.forward * ForwardSpeed;
}
}
void UpdateBackwardMovement()
{
if (Input.GetKey(KeyCode.S) || Input.GetAxisRaw("Vertical") < -0.1f)
{
m_CurrentMovement = -transform.forward * BackwardSpeed;
}
}
void UpdateStrafeMovement()
{
if (Input.GetKey(KeyCode.Q) == true)
{
m_CurrentMovement = -transform.right * StrafeSpeed;
}
if (Input.GetKey(KeyCode.E) == true)
{
m_CurrentMovement = transform.right * StrafeSpeed;
}
}
void UpdateRotateMovement()
{
if (Input.GetKey(KeyCode.A) || Input.GetAxisRaw("Horizontal") < -0.1f)
{
m_CurrentTurnSpeed = -RotateSpeed;
transform.Rotate(0.0f, -RotateSpeed * Time.deltaTime, 0.0f);
}
if (Input.GetKey(KeyCode.D) || Input.GetAxisRaw("Horizontal") > 0.1f)
{
m_CurrentTurnSpeed = RotateSpeed;
transform.Rotate(0.0f, RotateSpeed * Time.deltaTime, 0.0f);
}
}
void UpdateJump()
{
groundedPlayer = m_CharacterController.isGrounded;
if (groundedPlayer && playerVelocity.y < 0)
{
playerVelocity.y = 0f;
}
if (Input.GetButtonDown("Jump") && groundedPlayer)
{
playerVelocity.y += Mathf.Sqrt(jumpHeight * -3.0f * gravityValue);
m_Animator.SetTrigger("Jump");
print("Jumping Now");
}
playerVelocity.y += gravityValue * Time.deltaTime;
m_CharacterController.Move(playerVelocity * Time.deltaTime);
}
}
Best guess is that "m_PhotonView.isMine" is not returning true on the frames where you're missing input. It only checks jump input for that frame, so if the last frame you pressed it but jumping wasn't checked then that input is lost forever. First test this. Change the update code to this:
void Update()
{
if (Input.GetButtonDown("Jump")) { Debug.Log("Jump was pressed at {Time.time}"); }
if (m_PhotonView.isMine == true)
{
if (Input.GetButtonDown("Jump")) { Debug.Log("Attempting Jump at {Time.time}"); }
ResetSpeedValues();
UpdateRotateMovement();
UpdateForwardMovement();
UpdateBackwardMovement();
UpdateStrafeMovement();
MoveCharacterController();
UpdateJump();
ApplySynchronizedValues();
}
UpdateAnimation();
}
Then play the game and jump a bunch. The first debug log line should happen every time you click the spacebar no matter what. The second debug line would only happen if physics are calculated that frame. Both have times attached. Keep jumping until the jump doesn't work. If that jump only produces the first debug log and not the second, then I am correct and that is your issue.
If so, then it's an easy fix. Add a new bool variable called "jumpInput". Whenever you check if jump was pressed, instead check if "jumpInput" is true. Then, change update to this:
void Update()
{
if (Input.GetButtonDown("Jump")) { jumpInput = true; }
if (m_PhotonView.isMine == true)
{
ResetSpeedValues();
UpdateRotateMovement();
UpdateForwardMovement();
UpdateBackwardMovement();
UpdateStrafeMovement();
MoveCharacterController();
UpdateJump();
ApplySynchronizedValues();
jumpInput = false;
}
UpdateAnimation();
}
This way if you pressed jump, it's set to true... but it's only set to false after physics are done. So if you press jump on frame 20 and physics are somehow not calculated until frame 25, it'll still know that you pressed jump at some point and thus execute it. If you're using networking, you might want to also have another variable that's what frame jump was pressed. That way you can figure out how many frames it's been since input and compensate for missed time in the jump if necessary.

Unity 2D Jump script

So I was trying to implement double jumping in my game, which doesn't work. And now, somehow, not only can't my players double jump, they can't even jump either!
update: they can jump now, still can't double jump though.
This is my whole movement script:
using UnityEngine;
namespace Players
{
public class Actor : MonoBehaviour
{
//in order to control both players using 1 script.
public int playerIdx;
//Variables.
public float movementSpeed = 150f;
public float jumpForce = 250f;
//Ground stuff.
public LayerMask whatIsGround;
public bool grounded;
//boolean stuff.
private bool facingRight;
private bool moving;
//Needed to check if player is on the ground.
public Transform groundCheck;
//Limit player's movement speed.
public float maxMovementSpeed = 400f;
//Double jump stuff.
private bool doubleJumpReady;
//rb
private Rigidbody2D rb;
// Start is called before the first frame update
void Start()
{
doubleJumpReady = true;
rb = GetComponent<Rigidbody2D>();
facingRight = true;
}
// Update is called once per frame
void FixedUpdate()
{
SlowDown();
}
private void LateUpdate()
{
grounded = Physics2D.OverlapCircle(groundCheck.position, 0.1f, whatIsGround);
if (grounded)
doubleJumpReady = true;
}
private void SlowDown()
{
if (moving) return;
//if player is not moving, slow them down.
if (rb.velocity.x > 0.2f)
rb.AddForce(movementSpeed * Time.deltaTime * -Vector2.right);
if (rb.velocity.x < -0.2f)
rb.AddForce(movementSpeed * Time.deltaTime * Vector2.right);
}
public void Move(int dir)
{
//Flip the player.
Flip(dir);
//Moving the player.
moving = true;
float xVel = rb.velocity.x; //Get x velocity.
if ( dir > 0)
rb.AddForce(movementSpeed * Time.deltaTime * Vector2.right * dir);
else if (dir < 0)
rb.AddForce(movementSpeed * Time.deltaTime * Vector2.right * dir);
else if (dir == 0) { } //do nothing.
//Help player turn around faster.
if (xVel > 0.2f && dir < 0)
rb.AddForce(movementSpeed * 3.2f * Time.deltaTime * -Vector2.right);
if (xVel < 0.2f && dir > 0)
rb.AddForce(movementSpeed * 3.2f * Time.deltaTime * Vector2.right);
}
private void Flip(int dir)
{
if (facingRight && dir == -1 || !facingRight && dir == 1)
{
facingRight = !facingRight;
transform.Rotate(0f, 180f, 0f);
}
}
protected void Jump()
{
if (grounded)
{
rb.AddForce(Vector2.up * jumpForce);
grounded = false;
doubleJumpReady = true;
}
else if (!grounded && doubleJumpReady)
{
rb.AddForce(Vector2.up * jumpForce);
doubleJumpReady = false;
}
}
}
}
I don't know if it is because of my jump script, or my player script:
void Update()
{
if (playerIdx == 1)
{
if (Input.GetKey(KeyCode.A))
Move(-1);
if (Input.GetKey(KeyCode.D))
Move(1);
if (Input.GetKey(KeyCode.W))
Jump();
}
if (playerIdx == 2)
{
if (Input.GetKey(KeyCode.LeftArrow))
Move(-1);
if (Input.GetKey(KeyCode.RightArrow))
Move(1);
if (Input.GetKey(KeyCode.UpArrow))
Jump();
}
}
So how can I fix this?
as far as i can see you never reset the
doubleJumpReady = false;
Variable. To fix this simply change the jump code to:
protected void Jump()
{
if (grounded)
{
rb.AddForce(Vector2.up * jumpForce);
grounded = false;
doubleJumpReady = true;
}
else if (!grounded && doubleJumpReady)
{
rb.AddForce(Vector2.up * jumpForce);
doubleJumpReady = false;
}
}
Hope it works ;).
EDIT:
grounded is set by overlapping spheres. Therefore no need to set it here.
Use this code and press your jump btn 2 times and see if the Debug.Log message shows up. Also, your player ID (idx is not needed.) As far as i can see your script is attached two to different objects. Therefore their variables are not shared anyways.
protected void Jump()
{
if (grounded)
{
rb.AddForce(Vector2.up * jumpForce);
doubleJumpReady = true;
}
else if (!grounded && doubleJumpReady)
{
rb.AddForce(Vector2.up * jumpForce);
doubleJumpReady = false;
Debug.Log("I am double jumping");
}
}
And the final problem is, you do not execute one of your jumps you execute both at once.
THis happens due to your execution.
Input.GetKey(KeyCode.UP)
instead use:
Input.GetKeyDown(KeyCode.Up);
GetKeyDown returns true when the button is pressed.
GetKey returns true WHILE the button is pressed.
Hope it works now ;)
I would implement it with a counter, you can set the number of jumps you want.
The code would be like this:
jumpCount = 0;
protected void Jump()
{
if(!grounded && jumpCount < 2)
{
jumpCount++;
rb.AddForce(Vector2.up * jumpForce);
}
if(grounded)
jumpCount = 0;
}
Going off the assumption that you can now perform the normal jump again after reading the comments. I think the reason you can't 'double jump' is that when you call the Jump() method, you don't just call it once, you call it twice, so what happens is the player jumps and then immediately double jumps and so you don't actually notice that the double jump has occurred. You could make it so that your doubleJumpReady boolean is only true after a set amount of time after you have jumped initially using some sort of co-routine or something I implemented for a sort of double jump mechanic once was that the user could press the jump button again to double jump only when the player had reached the maximum height of the initial jump or after.

Categories