I have a simple code that makes my character jump,
My character can jump as long as he's current jump is lower than the maxNumber of jumps.
and every time he jumps i add 1 to his current jump.
my problem is the jump counter doesnt always add one? even when the character jumps, he jumps with no problems but the counter doesnt always respond.
he is allowed to jump twice, but from time to time the player does hes first jump without anything being added to his current jump.
// Jumping
public int maxNoJumps = 2;
public int currentJumpNo = 0;
public bool canPlayerJump = false;
public float JumpVelocity = 25;
void Update ()
{
grounded = Physics2D.Linecast (transform.position, groundCheck.position, 1 << LayerMask.NameToLayer ("Ground"));
if (grounded)
{
currentJumpNo = 0;
}
// SETTING IF PLAYER IS ABLE TO JUMP
if (currentJumpNo < maxNoJumps)
{
canPlayerJump = true;
} else
{
canPlayerJump = false;
}
// IF PLAYER PRESSES SPACE AND IS ABLE TO JUMP
if (Input.GetKeyDown (KeyCode.Space) && canPlayerJump)
{
currentJumpNo+=1;
rb2d.velocity = new Vector2 (rb2d.velocity.x, JumpVelocity);
}
}
like i said the player jumps even when the counter doesnt go up allowing him sometimes to jump 3 times.
also i have tried it in fixed update and it's still the same and incrementing in different ways.
thank in advance for helping
I would suggest keeping your jumps seperate and using booleans instead.
For instance instead of
if (currentJumpNo < maxNoJumps)
{
canPlayerJump = true;
} else
{
canPlayerJump = false;
}
use:
if (grounded)
{
canPlayerJump = true;
doubleJump = true;
} else if(doubleJump)
{
canPlayerJump = true;
}
else
{
canPlayerJump = false;
}
and then in your jump section
if (Input.GetKeyDown (KeyCode.Space) && canPlayerJump)
{
doubleJump = (grounded)? true:false;
rb2d.velocity = new Vector2 (rb2d.velocity.x, JumpVelocity);
}
this basically says if they are grounded before they jump they still have double jump. If they are not grounded they lose the double jump.
I hope very much that this will help you in your project. If the problem persists, just reply and I will see if I can create an alternate jump function that should work as well.
Related
As the title suggests, I am trying to make a double jump in my Character Controller. The problem is, that using my code the character makes one big jump instead of making a second one.
Here is my code:
if (m_Grounded && jump)
{
// Add a vertical force to the player.
m_Grounded = false;
m_Rigidbody2D.AddForce(new Vector2(0f, m_JumpForce));
canDoubleJump = true;
} else if (canDoubleJump)
{
m_Rigidbody2D.AddForce(transform.up * m_Thrust, ForceMode2D.Impulse);
canDoubleJump = false;
}
the defined variables are:
bool canDoubleJump;
public float m_Thrust = 200f;
[SerializeField] private float m_JumpForce = 400f;
What I have tried so far:
I tried adding the same Vector2 jumpForce in to the double jump place, but then it's just one giant jump even bigger than right now.
I tried changing else if to
else if (canDoubleJump && Input.GetButtonDown("Jump"))
Nothing seems to work. All ideas are appreciated <3
EDIT
I somehow fixed this problem with this code if anyone ever needs:
// If the player should jump...
if (m_Grounded && jump)
{
// Add a vertical force to the player.
m_Grounded = false;
m_Rigidbody2D.AddForce(transform.up * m_JumpForce, ForceMode2D.Impulse);
canDoubleJump = true;
}
else if (canDoubleJump && !m_Grounded && jump)
{
m_Rigidbody2D.AddForce(transform.up * m_JumpForce, ForceMode2D.Impulse);
canDoubleJump = false;
}
Now the only problem left is adding touches to the physics, but the idea works.
Thanks everyone for your help!
What does the jump variable do? Because this entire if statement needs to be inside another one that goes like
if(Input.GetButtonDown("Jump"))
{
That way there is no chance of it being pressed twice in case you did the jump condition the wrong way.
If that doesn't work or if that's already how it is set up, try adding this to the else if condition
} else if (canDoubleJump && ! m_Grounded)
I'm just throwing in ideas as I'm confused why it's not working as-is.
Your code is not very clear but you can use this code it works fine for me
When the jump is called the first time, it jumps once and when it is called and the player doesn't touch the ground, it only jumps one more jump
public Rigidbody2D m_Rigidbody2D;
public float m_Thrust = 400;
public bool isGrounded;
public bool TowJump;
void OnCollisionEnter2D(Collision2D collision)
{
if (collision.gameObject.tag == "Ground"){
isGrounded = true;
TowJump = false;
}
}
void OnCollisionExit2D(Collision2D collision)
{
if (collision.gameObject.tag == "Ground"){
isGrounded = false;
}
}
public void Jumped()
{
if(TowJump == false && isGrounded == false)
{
m_Rigidbody2D.AddForce(transform.up * m_Thrust, ForceMode2D.Impulse);
TowJump = true;
}
if(isGrounded == true)
{
m_Rigidbody2D.AddForce(transform.up * m_Thrust, ForceMode2D.Impulse);
}
}
Note, you must tag the floor with the name "Ground"
EDIT
The code works without problems, try to follow the steps as shown in the picture
Player
Ground
I'm working on a sports game where if a player skates into the goalie crease, I want all the player positions to reset to the center of the ice and do a three second countdown before play resumes.
I have tried to hardcode the starting position for the main player in a variable called PlayerStart and I call Player.transform.position = PlayerStart. When I did this, the player didn't move so I tried to switch the object I was setting as the player. This did what I wanted, but the mouse functionality changes for some reason and when the countdown ends, the player just goes right back to the position they were in before the crease violation was called.
Other things I've tried:
transform.SetPositionAndRotation
PlayerStart = Player.transform.position (instead of hard coding the numbers in)
Here is my code:
public class Crease : MonoBehaviour
{
private float Delay = 0;
private bool CreaseViolated = false;
private GameObject Player;
public Vector3 PlayerStart;
// Start is called before the first frame update
void Start()
{
Ring = GameObject.Find("Ring");
Player = GameObject.Find("FPSController");
PlayerStart = new Vector3(29.75f, 6.03999996f, 4.42000008f);
}
// Update is called once per frame
void Update()
{
Delay -= Time.deltaTime;
if (CreaseViolated)
{
CreaseViolation();
}
}
void OnTriggerEnter(Collider other)
{
if (other.gameObject.name != "Ring"
&& other.gameObject.name != "TeamGoalie"
&& other.gameObject.name != "OpponentGoalie")
{
CreaseViolated = true;
Delay = 3;
}
}
void CreaseViolation()
{
if (Delay > 0)
{
PlayerTip.GetComponent<PickupRing>().HasRing = false;
Opponent.GetComponent<AI>().HasRing = false;
Ring.transform.parent = null;
}
else
{
text.text = " ";
if (CreaseViolated)
{
Debug.Log("Player position before: " + Player.transform.position);
Player.transform.position = PlayerStart;
Debug.Log("Player position after: " + Player.transform.position);
//Player.transform.SetPositionAndRotation(PlayerStart + new Vector3(0f, 0.800000012f, 0f), new Quaternion(0f, -0.707106829f, 0f, 0.707106829f) + new Quaternion(0f, 0f, 0f, 1f));
GameObject.Find("Countdown").GetComponent<CountdownText>().timeRemaining = 4;
CreaseViolated = false;
}
}
}
}
Here is a short YouTube video showing my code and the demo: https://www.youtube.com/watch?v=mZt_4AppBh8
this problem is all solved now thanks to an awesome person at my university helping me out! The solution was disabling the CharacterController before repositioning the player and then enabling it again after.
So this:
Player.transform.position = PlayerStart;
in the CreaseViolation function becomes
cc.enabled = false;
PlayerController.transform.position = PlayerControllerStart;
cc.enabled = true;
with cc being declared earlier as
private CharacterController cc;
and in the start function I assigned it with the value
cc = Player.GetComponent<CharacterController>();
with PlayerController being set to the FPSController.
I renamed Player to PlayerController for more clarity.
Hopefully this helps anyone having the same problem I was having!
The Above answer works perfectly but might bring some issues when doing networked changes in player transform positions.
The reason the transform change doesn't work without disabling the Character Controller is because the character controller overrides the transforms.
To Disable this and enable an Auto Synchronization between transforms GoTo:
Edit > Project Settings > Physics > (Enable Auto Sync Transforms)
I have a platformer game in unity 2D where my I'm trying to make my character have a slight hangtime delay falling down whenever players reached the edge of a platform they will have a small amount of time to jump. However if jump button is press twice quickly the character makes a double jump, character only needs to jump once if not falling on the edge of the platform.
Screenshot scene
Here is what I have done so far for my jumping mechanics:
float Hangtime;
float HangCounter = .3f;
void Update () {
JumpRequest();
}
void FixedUpdate() {
HangtimeImplents();
Jump();
isGrounded = Physics2D.OverlapCircle(feet.position, groundradius, groundlayers);
}
// For Jump --------------
void HangtimeImplents() {
if (isGrounded) // checks if grounded is true then hangtime is always .3f
{
Hangtime = HangCounter;
}
else
{
Hangtime -= Time.deltaTime;
}
}
public void JumpRequest() {
if (CrossPlatformInputManager.GetButtonDown("Jump") && Hangtime > 0f) // checks if Jump Button is pressed and Hangtime is greated than 0
{
isGrounded = false;
Jumprequest = true;
}
}
void Jump() // Jump Function if Jumprequest is true
{
if (Jumprequest)
{
mybody.AddForce(new Vector2(0, jumpforce) , ForceMode2D.Impulse);
}
Jumprequest = false;
}
There are many solutions, but the easiest ways is updating the code like this:
if (CrossPlatformInputManager.GetButtonDown("Jump") && Hangtime == HangCounter) // checks if Jump Button is pressed and Hangtime is greated than 0
{
isGrounded = false;
Jumprequest = true;
}
On your code, Time.deltaTime is less than the frame completion time.
So even isGrounded is false, Hangtime is still greater than 0.
Make an operator called jumpTimes
public int maximizeJumpTimes;
private int jumpTimes
When the player jump, Minus the jumpTimes; and if the jumpTimes == 0 make the player can't jump.
Hope this help :>
I am trying to disable a player movement for 1 second when it collides with a specific object. The object collides and it detects that just fine however currently the entire game freezes and does not return.
Ive implemented a 0 value (as found in other examples) to the X and Y movements with a timer. This freezes the game entirely and I have to kill process and reopen Unity.
public Boolean frozen = false;
float shipDisabledTimer = 1f;
// Start is called before the first frame update
void Start()
{
SetUpMoveBoundaries();
gameSession = FindObjectOfType<GameSession>();
}
// Update is called once per frame
void Update()
{
Move(frozen);
Fire();
if (SceneManager.GetActiveScene() == SceneManager.GetSceneByName("Game"))
{
if (Input.GetKey(KeyCode.Escape))
SceneManager.LoadScene("Game Over");
}
}
public void Move(Boolean Frozen)
{
UnityEngine.Debug.Log("Update1 - " + Frozen);
var deltaX = Input.GetAxis("Horizontal") * Time.deltaTime * moveSpeed;
var deltaY = Input.GetAxis("Vertical") * Time.deltaTime * moveSpeed;
var newXPos = Mathf.Clamp(transform.position.x + deltaX, xMin, xMax);
var newYPos = Mathf.Clamp(transform.position.y + deltaY, yMin, yMax);
transform.position = new Vector2(newXPos, newYPos);
if (frozen == true)
{
float timer = 0f;
timer += Time.deltaTime;
UnityEngine.Debug.Log(frozen);
while (timer < shipDisabledTimer)
{
newXPos = 0;
newYPos = 0;
}
disableship(frozen = false);
}
}
public Boolean disableship(Boolean frozen)
{
return frozen;
}
private void OnTriggerEnter2D(Collider2D other)
{
UnityEngine.Debug.Log(other.gameObject.name);
if (other.gameObject.name.Equals("Orb Shot(Clone)"))
{
UnityEngine.Debug.Log("Orb hit player");
//StartCoroutine(countdownTimer());
disableship(frozen = true);
}
}
The ship alone should freeze. No other game object should freeze. All scores, counters, enemies should continue onward. Only the player X,Y are disabled for 1 second.
This
while (timer < shipDisabledTimer)
{
newXPos = 0;
newYPos = 0;
}
entirely blocks your game's main thread since the timer nor shipDisabledTime are not changed within the loop so it will never end.
There are a few ways you could go here.
Invoke
Invoke allows you to call a method after a certain delay so you could easily do something like
public void Freeze()
{
frozen = true;
Invoke("Unfreeze"; shipDisabledTime);
}
private void Unfreeze()
{
frozen = false;
}
Coroutine
A Coroutine is like a temporary Update method. Simplest way in your case is using WaitForSeconds and do something like
public void Freeze()
{
StartCoroutine (FreezeRoutine());
}
private IEnumerator FreezeRoutine()
{
frozen = true;
yield return new WaitForSeconds(shipDisabledTime);
frozen = false;
}
simple timer
Or you could use a simple timer but in Update (not a while loop) like
private float timer = -1;
public void Freeze()
{
frozen = true;
timer = shipDisabledTime;
}
private void Update()
{
if(timer > 0)
{
timer -= Time.deltaTime;
if(timer <= 0)
{
timer = -1;
frozen = false;
}
}
...
}
And then either way you simply don't take any movement input in the meantime
public void Move()
{
if(frozen) return;
...
}
Except for the Invoke solution you can also extend them and decided whether you want to e.g. stack multiple freezes, ignore them while already frozen or simply start the timers over.
General note: In c# rather simply use bool it's basically the same but easier to read and write ;)
Also note that this call
disableship(frozen = false);
...
public Boolean disableship(Boolean frozen)
{
return frozen;
}
Is pretty strange ... first of all this method does absolutely nothing than just return the same value you pass in as parameter .. you are hiding the frozen field with a same named parameter so this does nothing!
Second your method returns a Boolean but you are not assigning it to anything.
If you want to change the value simply set it using frozen = XY but don't pass this in as parameter further.
Aaand avoid calling Debug.Log every frame .. it will slow down your app even in a build where you don't even see the log!
I have some problems with a script for a "Field Of View" of the enemy. After watching a tutorial to create a field of view for the player I thought I can switch it to the enemy's so that they detect the player and do some other stuff. I created a boolean variable playerInRange to detect if the enemies can detect the player and set this variable to true or false.
It works fine with just one enemy. When I add another one, the new enemy will not detect the player. So maybe it is related to the coroutine, but I am not sure.
Here is a little bit of my code:
void Start() {
StartCoroutine("FindTargetsWithDelay", .2 f);
}
IEnumerator FindTargetsWithDelay(float delay) {
while (true) {
yield
return new WaitForSeconds(delay);
FindVisibleTargets();
}
}
public void FindVisibleTargets() {
visibleTargets.Clear();
Collider[] targetsInViewRadius = Physics.OverlapSphere(transform.position, viewRadius, targetMask);
for (int i = 0; i < targetsInViewRadius.Length; i++) {
Transform target = targetsInViewRadius[i].transform;
Vector3 dirToTarget = (target.position - transform.position).normalized;
if (Vector3.Angle(transform.forward, dirToTarget) < viewAngle / 2) {
float dstToTarget = Vector3.Distance(transform.position, target.position);
if (!Physics.Raycast(transform.position, dirToTarget, dstToTarget, obstacleMask)) {
// Not so nice solution!
// The movement should be in a separate script!
visibleTargets.Add(target);
nav.SetDestination(player.position);
anim.SetBool("IsRunning", true);
if (dstToTarget < attackRange) {
playerInRange = true;
Debug.Log(playerInRange);
}
}
} else {
anim.SetBool("IsRunning", false);
playerInRange = false;
Debug.Log(playerInRange);
}
}
}
Thank you guys for your little hint. It was really a little hierarchy problem :( Sorry for that newbie/DAU question.
Cheers
Nico