Rug1241
Rug1241

Reputation: 13

FPS Projectile firing from the wrong place

I'm trying to make a basic FPS game in Unity and I'm having an issue where the projectiles I shoot won't instantiate in the right place. From what I can tell, the projectile instantiates in roughly the same position relative to the player regardless of where I look (that position being a little to the left of the starting angle of the player).

Here's a 20 second video demonstration of what I'm talking about.

https://youtu.be/WLVuqUtMqd0

Even when I'm facing the exact opposite direction of where the projectile usually instantiates it still spawns in the same place, which means the projectile ends up spawning behind the player and hitting the player.

I tried using Debug.DrawRay() to see if maybe the firepoint itself is wrong (which would be shown by the ray starting somewhere other than the gun barrel). However it seems like the starting point of the ray is correct every time.

I'm not sure if this is related to the issue above, but I have noticed that the ray direction is wrong if I'm looking a little below the horizon. It seems like the projectile's direction is correct regardless though.

Ray directions when looking slightly above the horizon vs. lower

Here's my code for intantiating/shooting the projectile. I think the most relevant parts are probably shootProjectile() and instantiateProjectile(). This script is attached to the player character.

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

public class Shooting : MonoBehaviour
{
    public Camera cam;
    public GameObject projectile;
    public Transform firePoint;//firePoint is set to the end of the barrel.
    public float projectileSpeed = 40;
    //private var ray;
    private Vector3 destination;
    // Start is called before the first frame update
    void Start()
    {
       
    }

    // Update is called once per frame
    void Update()
    {
        //var ray = cam.ViewportPointToRay(new Vector3(0.5f,0.5f,0));
        //Debug.DrawRay(ray.origin, ray.direction);
        if(Input.GetButtonDown("Fire1")) {
            //player is shooting
            ShootProjectile();
        }
    }

    void ShootProjectile() {
        Ray ray1 = cam.ScreenPointToRay(Input.mousePosition);
        
        //Debug.Log(ray.direction);
        RaycastHit hit;
        if(Physics.Raycast(ray1, out hit))//checking whether the player is going to hit something
        {
            destination = hit.point;
        }
        else {
            destination = ray1.GetPoint(1000);
        }
        Debug.DrawRay(firePoint.position, destination, Color.white, 10f);
        InstantiateProjectile(firePoint);

        
    }
    
    void InstantiateProjectile(Transform firePoint) {
        var projectileObj = Instantiate (projectile, firePoint.position, Quaternion.identity) as GameObject;//projectile is instantiated
        projectileObj.GetComponent<Rigidbody>().velocity = (destination - firePoint.position).normalized * projectileSpeed;//projectile is set in motion
    }
}

Here's the location of firePoint.

firePoint (i.e. where the projectile should instantiate)

I would appreciate any help on this, as I've been trying to fix it (on and off) for several days, and really have no idea what the problem is.

Edit: Here's my player movement script(s) as well. The first one is PlayerController.cs, and it converts the player's inputs into the appropriate movement vectors and camera rotation. It then calls methods from PlayerMotor.cs, which actually performs the movements.

PlayerController

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[RequireComponent(typeof(PlayerMotor))]
public class PlayerController : MonoBehaviour
{
    [SerializeField]
    public float speed = 10f;
    [SerializeField]
    private float lookSens = 3f;

    private PlayerMotor motor;

    


    void Start() {
        motor = GetComponent<PlayerMotor>();
        //Debug.Log("PlayerControllerStart");
        Cursor.lockState = CursorLockMode.Locked;
    }

