射击脚本
public class Weapon : MonoBehaviour { public GameObject bulletPrefab; // 子弹预制体 public Transform bulletSpawn; // 子弹生成位置 public float bulletVelocity = 30f; // 子弹速度 public float bulletPrefabLifeTime = 3f; // 子弹存在时间 void Update() { // 按下鼠标左键 if (Input.GetKeyDown(KeyCode.Mouse0)) { FireWeapon(); // 开火 } } private void FireWeapon() { // 实例化子弹 GameObject bullet = Instantiate(bulletPrefab, bulletSpawn.position, Quaternion.identity); // 发射子弹 bullet.GetComponent<Rigidbody>().AddForce(bulletSpawn.forward.normalized * bulletVelocity, ForceMode.Impulse); // 一段时间后销毁子弹 StartCoroutine(DestroyBulletAfterTime(bullet, bulletPrefabLifeTime)); } private IEnumerator DestroyBulletAfterTime(GameObject bullet, float delay) { yield return new WaitForSeconds(delay); Destroy(bullet); } }
新增子弹预制体
如果没有子弹模型,可以新建一个胶囊体缩小尺寸代替
子弹挂载脚本,实现触碰销毁
public class Bullet : MonoBehaviour { private void OnCollisionEnter(Collision collision) { if (collision.gameObject.CompareTag("Target")) { // 打中目标并输出信息 print("Hit " + collision.gameObject.name + "!"); // 销毁目标物体 Destroy(collision.gameObject); } } }
子弹添加拖尾效果
新增拖尾材质Particles/Standard Unlit,配置参数
绑定材质
扩展
分享一个实体子弹射击脚本,一个脚本就可实现
public class ProjectileGunTutorial : MonoBehaviour { //子弹 public GameObject bullet; //发射力量 public float shootForce, upwardForce; //枪支状态 public float timeBetweenShooting, spread, reloadTime, timeBetweenShots; public int magazineSize, bulletsPerTap; public bool allowButtonHold; int bulletsLeft, bulletsShot; //布尔值 bool shooting, readyToShoot, reloading; //引用 public Camera fpsCam; public Transform attackPoint; //图形 public GameObject muzzleFlash; public TextMeshProUGUI ammunitionDisplay; //修复bug:D public bool allowInvoke = true; private void Awake() { //确保弹夹是满的 bulletsLeft = magazineSize; readyToShoot = true; } private void Update() { MyInput(); //设置弹药显示,如果存在的话:D if (ammunitionDisplay != null) ammunitionDisplay.SetText(bulletsLeft / bulletsPerTap + " / " + magazineSize / bulletsPerTap); } private void MyInput() { //检查是否允许按住按钮并采取相应的输入 if (allowButtonHold) shooting = Input.GetKey(KeyCode.Mouse0); else shooting = Input.GetKeyDown(KeyCode.Mouse0); //重新装填 if (Input.GetKeyDown(KeyCode.R) && bulletsLeft < magazineSize && !reloading) Reload(); //尝试在没有子弹的情况下射击时自动重新加载 if (readyToShoot && shooting && !reloading && bulletsLeft <= 0) Reload(); //射击 if (readyToShoot && shooting && !reloading && bulletsLeft > 0) { //将射出的子弹数设为0 bulletsShot = 0; Shoot(); } } private void Shoot() { readyToShoot = false; //使用射线投射找到精确的命中位置 Ray ray = fpsCam.ViewportPointToRay(new Vector3(0.5f, 0.5f, 0)); //只是通过当前视图中间的射线 RaycastHit hit; //检查射线是否击中物体 Vector3 targetPoint; if (Physics.Raycast(ray, out hit)) targetPoint = hit.point; else targetPoint = ray.GetPoint(75); //只是远离玩家的一个点 //计算从攻击点到目标点的方向 Vector3 directionWithoutSpread = targetPoint - attackPoint.position; //计算散射 float x = Random.Range(-spread, spread); float y = Random.Range(-spread, spread); //计算带有散射的新方向 Vector3 directionWithSpread = directionWithoutSpread + new Vector3(x, y, 0); //只需将散射添加到最后的方向 //实例化子弹/抛射物 GameObject currentBullet = Instantiate(bullet, attackPoint.position, Quaternion.identity); //将实例化的子弹存储在currentBullet中 //将子弹旋转到射击方向 currentBullet.transform.forward = directionWithSpread.normalized; //为子弹添加力量 currentBullet.GetComponent<Rigidbody>().AddForce(directionWithSpread.normalized * shootForce, ForceMode.Impulse); currentBullet.GetComponent<Rigidbody>().AddForce(fpsCam.transform.up * upwardForce, ForceMode.Impulse); //实例化枪口火焰,如果有的话 if (muzzleFlash != null) Instantiate(muzzleFlash, attackPoint.position, Quaternion.identity); bulletsLeft--; bulletsShot++; //调用resetShot函数(如果尚未调用),使用timeBetweenShooting if (allowInvoke) { Invoke("ResetShot", timeBetweenShooting); allowInvoke = false; } //如果超过一个bulletsPerTap,请确保重复射击函数 if (bulletsShot < bulletsPerTap && bulletsLeft > 0) Invoke("Shoot", timeBetweenShots); } private void ResetShot() { //允许再次射击和调用 readyToShoot = true; allowInvoke = true; } private void Reload() { reloading = true; Invoke("ReloadFinished", reloadTime); //使用reloadTime作为延迟调用ReloadFinished函数 } private void ReloadFinished() { //填充弹夹 bulletsLeft = magazineSize; reloading = false; } }