Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice

Question Metroid clone ball help

Discussion in '2D' started by edyboy, Mar 5, 2023.

  1. edyboy

    edyboy

    Joined:
    Jul 23, 2017
    Posts:
    7
    I have a player, which can turn into a ball (Metroid-clone)
    I have a tile map, inside a grid (Layer is ground).
    I also have an empty game object named boundaries (That represent the boundaries of the camera) and obviously the layer for this is "No collision"

    upload_2023-3-5_6-44-27.png


    I went to my project setting and turn off all the interaction with that layer

    upload_2023-3-5_6-44-59.png


    Code (CSharp):
    1.  
    2.  
    3. void Update() {
    4.  
    5.             #region Check if the player is grounded
    6.  
    7.             _isGrounded = Physics2D.OverlapCircle(_groundPoint.position, 0.3f, _whatIsGround);
    8.  
    9.             #endregion
    10.  
    11.             #region Handle dashing and Movement
    12.  
    13.             if (Input.GetKeyDown(KeyCode.Z) && _canDash && _playrSprites[0].activeSelf) {
    14.                 StartCoroutine(DashCoroutine(0.5f));
    15.             } else {
    16.                 float horizontalInput = Input.GetAxisRaw("Horizontal");
    17.  
    18.                 _rb2D.velocity = new Vector2(horizontalInput * _moveSpeed, _rb2D.velocity.y);
    19.  
    20.                 SetLocalScale();
    21.             }
    22.  
    23.  
    24.             #endregion
    25.  
    26.             #region Handle jumping
    27.             if (Input.GetKeyDown(KeyCode.X) && (_isGrounded || _canDoubleJump)) {
    28.  
    29.                 if (_isGrounded) {
    30.                     _canDoubleJump = true;
    31.                 } else {
    32.                     _canDoubleJump = false;
    33.  
    34.                     _animator.SetTrigger("isDoubleJumping");
    35.                 }
    36.  
    37.                 _rb2D.velocity = new Vector2(_rb2D.velocity.x, _jumpForce);
    38.  
    39.             } else if (Input.GetKeyUp(KeyCode.X) && _rb2D.velocity.y > 0) {
    40.                 _rb2D.velocity = new Vector2(_rb2D.velocity.x, _jumpForce * 0.5f);
    41.             }
    42.  
    43.  
    44.             #endregion
    45.  
    46.             #region Handle shooting
    47.  
    48.             if (Input.GetKeyDown(KeyCode.C) && !_isBall) {
    49.                 Instantiate(_bullets, _shotPoint.position, Quaternion.identity).ChangeDirr(transform.localScale.x);
    50.                 _animator.SetTrigger("fireShot");
    51.             }
    52.             #endregion
    53.  
    54.             #region Ball mode
    55.  
    56.             if (Input.GetKeyDown(KeyCode.X)) {
    57.                 if (!_isBall) {
    58.                     StartCoroutine(TurnOnBallCoroutine(1.5f));
    59.                 }
    60.             }
    61.             if (Input.GetKeyUp(KeyCode.X)) {
    62.                 RaycastHit2D hit = Physics2D.Raycast(transform.position, Vector2.up, raycastDistance);
    63.                 Debug.DrawRay(transform.position, Vector2.up * raycastDistance, rayColor);
    64.                 Debug.Log(hit.collider.name);
    65.                 if (hit.collider == null) { // Raycast did not hit anything, allow turning off ball mode
    66.                     Debug.Log("turning off ball mode");
    67.                     TurnOffBall();
    68.                 }
    69.             }
    70.  
    71.             #endregion
    72.  
    73.             // Update the animator variables
    74.             _animator.SetFloat("isRunning", Mathf.Abs(_rb2D.velocity.x));
    75.             _animator.SetBool("isGrounded", _isGrounded);
    76.         }
    77.  
    78.         IEnumerator DashCoroutine(float dashDuration) {
    79.             float timer = 0f;
    80.             while (timer < dashDuration) {
    81.                 _canDash = false;
    82.                 _isDashing = true;
    83.                 _rb2D.velocity = new Vector2(_dashSpeed * transform.localScale.x, _rb2D.velocity.y);
    84.                 timer += Time.deltaTime;
    85.                 ShowAfterImage();
    86.                 yield return null;
    87.             }
    88.             _canDash = true;
    89.             _isDashing = false;
    90.         }
    91.  
    92.         IEnumerator TurnOnBallCoroutine(float time) {
    93.  
    94.             _isBall = true;
    95.             yield return new WaitForSeconds(time);
    96.             Debug.Log("ball");
    97.             TurnOnBall();
    98.         }
    99.  
    100.         private void SetLocalScale() {
    101.             if (Input.GetAxisRaw("Horizontal") > 0) {
    102.                 transform.localScale = new Vector3(1, 1, 1);
    103.             } else if (Input.GetAxisRaw("Horizontal") < 0) {
    104.                 transform.localScale = new Vector3(-1, 1, 1);
    105.             }
    106.         }
    107.  
    108.         private void ShowAfterImage() {
    109.             if (Time.time >= _nextAfterImageSpawnTime) {
    110.                 SpriteRenderer _spriteRenderer = Instantiate(_afterImageSpriteRender,
    111.                     transform.position,
    112.                     transform.rotation);
    113.  
    114.                 _spriteRenderer.sprite = _mySpriteRenderer.sprite;
    115.                 _spriteRenderer.transform.localScale = transform.localScale;
    116.                 _spriteRenderer.color = _afterImageColor;
    117.  
    118.                 Destroy(_spriteRenderer.gameObject, _afterImageLifeTime);
    119.                 _nextAfterImageSpawnTime = Time.time + _afterImageSpacing;
    120.             }
    121.         }
    122.  
    123.         private void TurnOnBall() {
    124.             Debug.Log("Turned Ball");
    125.             _isBall = true;
    126.             _playrSprites[1].SetActive(true);
    127.             _playrSprites[0].SetActive(false);
    128.  
    129.         }
    130.  
    131.         private void TurnOffBall() {
    132.             Debug.Log("Turn off ball");
    133.             _isBall = false;
    134.             _playrSprites[1].SetActive(false);
    135.             _playrSprites[0].SetActive(true);
    136.         }
    So, I have my ball, to be a child of my player, and by default is not active, and if I hold the X key, I run a coroutine, that turns my ball on, and I added a raycast, to see if I have something above me, if this is the case I don't turn of the ball
    But I have some problems with this
    - if I jump, when I hit the floor, I change into a ball
    - I can't turn back'
    If I print what the rayast hit, it was the "Camera boundaries" and of course I is not null



    '
    [1]: /storage/temp/205319-screenshot-2023-03-05-055207.png
    [2]: /storage/temp/205320-screenshot-2023-03-05-055219.png
     

    Attached Files:

  2. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    10,621
    It's not clear specifically what you're asking here.

    Are you simply saying that you perform a raycast and don't expect to "hit" the "Camera boundaries"? If so, you should see that you're not specifying which layer(s) you want to hit as you do in the OverlapCircle call.
     
  3. edyboy

    edyboy

    Joined:
    Jul 23, 2017
    Posts:
    7
    That works, many thanks, now I cannot turn back if there is something in the way perfect

    I have another problem

    Code (CSharp):
    1.  
    2. #region Ball mode
    3.  
    4.         if (Input.GetKeyDown(KeyCode.X)) {
    5.             if (!_isBall) {
    6.                 StartCoroutine(TurnOnBallCoroutine(1.5f));
    7.             }
    8.         }
    9.         if (Input.GetKeyUp(KeyCode.X)) {
    10.             layerMask = LayerMask.GetMask("Ground");
    11.             RaycastHit2D hit = Physics2D.Raycast(transform.position, Vector2.up, raycastDistance, layerMask);
    12.             Debug.DrawRay(transform.position, Vector2.up * raycastDistance, rayColor);
    13.             if (hit.collider == null) { // Raycast did not hit anything, allow turning off ball mode
    14.                 Debug.Log("turning off ball mode");
    15.                 TurnOffBall();
    16.             }
    17.  
    18. IEnumerator TurnOnBallCoroutine(float time) {
    19.         _isBall = true;
    20.         yield return new WaitForSeconds(time);
    21.         Debug.Log("ball");
    22.         TurnOnBall();
    23.     }
    24.     }
    25.     private void TurnOnBall() {
    26.         Debug.Log("Turned Ball");
    27.         _isBall = true;
    28.         _playrSprites[1].SetActive(true);
    29.         _playrSprites[0].SetActive(false);
    30.     }
    31.     private void TurnOffBall() {
    32.         Debug.Log("Turn off ball");
    33.         _isBall = false;
    34.         _playrSprites[1].SetActive(false);
    35.          _playrSprites[0].SetActive(true);
    Demo

    https://reccloud.com/u/n3hnfp0

    problem

    Sometimes when I jump, I tune into a ball for no apparent reason.

    I should be only allowed, to turn into a ball, if I hold the x button

    and then I must press x again to turn off ball mode

    What I want to do

    Press and hold, to turn into a ball, if my raycast doesn't collide with anything, then I return to normal

    and I assumed that

    Code (CSharp):
    1. Input.GetKeyUp(KeyCode.X))
    would take care of this
     
    Last edited: Mar 5, 2023
  4. BABIA_GameStudio

    BABIA_GameStudio

    Joined:
    Mar 31, 2020
    Posts:
    491
    Looking at your code you are using X for turning into a ball, but also using X to do a jump or double-jump. Change the key that you use for either the ball or the jump to be different, otherwise you are getting a clash between the two bits of code.
     
  5. edyboy

    edyboy

    Joined:
    Jul 23, 2017
    Posts:
    7
    Yes, I know but I want to turn into a ball when I hold it for some time (1.5).
    If a press the I should doble jump

    as I understand, one thing is pressing another thing is holding, there shouldn't be any conflict
     
  6. BABIA_GameStudio

    BABIA_GameStudio

    Joined:
    Mar 31, 2020
    Posts:
    491
    Not sure how you think there is no conflict as I can see nothing in your code where you do a counter to determine if the player has held the X key for any duration of time.
    GetKeyDown
    will simply be true if the key has been pushed down this frame, not after any amount of time and not if you are holding it. From the manual for
    GetKeyDown
    : "Returns true during the frame the user starts pressing down the key"

    So you have a conflict. As soon as you press X it will try to jump/double jump if possible, but then within the same frame it will check that same key down press and it will also change you into the ball.

    You need to rethink the way you are doing it so that the keys are mapped differently, or so that you use a timer to check if the key is being held and then released within a certain amount of time to figure out if it should be a jump (key not held long enough) or change into the ball (key was held long enough).
     
  7. edyboy

    edyboy

    Joined:
    Jul 23, 2017
    Posts:
    7
    I thought my courutine was supposed to take care of that that is 1.5 seconds
     
  8. edyboy

    edyboy

    Joined:
    Jul 23, 2017
    Posts:
    7
    Btw thank you so much for helping me, and I am sorry if my questions are kind of stupid
     
  9. BABIA_GameStudio

    BABIA_GameStudio

    Joined:
    Mar 31, 2020
    Posts:
    491
    Nope, afraid not. All that is doing is putting a delay in before it runs the code after
    WaitForSeconds
    . At a first thought about what you are wanting, you would need to have a timer variable and a threshold variable (for checking how long the player needs to hold the key to change into the ball) and use
    GetKey
    instead of
    GetKeyDown
    . In the
    GetKey
    check you would just increase your timer by
    Time.deltaTime
    . Then all of your jump and ball logic would be in a
    GetKeyUp
    , and in there you would check the timer and if it is above your threshold value it would do the ball change, otherwise (it's less than the threshold) it would do the jump/double-jump logic. Then regardless of whether it is the ball change or the jump, you would reset the timer to 0 ready for the next time the player presses the key.

    No worries. Everyone starts somewhere, and I'm one of the first to say that I am probably only intermediate level in Unity. There are very few stupid questions.
     
  10. edyboy

    edyboy

    Joined:
    Jul 23, 2017
    Posts:
    7
    Coud you provide a basic example
     
  11. Chubzdoomer

    Chubzdoomer

    Joined:
    Sep 27, 2014
    Posts:
    107
    I'm not trying to tell you how to design your own game, but just an opinion: that is a very poor control setup.

    Most players will instinctively hold down the jump button after performing a jump (whether it's a single or double-jump) simply because most games--even Metroid and Super Metroid!--feature a variable jump height based on how long the button has been held.

    Instead, you ought to strongly consider either moving it to an entirely different button or just handling it the same way the old Metroid games do by requiring players to press down to morph.
     
  12. edyboy

    edyboy

    Joined:
    Jul 23, 2017
    Posts:
    7
    Thank you so much. I put it in the down arrow.

    But I have an unexpected behavior, I want the player to be able to turn off ball mode, as soon as I don't have anything above my head.

    In other wor
    Code (CSharp):
    1.  
    2.         if (Input.GetKeyDown(KeyCode.DownArrow) && !_isBall) {
    3.             TurnOnBall();
    4.             int layerMask = LayerMask.GetMask("Ground");
    5.             RaycastHit2D hit = Physics2D.Raycast(transform.position, Vector2.up, raycastDistance, layerMask);
    6.             Debug.DrawRay(transform.position, Vector2.up * raycastDistance, rayColor);
    7.         }
    8.         if (Input.GetKeyUp(KeyCode.DownArrow) && _isBall) {
    9.             int layerMask = LayerMask.GetMask("Ground");
    10.             RaycastHit2D hit = Physics2D.Raycast(transform.position, Vector2.up, raycastDistance, layerMask);
    11.             Debug.DrawRay(transform.position, Vector2.up * raycastDistance, rayColor);
    12.             if (hit.collider != null) {
    13.                 return;
    14.             } // Raycast did not hit anything, allow turning off ball mode
    15.             TurnOffBall();
    ds, I want you to hold down the arrow down to turn ball mode and turn it of if you release the down arrow and there is no ground above you

    this works but if i enter a narrow space, I must press again the down arrow, to return to normal

    recording-2023-03-07-00-19-17.webm (reccloud.com)

    as you can see when I exit the tunnel and lift the key, it doesn't turn off ball mode
     
    Last edited: Mar 6, 2023