You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Unity项目中子弹反向射向玩家的技术求助

问题分析与解决方案

核心问题

子弹反向射向玩家的最可能原因是**cursorPosition的获取方式错误**,导致Fire方法传入的targetPosition并非鼠标在世界空间中指向的正确位置,而是指向了玩家/相机所在位置,最终子弹LookAt这个点后朝向玩家发射。

比如如果你的cursorPosition是通过Camera.main.ScreenToWorldPoint(Input.mousePosition)直接获取的,由于Input.mousePosition的Z值为0,透视相机下会返回相机自身的位置(第一人称视角下就是玩家位置),子弹自然会反向射向玩家。

修复步骤

  1. 替换cursorPosition的获取逻辑,改用射线检测获取鼠标在场景中的命中点:
// 在PlayerBehaviour中获取cursorPosition的代码
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out RaycastHit hitInfo))
{
    cursorPosition = hitInfo.point;
}
  1. 确保场景中有可被射线检测的地面/碰撞体(比如给地面添加Collider组件)。

额外排查点

  • 检查子弹预制体的Transform轴方向:确保子弹的Z轴(默认forward方向)与模型前进方向一致,如果模型的前进方向是X轴,需要调整代码为ourRigidBody.velocity = transform.right * bulletSpeed;
  • 确认武器的transform.forward确实指向前方:可以在Scene视图中选中武器GameObject,查看Gizmos的蓝色轴(forward)方向是否正确。

原问题代码

WeaponBehaviour.cs

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

public class WeaponBehaviour : MonoBehaviour
{
    public GameObject bulletPrefab;
    public float secondsBetweenShots;
    float secondSinceLastShot;
    public float accuracy; // if accuracy is 0, the game will crash
    public float numberOfProjectiles;
    public AudioSource audioSource;
    public float kickAmount;


    // Start is called before the first frame update
    void Start()
    {
        secondSinceLastShot = secondsBetweenShots;
        audioSource = GetComponent<AudioSource>();

    }

    // Update is called once per frame
    void Update()
    {
        //firing
        secondSinceLastShot += Time.deltaTime;
            
    }

    public void Fire(Vector3 targetPosition)
        { 
            if (secondSinceLastShot > secondsBetweenShots)
            {
                //ready to fire
                Debug.Log("T" + targetPosition);
                //this next line makes it so that when you fire your weapon, the enemies immediately start spawning
                references.levelManager.alarmSounded = true;
                audioSource.Play();
                references.screenshake.joltVector = transform.forward * -kickAmount;

                for (
                 int iterationCount=0;  //declare a variable to keep track of how many iterations we've done
                 iterationCount < numberOfProjectiles;  //set a limit for how high this variable can go
                 iterationCount++   //run this after each time we iterate - increase the iteration count
                )
                {
                    GameObject newBullet = Instantiate(bulletPrefab, transform.position + transform.forward, transform.rotation);
                    //offset that target position by a random amount according to our inaccuracy
                    float inaccuracy = Vector3.Distance(transform.position, targetPosition) / accuracy;
                    Vector3 inaccuratePosition = targetPosition; 
                    inaccuratePosition.x += Random.Range(-inaccuracy, inaccuracy);
                    inaccuratePosition.z += Random.Range(-inaccuracy, inaccuracy);
                    newBullet.transform.LookAt(inaccuratePosition);
                    Debug.Log(inaccuratePosition);
                    secondSinceLastShot = 0;
                    //newBullet.name = iterationCount.ToString();
                }
            }
        }

}

BulletBehaviour.cs

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

public class BulletBehaviour : MonoBehaviour
{

    public float bulletSpeed;
    public float secondsUntilDestroyed;
    public float damage;


    
    // Start is called before the first frame update
    void Start()
    {
       Rigidbody ourRigidBody = GetComponent<Rigidbody>();
       ourRigidBody.velocity = transform.forward * bulletSpeed;
        //rigidbody.velocity = transform.forward * bulletSpeed;
    }

    // Update is called once per frame
    protected virtual void Update()
    {
        secondsUntilDestroyed -= Time.deltaTime;

        if (secondsUntilDestroyed < 1)

        {
            transform.localScale *= secondsUntilDestroyed;
        }

        if (secondsUntilDestroyed < 0)
        {
            Destroy(gameObject);
        }
    }

    private void OnCollisionEnter(Collision thisCollision)
    {
        GameObject theirGameObject = thisCollision.gameObject;
        //after thisCollision. the options are GetEnumerator, GetHashCode, GetType, impactForceSum, impulse, other, relativeVelocity
        //rigidBody, ToString, contacts, Equals, frictionForce\sum, gameObject, GetContact, GetContacts.
        
        
        //them.TakeDamage(1;)
        HealthSystem theirHealthSystem = theirGameObject.GetComponent<HealthSystem>();


        if (theirHealthSystem != null) 
        {
            theirHealthSystem.TakeDamage(damage);       
        }

        Destroy(gameObject);
        
    }

}

PlayerBehaviour 触发射击代码片段

if( weapons.Count > 0 && Input.GetButton("Fire1"))
{
         //tell our weapon to fire
         weapons[selectedWeaponIndex].Fire(cursorPosition);
}

内容的提问来源于stack exchange,提问作者Arcader Master

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 13:27:03