Unity C# 2D Platform game - Projectile doesn't shoot into the direction the player is facing

402 views Asked by At

As the title describes, I am trying to make my projectile go into the direction the player is facing. Currently the projectile only shoots towards the right, even if the player is facing left.

*** I am new to C# (learning)** *** I am following tutorials to reach the point I am at currently**

I have tried various methods but everything I have tried failed so far. The community of StackOverflow is my last hope.

There are (what I believe) three different scripts that are important regarding this mechanic. It is a player attack mechanic that shoots a projectile.

The three different scripts are called:

  • Projectile.cs
  • PlayerAttack.cs
  • PlayerMovement.cs

Code Projectile:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Projectile : MonoBehaviour
{
  [SerializeField] private float speed;
  private float direction;
  private bool hit;

  private Animator anim;
  private BoxCollider2D boxCollider;
  

  private void Awake()
  {
    anim = GetComponent<Animator>();
    boxCollider = GetComponent<BoxCollider2D>();
  }

  private void Update()
  {
    if(hit) return;
    float movementSpeed = speed * Time.deltaTime * direction;
    transform.Translate(movementSpeed, 0, 0);
  }

  private void OnTriggerEnter2D(Collider2D collision)
  {
    hit = true;
    boxCollider.enabled = false;
    anim.SetTrigger("explode");
  }

    public void SetDirection(float _direction)
    {
        direction = _direction;
        gameObject.SetActive(true);
        hit = false;
        boxCollider.enabled = true;

        float localScaleX = transform.localScale.x;
        if(Mathf.Sign(localScaleX) != _direction)
            localScaleX = -localScaleX;

            transform.localScale = new Vector3(localScaleX, transform.localScale.y, transform.localScale.z);
    }

    private void Deactivate()
    {
        gameObject.SetActive(false);
    }
}

Code PlayerAttack:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerAttack : MonoBehaviour
{
    [SerializeField] private float attackCooldown;
    [SerializeField] private Transform firePoint;
    [SerializeField] private GameObject[] ghostballs;

    private Animator anim;
    private PlayerMovement playerMovement;
    private float cooldownTimer = Mathf.Infinity;

    private void Awake()
    {
        anim = GetComponent<Animator>();
        playerMovement = GetComponent<PlayerMovement>();
    }

    private void Update()
    {
        if (Input.GetMouseButton(0) && cooldownTimer > attackCooldown && playerMovement.canAttack())
            Attack();

        cooldownTimer += Time.deltaTime;
    }

    private void Attack()
    {
        anim.SetTrigger("attack");
        cooldownTimer = 0;

        ghostballs[FindGhostball()].transform.position = firePoint.position;
        ghostballs[FindGhostball()].GetComponent<Projectile>().SetDirection(Mathf.Sign(transform.localScale.x));
    }
    private int FindGhostball()
    {
        for (int i = 0; i < ghostballs.Length; i++)
        {
            if (!ghostballs[i].activeInHierarchy)
                return i;
        }
        return 0;
    }
}

Code PlayerMovement:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerMovement : MonoBehaviour
{   //variables
    private Rigidbody2D rb;
    private BoxCollider2D coll;
    private SpriteRenderer sprite;
    private Animator anim;

    [SerializeField] private LayerMask jumpableGround;

    private float dirX = 0f;
    [SerializeField] private float moveSpeed = 7f;
    [SerializeField] private float jumpForce = 14f;

    private enum MovementState { idle, running, jumping, falling }
    
    [SerializeField] private AudioSource jumpSoundEffect;
    
    // Start is called before the first frame update
    private void Start()
    {
        rb = GetComponent<Rigidbody2D>();
        coll = GetComponent<BoxCollider2D>();
        sprite = GetComponent<SpriteRenderer>();
        anim = GetComponent<Animator>();
    }

    // Update is called once per frame
    private void Update()
    {
        dirX = Input.GetAxisRaw("Horizontal");
        rb.velocity = new Vector2(dirX * moveSpeed, rb.velocity.y);

        if (Input.GetButtonDown("Jump") && IsGrounded())
        {
            jumpSoundEffect.Play();
            rb.velocity = new Vector2(rb.velocity.x, jumpForce);
        }

        AnimState();
    }

    private void AnimState()
    {
        MovementState state;

        if (dirX > 0f)
        {
            state = MovementState.running;
            sprite.flipX = false;
        }
        else if (dirX < 0f)
        {
            state = MovementState.running;
            sprite.flipX = true;
        }
        else 
        {
            state = MovementState.idle;
        }

        if (rb.velocity.y > .1f)
        {
            state = MovementState.jumping;
        }
        else if (rb.velocity.y < -.1f)
        {
            state = MovementState.falling;
        }

        anim.SetInteger("state", (int)state);
    }

    private bool IsGrounded()
    {
        return Physics2D.BoxCast(coll.bounds.center, coll.bounds.size, 0f, Vector2.down, .1f, jumpableGround);
    }

    public bool canAttack()
    {
        return IsGrounded();
    }
}

I appreciate your time investment into resolving my issue. I hope I will find a solution soon since this is a big hurdle for me currently and stops me from progressing.

I have tried messing around with the SetDirection method as I believe there lays the problem but I am uncertain. As I stated in the beginning, I am totally new to C# and Unity.

public void SetDirection(float _direction)
{
    lifetime = 0;
    direction = _direction;
    gameObject.SetActive(true);
    hit = false;
    boxCollider.enabled = true;

    float localScaleX = transform.localScale.x;
    if (Mathf.Sign(localScaleX) != _direction)
    {
        localScaleX = -localScaleX;
        speed = -speed; // Flip the sign of the speed if facing opposite direction
    }

    transform.localScale = new Vector3(localScaleX, transform.localScale.y, transform.localScale.z);
}

3

There are 3 answers

2
Raphael Frei On

You can set the direction that you want inside of transform.Translate().

Like this:

float movementSpeed = speed * Time.deltaTime;
transform.Translate(Vector3.forward * movementSpeed); // Others parameters are not required...
// You can also use Vector3.backward, Vector3.right, left, ...

This way, the system handles with the direction himself.

0
杜韋霆 On

From the script you're posting, my guess would be:

  1. The goastballs aren't in the list so SetDirection() in Projectile.cs doesn't get called.

  2. The script finds the wrong goastball to change direction, which means the implementation of FindGoastball() might need some reviewing

0
CodeOverflow On

Apparently, I have followed the same tutorial as you and I think you solved your own problem. I tried what you did in SetDirection() (as I encountered the same problem):

speed = -speed;

and it worked for me! The player can now shoot both left and right. If you have not tested this, please do.