1

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 Answers3

0

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.

Raphael Frei
  • 361
  • 1
  • 10
  • Forgive my ignorance, but replacing this with my code did not solve my issue. The projectile still doesn't go towards the left and stays right. With this implemented, the projectile also doesn't move. Do I have to do additional things in order for your answer to work for my situation? Thanks for your help! – Joey Felling Apr 20 '23 at 18:30
  • Just to make sure... See if HIT is not starting as true by starting the game and looking at the fireball with DEBUG enabled on inspector. (Or to make more sure yet, set it as false in Awake or Start method) – Raphael Frei Apr 20 '23 at 19:17
0

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.

Super Kai - Kazuya Ito
  • 22,221
  • 10
  • 124
  • 129
CodeOverflow
  • 15
  • 1
  • 4
-1

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

杜韋霆
  • 14
  • 2