文章目錄
- 前言
- 模型素材
- 文章用到的粒子火光特效
- 射擊效果
- 換彈
- 瞄準
- 開槍抖動效果
- 設置顯示文本
- 最終代碼
- 不同武器射擊效果
- 1. 手槍
- 2. 機槍
- 3. 狙擊槍
- 4. 霰彈槍
- 5. 加特林
- 其他
- 感謝
- 完結
前言
實現FPS槍支不同武器效果,比如手槍,噴子,狙擊槍,機槍,其實我最開始的想法是先做一個基類腳本,寫一些公共屬性和方法,然后再起不同的武器腳本這個基礎基類,實現不同的武器效果。
這樣的實現思路其實是沒什么問題的,直到我看到這個視頻:https://www.youtube.com/watch?v=bqNW08Tac0Y,作者只用一個腳本就實現了不同的武器效果更加方便,下面我就參考一下作者的思路實現一下大致的效果。
順帶說一下,在第一人稱射擊(FPS)游戲中實現子彈射擊效果,可以通過不同的技術和方法來完成。以下是幾種常見的實現方式:
-
射線投射(Raycasting):
這是最常用的方法之一。射線投射意味著從槍口發出一個虛擬的射線,并檢測這個射線與游戲世界中的對象之間的交互。如果射線與某個對象相交,那么就可以認為子彈擊中了該對象。實現步驟:
- 從玩家的攝像機或槍口位置發出一條射線。
- 使用物理引擎提供的射線投射功能來檢測射線路徑上的碰撞。
- 如果射線與對象相交,根據交互結果執行相應的邏輯,比如扣除生命值、播放受擊動畫等。
- 在射擊點顯示擊中效果,如粒子效果或貼圖。
-
拋射物模擬(Projectile Simulation):
對于需要模擬子彈飛行軌跡的情況,比如遠距離狙擊、火箭筒或者拋射武器,可以使用拋射物模擬。實現步驟:
- 創建一個子彈實體,并賦予它初始速度和方向。
- 通過物理引擎模擬子彈的飛行軌跡,考慮重力、空氣阻力等因素。
- 檢測子彈與其他對象的碰撞,并在碰撞發生時處理相應的邏輯。
- 在子彈飛行過程中可以添加軌跡效果,如拖尾。
每種方法都有其適用場景和優缺點。射線投射適合快速射擊和近距離交火,拋射物模擬適合遠距離和弧線射擊。在實際開發中,這些方法可以組合使用,以達到最佳的效果。
模型素材
不會配置模型可以看我之前的文章,進行下載和配置:
unity中導入下載的3D模型及albedo/baseColor、normal 、AO/Occlus、metallic、roughness貼圖紋理設置
文章用到的粒子火光特效
https://assetstore.unity.com/packages/vfx/particles/legacy-particle-pack-73777
射擊效果
[Tooltip("是否正在射擊")]
bool shooting;
[Tooltip("是否允許按住射擊")]
public bool allowButtonHold;
[Tooltip("是否可以射擊")]
bool readyToShoot;
[Tooltip("是否在換彈")]
bool reloading;
[Tooltip("彈夾容量")]
public int magazineSize;
[Tooltip("當前彈夾容量")]
public int bulletsLeft;
[Tooltip("儲備彈藥容量")]
public int reservedAmmoCapacity = 300;
[Tooltip("當前剩余射擊發射的子彈數")]
public int bulletsShot;
[Tooltip("槍口火焰特效")]
public ParticleSystem muzzleFlash;
[Tooltip("子彈擊中效果")]
public GameObject bulletHoleGraphic;
[Tooltip("射擊間隔時間")]
public float timeBetweenShooting;
[Tooltip("連發射擊之間的間隔時間")]
public float timeBetweenShots;
[Tooltip("射擊時的散布度")]
public float spread;
[Tooltip("射擊的最大距離")]
public float range;
[Tooltip("每次射擊發射的子彈數")]
public int bulletsPerTap;
[Tooltip("是否允許按住射擊")]
public bool allowButtonHold;
[Tooltip("每次射擊造成的傷害")]
public int damage; // 傷害public Camera fpsCam;private void Awake()
{bulletsLeft = magazineSize;readyToShoot = true;
}private void Update()
{MyInput();
}private void MyInput()
{if (allowButtonHold)shooting = Input.GetKey(KeyCode.Mouse0);elseshooting = Input.GetKeyDown(KeyCode.Mouse0);// 射擊if (readyToShoot && shooting && !reloading && bulletsLeft > 0){bulletsShot = bulletsPerTap;Shoot();}
}private void Shoot()
{readyToShoot = false;// 散布float x = Random.Range(-spread, spread);float y = Random.Range(-spread, spread);// 計算帶有散布的射擊方向Vector3 direction = fpsCam.transform.forward + new Vector3(x, y, 0);//場景顯示紅線,方便調試查看Debug.DrawRay(fpsCam.transform.position, direction * range, Color.red);// 射線檢測if (Physics.Raycast(fpsCam.transform.position, direction, out RaycastHit rayHit, range)){Debug.Log(rayHit.collider.name);muzzleFlash.Play();//槍口火焰/火光//TODO:相機震動if (rayHit.collider.CompareTag("Enemy")){Debug.Log("擊中敵人");Rigidbody rb = rayHit.transform.GetComponent<Rigidbody>();if (rb != null){rb.constraints = RigidbodyConstraints.None; // 解除剛體約束rb.AddForce(transform.parent.transform.forward * 500); // 給敵人施加一個力}// 擊中敵人特效var res1 = Instantiate(bulletHoleGraphic, rayHit.point, Quaternion.Euler(0, 180, 0));Destroy(res1, 0.5f);//TODO:扣血}}bulletsLeft--;bulletsShot--;Invoke("ResetShot", timeBetweenShooting);if (bulletsShot > 0 && bulletsLeft > 0)Invoke("Shoot", timeBetweenShots);
}private void ResetShot()
{readyToShoot = true;
}
換彈
private void MyInput()
{//。。。if (Input.GetKeyDown(KeyCode.R) && bulletsLeft < magazineSize && !reloading)Reload();
}//換彈
private void Reload()
{reloading = true;Invoke("ReloadFinished", reloadTime);
}private void ReloadFinished()
{if (reservedAmmoCapacity <= 0) return;//計算需要填裝的子彈數=1個彈匣子彈數-當前彈匣子彈數int bullectToLoad = magazineSize - bulletsLeft;//計算備彈需扣除子彈數int bullectToReduce = (reservedAmmoCapacity >= bullectToLoad) ? bullectToLoad : reservedAmmoCapacity;reservedAmmoCapacity -= bullectToReduce;//減少備彈數bulletsLeft += bullectToReduce;//當前子彈數增加bulletsLeft = magazineSize;reloading = false;
}
瞄準
private void MyInput()
{//。。。//瞄準DetermineAim();
}void DetermineAim()
{Vector3 target = normalLocalPosition; // 默認目標位置為正常瞄準時的本地位置if (Input.GetMouseButton(1)) target = aimingLocalPosition; // 如果按下鼠標右鍵,目標位置為瞄準時的本地位置Vector3 desiredPosition = Vector3.Lerp(transform.localPosition, target, Time.deltaTime * aimSmoothing); // 使用插值平滑過渡到目標位置transform.localPosition = desiredPosition; // 更新槍支的本地位置
}
效果
開槍抖動效果
如果你的槍模型沒有開槍動畫
的話,這個方法就很方便了
private void Shoot()
{transform.localPosition -= Vector3.forward * 0.1f; // 后坐力使槍支向后移動//。。。
}
設置顯示文本
private void Update()
{//。。。SetUI();
}// 設置文本
private void SetUI()
{text.SetText(bulletsLeft + " / " + reservedAmmoCapacity);
}
最終代碼
public class GunSystem : MonoBehaviour
{public Camera fpsCam;[Header("槍械狀態")][Tooltip("是否正在射擊")]bool shooting;[Tooltip("是否可以射擊")]bool readyToShoot;[Tooltip("是否在換彈")]bool reloading;[Header("彈夾")][Tooltip("彈夾容量")]public int magazineSize;[Tooltip("當前彈夾容量")]public int bulletsLeft;[Tooltip("儲備彈藥容量")]public int reservedAmmoCapacity = 300;[Tooltip("當前剩余射擊發射的子彈數")]public int bulletsShot;[Header("射擊")][Tooltip("射擊間隔時間")]public float timeBetweenShooting;[Tooltip("射擊時的散布度")]public float spread;[Tooltip("射擊的最大距離")]public float range;[Tooltip("每次射擊發射的子彈數")]public int bulletsPerTap;[Tooltip("是否允許按住射擊")]public bool allowButtonHold;[Tooltip("每次射擊造成的傷害")]public int damage; // 傷害[Tooltip("裝填彈藥的時間")]public float reloadTime;[Tooltip("連發射擊之間的間隔時間")]public float timeBetweenShots;[Header("瞄準")][Tooltip("正常情況的本地位置")]public Vector3 normalLocalPosition;[Tooltip("瞄準時的本地位置")]public Vector3 aimingLocalPosition;[Tooltip("瞄準過程的平滑度")]public float aimSmoothing = 10;[Header("效果")][Tooltip("槍口火焰特效")]public ParticleSystem muzzleFlash;[Tooltip("子彈擊中效果")]public GameObject bulletHoleGraphic;[Header("UI")]public TextMeshProUGUI text; // 彈藥顯示文本private void Awake(){bulletsLeft = magazineSize;readyToShoot = true;}private void Update(){MyInput();SetUI();}// 設置文本private void SetUI(){text.SetText(bulletsLeft + " / " + reservedAmmoCapacity);}private void MyInput(){if (allowButtonHold)shooting = Input.GetKey(KeyCode.Mouse0);elseshooting = Input.GetKeyDown(KeyCode.Mouse0);// 射擊if (readyToShoot && shooting && !reloading && bulletsLeft > 0){bulletsShot = bulletsPerTap;Shoot();}//換彈if (Input.GetKeyDown(KeyCode.R) && bulletsLeft < magazineSize && !reloading)Reload();//瞄準DetermineAim();}private void Shoot(){readyToShoot = false;transform.localPosition -= Vector3.forward * 0.1f; // 后坐力使槍支向后移動// 散布float x = Random.Range(-spread, spread);float y = Random.Range(-spread, spread);// 計算帶有散布的射擊方向Vector3 direction = fpsCam.transform.forward + new Vector3(x, y, 0);//場景顯示紅線,方便調試查看Debug.DrawRay(fpsCam.transform.position, direction * range, Color.red);// 射線檢測if (Physics.Raycast(fpsCam.transform.position, direction, out RaycastHit rayHit, range)){Debug.Log(rayHit.collider.name);muzzleFlash.Play();//槍口火焰/火光//相機震動if (rayHit.collider.CompareTag("Enemy")){Debug.Log("擊中敵人");// Rigidbody rb = rayHit.transform.GetComponent<Rigidbody>();// if (rb != null)// {// rb.constraints = RigidbodyConstraints.None; // 解除剛體約束// rb.AddForce(transform.parent.transform.forward * 500); // 給敵人施加一個力// }// 擊中敵人特效var res = Instantiate(bulletHoleGraphic, rayHit.point, Quaternion.Euler(0, 180, 0));res.transform.parent = rayHit.transform;//設置父類//TODO:扣血}}bulletsLeft--;bulletsShot--;Invoke("ResetShot", timeBetweenShooting);if (bulletsShot > 0 && bulletsLeft > 0)Invoke("Shoot", timeBetweenShots);}void DetermineAim(){Vector3 target = normalLocalPosition; // 默認目標位置為正常瞄準時的本地位置if (Input.GetMouseButton(1)) target = aimingLocalPosition; // 如果按下鼠標右鍵,目標位置為瞄準時的本地位置Vector3 desiredPosition = Vector3.Lerp(transform.localPosition, target, Time.deltaTime * aimSmoothing); // 使用插值平滑過渡到目標位置transform.localPosition = desiredPosition; // 更新槍支的本地位置}private void ResetShot(){readyToShoot = true;}//換彈private void Reload(){reloading = true;Invoke("ReloadFinished", reloadTime);}private void ReloadFinished(){if (reservedAmmoCapacity <= 0) return;//計算需要填裝的子彈數=1個彈匣子彈數-當前彈匣子彈數int bullectToLoad = magazineSize - bulletsLeft;//計算備彈需扣除子彈數int bullectToReduce = (reservedAmmoCapacity >= bullectToLoad) ? bullectToLoad : reservedAmmoCapacity;reservedAmmoCapacity -= bullectToReduce;//減少備彈數bulletsLeft += bullectToReduce;//當前子彈數增加bulletsLeft = magazineSize;reloading = false;}
}
不同武器射擊效果
注意
:這里為了方便,我就用一把槍做演示了
1. 手槍
參數配置
效果
2. 機槍
參數
效果
3. 狙擊槍
參數,狙擊槍其實和手槍參數差不多,可以就需要修改射擊間隔時間、換彈時間和傷害
效果
4. 霰彈槍
參數
效果
5. 加特林
參數
效果
其他
可以看到其實還有很多功能沒有實現,比如后座力或者放大鏡等等效果,這篇文章說的已經夠多了,后面我再單獨做其他內容的探究吧!
感謝
【視頻】https://www.youtube.com/watch?v=bqNW08Tac0Y
完結
贈人玫瑰,手有余香!如果文章內容對你有所幫助,請不要吝嗇你的點贊評論和關注
,以便我第一時間收到反饋,你的每一次支持
都是我不斷創作的最大動力。當然如果你發現了文章中存在錯誤
或者有更好的解決方法
,也歡迎評論私信告訴我哦!
好了,我是向宇
,https://xiangyu.blog.csdn.net
一位在小公司默默奮斗的開發者,出于興趣愛好,于是最近才開始自習unity。如果你遇到任何問題,也歡迎你評論私信找我, 雖然有些問題我可能也不一定會,但是我會查閱各方資料,爭取給出最好的建議,希望可以幫助更多想學編程的人,共勉~