目录
🎮MouseLook摄像机旋转脚本
🎮PickUpItem武器拾取脚本
🎮PlayerController玩家控制器
🎮Inventory武器库
🎮Weapon武器抽象类
🎮Weapon_AutomaticGun武器脚本
其实这个教程很早就收藏了就是被20个小时时长给吓到了一直没开始。终于在七月中旬的时候开始动手跟着做了。
最近一直加班也很忙,参与的第一个项目计划在年底上线,也是有项目经验的人了哈哈。然后拖拖拉拉的一直跟着教程做,只是个半成品敌人部分还没做,以我目前的精力就先做到这里了。原教程链接:第一人称射击游戏教程2.0【已完结】_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV1J8411d75s/?spm_id_from=333.999.0.0&vd_source=6b664fb3236a2f45f99b9eb3868cdf31
教程讲的很细,有C#和Unity基础的做到我现在这样没什么太大的问题,但教程讲的也有点慢,所以时长才会达到二十小时,我看的时候是1.5倍速看的。
主要功能:枪械有AK、消音m4、格洛克、霰弹枪、狙击枪;玩家行走、奔跑、跳跃、下蹲;
不同枪械不同的装弹方式、瞄准状态、准星动态变化、全自动半自动模式切换、武器库切换武器、随机弹道、武器的检视功能。
演示视频:
20240920
由于间隔时间太长本篇笔记就不从头开始讲了,只把核心脚本代码和Demo源码放出来,脚本代码也有对应的注释,感兴趣的话大家可以结合原教程博主和下面的核心代码一起看。
MouseLook摄像机旋转脚本
using System.Collections;
using System.Collections.Generic;
using UnityEngine;/// <summary>
/// 摄像机旋转
/// 玩家左右旋转控制实现左右移动
/// 摄像机上下旋转控制视线上下移动
/// </summary>
public class MouseLook : MonoBehaviour
{[Tooltip("视野灵敏度")]public float mouseSenstivity = 400f;public Transform playerBody; //玩家位置private float yRotation = 0f; //摄像机上下旋转的数值private CharacterController characterController;[Tooltip("当前摄像机的初始高度")] public float height = 1.8f;private float interpolationSpeed = 12f;// Start is called before the first frame updatevoid Start(){Cursor.lockState = CursorLockMode.Locked;//playerBody = transform.GetComponent<PlayerController>().transform;characterController = GetComponentInParent<CharacterController>();}// Update is called once per framevoid Update(){float mouseX = Input.GetAxis("Mouse X") * mouseSenstivity * Time.deltaTime;float mouseY = Input.GetAxis("Mouse Y") * mouseSenstivity * Time.deltaTime;yRotation -= mouseY;//上下旋转//限制旋转大小yRotation = Mathf.Clamp(yRotation, -60f, 60f);transform.localRotation = Quaternion.Euler(yRotation,0f,0f);//摄像机上下旋转playerBody.Rotate(Vector3.up * mouseX);//玩家左右移动//当人物下蹲或站立时,摄像机的高度也会发生变化float heightTarget = characterController.height * 0.9f;height = Mathf.Lerp(height, heightTarget, interpolationSpeed * Time.deltaTime);//设置下蹲站立时的摄像机高度transform.localPosition = Vector3.up * height; }
}
PickUpItem武器拾取脚本
using UnityEngine;/// <summary>
/// 武器拾取
/// </summary>
public class PickUpItem : MonoBehaviour
{[Tooltip("武器旋转的速度")] private float rotateSpeed;[Tooltip("武器编号")] public int itemID;private GameObject weaponModel;// Start is called before the first frame updatevoid Start(){rotateSpeed = 100f;}// Update is called once per framevoid Update(){transform.eulerAngles += new Vector3(0, rotateSpeed * Time.deltaTime, 0);}private void OnTriggerEnter(Collider other){if (other.name == "Player"){PlayerController player = other.GetComponent<PlayerController>();//查找获取 Inventory 物体下的各个武器物体weaponModel = GameObject.Find("Player/Assult_Rife_Arm/Inventory/").gameObject.transform.GetChild(itemID).gameObject;//Debug.Log(weaponModel.name);player.PickUpWeapon(itemID,weaponModel);Destroy(gameObject);}}
}
PlayerController玩家控制器
using UnityEngine;
using UnityEngine.UI;public class PlayerController : MonoBehaviour
{private CharacterController characterController;public Vector3 moveDirection; //人物控制方向private AudioSource audioSource;[Header("玩家数值")] private float Speed;[Tooltip("行走速度")] public float walkSpeed;[Tooltip("奔跑速度")] public float runSpeed;[Tooltip("下蹲速度")] public float crouchSpeed;[Tooltip("玩家生命值")] public float playerHealth;[Tooltip("跳跃的力")] public float jumpForce;[Tooltip("下落的力")] public float fallForce;[Tooltip("下蹲时玩家的高度")] public float crouchHeight;[Tooltip("站立时玩家的高度")] public float standHeight;[Header("键位设置")] [Tooltip("奔跑")] private KeyCode runInputName = KeyCode.LeftShift;[Tooltip("跳跃")] private KeyCode jumpInputName = KeyCode.Space;[Tooltip("下蹲")] private KeyCode crouchInputName = KeyCode.LeftControl;[Header("玩家属性判断")] public MovementState state;private CollisionFlags collisionFlags;public bool isWalk;public bool isRun;public bool isJump;public bool isGround;//玩家是否在地面上public bool isCanCrouch; //判断玩家是否可以下蹲public bool isCrouching; //判断玩家是否处于下蹲状态private bool playerIsDead; //判断玩家是否死亡private bool isDamage; //判断玩家是否受到伤害public LayerMask crouchLayerMask;public Text playerHealthUI;[Header("音效")] [Tooltip("行走音效")] public AudioClip walkSound;[Tooltip("奔跑音效")] public AudioClip runningSound;private Inventory inventory;// Start is called before the first frame updatevoid Start(){characterController = GetComponent<CharacterController>();audioSource = GetComponent<AudioSource>();walkSpeed = 4f;runSpeed = 6f;crouchSpeed = 2f;jumpForce = 0f;fallForce = 10f;isGround = true;crouchHeight = 1f;standHeight = characterController.height;inventory = GetComponentInChildren<Inventory>();playerHealth = 100f;playerHealthUI.text = "生命值:" + playerHealth;}// Update is called once per framevoid Update(){CanCrouch();if (Input.GetKey(crouchInputName)){Crouch(true);}else{Crouch(false);}PlayerFootSoundSet();Moving();Jump();}public void Moving(){//GetAxis 不急停 GetAxisRaw 急停float h = Input.GetAxisRaw("Horizontal");float v = Input.GetAxisRaw("Vertical");isRun = Input.GetKey(runInputName);isWalk = (Mathf.Abs(h) > 0 || Mathf.Abs(v) > 0) ? true : false;if (isRun && isGround && isCanCrouch && !isCrouching){state = MovementState.running;Speed = runSpeed;}else if(isGround){state = MovementState.walking;Speed = walkSpeed;if (isCrouching){state = MovementState.crouching;Speed = crouchSpeed;}}//同时按下蹲和奔跑时,人物应该是下蹲速度if (isRun && isCrouching){state = MovementState.crouching;Speed = crouchSpeed;}moveDirection = (transform.right * h + transform.forward * v).normalized;characterController.Move(moveDirection * Speed * Time.deltaTime); //人物移动}public void Jump(){if(!isCanCrouch) return; //不能进行站立时也不能进行跳跃操作isJump = Input.GetKeyDown(jumpInputName);//按下跳跃键并且在地面上才会进行跳跃if (isJump && isGround){isGround = false;jumpForce = 5f;}//如果当前没有按下空格并且在检测地面上,那么isGround判断为falseelse if(!isJump && isGround){isGround = false;jumpForce = -2f;}//此时玩家已经跳起来了if (!isGround){jumpForce = jumpForce - fallForce * Time.deltaTime;Vector3 jump = new Vector3(0,jumpForce * Time.deltaTime,0); //将向上的力转换为V3坐标collisionFlags = characterController.Move(jump); //调用角色控制器移动方法,向上方模拟跳跃/*判断玩家是否在地面上 *CollisionFlags: characterController 内置的碰撞位置标识号* CollisionFlags.Below: 在地面上*/if (collisionFlags == CollisionFlags.Below){isGround = true;jumpForce = 0f;}//玩家什么都没有碰到说明不在地面上// if (isGround && collisionFlags == CollisionFlags.None)// {// isGround = false;// }}}//判断人物是否可以下蹲//isCanCrouch: true说明头上没有物体可以下蹲public void CanCrouch(){//获取人物头顶的高度V3位置Vector3 sphereLocation = transform.position + Vector3.up * standHeight;//Debug.Log("sphereLocation:" + sphereLocation);//判断头顶和他自己碰撞的个数是否等于0isCanCrouch = (Physics.OverlapSphere(sphereLocation,characterController.radius,crouchLayerMask).Length) == 0;//// Collider[] colis = Physics.OverlapSphere(sphereLocation, characterController.radius, crouchLayerMask);// for (int i = 0; i < colis.Length; i++)// {// Debug.Log("colis:" + colis[i].name);// }// Debug.Log("colis:" + colis.Length);//Debug.Log("isCanCrouch:" + isCanCrouch);}//下蹲public void Crouch(bool newCrouching){if(!isCanCrouch) return; //不可下蹲时(在隧道),不能进行站立isCrouching = newCrouching;characterController.height = isCrouching ? crouchHeight : standHeight; //根据下蹲状态设置下蹲characterController.center = characterController.height / 2.0f * Vector3.up; //将角色控制器的中心位置Y,从头顶往下减少一半的高度}//人物移动音效public void PlayerFootSoundSet(){//sqrMagnitude 返回该向量的平方长度if (isGround && moveDirection.sqrMagnitude > 0){audioSource.clip = isRun ? runningSound : walkSound;if (!audioSource.isPlaying){//播放行走或者奔跑音效audioSource.Play();}}else{if (audioSource.isPlaying){//暂停播放行走或奔跑音效audioSource.Pause();}}//下蹲时不播放新增音效if (isCrouching){if (audioSource.isPlaying){audioSource.Pause();}}}/// <summary>/// 拾取武器/// </summary>public void PickUpWeapon(int itemID,GameObject weapon){//捡到武器后,在武器库里添加,否则补充弹药if (inventory.weapons.Contains(weapon)){weapon.GetComponent<Weapon_AutomaticGun>().bulletLeft =weapon.GetComponent<Weapon_AutomaticGun>().bulletMag * 5;weapon.GetComponent<Weapon_AutomaticGun>().UpdateAmmoUI();Debug.Log("集合里已存在此枪械,补充弹药");return;}else{inventory.AddWeapon(weapon);}}/// <summary>/// 玩家生命值/// </summary>/// <param name="damage">接收到的伤害值</param>public void PlayerHealth(float damage){playerHealth -= damage;isDamage = true;playerHealthUI.text = "生命值:" + playerHealth;if (playerHealth <= 0){playerIsDead = true;playerHealthUI.text = "玩家死亡";Time.timeScale = 0; //游戏暂停}}public enum MovementState{walking,running,crouching,idle}
}
Inventory武器库
using System.Collections.Generic;
using System.Linq;
using UnityEngine;/// <summary>
/// 武器库
/// 人物的武器切换,添加,去除功能
/// </summary>
public class Inventory : MonoBehaviour
{//武器库public List<GameObject> weapons = new List<GameObject>();//当前使用的武器编号public int currentWeaponID;// Start is called before the first frame updatevoid Start(){currentWeaponID = -1;}// Update is called once per framevoid Update(){ChargeCurrentWeaponID();}/// <summary>/// 更新武器编号/// </summary>public void ChargeCurrentWeaponID(){//鼠标滚轮向下 -0.1 向上 0.1 不动 0if (Input.GetAxis("Mouse ScrollWheel") < 0){//下一把武器ChargeWeapon(currentWeaponID + 1);}else if(Input.GetAxis("Mouse ScrollWheel") > 0){//上一把武器ChargeWeapon(currentWeaponID - 1);}//通过数字键盘来切换武器for (int i = 0; i < 10; i++){if (Input.GetKeyDown(KeyCode.Alpha0 + i)){int num = 0;if (i == 10){num = 10;}else{num = i - 1;}/* 只有数字小于武器列表个数的时候才能进一步处理如果有3把武器,但按下4-9,那么是无意义的,不处理*/if (num<weapons.Count){ChargeWeapon(num);}}}}/// <summary>/// 武器切换/// </summary>/// <param name="weaponID">武器编号</param>public void ChargeWeapon(int weaponID){if(weapons.Count == 0 )return;//如果切换到最大索引编号的枪的时候,就调出第一把枪//如果切换到最小索引编号的枪的时候,就调出最后一把枪//IndexOf: 获取列表中元素首次出现的索引//Max list里取最大元素if (weaponID > weapons.Max(weapons.IndexOf)){weaponID = weapons.Min(weapons.IndexOf);}else if (weaponID < weapons.Min(weapons.IndexOf)){weaponID = weapons.Max(weapons.IndexOf);}if (weaponID == currentWeaponID){//只有一种武器不进行切换return;}//更新武器索引currentWeaponID = weaponID;//根据武器编号,显示出对应的武器for (int i = 0; i < weapons.Count; i++){if (weaponID == i){weapons[i].gameObject.SetActive(true);}else{weapons[i].gameObject.SetActive(false);}}}/// <summary>/// 拾取武器/// </summary>public void AddWeapon(GameObject weapon){if (weapons.Contains(weapon)){print("集合已存在此枪械");return;}else{if (weapons.Count < 10) //最多携带3把枪{weapons.Add(weapon);ChargeWeapon(currentWeaponID+1);//显示武器weapon.gameObject.SetActive(true);}}}/// <summary>/// 丢弃武器/// </summary>public void ThrowWeapon(GameObject weapon){if (!weapons.Contains(weapon) || weapons.Count == 0){print("没有这个武器,无法抛弃");return;}else{weapons.Remove(weapon);//集合中删除对应的武器ChargeWeapon(currentWeaponID-1);weapon.gameObject.SetActive(false);}}
}
Weapon武器抽象类
using UnityEngine;public abstract class Weapon : MonoBehaviour
{public abstract void GunFire();public abstract void Reload();public abstract void ExpandingCrossUpdate(float expanDegree);public abstract void DoReloadAnimation();public abstract void AimIn();public abstract void AimOut();
}
Weapon_AutomaticGun武器脚本
using UnityEngine;
using Random = UnityEngine.Random;
using System.Collections;
using System.Collections.Generic;
using System.Numerics;
using UnityEngine.UI;
using Quaternion = UnityEngine.Quaternion;
using Vector3 = UnityEngine.Vector3;//武器音效内部类
[System.Serializable]
public class SoundClips
{public AudioClip shootSound; //开火音效public AudioClip silencerShootSound; //开火音效带消音器public AudioClip reloadSoundAmmotLeft; //换子弹音效public AudioClip reloadSoundOutOfAmmo; //换子弹并拉枪栓(一个弹匣打完)public AudioClip aimSound; //瞄准音效
}public class Weapon_AutomaticGun : Weapon
{public Animator animator;private PlayerController playerController;private Camera mainCamera;public Camera gunCamera;public bool IS_AUTORIFLE; //是否是自动武器public bool IS_SEMIGUN; //是否半自动武器[Header("武器部件位置")] [Tooltip("射击的位置")] public Transform ShootPoint; //射线打出的位置 public Transform BulletShootPoint; //子弹特效打出的位置[Tooltip("子弹壳抛出的位置")] public Transform CasingBulletSpawnPoint;[Header("子弹预制体和特效")] public Transform bulletPrefab; //子弹public Transform casingPrefab; //子弹抛壳[Header("枪械属性")] [Tooltip("武器射程")] private float range;[Tooltip("武器射速")] public float fireRate;private float originRate; //原始射速private float SpreadFactor; //射击的一点偏移量private float fireTimer; //计时器 控制武器射速private float bulletForce; //子弹发射的力[Tooltip("当前武器的每个弹匣子弹数")] public int bulletMag;[Tooltip("当前子弹数")] public int currentBullets;[Tooltip("备弹")] public int bulletLeft;public bool isSilencer; //是否装备消音器private int shotgunFragment = 8;//1次打出的子弹数[Header("特效")] public Light muzzleflashLight; //开火灯光private float lightDuration; //灯光持续时间public ParticleSystem muzzlePatic; //灯光火焰粒子特效1public ParticleSystem sparkPatic; //灯光火焰粒子特效2(火星子)vpublic int minSparkEmission = 1;public int maxSparkEmission = 7;[Header("音源")] private AudioSource mainAudioSource;public SoundClips soundClips;//private AudioSource shootAudioSource;[Header("UI")] public Image[] crossQuarterImgs; //准心public float currentExoanedDegree; //当前准心的开合度private float crossExpanedDegree; //每帧准心开合度private float maxCrossDegree;//最大开合度public Text ammoTextUI;public Text shootModeTextUI;public PlayerController.MovementState state;private bool isReloading; //判断是否在换弹[Header("键位设置")] [SerializeField] [Tooltip("填装子弹按键")] private KeyCode reloadInputName = KeyCode.R;[SerializeField] [Tooltip("自动半自动切换按键")] private KeyCode gunShootModelInputName = KeyCode.V;[SerializeField] [Tooltip("检视武器按键")] private KeyCode inspectInputName = KeyCode.F;//切换全自动半自动public ShootMode shootingMode;private bool GunShootInput;private int modeNum; //模式切换的一个中间参数(1:全自动,2:半自动)private string shootModeName;//瞄准private bool isAiming;private Vector3 sniperingFiflePosition; //枪默认的初始位置public Vector3 sniperingFifleOnPosition; //开始瞄准的模型位置//------------------------------修改瞄准时的声音播放问题,只针对本项目,与B站教程无关private bool IsAimInPlay = true;[Header("狙击镜设置")] [Tooltip("狙击镜材质")] public Material scopeRenderMaterial;[Tooltip("当没有进行瞄准时狙击镜的颜色")] public Color fadeColor;[Tooltip("当瞄准时狙击镜的颜色")] public Color defaultColor;private void Awake(){playerController = GetComponentInParent<PlayerController>();mainAudioSource = GetComponent<AudioSource>();animator = GetComponent<Animator>();mainCamera = Camera.main;}private void Start(){muzzleflashLight.enabled = false;crossExpanedDegree = 50f;maxCrossDegree = 300f;lightDuration = 0.02f;range = 300f;bulletLeft = bulletMag * 5;currentBullets = bulletMag;bulletForce = 100f;originRate = 0.1f;//随机弹道精准度SpreadFactor = 0.05f;UpdateAmmoUI();//根据不同枪械,游戏刚开始时进行不同射击模式设置if (IS_AUTORIFLE){modeNum = 1;shootModeName = "全自动";shootingMode = ShootMode.AutoRifle;UpdateAmmoUI();}if (IS_SEMIGUN){modeNum = 0;shootModeName = "半自动";shootingMode = ShootMode.SemiGun;UpdateAmmoUI();}//isAiming是否是瞄准状态isAiming = false;sniperingFiflePosition = transform.localPosition;}private void Update(){// 自动枪械鼠标输入方式 可以在 GetMouseButton 和 GetMouseButtonDown 里切换if (IS_AUTORIFLE){//切换射击模式(全自动和半自动)if (Input.GetKeyDown(gunShootModelInputName) && modeNum != 1){modeNum = 1;shootModeName = "全自动";shootingMode = ShootMode.AutoRifle;UpdateAmmoUI();}else if(Input.GetKeyDown(gunShootModelInputName) && modeNum != 0){modeNum = 0;shootModeName = "半自动";shootingMode = ShootMode.SemiGun;UpdateAmmoUI();}//控制射击模式的转换 后面就要用代码去动态控制了switch (shootingMode){case ShootMode.AutoRifle:GunShootInput = Input.GetMouseButton(0);fireRate = originRate;break;case ShootMode.SemiGun:GunShootInput = Input.GetMouseButtonDown(0);fireRate = 0.2f;break;}}else{//半自动枪械鼠标输入方式改为GetMouseButtonDownGunShootInput = Input.GetMouseButtonDown(0);}state = playerController.state;//这里实时获取任务的移动状态(行走,奔跑,下蹲)if (state == PlayerController.MovementState.walking && Vector3.SqrMagnitude(playerController.moveDirection)>0 && state != PlayerController.MovementState.running && state != PlayerController.MovementState.crouching){//移动时的准心开合度ExpandingCrossUpdate(crossExpanedDegree);}else if (state != PlayerController.MovementState.walking && state == PlayerController.MovementState.running &&state != PlayerController.MovementState.crouching){//奔跑时的准心开合度(2倍)ExpandingCrossUpdate(crossExpanedDegree*2);}else{//站立或者下蹲时,不调整准心开合度ExpandingCrossUpdate(0);}if (GunShootInput && currentBullets > 0){//霰弹枪射击1次同时打出8次射线,其余枪械正常1次射线if (IS_SEMIGUN && gameObject.name == "4"){shotgunFragment = 8;}else{shotgunFragment = 1;}//开枪射击GunFire(); }//腰射和瞄准射击精准不同SpreadFactor = (isAiming) ? 0.01f : 0.1f;if (Input.GetKeyDown(inspectInputName)){animator.SetTrigger("inspect");}//计时器if (fireTimer < fireRate){fireTimer += Time.deltaTime;}//playerController.isWalk;animator.SetBool("Run",playerController.isRun);animator.SetBool("Walk",playerController.isWalk);//两种换子弹的动画(包括霰弹枪换弹动画逻辑)AnimatorStateInfo info = animator.GetCurrentAnimatorStateInfo(0);if (info.IsName("reload01") || info.IsName("reload") || info.IsName("reload_open") ||info.IsName("reload_insert") ||info.IsName("reload_insert 1") ||info.IsName("reload_insert 2") ||info.IsName("reload_insert 3") ||info.IsName("reload_insert 4") ||info.IsName("reload_insert 5") ||info.IsName("reload_close")){isReloading = true;}else{isReloading = false;}//根据当前霰弹枪子弹数填装的数量判断结束insert动画//解决了本来放2个子弹,但实际上添加子弹的动画播放了3次的问题if ((info.IsName("reload_insert") ||info.IsName("reload_insert 1") ||info.IsName("reload_insert 2") ||info.IsName("reload_insert 3") ||info.IsName("reload_insert 4") ||info.IsName("reload_insert 5")) && currentBullets == bulletMag){//当前霰弹枪子弹填装完毕,随时结束换弹判断animator.Play("reload_close");isReloading = false;}//按R换弹if (Input.GetKeyDown(reloadInputName) && currentBullets < bulletMag && bulletLeft > 0 && !isReloading && !isAiming){DoReloadAnimation();}//鼠标右键进入瞄准if (Input.GetMouseButton(1) && !isReloading && !playerController.isRun){isAiming = true;animator.SetBool("Aim",isAiming);//瞄准时需要微调一下枪的模型位置transform.localPosition = sniperingFifleOnPosition;AimIn();}else if(Input.GetMouseButtonUp(1)){isAiming = false;animator.SetBool("Aim",isAiming);transform.localPosition = sniperingFiflePosition;AimOut();}}//射击public override void GunFire(){//控制射速或当前没有子弹的时候就不可以发射了//animator.GetCurrentAnimatorStateInfo(0).IsName("take_out") 获取第0层动画中名为take_out动画的状态if (fireTimer < fireRate || playerController.isRun || currentBullets <= 0 || animator.GetCurrentAnimatorStateInfo(0).IsName("take_out") || isReloading) return;//调用协程控制开火灯光StartCoroutine(MuzzleFlashLightCon());muzzlePatic.Emit(1);//每次发射一个枪口火焰sparkPatic.Emit(Random.Range(minSparkEmission,maxSparkEmission));//发射枪口火星粒子StartCoroutine(Shoot_Crss()); //增大准心大小if (!isAiming){//播放普通开火动画(使用动画的淡入淡出效果)animator.CrossFadeInFixedTime("fire",0.1f);}else{//瞄准状态下,播放瞄准开火动画animator.Play("aim_fire",0,0);}for (int i = 0; i < shotgunFragment; i++){RaycastHit hit;Vector3 shootDirection = ShootPoint.forward;//射击方向shootDirection = shootDirection + ShootPoint.TransformDirection(new Vector3(Random.Range(-SpreadFactor,SpreadFactor),Random.Range(-SpreadFactor,SpreadFactor)));//射线参数: 发射点,方向,碰撞信息,最大距离(射线检测的方式从屏幕正中心射出)if (Physics.Raycast(ShootPoint.position, shootDirection, out hit, range)){Transform bullet;if (IS_AUTORIFLE || (IS_SEMIGUN && gameObject.name=="2")){//实例化出子弹拖尾特效(拖尾特效里包含击中和弹孔特效)bullet = (Transform)Instantiate(bulletPrefab, BulletShootPoint.transform.position,BulletShootPoint.transform.rotation);}else{//霰弹枪特殊处理下,将子弹限制位置设定到 hit.pointbullet = Instantiate(bulletPrefab, hit.point, Quaternion.FromToRotation(Vector3.up, hit.normal));}//给子弹拖尾向前的力(加上射线打出去的偏移值)bullet.GetComponent<Rigidbody>().velocity = (bullet.transform.forward + shootDirection)*bulletForce;//Debug.Log("打到了" + hit.transform.gameObject.name);} }//实例抛弹壳Instantiate(casingPrefab, CasingBulletSpawnPoint.transform.position, CasingBulletSpawnPoint.transform.rotation);//根据是否装备消音器,切换不同的射击音效mainAudioSource.clip = isSilencer ? soundClips.silencerShootSound : soundClips.shootSound;//mainAudioSource.clip = soundClips.shootSound;mainAudioSource.Play(); //播放射击音效fireTimer = 0f; //重置计时器currentBullets--; //子弹数量减少UpdateAmmoUI();//ExpendCross(30);}//设置开火的灯光IEnumerator MuzzleFlashLightCon(){muzzleflashLight.enabled = true;yield return new WaitForSeconds(lightDuration);muzzleflashLight.enabled = false;}//换弹逻辑public override void Reload(){if(bulletLeft <= 0) return;//计算需要填充的子弹int bulletToload = bulletMag - currentBullets;//计算备弹扣除的子弹数int bulletToReduce = bulletLeft >= bulletToload ? bulletToload : bulletLeft;bulletLeft -= bulletToReduce;//备弹减少currentBullets += bulletToReduce;//当前子弹增加UpdateAmmoUI();}/// <summary>/// 霰弹枪换弹逻辑/// </summary>public void ShotGunReload(){if (currentBullets < bulletMag){currentBullets++;bulletLeft--;UpdateAmmoUI();}else{animator.Play("reload_close");return;}if(bulletLeft <= 0) return;}//根据指定大小,来增加或减小准心的开合度public override void ExpandingCrossUpdate(float expanDegree){if (currentExoanedDegree < expanDegree-5){ExpendCross(150 * Time.deltaTime);}else if(currentExoanedDegree > expanDegree + 5){ExpendCross(-300 * Time.deltaTime);}}//换弹(播放动画)public override void DoReloadAnimation(){if (!(IS_SEMIGUN && (gameObject.name == "4" || gameObject.name == "6"))){if (currentBullets > 0 && bulletLeft > 0){animator.Play("reload01",0,0);Reload();mainAudioSource.clip = soundClips.reloadSoundAmmotLeft;mainAudioSource.Play();}if (currentBullets == 0 && bulletLeft > 0){animator.Play("reload",0,0);Reload();mainAudioSource.clip = soundClips.reloadSoundOutOfAmmo;mainAudioSource.Play();} }else{if(currentBullets == bulletMag) return;//霰弹枪换子弹动画触发animator.SetTrigger("shotgun_reload");}}//进入瞄准,隐藏准心,摄像机视野变近public override void AimIn(){float currentVelocity = 0f;for (int i = 0; i < crossQuarterImgs.Length; i++){crossQuarterImgs[i].gameObject.SetActive(false);}//狙击枪瞄准的时候,改变gunCamera的视野和瞄准镜的颜色if (IS_SEMIGUN && (gameObject.name == "6")){scopeRenderMaterial.color = defaultColor;gunCamera.fieldOfView = 10;}//瞄准的时候摄像机视野变近mainCamera.fieldOfView = Mathf.SmoothDamp(30,60,ref currentVelocity,0.1f);if (IsAimInPlay == true){IsAimInPlay = false;mainAudioSource.clip = soundClips.aimSound;mainAudioSource.Play(); }}//退出瞄准,显示准心,摄像机视野恢复public override void AimOut(){float currentVelocity = 0f;for (int i = 0; i < crossQuarterImgs.Length; i++){crossQuarterImgs[i].gameObject.SetActive(true);}//狙击枪瞄准的时候,改变gunCamera的视野和瞄准镜的颜色if (IS_SEMIGUN && (gameObject.name == "6")){scopeRenderMaterial.color = fadeColor;gunCamera.fieldOfView = 35;}mainCamera.fieldOfView = Mathf.SmoothDamp(60,30,ref currentVelocity,0.1f);if (IsAimInPlay == false){IsAimInPlay = true;mainAudioSource.clip = soundClips.aimSound;mainAudioSource.Play(); }}//改变准心的开合度,并且记录当前准心开合度public void ExpendCross(float add){crossQuarterImgs[0].transform.localPosition += new Vector3(-add,0,0); //左准心crossQuarterImgs[1].transform.localPosition += new Vector3(add,0,0); //右准心crossQuarterImgs[2].transform.localPosition += new Vector3(0,add,0); //上准心crossQuarterImgs[3].transform.localPosition += new Vector3(0,-add,0); //下准心currentExoanedDegree += add;//保持当前准心开合度currentExoanedDegree = Mathf.Clamp(currentExoanedDegree, 0, maxCrossDegree);//限制准心开合度大小}//协程,调用准心开合度,1帧执行了7次//只负责射击时瞬间增大准心public IEnumerator Shoot_Crss(){yield return null;for (int i = 0; i < 7; i++){ExpendCross(Time.deltaTime*500);}}//更新子弹的UIpublic void UpdateAmmoUI(){ammoTextUI.text = currentBullets + "/" + bulletLeft;shootModeTextUI.text = shootModeName;}public enum ShootMode{AutoRifle,SemiGun}
}
游戏素材资源包下载地址:【免费】FPS游戏资源包FPS游戏资源包资源-CSDN文库
Demo源码资源下载地址:五种武器FPS游戏Demo资源-CSDN文库