    void Update() {
        //Debug.Log("PlayerControllerUpdate");
        //calculate movement velocity as 3D vector.
        float xMov = Input.GetAxisRaw("Horizontal");
        float zMov = Input.GetAxisRaw("Vertical");
        Vector3 movHorizontal = transform.right * xMov;
        Vector3 movVertical = transform.forward * zMov;

        Vector3 velocity = (movHorizontal + movVertical).normalized * speed;
        motor.move(velocity);
        //speed*=(float)1.15;

        //rotation

        float yRot = Input.GetAxisRaw("Mouse X");
        Vector3 rotation = new Vector3 (0f, yRot, 0f) * lookSens;

        motor.rotate(rotation);

        float xRot = Input.GetAxisRaw("Mouse Y");
        Vector3 cameraRotation = new Vector3 (xRot, 0f, 0f) * lookSens;

        motor.rotateCamera(cameraRotation);

        if (Input.GetKeyDown(KeyCode.Space) == true && motor.isGrounded()) {
            
            motor.canJump=true;
        }
        if (Input.GetKey(KeyCode.W) == true) {
            
            motor.accel=true;
        }
        else{
            motor.accel=false;
        }
        
        
    }
}


PlayerMotor:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[RequireComponent(typeof(Rigidbody))]
public class PlayerMotor : MonoBehaviour
{
    [SerializeField]
    private Camera cam;



    private Vector3 velocity = Vector3.zero;
    private Vector3 rotation = Vector3.zero;
    private Vector3 cameraRotation = Vector3.zero;
    private Vector3 jumpVector = new Vector3 (0f, 5f, 0f);
    private PlayerController pc;
    private Rigidbody rb;
    public bool canJump;
    public bool accel;
    public float acceleration;
    int jumpCount;

    void Start() {
        rb = GetComponent<Rigidbody>();
        pc = GetComponent<PlayerController>();
        canJump=false;
        jumpCount=0;
        accel=false;
        //acceleration = 1.0;
        //distToGround = collider.bounds.extents.y;
        //Debug.Log("PlayerMotorStart");
    }
    //sets velocity to a given movement vector.
    public void move(Vector3 _velocity) {
        velocity = _velocity;
    }
    public void rotate(Vector3 _rotation) {
        rotation = _rotation;
    }
    public void rotateCamera(Vector3 _cameraRotation) {
        cameraRotation = _cameraRotation;
    }
    public bool isGrounded() {
        return Physics.Raycast(transform.position, -Vector3.up, (float)2.5);
        
    }
    public void jump() {
        rb.AddForce(transform.up * 250f);
        //Debug.Log("Jump"+jumpCount);
        jumpCount++;
        canJump=false;
    }
    void FixedUpdate() {
        performMovement();
        performRotation();
        if (canJump) {
            
            jump();
        }
        //Debug.Log("PlayerMotorUpdate");
        if(accel&&pc.speed<20f&&isGrounded())
        {
            //Debug.Log("W Pressed");
            pc.speed*=(float)1.005;
        }
        else if(!accel) {
            pc.speed=7f;
            
        }
    }

    void performMovement() {
        if(velocity != Vector3.zero) {
            rb.MovePosition(rb.position + velocity * Time.fixedDeltaTime);
            //Debug.Log("Movement");
        }
    }
    void performRotation() {
        
            rb.MoveRotation(rb.rotation * Quaternion.Euler(rotation));
            if(cam != null) {
                cam.transform.Rotate(-cameraRotation);
            }
    }
    
}

Here are some pictures of my projectile prefab as well.

The main object the projectile comprises of

The Projectile's trail

Upvotes: 1

Views: 504

Answers (1)

derHugo
derHugo

Reputation: 90813

To solve one first confusion: The method Debug.DrawRay takes as paramters

  • a start position
  • a ray direction(!)

You are passing in another position which is not what you want.

This might only work "accidentally" as long as you stand on 0,0,0 so maybe it wasn't that notable but the debt ray definitely points into a wrong direction.

Rather do e.g.

Debug.DrawRay(firePoint.position, destination - firePoint.position, Color.white, 10f);

Or instead rather use Debug.DrawLine which rather actually takes

  • start position
  • end position

Then you don't need to calculate the direction first

Debug.DrawLine(firePoint.position, destination, Color.white, 10f);

Then I suspect the following is happening: The spawned projectileObj actually is placed correctly but it is the visuals inside that projectile prefab that have a local offset against the parent prefab root.

Since you always spawn the projectile in world rotation Quaternion.identity that offset stays the same no matter where you look.

I guess a simple fix would already be to also apply the same rotation like

var projectileObj = Instantiate (projectile, firePoint.position, firePoint.rotation);

Upvotes: 0

Related Questions