我是靠谱客的博主 唠叨项链,最近开发中收集的这篇文章主要介绍Unity教学项目Ceator Kit:FPS 源代码学习笔记(二)Weapon类,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.Serialization;
using UnityEngine.UI;
#if UNITY_EDITOR
using UnityEditor;
#endif
public class Weapon : MonoBehaviour
{
static RaycastHit[] s_HitInfoBuffer = new RaycastHit[8];//受击射线
public enum TriggerType//碰触类型
{
Auto,//自动
Manual//手动
}
public enum WeaponType//武器类型
{
Raycast,//射线
Projectile//投掷物
}
public enum WeaponState//武器状态
{
Idle,//等待
Firing,//开火
Reloading//重填
}
[System.Serializable]
public class AdvancedSettings//高级设置
{
public float spreadAngle = 0.0f;//传播角度
public int projectilePerShot = 1;//射弹
public float screenShakeMultiplier = 1.0f;//屏幕抖动倍增器
}
public TriggerType triggerType = TriggerType.Manual;//触碰类型初始化为手动
public WeaponType weaponType = WeaponType.Raycast;//武器类型初始化为射线
public float fireRate = 0.5f;//开火频率
public float reloadTime = 2.0f;//装弹时间
public int clipSize = 4;//剪辑大小,音频
public float damage = 1.0f;//伤害
[AmmoType]
public int ammoType = -1;//弹药类型
public Projectile projectilePrefab;//投掷物预制体
public float projectileLaunchForce = 200.0f;//投掷物发射力
public Transform EndPoint; //结束点
public AdvancedSettings advancedSettings;//高级设置
[Header("Animation Clips")]//面板显示时给予一个开头的标识
public AnimationClip FireAnimationClip;//开火动画音频
public AnimationClip ReloadAnimationClip;//装弹动画音频
[Header("Audio Clips")]
public AudioClip FireAudioClip;//开火音频
public AudioClip ReloadAudioClip;//装弹音频
[Header("Visual Settings")]
public LineRenderer PrefabRayTrail;//预制射线追踪
[Header("Visual Display")]
public AmmoDisplay AmmoDisplay;//弹药显示
public bool triggerDown//是否碰触
{
get { return m_TriggerDown; }
set
{
m_TriggerDown = value;
if (!m_TriggerDown) m_ShotDone = false;
}
}
public WeaponState CurrentState => m_CurrentState;//当前状态,=>不知道是什么意思
public int ClipContent => m_ClipContent;//音频内容
public Controller Owner => m_Owner;//所有者
Controller m_Owner;//所有者
Animator m_Animator;//动画管理器
WeaponState m_CurrentState;//武器状态
bool m_ShotDone;//射击完成
float m_ShotTimer = -1.0f;//射击计时器
bool m_TriggerDown;//触碰
int m_ClipContent;//自身音频的内容
AudioSource m_Source;//资源
Vector3 m_ConvertedMuzzlePos;//转换后枪口的位置
class ActiveTrail//射击光线的拖尾
{
public LineRenderer renderer;//光线渲染
public Vector3 direction;//方向
public float remainingTime;//持续时间
}
List<ActiveTrail> m_ActiveTrails = new List<ActiveTrail>();//光线拖尾列表
Queue<Projectile> m_ProjectilePool = new Queue<Projectile>();//投掷物
int fireNameHash = Animator.StringToHash("fire");//开火
int reloadNameHash = Animator.StringToHash("reload");//装弹
void Awake()
{
m_Animator = GetComponentInChildren<Animator>();//获取到当前位置孩子中的动画控制器
m_Source = GetComponentInChildren<AudioSource>();//获取音频资源器
m_ClipContent = clipSize;//音频大小
if (PrefabRayTrail != null)//拖尾预制体不为空
{
const int trailPoolSize = 16;//拖尾池大小
PoolSystem.Instance.InitPool(PrefabRayTrail, trailPoolSize);//初始化对象池
}
if (projectilePrefab != null)//投掷物预制体不为空
{
//a minimum of 4 is useful for weapon that have a clip size of 1 and where you can throw a second
//or more before the previous one was recycled/exploded.
int size = Mathf.Max(4, clipSize) * advancedSettings.projectilePerShot;//控制投掷物体多少
for (int i = 0; i < size; ++i)
{
Projectile p = Instantiate(projectilePrefab);//实例化
p.gameObject.SetActive(false);//隐藏
m_ProjectilePool.Enqueue(p);//退出队列
}
}
}
public void PickedUp(Controller c)//指定当前武器归属权
{
m_Owner = c;
}
public void PutAway()//
{
m_Animator.WriteDefaultValues();//为动画器写入默认值
for (int i = 0; i < m_ActiveTrails.Count; ++i)//制造拖尾并隐藏
{
var activeTrail = m_ActiveTrails[i];
m_ActiveTrails[i].renderer.gameObject.SetActive(false);
}
m_ActiveTrails.Clear();//清空
}
public void Selected()//被选中
{
var ammoRemaining = m_Owner.GetAmmo(ammoType);//获取仓库中的弹药量
//gun get disabled when ammo is == 0 and there is no more ammo in the clip, so this allow to re-enable it if we
//grabbed ammo since last time we switched
gameObject.SetActive(ammoRemaining != 0 || m_ClipContent != 0);//只要还有弹药或者音频内容不为0,就一直显示
//设置动画
if (FireAnimationClip != null)
m_Animator.SetFloat("fireSpeed",
FireAnimationClip.length / fireRate);
if(ReloadAnimationClip != null)
m_Animator.SetFloat("reloadSpeed", ReloadAnimationClip.length / reloadTime);
m_CurrentState = WeaponState.Idle;//当前状态为等待或者说初始
triggerDown = false;//初始化
m_ShotDone = false;//射击完成
WeaponInfoUI.Instance.UpdateWeaponName(this);//刷新当前武器名称
WeaponInfoUI.Instance.UpdateClipInfo(this);//刷新射击声音
WeaponInfoUI.Instance.UpdateAmmoAmount(m_Owner.GetAmmo(ammoType));//刷新弹药量
if(AmmoDisplay)//弹药显示
AmmoDisplay.UpdateAmount(m_ClipContent, clipSize);
if (m_ClipContent == 0 && ammoRemaining != 0)//装弹的时候弹药储备不能为空,同时音频内容为空
{
//this can only happen if the weapon ammo reserve was empty and we picked some since then. So directly
//reload the clip when wepaon is selected
int chargeInClip = Mathf.Min(ammoRemaining, clipSize);//
m_ClipContent += chargeInClip;
if(AmmoDisplay)//显示弹药
AmmoDisplay.UpdateAmount(m_ClipContent, clipSize);
m_Owner.ChangeAmmo(ammoType, -chargeInClip);
//改变弹药
WeaponInfoUI.Instance.UpdateClipInfo(this);//刷新音频
}
m_Animator.SetTrigger("selected");//设置为被选中
}
public void Fire()//开火
{
if (m_CurrentState != WeaponState.Idle || m_ShotTimer > 0 || m_ClipContent == 0)//状态必须是等待状态,射击计时器大于0,音频内容等于0
return;
m_ClipContent -= 1;
m_ShotTimer = fireRate;//计时器等于开火频率
if(AmmoDisplay)//弹药显示
AmmoDisplay.UpdateAmount(m_ClipContent, clipSize);
WeaponInfoUI.Instance.UpdateClipInfo(this);//刷新音频
//the state will only change next frame, so we set it right now.
m_CurrentState = WeaponState.Firing;//更新状态
m_Animator.SetTrigger("fire");//设置触发器为开火
m_Source.pitch = Random.Range(0.7f, 1.0f);//随机音调
m_Source.PlayOneShot(FireAudioClip);//播放开火音效
CameraShaker.Instance.Shake(0.2f, 0.05f * advancedSettings.screenShakeMultiplier);//摄像机抖动
if (weaponType == WeaponType.Raycast)//判断射击方式
{
for (int i = 0; i < advancedSettings.projectilePerShot; ++i)
{
RaycastShot();
}
}
else
{
ProjectileShot();
}
}
void RaycastShot()//射线射击
{
//compute the ratio of our spread angle over the fov to know in viewport space what is the possible offset from center
float spreadRatio = advancedSettings.spreadAngle / Controller.Instance.MainCamera.fieldOfView;//传播角度
Vector2 spread = spreadRatio * Random.insideUnitCircle;//传播
RaycastHit hit;
Ray r = Controller.Instance.MainCamera.ViewportPointToRay(Vector3.one * 0.5f + (Vector3)spread);//返回从相机出发穿过视点的一射线
Vector3 hitPosition = r.origin + r.direction * 200.0f;//被接触位置
if (Physics.Raycast(r, out hit, 1000.0f, ~(1 << 9), QueryTriggerInteraction.Ignore))//武器layer
{
Renderer renderer = hit.collider.GetComponentInChildren<Renderer>();//获取到被接触物体下的渲染
ImpactManager.Instance.PlayImpact(hit.point, hit.normal, renderer == null ? null : renderer.sharedMaterial);
//if too close, the trail effect would look weird if it arced to hit the wall, so only correct it if far
if (hit.distance > 5.0f)
hitPosition = hit.point;
//this is a target
if (hit.collider.gameObject.layer == 10)//如果被碰触物体是第十层
{
Target target = hit.collider.gameObject.GetComponent<Target>();//就获取其目标组件
target.Got(damage);//给予伤害
}
}
if (PrefabRayTrail != null)//预制体拖尾存在
{
var pos = new Vector3[] { GetCorrectedMuzzlePlace(), hitPosition };
var trail = PoolSystem.Instance.GetInstance<LineRenderer>(PrefabRayTrail);//获取拖尾对象池中的对象
trail.gameObject.SetActive(true);//显示拖尾
trail.SetPositions(pos);//设置位置
m_ActiveTrails.Add(new ActiveTrail()
{
remainingTime = 0.3f,//维系时间
direction = (pos[1] - pos[0]).normalized,//方向
renderer = trail//渲染
});
}
}
void ProjectileShot()//投掷
{
for (int i = 0; i < advancedSettings.projectilePerShot; ++i)
{
float angle = Random.Range(0.0f, advancedSettings.spreadAngle * 0.5f);//角度
Vector2 angleDir = Random.insideUnitCircle * Mathf.Tan(angle * Mathf.Deg2Rad);//角度方向
Vector3 dir = EndPoint.transform.forward + (Vector3)angleDir;//目标方向和角度方向之和
dir.Normalize();//归一化
var p = m_ProjectilePool.Dequeue();//对象池操作
p.gameObject.SetActive(true);
p.Launch(this, dir, projectileLaunchForce);
}
}
//For optimization, when a projectile is "destroyed" it is instead disabled and return to the weapon for reuse.
public void ReturnProjecticle(Projectile p)//增加投掷物
{
m_ProjectilePool.Enqueue(p);
}
public void Reload()//重装弹药
{
if (m_CurrentState != WeaponState.Idle || m_ClipContent == clipSize)//判断状态
return;
int remainingBullet = m_Owner.GetAmmo(ammoType);//获取弹药量
if (remainingBullet == 0)//如果没有弹药了,就隐藏武器
{
//No more bullet, so we disable the gun so it's not displayed anymore and change weapon
gameObject.SetActive(false);
return;
}
if (ReloadAudioClip != null)//装弹声音不为空,就随机音量,播放以此
{
m_Source.pitch = Random.Range(0.7f, 1.0f);
m_Source.PlayOneShot(ReloadAudioClip);
}
int chargeInClip = Mathf.Min(remainingBullet, clipSize - m_ClipContent);
//the state will only change next frame, so we set it right now.
m_CurrentState = WeaponState.Reloading;//状态
m_ClipContent += chargeInClip;
if(AmmoDisplay)//弹药显示刷新
AmmoDisplay.UpdateAmount(m_ClipContent, clipSize);
m_Animator.SetTrigger("reload");//动画
m_Owner.ChangeAmmo(ammoType, -chargeInClip);//改变弹药
WeaponInfoUI.Instance.UpdateClipInfo(this);//刷新武器UI
}
void Update()
{
UpdateControllerState(); //刷新控制器状态
if (m_ShotTimer > 0)//射击计时器
m_ShotTimer -= Time.deltaTime;
Vector3[] pos = new Vector3[2];
for (int i = 0; i < m_ActiveTrails.Count; ++i)//控制运动中的光束
{
var activeTrail = m_ActiveTrails[i];
activeTrail.renderer.GetPositions(pos);
activeTrail.remainingTime -= Time.deltaTime;
pos[0] += activeTrail.direction * 50.0f * Time.deltaTime;
pos[1] += activeTrail.direction * 50.0f * Time.deltaTime;
m_ActiveTrails[i].renderer.SetPositions(pos);
if (m_ActiveTrails[i].remainingTime <= 0.0f)
{
m_ActiveTrails[i].renderer.gameObject.SetActive(false);
m_ActiveTrails.RemoveAt(i);
i--;
}
}
}
void UpdateControllerState()//刷新控制器状态
{
m_Animator.SetFloat("speed", m_Owner.Speed);//速度
m_Animator.SetBool("grounded", m_Owner.Grounded);//在地否
var info = m_Animator.GetCurrentAnimatorStateInfo(0);//获取动画管理器状态
WeaponState newState;
if (info.shortNameHash == fireNameHash)//开火
newState = WeaponState.Firing;
else if (info.shortNameHash == reloadNameHash)//装弹
newState = WeaponState.Reloading;
else
newState = WeaponState.Idle;
if (newState != m_CurrentState)//自动装弹
{
var oldState = m_CurrentState;
m_CurrentState = newState;
if (oldState == WeaponState.Firing)
{//we just finished firing, so check if we need to auto reload
if(m_ClipContent == 0)
Reload();
}
}
if (triggerDown)//碰触完成
{
if (triggerType == TriggerType.Manual)
{
if (!m_ShotDone)
{
m_ShotDone = true;
Fire();
}
}
else
Fire();
}
}
/// <summary>
/// This will compute the corrected position of the muzzle flash in world space. Since the weapon camera use a
/// different FOV than the main camera, using the muzzle spot to spawn thing rendered by the main camera will appear
/// disconnected from the muzzle flash. So this convert the muzzle post from
/// world -> view weapon -> clip weapon -> inverse clip main cam -> inverse view cam -> corrected world pos
/// </summary>
/// <returns></returns>
public Vector3 GetCorrectedMuzzlePlace()//返回弹药位置
{
Vector3 position = EndPoint.position;
position = Controller.Instance.WeaponCamera.WorldToScreenPoint(position);
position = Controller.Instance.MainCamera.ScreenToWorldPoint(position);
return position;
}
}
public class AmmoTypeAttribute : PropertyAttribute
{
}
public abstract class AmmoDisplay : MonoBehaviour//抽象类
{
public abstract void UpdateAmount(int current, int max);
}
#if UNITY_EDITOR
//没有调用,目前不知道具体作用,以后再说吧
[CustomPropertyDrawer(typeof(AmmoTypeAttribute))]//自定义特定类型的编辑器界面
public class AmmoTypeDrawer : PropertyDrawer
{
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)//重写界面
{
AmmoDatabase ammoDB = GameDatabase.Instance.ammoDatabase;
if (ammoDB.entries == null || ammoDB.entries.Length == 0)
{
EditorGUI.HelpBox(position, "Please define at least 1 ammo type in the Game Database", MessageType.Error);
}
else
{
int currentID = property.intValue;
int currentIdx = -1;
//this is pretty ineffective, maybe find a way to cache that if prove to take too much time
string[] names = new string[ammoDB.entries.Length];
for (int i = 0; i < ammoDB.entries.Length; ++i)
{
names[i] = ammoDB.entries[i].name;
if (ammoDB.entries[i].id == currentID)
currentIdx = i;
}
EditorGUI.BeginChangeCheck();
int idx = EditorGUI.Popup(position, "Ammo Type", currentIdx, names);
if (EditorGUI.EndChangeCheck())
{
property.intValue = ammoDB.entries[idx].id;
}
}
}
}
[CustomEditor(typeof(Weapon))]//自定义编辑器界面
public class WeaponEditor : Editor
{
SerializedProperty m_TriggerTypeProp;
SerializedProperty m_WeaponTypeProp;
SerializedProperty m_FireRateProp;
SerializedProperty m_ReloadTimeProp;
SerializedProperty m_ClipSizeProp;
SerializedProperty m_DamageProp;
SerializedProperty m_AmmoTypeProp;
SerializedProperty m_ProjectilePrefabProp;
SerializedProperty m_ProjectileLaunchForceProp;
SerializedProperty m_EndPointProp;
SerializedProperty m_AdvancedSettingsProp;
SerializedProperty m_FireAnimationClipProp;
SerializedProperty m_ReloadAnimationClipProp;
SerializedProperty m_FireAudioClipProp;
SerializedProperty m_ReloadAudioClipProp;
SerializedProperty m_PrefabRayTrailProp;
SerializedProperty m_AmmoDisplayProp;
void OnEnable()
{
m_TriggerTypeProp = serializedObject.FindProperty("triggerType");
m_WeaponTypeProp = serializedObject.FindProperty("weaponType");
m_FireRateProp = serializedObject.FindProperty("fireRate");
m_ReloadTimeProp = serializedObject.FindProperty("reloadTime");
m_ClipSizeProp = serializedObject.FindProperty("clipSize");
m_DamageProp = serializedObject.FindProperty("damage");
m_AmmoTypeProp = serializedObject.FindProperty("ammoType");
m_ProjectilePrefabProp = serializedObject.FindProperty("projectilePrefab");
m_ProjectileLaunchForceProp = serializedObject.FindProperty("projectileLaunchForce");
m_EndPointProp = serializedObject.FindProperty("EndPoint");
m_AdvancedSettingsProp = serializedObject.FindProperty("advancedSettings");
m_FireAnimationClipProp = serializedObject.FindProperty("FireAnimationClip");
m_ReloadAnimationClipProp = serializedObject.FindProperty("ReloadAnimationClip");
m_FireAudioClipProp = serializedObject.FindProperty("FireAudioClip");
m_ReloadAudioClipProp = serializedObject.FindProperty("ReloadAudioClip");
m_PrefabRayTrailProp = serializedObject.FindProperty("PrefabRayTrail");
m_AmmoDisplayProp = serializedObject.FindProperty("AmmoDisplay");
}
public override void OnInspectorGUI()
{
serializedObject.Update();
EditorGUILayout.PropertyField(m_TriggerTypeProp);
EditorGUILayout.PropertyField(m_WeaponTypeProp);
EditorGUILayout.PropertyField(m_FireRateProp);
EditorGUILayout.PropertyField(m_ReloadTimeProp);
EditorGUILayout.PropertyField(m_ClipSizeProp);
EditorGUILayout.PropertyField(m_DamageProp);
EditorGUILayout.PropertyField(m_AmmoTypeProp);
if (m_WeaponTypeProp.intValue == (int)Weapon.WeaponType.Projectile)
{
EditorGUILayout.PropertyField(m_ProjectilePrefabProp);
EditorGUILayout.PropertyField(m_ProjectileLaunchForceProp);
}
EditorGUILayout.PropertyField(m_EndPointProp);
EditorGUILayout.PropertyField(m_AdvancedSettingsProp, new GUIContent("Advance Settings"), true);
EditorGUILayout.PropertyField(m_FireAnimationClipProp);
EditorGUILayout.PropertyField(m_ReloadAnimationClipProp);
EditorGUILayout.PropertyField(m_FireAudioClipProp);
EditorGUILayout.PropertyField(m_ReloadAudioClipProp);
if (m_WeaponTypeProp.intValue == (int)Weapon.WeaponType.Raycast)
{
EditorGUILayout.PropertyField(m_PrefabRayTrailProp);
}
EditorGUILayout.PropertyField(m_AmmoDisplayProp);
serializedObject.ApplyModifiedProperties();
}
}
#endif

最后

以上就是唠叨项链为你收集整理的Unity教学项目Ceator Kit:FPS 源代码学习笔记(二)Weapon类的全部内容,希望文章能够帮你解决Unity教学项目Ceator Kit:FPS 源代码学习笔记(二)Weapon类所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(50)

评论列表共有 0 条评论

立即
投稿
返回
顶部