using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Rolling : MonoBehaviour
{
public NaviManager naviManager;
public float speed;
public float timeToStopRolling;
public string onCollisionState;
private Rigidbody rb;
private bool isTouching = false;
private bool stopRollingCoroutine = false;
private bool stopRolling = false;
// Start is called before the first frame update
void Start()
{
rb = GetComponent<Rigidbody>();
}
// Update is called once per frame
void FixedUpdate()
{
if (naviManager.crateOpenOnce)
{
if (isTouching)
{
if(stopRollingCoroutine == false)
{
StartCoroutine(WaitBeforeStopRolling());
stopRollingCoroutine = true;
}
if (stopRolling == false)
{
rb.AddForceAtPosition(Vector3.right * speed * Time.deltaTime, transform.position);
}
}
else
{
rb.velocity = rb.velocity * 0.95f * Time.deltaTime;
}
}
}
private void OnCollisionEnter(Collision collision)
{
if (collision.gameObject.name == "Crate_0_0")
{
onCollisionState = "Touching !!!";
isTouching = true;
}
if(collision.gameObject.name == "Stair")
{
speed = 100;
}
}
IEnumerator WaitBeforeStopRolling()
{
yield return new WaitForSeconds(timeToStopRolling);
stopRolling = true;
}
}
I want to stop the transform from rolling at once or slowly but the goal is to make it stop rolling after X seconds.
After for example 3 seconds I'm setting the flag stopRolling to true and I used a breakpoint and it is true but the object is kept rolling.
Should I remove force somehow to make it stop?
The object that is rolling has a Rigidbody Use Gravity set to true. and a Sphere Collider.
I didn't do anything to make it start rolling. The object starts rolling once he touches the "Crate_0_0" once the flag isTouching is true.
I also tried to set both velocity and angularVelocity to Vector3.zero but it didn't stop it either.
IEnumerator WaitBeforeStopRolling()
{
yield return new WaitForSeconds(timeToStopRolling);
rb.velocity = Vector3.zero;
rb.angularVelocity = Vector3.zero;
stopRolling = true;
}
Setting the Rigidbody isKinematic to true stop the object but that stop it at once. What if I want to stop the object slowly smooth instead of using isKinematic?
Related
I followed a tutorial on Youtube and it was from a man named Dani. I also followed another one from someone called Dave / GameDevelopment on how to pick up objects in Unity 3D. I have ran into a problem where if I pick up a weapon that was on the ground by default it does not work. My gun floats in the air. I believe the main reason for this (don't quote me on this) is that the Gun is some how not becoming a child of the camera, which is what supposed to happen. Please fix and thanks.
This is my pick up script
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PickUpScript : MonoBehaviour
{
public GrappleGun gunScript;
public Rigidbody rb;
public BoxCollider coll;
public Transform player, gunContainer, fpsCam;
public float pickUpRange;
public float dropForwardForce, dropUpwardForce;
public bool equipped;
public static bool slotFull;
private void Start()
{
if(!equipped)
{
gunScript.enabled = false;
rb.isKinematic = false;
coll.isTrigger = false;
}
if (equipped)
{
gunScript.enabled = true;
rb.isKinematic = true;
coll.isTrigger = true;
slotFull = true;
}
}
private void Update()
{
//equipped and e is pressed
Vector3 distanceToPlayer = player.position - transform.position;
if (!equipped && distanceToPlayer.magnitude <= pickUpRange && Input.GetKeyDown(KeyCode.E) && !slotFull) PickUp();
//Drop if equipped and q is pressed
if (equipped && Input.GetKeyDown(KeyCode.Q)) Drop();
}
private void PickUp()
{
equipped = true;
slotFull = true;
transform.SetParent(gunContainer);
transform.localPosition = Vector3.zero;
transform.localRotation = Quaternion.Euler(Vector3.zero);
transform.localScale = Vector3.one;
//Enable rb and km and coll = trigger
rb.isKinematic = true;
coll.isTrigger = true;
//Enable Script
gunScript.enabled = true;
}
private void Drop()
{
equipped = false;
slotFull = false;
transform.SetParent(null);
//Enable rb and km and coll = trigger
rb.isKinematic = false;
coll.isTrigger = false;
//GunCarries momentum of player
rb.velocity = player.GetComponent<Rigidbody>().velocity;
//AddForce
rb.AddForce(fpsCam.forward * dropForwardForce, ForceMode.Impulse);
rb.AddForce(fpsCam.up * dropUpwardForce, ForceMode.Impulse);
//Enable Script
gunScript.enabled = false;
}
}
This is grapple hook script
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GrappleGun : MonoBehaviour
{
private LineRenderer lr;
private Vector3 grapplePoint;
public LayerMask whatIsGrappable;
public Transform gunTip, camera, player;
private float maxDistance = 50f;
private SpringJoint joint;
void Awake()
{
lr = GetComponent<LineRenderer>();
}
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
if (Input.GetMouseButtonDown(0))
{
StartGrapple();
}
if (Input.GetMouseButtonUp(0))
{
StopGrapple();
}
}
void LateUpdate()
{
DrawRope();
}
void StartGrapple()
{
RaycastHit hit;
if (Physics.Raycast(camera.position, camera.forward, out hit, maxDistance, whatIsGrappable))
{
grapplePoint = hit.point;
joint = player.gameObject.AddComponent<SpringJoint>();
joint.autoConfigureConnectedAnchor = false;
joint.connectedAnchor = grapplePoint;
float distanceFromPoint = Vector3.Distance(player.position, grapplePoint);
//the distance
joint.maxDistance = distanceFromPoint * 0.8f;
joint.minDistance = distanceFromPoint * 0.25f;
joint.spring = 5f;
joint.damper = 0;
joint.massScale = 1f;
lr.positionCount = 2;
}
}
void DrawRope()
{
//if not grappling
if (!joint) return;
lr.SetPosition(0, gunTip.position);
lr.SetPosition(1, grapplePoint);
}
void StopGrapple()
{
lr.positionCount = 0;
Destroy(joint);
}
public bool IsGrappling()
{
return joint != null;
}
public Vector3 GetGrapplePoint()
{
return grapplePoint;
}
}
I think a better way for your situation would be to create an empty game object where you want the grappling gun to be held when it's picked up, and the empty game object parented to the camera from the scene. Then from the script, you can write these lines of code.
To reference the empty game object:
public transform gunHoldPosition;
And you can attach that from the scene view in Unity.
Then to make the gun snap to the position when it's held:
private void PickUp()
{
equipped = true;
slotFull = true;
transform.localPosition = Vector3.zero;
transform.localScale = Vector3.one;
//Enable rb and km and coll = trigger
rb.isKinematic = true;
coll.isTrigger = true;
//Enable Script
gunScript.enabled = true;
}
Now In the Update function, you have to write a little more:
private void Update()
{
//equipped and e is pressed
Vector3 distanceToPlayer = player.position - transform.position;
if (!equipped && distanceToPlayer.magnitude <= pickUpRange && Input.GetKeyDown(KeyCode.E) && !slotFull) PickUp();
//Drop if equipped and q is pressed
if (equipped && Input.GetKeyDown(KeyCode.Q)) Drop();
if (equipeed){
/* If this script is on the gun itself, then you just have to do transform.position, but if it's on some other object then you have to reference the gun that's being picked with the variable of 'public gameObject gun;' up and say gun.transform.position*/
transform.position = gunHoldPosition.position;
}
}
I'm not exactly sure why you have to do this in the Update function, but once I made a pickup script and the same thing happened to me, so I had to put the transform.postition in the Update Function. Hopefully this works for you if it doesn't reply to me and I will try something else.
Also sorry that I am late to answer my question even though I fixed it really fast. I was making the object with the script, aka a child of the model without actually making the model a child of thing.
I'm new at Unity and C#. I started this 2D game few weeks ago.
So basically what I want to do is when the player is hit by "FrostBullet", it should deal the damage and slow that player over time. In my script the player is hit by FrostBullet, it deals the damage and the slow is constant, not over time.
I want it to be like, when the player is hit by FrostBullet, the player should be slowed for 2 sec and than his movement speed is back to normal.
So this is how my code look like:
public class FrostHit : MonoBehaviour
{
float maxS = 40f;
float slowSpeed = 20f;
public Rigidbody2D rb;
int damage = -5;
int timeOfReducedSpeed = 2;
bool isHit = false;
void Start()
{
rb.velocity = transform.right * slowSpeed;
}
void OnTriggerEnter2D(Collider2D other)
{
if (other.gameObject.tag == "Player_Njinja")
{
HealthNjinja healthNjinja = other.gameObject.GetComponent<HealthNjinja>();
healthNjinja.ModifyHealth(damage);
PlayerMovementNjinja reduceMoveSpeed = other.gameObject.GetComponent<PlayerMovementNjinja>();
reduceMoveSpeed.runSpeed = slowSpeed;
Destroy(gameObject);
}
PlayerMovementKnight maxSpeed = other.gameObject.GetComponent<PlayerMovementKnight>();
maxSpeed.runSpeed = maxS;
HealthKnight healthKnight = other.gameObject.GetComponent<HealthKnight>();
if (other.gameObject.tag == "Player_Knight")
{
isHit = true;
healthKnight.ModifyHealth(damage);
StartCoroutine(speedTime());
Destroy(gameObject);
}
IEnumerator speedTime()
{
while (isHit == true)
{
slowPlayer();
yield return new WaitForSeconds(timeOfReducedSpeed);
revertSpeed();
}
}
void slowPlayer()
{
maxSpeed.runSpeed = slowSpeed;
}
void revertSpeed()
{
maxSpeed.runSpeed = maxS;
}
}
}
Also my PlayerMovement code:
public class PlayerMovementKnight : MonoBehaviour
{
public CharacterController2D controller;
public Animator animator;
public float runSpeed = 40f;
float horizontalMove = 0f;
bool jump = false;
bool crouch = false;
public WeaponKnight Bullet;
public bool grounded;
void Update()
{
horizontalMove = Input.GetAxisRaw("Horizontal") * runSpeed;
animator.SetFloat("Speed", Mathf.Abs(horizontalMove));
if (Input.GetButtonDown("Jump"))
{
jump = true;
animator.SetBool("IsJumping", true);
}
if (Input.GetButtonDown("Crouch"))
{
crouch = true;
}
else if (Input.GetButtonUp("Crouch"))
{
crouch = false;
}
if (grounded && GetComponent<Bullet>().knockBack == false)
{
GetComponent<Rigidbody2D>().velocity = new Vector2(0, 0);
}
}
public void OnLanding()
{
animator.SetBool("IsJumping", false);
}
public void OnCrouching(bool isCrouching)
{
animator.SetBool("IsCrouching", isCrouching);
}
void FixedUpdate()
{
controller.Move(horizontalMove * Time.fixedDeltaTime, crouch, jump);
jump = false;
}
}
The issue seems to be that your bool isHit, that is set to true before calling the Coroutine, is never reset to false.
So the loop:
while (isHit == true)
{
slowPlayer();
yield return new WaitForSeconds(timeOfReducedSpeed);
revertSpeed();
}
is really an infinite loop.
You might want to reset the bool to false somewhere.
Edit:
Following to comments, I see now that your script is attached to the bullet GameObject. After starting the Coroutine, and slowing down the player, you Destroy(gameObject);
After that, the script gets destroyed too, and the Coroutine is therefore stopped...and nothing remains to restore player’s speed back to normal.
You should move the Coroutine to the player’s script, and start it when needed. This way, it would still exist after you destroy the bullet.
Your bool isHit is never set to false. Resulting in the Player being slowed and then being reset forever after he get's hit once.
As well as creating isHit in the first place isn't needed. Why would you need to check that the Player has been hit when you enter the IEnumerator only when the Player_Knight has been hit anyway.
Code Example:
public class FrostHit : MonoBehaviour
{
float maxS = 40f;
float slowSpeed = 20f;
public Rigidbody2D rb;
int damage = -5;
int timeOfReducedSpeed = 2;
void Start()
{
rb.velocity = transform.right * slowSpeed;
}
void OnTriggerEnter2D(Collider2D other)
{
if (other.gameObject.tag == "Player_Njinja")
{
HealthNjinja healthNjinja = other.gameObject.GetComponent<HealthNjinja>();
healthNjinja.ModifyHealth(damage);
PlayerMovementNjinja mSpeedNinja = other.gameObject.GetComponent<PlayerMovementNjinja>();
StartCoroutine(speedTimeNinja());
}
else if (other.gameObject.tag == "Player_Knight")
{
HealthKnight healthKnight = other.gameObject.GetComponent<HealthKnight>();
healthKnight.ModifyHealth(damage);
PlayerMovementKnight mSpeedKnight = other.gameObject.GetComponent<PlayerMovementKnight>();
StartCoroutine(speedTimeKnight());
}
// Destroy Bullet
Destroy(gameObject);
}
IEnumerator speedTimeKnight()
{
mSpeedKnight.runSpeed = slowSpeed;
yield return new WaitForSeconds(timeOfReducedSpeed);
mSpeedKnight.runSpeed = maxS;
}
IEnumerator speedTimeNinja()
{
mSpeedNinja.runSpeed = slowSpeed;
yield return new WaitForSeconds(timeOfReducedSpeed);
mSpeedNinja.runSpeed = maxS;
}
}
Things I've changed and the reason:
Removed bool as well as while loop, didn't solve any real purpose
Seperated IEnumerator and 2 Functions from inside OnTriggerEnter2D()
Added Destroy(gameObject); to the end, because the bullets always gets destroyed anyway
Removed maxSpeed.runSpeed = maxS;, because the speed get's set/reset already anyway. There is no reason to reset the speed after we collided with the bullet.
Put GetComponent for Player_Knight into the else if, there is no reason to execute the GetComponent even if the Player_Njinja gets hit.
Removed reduceMoveSpeed.runSpeed = slowSpeed; the Player_Njinja only gets slowed and never regains his speed. Instead use the IEnumerator again
Added another IEnumerator so that both Player_Njinja and Player_Knight get slowed and unslowed correctly (it would now be even possible to create different slow times depending on which player gets hit)
Removed functions, it's not really worth it to write a function for one line of code. Especially when you can just set it in IEnumerator without needing more lines of code
The jump button is not working in my unity, as my player is not jumping. I followed a tutorial but it does not seem to work. Must be something wrong with my code but I cannot seem to work it out as there are no errors.
I am not sure but, I created an image in unity and placed it in canvas and named it "Fixed Joybutton", with the script Joybutton.cs, but when I run it and try to click it, it wont make my player jump. I thought the problem was the "joybutton = FindObjectOfType();" and changed it to "joybutton = GetComponent();" but still doesnt work.
This is the code for the jump button itself, Joybutton.cs:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
{
[HideInInspector]
public bool Pressed;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
}
public void OnPointerDown(PointerEventData eventData)
{
Pressed = true;
}
public void OnPointerUp(PointerEventData eventData)
{
Pressed = false;
}
}
// and this is the code that is in my player, PlayerController.cs:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class PlayerController : MonoBehaviour
{
protected Joystick joystick;
protected Joybutton joybutton;
protected bool jump;
public float speed;
public Text playerDisplay;
public Text scoreDisplay;
public bool isFlat = true;
private Rigidbody rb;
public static int count = 0;
private void Start()
{
joystick = FindObjectOfType<Joystick>();
joybutton = FindObjectOfType<Joybutton>();//this is to find the object, I changed it to "joybutton = GetComponent<Joybutton>();" but still doesnt work
rb = GetComponent<Rigidbody>();
}
private void FixedUpdate()
{
rb.velocity = new Vector3(joystick.Horizontal * 5f, rb.velocity.y, joystick.Vertical * 5f); // joystick to move my player, its working
if (!jump && joybutton.Pressed)// this is for the jump, that is not working
{
jump = true;
rb.velocity += Vector3.up * 10f;
}
if (jump && !joybutton.Pressed)
{
jump = false;
}
}
public void OnTriggerEnter(Collider other)
{
GameObject gameControl = GameObject.Find("Timer");//name of my gameobject
Loading loading = gameControl.GetComponent<Loading>();
if (other.gameObject.CompareTag("Pick Up"))
{
other.gameObject.SetActive(false);
count = count + 1;
Awake();
}
else if (other.gameObject.CompareTag("Time Boost"))
{
other.gameObject.SetActive(false);
loading.sec += 10;
if (loading.sec > 60)
{
int sec1 = loading.sec - 60;
loading.sec = sec1;
loading.minutes ++;
}
Awake();
}
}
private void Awake()
{
if (DBManager.username == null)
{
UnityEngine.SceneManagement.SceneManager.LoadScene(0);
}
playerDisplay.text = "Player: " + DBManager.username;//display username
scoreDisplay.text = "Score: " + count.ToString();//display score
}
}
//Player is moving but cannot jump. Please show me the error in my code and solution. Thank you so much.
There's a lot to unpack here.
UI Events like OnPointerDown occur on the frame cycle. FixedUpdate happens on the fixed cycle. There is a good chance that you have a very high framerate, resulting in 5-6 frames per fixed cycle. Your joybutton will only be set to "pressed" for one frame, so there is a very high chance that the joybutton triggers OnPointerUp before the jump can occur.
The quickest solution to this problem would be to not "reset" the joybutton's pressed state using OnPointerUp, and instead reset it when the jump is processed in your PlayerController. That being said, can you help me understand why these 2 scripts are separated?
Rather than set the .Pressed state to false in the JoyButton script, reset it after you consume it in your PlayerController:
if (!jump && joybutton.Pressed)// this is for the jump, that is not working
{
jump = true;
joybutton.Pressed = false; //Add this line of code
rb.velocity += Vector3.up * 10f;
}
How do yo do for avoiding the player to jump twice in the air?
private void FixedUpdate()
{
rb.velocity = new Vector3(
joystick.Horizontal * 5f, rb.velocity.y,
joystick.Vertical * 5f);
if (!jump && joybutton.Pressed)
{
jump = true;
rb.velocity += Vector3.up * 10f;
}
if (jump && !joybutton.Pressed)
{
jump = false;
}
}
I am working on solo project that is horizontal 2D infinite runner. I have a problem in jumping mechanics where player can hold the jump button and jump as soon as he touches the ground. I want to force player to release the button to be able to jump again. I want to make same mechanics when he is floating(at the end of the jump when player starts falling down its y velocity is being reduced for few bits of second). I am following single responsibility principle so jumping and floating are 2 seperate scripts.
I have tried to implement a timer that will count while player is touching the ground and after some time the player would be able to jump again, but didn't manage to get asked result because you can hold the jump button all the time and after determined time spent on ground player would just jump again without releasing the button.
public class Jumping : MonoBehaviour
{
public bool isJumping;
public bool isGrounded;
public float speedUp;
public float verticalAxis;
public float timePassed;
public float jumpLimit;
private Rigidbody2D rb;
// Start is called before the first frame update
void Start()
{
rb = gameObject.GetComponent<Rigidbody2D>();
}
// Update is called once per frame
void Update()
{
verticalAxis = Input.GetAxisRaw("Vertical");
if (verticalAxis > 0 && timePassed == 0)
{
isJumping = true;
}
}
private void FixedUpdate()
{
if (isJumping)
{
Jump();
}
}
private void OnCollisionStay2D(Collision2D collision)
{
if (collision.collider.tag == "Ground")
{
isGrounded = true;
timePassed = 0f;
}
}
private void Jump()
{
isGrounded = false;
//Modifying y component of players rigidbody(jumping)
Vector2 velocity = Vector2.up * speedUp * verticalAxis;
rb.velocity = velocity;
//Counting time when jumping
timePassed += Time.deltaTime;
if (timePassed >= jumpLimit)
{
isJumping = false;
}
}
}
P.S. I have tried to shrink the code in this question as stackoverflow suggests but I didn't know what to cut out because according to me everything is relevant and crucial to solving the problem. Thanks!
without knowing the rest of your code I would use an additional flag and obviously something for getting the cool down delay like
public float jumpCoolDownTime;
private float coolDownTimer;
private bool canJump;
void Update()
{
// if no can jump you can't jump again
if(!canJump)
{
verticalAxis = Input.GetAxisRaw("Vertical");
if (verticalAxis > 0)
{
canJump = false;
isJumping = true;
coolDownTimer = 0;
}
}
else
{
// run timer to enable jump again
if(isGrounded) coolDownTimer += Time.deltaTime;
if(coolDownTimer >= jumpCoolDownTime)
{
canJump = true;
}
}
}
You could however simplify the code a bit using Coroutines
public bool isJumping;
public float speedUp;
public float verticalAxis;
public float jumpLimit;
public float coolDownTime;
private Rigidbody2D rb;
// Start is called before the first frame update
void Start()
{
rb = gameObject.GetComponent<Rigidbody2D>();
}
void Update()
{
// do nothing is already jumping
if(isJumping) return;
verticalAxis = Input.GetAxisRaw("Vertical");
if (verticalAxis > 0)
{
// start jumping
StartCoroutine(Jump());
}
}
private void OnCollisionEnter2D(Collision2D collision)
{
if (collision.collider.tag != "Ground") return;
isGrounded = true;
StartCoroutine(CoolDown());
}
private IEnumerator Jump()
{
isJumping = true;
isGrounded = false;
coolDownTimer = 0;
var timePassed = 0f;
while(timePassed < jumpLimit)
{
// wait for the FixedUpdate call
yield return new WaitForFixedUpdate();
Vector2 velocity = Vector2.up * speedUp * verticalAxis;
rb.velocity = velocity;
}
}
private IEnumerator CoolDown()
{
yield return new WaitForSeoncds(coolDownTime);
isJumping = false;
}
If you are using the axis to get the jump, you can see if the player have released the button by:
if (GetAxis("Vertical") == 0.0) { //Button up
// do stuff
}
if(GetAxis("Vertical") > 0)
{
//Button down, is jumping
}
In Unity axis aren't booleans, they are coefficients, if you play with a controller you can set different behaviors for different intensities.
i think i m making a mistake in controlling the animation transition and their parameters.. i m a complete beginner to unity.. can someone help me on it pls.. the jump animation keeps quitting in middle and return to idle without being completed..
Transition are
Idle to run ; isRunning = true
Run to idle ; isRunning = false
Run to jump ; isJumping = true
idle to jump ; isjumping = true
jump to idle ; isjumpinh = false
i have tried doing several things but it didnt work out.. if this is a beginner problem i m sorry but i cant get it to work
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Player : MonoBehaviour
{
private Animator anim;
private Rigidbody rb;
float runningSpeed = 30f;
float jumpForce = 20f;
bool isJumping = false;
bool isRunning = false;
// Start is called before the first frame update
void Start()
{
anim = this.gameObject.GetComponent<Animator>();
rb = this.gameObject.GetComponent<Rigidbody>();
}
// Update is called once per frame
void FixedUpdate()
{
move();
if (Input.GetKeyDown(KeyCode.Space))
{
jump();
}
animate();
}
void move()
{
if (Input.GetKey(KeyCode.W))
{
transform.Translate(runningSpeed * Vector3.forward * Time.deltaTime);
isRunning = true;
}
else
{
isRunning = false;
}
}
void jump()
{
rb.AddForce(jumpForce * Vector3.up , ForceMode.Impulse);
isJumping = true;
}
void animate()
{
anim.SetBool("isRunning",isRunning);
anim.SetBool("isJumping", isJumping);
}
private void OnCollisionEnter(Collision collision)
{
if (collision.transform.name == "Ground" && isJumping)
isJumping = false;
}
}