DestoryVirus
一、AudioSourceManager
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class AudioSourceManager : MonoBehaviour
{
public static AudioSourceManager Instance { get; private set; }
public SoundPlayer soundPlayer;
// Start is called before the first frame update
void Start()
{
Instance = this;
PoolManager.Instance.InitPool(soundPlayer,20);
}
public void playSound(AudioClip audioClip,float pitchMin=1,float pitchMax=1)
{
PoolManager.Instance.GetInstance<SoundPlayer>(soundPlayer).PlayClip(audioClip,pitchMin,pitchMax);
}
}
二、BloodCell
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class BloodCell : Enemy
{
public int addValue;//加血
public AudioClip audioClip;
protected override void OnCollisionEnter(Collision collision)
{
base.OnCollisionEnter(collision);
if (collision.transform.tag=="Player")
{
AudioSourceManager.Instance.playSound(audioClip);
playerTrans.GetComponent<DoctorController>().TakeDamage(-addValue);
gameObject.SetActive(false);
}
}
public override void TakeDamage(float damageValue)
{
base.TakeDamage(damageValue);
if (currentHealth<=0)
{
AudioSourceManager.Instance.playSound(audioClip);
playerTrans.GetComponent<DoctorController>().TakeDamage(-addValue);
}
}
}
三、Boss
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Boss : Enemy
{
protected Animator animator;
protected bool isWaking;
protected bool isDead;//是否死亡
public Light pointLight;
private bool isAngryState;
public bool hasSkill;//是否有技能
public AnimationClip attackAnimationClip;
public float attackSpeed;
public GameObject keyGo;
public float takeDamageTime;
private float takeDamageTimer;
public AudioClip deadClip;
// public Transform targetTrans;
protected override void Start()
{
base.Start();
animator = GetComponentInChildren<Animator>();
animator.SetFloat("Born", -0.5f);
currentState = ENEMYSTATE.IDLE;
//float result= Vector3.Dot(transform.forward, targetTrans.position - transform.position);
//if (result>0)
//{
// Debug.Log("在前方");
//}
//else
//{
// Debug.Log("在后方");
//}
//float result = Vector3.Cross(transform.forward, targetTrans.position).y;
//float result = Vector3.Dot(transform.right, targetTrans.position);
//if (result<0)
//{
// Debug.Log("在左方");
//}
//else
//{
// Debug.Log("在右方");
//}
currentMoveSpeed = moveSpeed;
if (attackAnimationClip!=null)
{
animator.SetFloat("AttackSpeed",attackSpeed);
actRestTime = attackAnimationClip.length / attackSpeed;
}
else
{
animator.SetFloat("AttackSpeed", 1);
}
takeDamageTimer = takeDamageTime;
}
protected override void Update()
{
if (isDead)
{
transform.Translate(-Vector3.up * Time.deltaTime * moveSpeed);
pointLight.intensity -= Time.deltaTime*2;
if (transform.position.y<=-10)
{
Destroy(gameObject);
}
return;
}
takeDamageTimer -= Time.deltaTime;
base.Update();
}
protected override void EnemyAct()
{
if (isWaking||isDead)
{
return;
}
base.EnemyAct();
switch (currentState)
{
case ENEMYSTATE.PATROL:
break;
case ENEMYSTATE.CHASE:
//animator.ResetTrigger("Hit");
animator.ResetTrigger("Attack");
if (hasSkill)
{
animator.ResetTrigger("UseSkill");
}
animator.SetBool("Moving", true);
if (isAngryState)
{
animator.SetFloat("MoveSpeed", 1);
currentMoveSpeed=moveSpeed*3;
}
break;
case ENEMYSTATE.ATTACK:
animator.SetBool("Moving", false);
animator.ResetTrigger("Hit");
if (isAngryState&&hasSkill)
{
animator.SetTrigger("UseSkill");
}
else
{
animator.SetTrigger("Attack");
}
break;
case ENEMYSTATE.RETURN:
animator.SetBool("Moving", true);
animator.ResetTrigger("Hit");
break;
case ENEMYSTATE.IDLE:
animator.SetBool("Moving", false);
break;
case ENEMYSTATE.WARN:
// animator.ResetTrigger("Hit");
break;
case ENEMYSTATE.USESKILL:
break;
default:
break;
}
}
/// <summary>
/// 苏醒
/// </summary>
protected void Warn()
{
if (isWaking)
{
return;
}
float wakeValue = animator.GetFloat("Born");
if (wakeValue<0)//还没苏醒过
{
animator.SetFloat("Born",1);
isWaking = true;
Invoke("FinishWaking",9);
animator.Play("Born", 0, 0);
}
else
{
animator.SetTrigger("Roar");
transform.LookAt(new Vector3(playerTrans.position.x, transform.position.y
, playerTrans.position.z));
}
}
/// <summary>
/// 完成苏醒
/// </summary>
private void FinishWaking()
{
animator.ResetTrigger("Hit");
isWaking = false;
}
protected override void CheckDistance()
{
if (currentState == ENEMYSTATE.RETURN||isWaking||isDead)
{
return;
}
float distance = Vector3.Distance(playerTrans.position, transform.position);
if (distance<1.5*chaseRange&¤tState!=ENEMYSTATE.WARN&&
currentState!=ENEMYSTATE.ATTACK&&
currentState!=ENEMYSTATE.CHASE)
{
Warn();
currentState = ENEMYSTATE.WARN;
}
if (distance <= attackRange && enemyFunction.canAttack)
{
currentState = ENEMYSTATE.ATTACK;
}
else if (distance <= chaseRange && enemyFunction.canChase)
{
currentState = ENEMYSTATE.CHASE;
}
else if (distance >= safeRange && enemyFunction.canReturn)
{
if (currentState == ENEMYSTATE.CHASE&&enemyFunction.canChase)
{
currentState = ENEMYSTATE.RETURN;
}
}
}
protected override void ReturnToInitPos()
{
if (!CanMove())
{
return;
}
currentHealth = initHealth;
if (Vector3.Distance(transform.position,initPos)<=2)
{
currentState = ENEMYSTATE.IDLE;
isAngryState = false;
transform.eulerAngles = new Vector3(0, 180, 0);
currentMoveSpeed = moveSpeed;
chaseRange = initChaseRange;
safeRange = initSafeRange;
return;
}
transform.Translate(transform.forward * Time.deltaTime * currentMoveSpeed,Space.World);
transform.LookAt(new Vector3(initPos.x,transform.position.y,initPos.z));
}
public void TakeDamage(float damageValue,Vector3 hitPos)
{
if (isWaking||isDead)
{
return;
}
currentHealth -= damageValue;
base.RecoverAttackRangeValue();
if (currentState==ENEMYSTATE.IDLE||currentState==ENEMYSTATE.WARN)
{
chaseRange = initChaseRange * 2;
safeRange = initSafeRange * 1.5f;
}
if (currentHealth<=initHealth/3&&!isAngryState)
{
isAngryState = true;
}
if (takeDamageTimer<=0)
{
if (soundPlayer != null)
{
soundPlayer.PlayRandomSound();
}
else
{
AudioSourceManager.Instance.playSound(hurtClip);
}
animator.SetTrigger("Hit");
animator.SetFloat("HitY", 0);
animator.SetFloat("HitX", 0);
float y = Vector3.Dot(transform.forward, hitPos - transform.position);
float x = Vector3.Dot(transform.right, hitPos);
Debug.Log("x:" + x);
Debug.Log("y:" + y);
if (ForwardBehindOrLeftRight(hitPos))
{
if (y > 0)
{
Debug.Log("在前方");
animator.SetFloat("HitY", 1);
}
else
{
Debug.Log("在后方");
animator.SetFloat("HitY", -1);
}
}
else
{
if (x > 0)
{
Debug.Log("在右方");
animator.SetFloat("HitX", 1);
}
else
{
Debug.Log("在左方");
animator.SetFloat("HitX", -1);
}
}
takeDamageTimer = takeDamageTime;
}
if (currentHealth>0)
{
return;
}
AudioSourceManager.Instance.playSound(deadClip);
animator.SetTrigger("Die");
isDead = true;
keyGo.SetActive(true);
keyGo.transform.position = transform.position+new Vector3(0,2,0);
rigid.isKinematic = true;
rigid.constraints = RigidbodyConstraints.FreezeAll;
}
/// <summary>
/// 判断前后或者左右影响度(true前后优先级更高,false左右优先级更高)
/// </summary>
/// <param name="targetPos"></param>
/// <returns></returns>
private bool ForwardBehindOrLeftRight(Vector3 targetPos)
{
float ZDistance =Mathf.Abs(transform.position.z - targetPos.z);
float XDistance = Mathf.Abs(transform.position.x - targetPos.x);
if (ZDistance>=XDistance)
{
return true;
}
else
{
return false;
}
}
protected override void Attack()
{
if (Time.time-lastActTime<attackRange)
{
return;
}
lastActTime = Time.time;
transform.LookAt(new Vector3(playerTrans.position.x, playerTrans.position.y,playerTrans.position.z));
}
protected bool CanMove()
{
return animator.GetCurrentAnimatorStateInfo(0).shortNameHash != Animator.StringToHash("Attack") &&
animator.GetCurrentAnimatorStateInfo(0).shortNameHash != Animator.StringToHash("Hit") &&
animator.GetCurrentAnimatorStateInfo(0).shortNameHash != Animator.StringToHash("Boar")&&
animator.GetCurrentAnimatorStateInfo(0).shortNameHash != Animator.StringToHash("UseSkill");
}
protected override void Chase()
{
if (!CanMove())
{
return;
}
transform.LookAt(new Vector3(playerTrans.position.x, playerTrans.position.y, playerTrans.position.z));
transform.Translate(transform.forward * Time.deltaTime * currentMoveSpeed, Space.World);
}
}
四、BossBehavior
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class BossBehavior : MonoBehaviour
{
public GameObject bossWeaponViewGo;//Boss武器的显示
private Transform playerTrans;
public GameObject skillWeapon;//Boss技能的武器 (具体与玩家交互的各种技能游戏物体)
public Transform attackTrans;//攻击位置
public AudioClip skillClip;
// Start is called before the first frame update
void Start()
{
playerTrans = GameObject.FindGameObjectWithTag("Player").transform;
if (skillWeapon!=null)
{
PoolManager.Instance.InitPool(skillWeapon, 10);
}
}
private void ShowWeapon()
{
bossWeaponViewGo.SetActive(true);
}
private void HideWeapon()
{
bossWeaponViewGo.SetActive(false);
}
/// <summary>
/// 隐藏
/// </summary>
private void CreatSkillBall()
{
bossWeaponViewGo.SetActive(false);
GameObject go= PoolManager.Instance.GetInstance<GameObject>(skillWeapon);
go.SetActive(true);
go.transform.SetParent(null);
go.transform.position = attackTrans.position;
go.transform.LookAt(playerTrans.position);
transform.parent.LookAt(playerTrans.position);
AudioSourceManager.Instance.playSound(skillClip);
}
}
五、BossWeapon
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class BossWeapon : MonoBehaviour
{
public int damageValue=10;//伤害值
private DoctorController doctorController;
public float moveSpeed = 5;
//public Vector3 targetPos;
private float destoryTime=10;//销毁计时器
private float timeVal;
public ParticleSystem destoryEffect; //特效引用
public bool canMove;//是否可以移动
private bool canTakeDamage;//开关
private float damageTimeVal;
public float initDamageTime = 2;
public bool isSmoke;//烟雾
// Start is called before the first frame update
void Start()
{
doctorController =GameObject.FindGameObjectWithTag("Player").GetComponent<DoctorController>();
if (destoryEffect!=null)
{
PoolManager.Instance.InitPool(destoryEffect, 10);
}
damageTimeVal = initDamageTime;
// transform.LookAt(targetPos);
}
// Update is called once per frame
void Update()
{
//技能型武器
if (damageTimeVal<=0)
{
canTakeDamage = true;
damageTimeVal = initDamageTime;
}
else
{
damageTimeVal -= Time.deltaTime;
}
//近战武器
if (canMove)
{
return;
}
//远程武器
transform.Translate(transform.forward * moveSpeed * Time.deltaTime, Space.World);
if (timeVal>=destoryTime)
{
gameObject.SetActive(false);//隐藏
gameObject.transform.SetParent(PoolManager.Instance.transform);
timeVal = 0;
}
else
{
timeVal += Time.deltaTime;
}
}
private void OnTriggerEnter(Collider other)
{
if (other.tag=="Player")
{
doctorController.TakeDamage(damageValue);
if (destoryEffect!=null)
{
ParticleSystem particleSystem= PoolManager.Instance.GetInstance<ParticleSystem>(destoryEffect);
particleSystem.gameObject.SetActive(true);
particleSystem.transform.position = transform.position;
particleSystem.time = 0;
particleSystem.Play();
}
if (canMove)
{
gameObject.transform.SetParent(PoolManager.Instance.transform);
timeVal = 0;
}
if (isSmoke)
{
if (canTakeDamage)
{
doctorController.TakeDamage(damageValue);
canTakeDamage = false;
damageTimeVal = initDamageTime;
}
}
else
{
doctorController.TakeDamage(damageValue);
gameObject.SetActive(false);//隐藏
}
}
}
private void OnTriggerStay(Collider other)
{
if (other.tag == "Player")
{
if (canTakeDamage)
{
doctorController.TakeDamage(damageValue);
canTakeDamage = false;
damageTimeVal = initDamageTime;
}
}
}
private void OnTriggerExit(Collider other)
{
if (other.tag=="Player")
{
canTakeDamage = true;
damageTimeVal = initDamageTime;
}
}
}
六、CameraShaker
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CameraShaker : MonoBehaviour
{
private Vector3 initPosition;
private float remainingShakeTime;
private float shakeStrength;
// Start is called before the first frame update
void Start()
{
initPosition = transform.localPosition;
}
// Update is called once per frame
void Update()
{
if (remainingShakeTime>0)
{
remainingShakeTime -= Time.deltaTime;
if (remainingShakeTime<=0)
{
transform.localPosition = initPosition;
}
else
{
Vector3 randomDir = Random.insideUnitSphere;
transform.localPosition = initPosition + shakeStrength*randomDir;
}
}
}
public void SetShakeValue(float shakeTime,float strength)
{
remainingShakeTime = shakeTime;
shakeStrength = strength;
}
}
七、DoctorController
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Linq;
public class DoctorController : MonoBehaviour
{
private CharacterController characterController;
public float moveSpeed;
public float mouseSensitivity;//角度值
private float angleY;
private float angleX;
private Transform cameraTrans;
public float runningSpeed;
private bool isGrounded;
public float jumpSpeed;
private CollisionFlags collisionFlags;
public int currentWeaponID;
private Dictionary<int,Weapon> weaponsDict;
private Transform weaponPlaceTrans;
private Dictionary<int, int> ammoInventory;//玩家的装备库(背包,当前玩家剩余某个武器以及其剩余的子弹数量)
public int currentHP;//当前HP
public int initHP;//初始HP
public float decreaseSpeed;
public float actualSpeed;
public CameraShaker cameraShaker;
public bool dead;
public Transform deadPositionTrans;
public AudioClip jumoClip;
public AudioClip landClip;
private bool canPlayLandClip;
public AudioClip deadClip;
public AudioClip hurtClip;
public Weapon[] weapons;
public GameObject gameOverPanel;
// Start is called before the first frame update
void Start()
{
characterController= GetComponent<CharacterController>();
moveSpeed = 5;
mouseSensitivity = 2.4f;
angleY = transform.eulerAngles.y;
cameraTrans = Camera.main.transform;
angleX = cameraTrans.eulerAngles.x;
//Cursor.visible = false;
//Cursor.lockState = CursorLockMode.Locked;
runningSpeed = 10;
jumpSpeed = 0;
isGrounded = true;
currentWeaponID = -1;
weaponPlaceTrans = cameraTrans.Find("WeaponPlace");
//weapons = new List<Weapon>()
//{
// weaponPlaceTrans.GetChild(0).GetComponent<Weapon>(),
// weaponPlaceTrans.GetChild(1).GetComponent<Weapon>(),
// weaponPlaceTrans.GetChild(2).GetComponent<Weapon>(),
//};
weaponsDict = new Dictionary<int, Weapon>();
ammoInventory = new Dictionary<int, int>();
currentHP = initHP;
UIManager.Instance.ShowOrHideWeaponUIView(false);
for (int i = 0; i < weapons.Length; i++)
{
weapons[i].gameObject.SetActive(false);
weapons[i].PickUp(this);
}
}
// Update is called once per frame
void Update()
{
if (dead)
{
return;
}
Move();
TurnAndLook();
Jump();
ChangeCurrentWeapon();
}
/// <summary>
/// 移动
/// </summary>
private void Move()
{
actualSpeed = Input.GetButton("Run") ? runningSpeed - decreaseSpeed : moveSpeed - decreaseSpeed;
Vector3 move = Vector3.zero;
float h = Input.GetAxis("Horizontal");
float v = Input.GetAxis("Vertical");
move = new Vector3(h, 0, v);
move.Normalize();
move = move * actualSpeed * Time.deltaTime;
move = transform.TransformDirection(move);
collisionFlags = characterController.Move(move);
if (h < -0.1f && v <= 0.1f)
{
actualSpeed = 0;
}
}
/// <summary>
/// 转向和上下看
/// </summary>
private void TurnAndLook()
{
if (GameManager.Instance.isGameing == true)
{
float turnAngle = Input.GetAxis("Mouse X") * mouseSensitivity;
angleY = angleY + turnAngle;
transform.eulerAngles = new Vector3(transform.eulerAngles.x, angleY, transform.eulerAngles.z);
float lookAngle = -Input.GetAxis("Mouse Y") * mouseSensitivity;
angleX = Mathf.Clamp(angleX + lookAngle, -90f, 90f);
cameraTrans.eulerAngles = new Vector3(angleX, cameraTrans.eulerAngles.y, cameraTrans.eulerAngles.z);
}
}
/// <summary>
/// 跳跃方法
/// </summary>
private void Jump()
{
if (Input.GetButton("Jump"))
{
isGrounded = false;
jumpSpeed = 5;
AudioSourceManager.Instance.playSound(jumoClip,0.8f,1.1f);
canPlayLandClip = true;
}
if (!isGrounded)//跳起来了,不在地面上
{
jumpSpeed = jumpSpeed - 10 * Time.deltaTime;
Vector3 jump = new Vector3(0, jumpSpeed * Time.deltaTime, 0);
collisionFlags= characterController.Move(jump);
if (collisionFlags==CollisionFlags.Below)
{
jumpSpeed = 0;
isGrounded = true;
}
}
if (isGrounded&&collisionFlags==CollisionFlags.None)
{
if (canPlayLandClip)
{
canPlayLandClip = false;
AudioSourceManager.Instance.playSound(landClip, 0.8f, 1.1f);
}
isGrounded = false;
}
}
/// <summary>
/// 具体切换武器
/// </summary>
/// <param name="id"></param>
private void ChangeWeapon(int id)
{
if (weaponsDict.Count==0)
{
return;
}
处理索引的上下边界
//if (id>= weaponsDict.Count)
//{
// id = 0;
//}
//else if (id<=-1)
//{
// id = weaponsDict.Count - 1;
//}
if (id>weaponsDict.Keys.Max())
{
id = weaponsDict.Keys.Min();
}
else if (id<weaponsDict.Keys.Min())
{
id = weaponsDict.Keys.Max();
}
if (id==currentWeaponID)//只有一种武器时不切换,否则会出现颠簸颤抖的情况
{
return;
}
while (!weaponsDict.ContainsKey(id))
{
if (id>currentHP)
{
id++;
}
else
{
id--;
}
}
//隐藏上一把武器
if (currentWeaponID!=-1)//排除第一次没有武器的情况
{
weaponsDict[currentWeaponID].PutAway();
}
//显示当前武器
weaponsDict[id].Selected();
currentWeaponID = id;
}
/// <summary>
/// 切换当前武器
/// </summary>
public void ChangeCurrentWeapon(bool aut0Change=false)
{
if (aut0Change)
{
//切换到最新拿到的一把
//ChangeWeapon(weaponsDict.Count-1);
ChangeWeapon(weaponsDict.Keys.Last());
}
else
{
if (Input.GetAxis("Mouse ScrollWheel") < 0)
{
ChangeWeapon(currentWeaponID + 1);
}
else if (Input.GetAxis("Mouse ScrollWheel") > 0)
{
ChangeWeapon(currentWeaponID - 1);
}
for (int i = 0; i < 10; i++)
{
if (Input.GetKeyDown(KeyCode.Alpha0 + i))
{
int num = 0;
if (i == 0)
{
num = 10;
}
else
{
num = i - 1;
}
if (weaponsDict.ContainsKey(num))
{
ChangeWeapon(num);
}
}
}
}
}
/// <summary>
/// 拾取武器
/// </summary>
/// <param name="weaponName"></param>
public void PickUpWeapon(int weaponID)
{
if (weaponsDict.ContainsKey(weaponID))
{
//补充弹药
Weapon weapon = weaponsDict[weaponID];
ammoInventory[weapon.GetID()] = weapon.GetInitAmount();
weapon.clipContent = weapon.clipSize;
if (currentWeaponID==weaponID)
{
UIManager.Instance.UpdateBulletNum(weapon.clipSize, weapon.GetInitAmount());
}
//if (!weapon.HasBullet())
//{
// weapon.Reload();
//}
}
else//当前这种名称的武器列表里没有
{
//GameObject weaponGo= Instantiate(Resources.Load<GameObject>("Prefabs/Weapons/"+weaponID.ToString()));
// weaponGo.transform.SetParent(weaponPlaceTrans);
// weaponGo.transform.localPosition = Vector3.zero;
// weaponGo.transform.localRotation = Quaternion.identity;
// weaponGo.gameObject.SetActive(false);
// Weapon weapon = weaponGo.GetComponent<Weapon>();
//weapon.PickUp(this);
weapons[weaponID].clipContent = weapons[weaponID].clipSize;
weaponsDict.Add(weaponID,weapons[weaponID]);
ammoInventory.Add(weaponID, weapons[weaponID].GetInitAmount());
ChangeWeapon(weaponID);
}
}
/// <summary>
/// 获取某一种武器在武器库中子弹的剩余数量
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
public int GetAmmoAmount(int id)
{
int value = 0;
ammoInventory.TryGetValue(id, out value);
return value;
}
/// <summary>
/// 更新当前武器库中某一种武器装备剩余子弹的数量
/// </summary>
/// <param name="id"></param>
/// <param name="value"></param>
public void UpdateAmmoAmount(int id,int value)
{
if (ammoInventory.ContainsKey(id))
{
ammoInventory[id] += value;
}
}
/// <summary>
/// 受伤害
/// </summary>
/// <param name="value"></param>
public void TakeDamage(int value)
{
if (dead)
{
return;
}
if (value<0)
{
if (currentHP<initHP)
{
currentHP -= value;
if (currentHP>=initHP)
{
currentHP = initHP;
}
}
}
else
{
currentHP -= value;
}
if (currentHP<=0)
{
dead = true;
gameOverPanel.SetActive(true);
cameraTrans.localPosition = deadPositionTrans.localPosition;
cameraTrans.eulerAngles = deadPositionTrans.eulerAngles;
weaponPlaceTrans.gameObject.SetActive(false);
currentHP = 0;
UIManager.Instance.ShowDeadUI();
AudioSourceManager.Instance.playSound(deadClip);
}
else
{
if (value>0)
{
AudioSourceManager.Instance.playSound(hurtClip);
UIManager.Instance.ShowTakeDamageView();
cameraShaker.SetShakeValue(0.2f, 0.5f);
}
}
UIManager.Instance.UpdateHPValue(currentHP);
}
}
八、Door
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Door : MonoBehaviour
{
public GameObject keyGo;
private bool unLocked;//是否打开
public float moveSpeed;
//public SoundPlayer soundPlayer;
public AudioClip openDoorClip;
public AudioClip cantOpenClip;
public AudioClip doorClip;
private void OnTriggerEnter(Collider other)
{
if (other.transform.tag == "Player")
{
if (!unLocked)
{
if (keyGo == null)
{
unLocked = true;
AudioSourceManager.Instance.playSound(openDoorClip);
AudioSourceManager.Instance.playSound(doorClip);
}
else
{
AudioSourceManager.Instance.playSound(cantOpenClip);
}
}
}
}
private void Update()
{
if (unLocked)
{
//soundPlayer.PlaySound(true);
transform.Translate(-Vector3.up * Time.deltaTime * moveSpeed * 0.3f);
if (transform.position.y <= -1.4)
{
Destroy(gameObject);
}
}
}
}
九、Enemy
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Enemy : MonoBehaviour
{
public float initHealth = 10;
public float currentHealth;//生命值S
public ParticleSystem destoryEffect;
public ENEMYSTATE currentState;//敌人状态
public float moveSpeed;
protected float lastActTime;
public float actRestTime;//行为转换的CD,攻击时长
private Quaternion targetRotation;
protected Vector3 initPos;
public EnemyFunction enemyFunction;
protected Transform playerTrans;
public float attackRange;//攻击距离
public float chaseRange;//追逐距离
public float safeRange;//安全距离
protected Rigidbody rigid;
protected float currentMoveSpeed;//目前移动速度
protected float initChaseRange;//初始追赶范围
protected float initSafeRange;
protected float initAttackRange;
public GameObject smokeColliderGo;
public SoundPlayer soundPlayer;
public AudioClip hurtClip;
public AudioClip attackClip;
private float canTurnTimer;
// Start is called before the first frame update
protected virtual void Start()
{
if (destoryEffect)
{
PoolManager.Instance.InitPool(destoryEffect, 4);
}
currentHealth = initHealth;
currentState = ENEMYSTATE.PATROL;
initPos = transform.position;
playerTrans = GameObject.FindGameObjectWithTag("Player").transform;
// playerTrans = GameObject.Find("Doctor").transform;
rigid = GetComponent<Rigidbody>();
initSafeRange = safeRange;
initChaseRange = chaseRange;
initAttackRange = attackRange;
currentMoveSpeed = moveSpeed;
canTurnTimer = Time.time;
}
// Update is called once per frame
protected virtual void Update()
{
EnemyAct();
CheckDistance();
}
/// <summary>
/// 受伤
/// </summary>
/// <param name="damageValue">伤害值</param>
public virtual void TakeDamage(float damageValue)
{
currentHealth -= damageValue;
RecoverAttackRangeValue();
if (soundPlayer!=null)
{
soundPlayer.PlayRandomSound();
}
else
{
AudioSourceManager.Instance.playSound(hurtClip);
}
if (currentHealth>0)
{
return;
}
if (destoryEffect)
{
ParticleSystem ps = PoolManager.Instance.GetInstance<ParticleSystem>(destoryEffect);
ps.time = 0;
ps.Play();
ps.transform.position = transform.position+new Vector3(0,0.5f,0);
}
gameObject.SetActive(false);
}
/// <summary>
/// 敌人动作
/// </summary>
protected virtual void EnemyAct()
{
switch (currentState)
{
case ENEMYSTATE.PATROL:
Move();
if (Time.time-lastActTime>actRestTime)
{
lastActTime = Time.time;
targetRotation = Quaternion.Euler(GetRandomEuler());
}
break;
case ENEMYSTATE.CHASE:
Chase();
break;
case ENEMYSTATE.ATTACK:
Attack();
break;
case ENEMYSTATE.RETURN:
break;
default:
break;
}
}
private void Move()
{
transform.Translate(transform.forward*Time.deltaTime*currentMoveSpeed,Space.World);
transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation,0.01f);
}
protected virtual void OnCollisionEnter(Collision collision)
{
if (currentState==ENEMYSTATE.PATROL&&collision.gameObject.layer!=11
&&Time.time-canTurnTimer>5)
{
canTurnTimer = Time.time;
targetRotation = Quaternion.LookRotation(-transform.forward,transform.up);
lastActTime = Time.time;
}
else if (currentState==ENEMYSTATE.CHASE||currentState==ENEMYSTATE.RETURN||currentState==ENEMYSTATE.ATTACK)
{
rigid.isKinematic = true;
Invoke("CloseIsKinematicState",1);
}
}
private void CloseIsKinematicState()
{
rigid.isKinematic = false;
}
protected virtual void ReturnToInitPos()
{
transform.Translate(transform.forward * Time.deltaTime * currentMoveSpeed, Space.World);
transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, 0.1f);
if (Vector3.Distance(transform.position,initPos)<=1)
{
currentState = ENEMYSTATE.PATROL;
}
}
/// <summary>
/// 检测与某一个对象的距离并转换敌人的状态
/// </summary>
protected virtual void CheckDistance()
{
if (currentState==ENEMYSTATE.RETURN)
{
return;
}
float distance = Vector3.Distance(playerTrans.position, transform.position);
if (distance <= attackRange&&enemyFunction.canAttack)
{
currentState = ENEMYSTATE.ATTACK;
}
else if (distance <= chaseRange&&enemyFunction.canChase)
{
currentState = ENEMYSTATE.CHASE;
}
else if (distance>=safeRange&&enemyFunction.canReturn)
{
if (currentState==ENEMYSTATE.PATROL&&Vector3.Distance(transform.position, initPos) >= 8)
{
currentState = ENEMYSTATE.RETURN;
targetRotation = Quaternion.LookRotation(initPos - transform.position, transform.up);
}
else if (currentState==ENEMYSTATE.CHASE)
{
currentState = ENEMYSTATE.RETURN;
targetRotation = Quaternion.LookRotation(initPos - transform.position, transform.up);
}
}
}
private Vector3 GetRandomEuler()
{
float x = 0, y = 0, z = 0;
if (enemyFunction.canRotateX)
{
x = Random.Range(1, 5) * 90;
}
if (enemyFunction.canRotateY)
{
y = Random.Range(1, 5) * 90;
}
if (enemyFunction.canRotateZ)
{
z = Random.Range(1, 5) * 90;
}
return new Vector3(x, y, z);
}
/// <summary>
/// 追逐方法
/// </summary>
protected virtual void Chase()
{
transform.LookAt(playerTrans);
transform.Translate(transform.forward * Time.deltaTime * currentMoveSpeed, Space.World);
}
/// <summary>
/// 攻击方法
/// </summary>
protected virtual void Attack()
{
Debug.Log(gameObject.name + "正在攻击!");
}
/// <summary>
/// 设置烟雾距离
/// </summary>
public void SetRange(float value)
{
attackRange = chaseRange = value;
currentState = ENEMYSTATE.IDLE;
}
/// <summary>
/// 恢复
/// </summary>
public void RecoverAttackRangeValue()
{
attackRange = initAttackRange;
chaseRange = initChaseRange;
if (enemyFunction.canPatrol)
{
currentState = ENEMYSTATE.PATROL;
}
}
/// <summary>
/// 用于烟雾弹检测
/// </summary>
/// <param name="state"></param>
public void SetSmokeCollierState(bool state)
{
smokeColliderGo.SetActive(state);
}
}
public enum ENEMYSTATE
{
PATROL,//巡逻
CHASE,//追逐
ATTACK,
RETURN,
//BOSS
IDLE,
WARN,//警戒状态(播放动画,看向玩家)
// WAKE,//出生状态,觉醒状态(第一次遇到玩家)
USESKILL//使用技能
}
[System.Serializable]
public struct EnemyFunction
{
public bool canPatrol;
public bool canChase;
public bool canAttack;
public bool canReturn;
public bool canRotateX;
public bool canRotateY;
public bool canRotateZ;
}
十、GameManager
using System.Collections;
using System.Collections.Generic;
using UnityEditor.Timeline;
using UnityEngine;
using UnityEngine.SceneManagement;
using static UnityEditor.FilePathAttribute;
public class GameManager : MonoBehaviour
{
public static GameManager Instance { get; private set; }
public bool isGameing = false;
private void Awake()
{
Instance = this;
Cursor.visible = true;
Cursor.lockState = CursorLockMode.None;
}
// Start is called before the first frame update
void Start()
{
Time.timeScale = 0f;
}
// Update is called once per frame
void Update()
{
}
public void StartGameBtn()
{
Cursor.visible = false;
Cursor.lockState = CursorLockMode.Locked;
Time.timeScale = 1.0f;
isGameing = true;
}
public void ReStartBtn()
{
SceneManager.LoadScene(0);
}
public void ExitGameBtn()
{
UnityEditor.EditorApplication.isPlaying = false;
Application.Quit();
}
public void ReTime()
{
}
}
十一、GameSuccessTiger
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GameSuccessTiger : MonoBehaviour
{
public GameObject gameScuessPanel;
public void OnTriggerEnter(Collider other)
{
Cursor.visible = true;
Cursor.lockState = CursorLockMode.None;
gameScuessPanel.SetActive(true);
GameManager.Instance.isGameing = false;
}
}
十二、GermSlime
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GermSlime : Enemy
{
public int damageValue;
protected override void Attack()
{
if (Time.time-lastActTime<actRestTime)
{
return;
}
transform.LookAt(playerTrans);
lastActTime = Time.time;
playerTrans.GetComponent<DoctorController>().TakeDamage(damageValue);
}
}
十三、GermSpike
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GermSpike : Enemy
{
public GameObject explosionEffect;//爆炸特效
public int damageValue;//伤害值
// Start is called before the first frame update
protected override void Start()
{
base.Start();
PoolManager.Instance.InitPool(explosionEffect,1);
}
// Update is called once per frame
protected override void Attack()
{
base.Attack();
if (Time.time - lastActTime < actRestTime)
{
return;
}
transform.LookAt(playerTrans);
lastActTime = Time.time;
playerTrans.GetComponent<DoctorController>().TakeDamage(damageValue);
GameObject effect = PoolManager.Instance.GetInstance<GameObject>(explosionEffect);
effect.transform.position = transform.position;
effect.transform.localScale = Vector3.one * 3;
effect.SetActive(true);
}
}
十四、Key
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Key : MonoBehaviour
{
public float rotateSpeed;
public AudioClip pickupClip;
//public Door door;
// Start is called before the first frame update
void Start()
{
rotateSpeed = 50;
}
// Update is called once per frame
void Update()
{
transform.eulerAngles += new Vector3(0, rotateSpeed * Time.deltaTime, 0);
}
private void OnTriggerEnter(Collider other)
{
if (other.tag=="Player")
{
// door.OpenDoor();
AudioSourceManager.Instance.playSound(pickupClip);
Destroy(gameObject);
}
}
}
十五、PickUpItem
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PickUpItem : MonoBehaviour
{
public float rotateSpeed;
public int itemID=-1;
public AudioClip pickupClip;
// Start is called before the first frame update
void Start()
{
rotateSpeed = 50;
}
// Update is called once per frame
void Update()
{
transform.eulerAngles += new Vector3(0, rotateSpeed * Time.deltaTime, 0);
}
private void OnTriggerEnter(Collider other)
{
if (other.name== "Doctor")
{
DoctorController doctorController = other.GetComponent<DoctorController>();
doctorController.PickUpWeapon(itemID);
//doctorController.ChangeCurrentWeapon(true);
AudioSourceManager.Instance.playSound(pickupClip);
Destroy(gameObject);
}
}
}
十六、PoolManager
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PoolManager : MonoBehaviour
{
public static PoolManager Instance { get; private set; }
//通过传入类型找到对应的池子
private Dictionary<Object, Queue<Object>> poolsDict;
private void Awake()
{
Instance = this;
poolsDict = new Dictionary<Object, Queue<Object>>();
}
/// <summary>
/// 初始化某一个对象池
/// </summary>
/// <param name="prefab">指定预制体的类型</param>
/// <param name="size">当前对应类型的对象池的长度</param>
public void InitPool(Object prefab,int size)
{
if (poolsDict.ContainsKey(prefab))
{
return;
}
Queue<Object> queue = new Queue<Object>();
for (int i = 0; i < size; i++)
{
Object go=Instantiate(prefab);
CreatGameObjectAndSetActive(go,false);
queue.Enqueue(go);
}
poolsDict[prefab] = queue;
}
private void CreatGameObjectAndSetActive(Object obj,bool active)
{
GameObject itemGo = null;
if (obj is Component)
{
Component component = obj as Component;
itemGo = component.gameObject;
}
else
{
itemGo= obj as GameObject;
}
itemGo.transform.SetParent(transform);
itemGo.SetActive(active);
}
public T GetInstance<T>(Object prefab)where T:Object
{
Queue<Object> queue;
if (poolsDict.TryGetValue(prefab,out queue ))
{
Object obj;
if (queue.Count>0)
{
obj= queue.Dequeue();
}
else
{
obj = Instantiate(prefab);
}
CreatGameObjectAndSetActive(obj, true);
queue.Enqueue(obj);
return obj as T;
}
Debug.LogError("还没有当前的资源池被实例化");
return null;
}
}
十七、Projectile
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Projectile : MonoBehaviour
{
private Rigidbody rigid;
public ParticleSystem explosionEffect;
public float destoryTime;
private float destoryTimer;
public bool destoryOnHit;
public float explosionRadius;//爆炸半径
private Collider[] sphereCastPool;//当前检测到的爆炸范围内的敌人
public float damageValue;//伤害值
public ParticleSystem bulletTrailEffect;
public ParticleSystem bulletSizeParticle;//如果子弹是粒子且需要变换大小
public AudioClip hitWallClip;
public AudioClip explosionClip;
// Start is called before the first frame update
void Awake()
{
rigid = GetComponent<Rigidbody>();
destoryTime = 3;
PoolManager.Instance.InitPool(explosionEffect, 4);
sphereCastPool = new Collider[10];
if (bulletTrailEffect!=null)
{
bulletTrailEffect.time = 0;
bulletTrailEffect.Play();
}
}
// Update is called once per frame
void Update()
{
destoryTimer += Time.deltaTime;
if (destoryTimer>destoryTime)
{
if (destoryOnHit)
{
DestoryProjectile();
}
else
{
DestoryExplosionProjectile();
}
}
}
/// <summary>
/// 子弹发射
/// </summary>
/// <param name="laucher">发射器</param>
/// <param name="direction">发射方向</param>
/// <param name="force">发射力的大小</param>
public void Launch(Weapon laucher,Vector3 direction,float force)
{
transform.position = laucher.GetShootPoint().position;
transform.forward = laucher.GetShootPoint().forward;
rigid.AddForce(direction * force);
}
private void DestoryExplosionProjectile()
{
if (explosionEffect!=null)
{
ParticleSystem effect = PoolManager.Instance.GetInstance<ParticleSystem>(explosionEffect);
effect.transform.position = transform.position;
effect.gameObject.SetActive(true);
effect.time = 0;
effect.Play();
}
gameObject.SetActive(false);
rigid.velocity = Vector3.zero;
rigid.angularVelocity = Vector3.zero;
gameObject.transform.rotation = Quaternion.identity;
destoryTimer = 0;
if (damageValue>0)
{
int count = Physics.OverlapSphereNonAlloc(transform.position, explosionRadius, sphereCastPool, 1 << 9);
for (int i = 0; i < count; i++)
{
sphereCastPool[i].GetComponent<Enemy>().TakeDamage(damageValue);
}
int bossCount = Physics.OverlapSphereNonAlloc(transform.position, explosionRadius, sphereCastPool, 1 << 10);
if (bossCount > 0)
{
sphereCastPool[0].GetComponentInParent<Boss>().TakeDamage(damageValue, transform.position);
}
}
AudioSourceManager.Instance.playSound(explosionClip);
}
private void DestoryProjectile(GameObject enemyGo=null)
{
if (explosionEffect != null)
{
ParticleSystem effect = PoolManager.Instance.GetInstance<ParticleSystem>(explosionEffect);
effect.transform.position = transform.position;
effect.gameObject.SetActive(true);
effect.time = 0;
effect.Play();
}
gameObject.SetActive(false);
rigid.velocity = Vector3.zero;
rigid.angularVelocity = Vector3.zero;
gameObject.transform.rotation = Quaternion.identity;
destoryTimer = 0;
TakeDamage(enemyGo);
}
private void OnCollisionEnter(Collision collision)
{
if (destoryOnHit)
{
DestoryProjectile(collision.gameObject);
}
}
private void OnTriggerEnter(Collider other)
{
if (destoryOnHit)
{
DestoryProjectile(other.gameObject);
}
}
/// <summary>
/// 设置聚能枪子弹大小
/// </summary>
public void SetBulletSize(float size)
{
bulletSizeParticle.startSize = size;
}
/// <summary>
/// 设置伤害值
/// </summary>
public void SetBulletDamageValue(float value)
{
damageValue = value;
}
/// <summary>
/// 伤害
/// </summary>
private void TakeDamage(GameObject enemyGo=null)
{
if (enemyGo!=null)
{
if (enemyGo.layer==9)
{
enemyGo.GetComponent<Enemy>().TakeDamage(damageValue);
}
else if (enemyGo.layer==10)
{
enemyGo.GetComponentInParent<Boss>().TakeDamage(damageValue, transform.position);
}
else
{
AudioSourceManager.Instance.playSound(hitWallClip);
}
}
}
}
十八、Smoke
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Smoke : MonoBehaviour
{
public ParticleSystem particleSystem;
private List<GameObject> enemyList = new List<GameObject>();
private void OnEnable()
{
particleSystem.time = 0;
particleSystem.Play();
Invoke("HideSelf",25);
}
private void HideSelf()
{
particleSystem.Stop();
Invoke("HideGameObject", 7);
}
private void HideGameObject()
{
gameObject.SetActive(false);
}
private void OnDisable()
{
CancelInvoke();
for (int i = 0; i < enemyList.Count; i++)
{
if (enemyList[i])
{
Enemy enemy = enemyList[i].GetComponentInParent<Enemy>();
if (enemy)
{
enemy.RecoverAttackRangeValue();
enemy.SetSmokeCollierState(true);
}
}
}
enemyList.Clear();
StopAllCoroutines();
}
private void OnTriggerExit(Collider other)
{
if (other.tag == "Enemy")
{
if (enemyList.Contains(other.gameObject))
{
enemyList.Remove(other.gameObject);
}
}
}
private void OnTriggerEnter(Collider other)
{
if (other.tag=="Enemy")
{
if (!enemyList.Contains(other.gameObject))
{
enemyList.Add(other.gameObject);
}
StartCoroutine(OutSmoke(other));
Debug.Log("进入烟雾");
}
}
IEnumerator OutSmoke(Collider other)
{
//进入迷失状态
yield return new WaitForSeconds(2);
Enemy enemy = other.GetComponentInParent<Enemy>();
if (enemy.enabled)
{
enemy.SetRange(2);
enemy.SetSmokeCollierState(false);
}
//解除迷失状态
yield return new WaitForSeconds(15);
if (enemy.enabled)
{
enemy.SetSmokeCollierState(true);
}
}
}
十九、SoundPlayer
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SoundPlayer : MonoBehaviour
{
public AudioSource audioSource;
public AudioClip clipSource;
public AudioClip[] audioClips;
public float currentPitchMin=1;
public float currentPitchMax=1;
private void Awake()
{
audioSource = GetComponent<AudioSource>();
if (clipSource)
{
audioSource.clip = clipSource;
}
}
/// <summary>
/// 外部调用
/// </summary>
/// <param name="audioClip"></param>
/// <param name="pitchMin"></param>
/// <param name="pitchMax"></param>
public void PlayClip(AudioClip audioClip,float pitchMin=1,float pitchMax=1)
{
audioSource.pitch = Random.Range(pitchMin,pitchMax);
audioSource.PlayOneShot(audioClip);
}
public void PlayRandomSound()
{
audioSource.pitch = Random.Range(currentPitchMin, currentPitchMax);
audioSource.PlayOneShot(audioClips[Random.Range(0,audioClips.Length)]);
}
public void PlaySound(bool loop=false)
{
if (audioSource.clip!=null)
{
if (!audioSource.isPlaying)
{
audioSource.loop = loop;
audioSource.Play();
}
}
}
}
二十、TelesopicView 倍镜
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class TelesopicView : MonoBehaviour
{
public float zoomLevel = 2;//倍镜缩放等级
public float OpenSpeed=100;
public float CloseSpeed=80;
private float initFOV;
private bool openTelesopicView;//倍镜开关
// Start is called before the first frame update
void Start()
{
initFOV = Camera.main.fieldOfView;
}
// Update is called once per frame
void Update()
{
if (openTelesopicView)
{
OpenTelesopicView();
}
else
{
CloseTelesopicView();
}
}
/// <summary>
/// 打开倍镜方法
/// </summary>
private void OpenTelesopicView()
{
if (Camera.main.fieldOfView!=initFOV/zoomLevel)
{
if (Mathf.Abs(Camera.main.fieldOfView - initFOV / zoomLevel)<5)
{
Camera.main.fieldOfView = initFOV / zoomLevel;
}
else
{
Camera.main.fieldOfView -= Time.deltaTime * OpenSpeed;
}
}
UIManager.Instance.OpenOrCloseTelesopicView();
}
/// <summary>
/// 关闭倍镜方法
/// </summary>
private void CloseTelesopicView()
{
if (Camera.main.fieldOfView != initFOV)
{
if (Mathf.Abs(Camera.main.fieldOfView - initFOV ) < 5)
{
Camera.main.fieldOfView = initFOV;
}
else
{
Camera.main.fieldOfView += Time.deltaTime * CloseSpeed;
}
}
UIManager.Instance.OpenOrCloseTelesopicView(false);
}
public void OpenTheTelesopicView(bool open=true)
{
openTelesopicView = open;
}
}
二十一、UIManager
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;
public class UIManager : MonoBehaviour
{
public GameObject imgSnipeRifle;
public GameObject imgTakeDamage;
public GameObject weaponUIViewGo;
public GameObject[] weaponUIGos;
public Text textHealth;
public Text textBulletNum;
public GameObject imgDaeaGo;
public AudioClip lossClip;
public static UIManager Instance { get; private set; }
private void Awake()
{
Instance = this;
}
public void OpenOrCloseTelesopicView(bool open=true)
{
imgSnipeRifle.SetActive(open);
}
public void ShowTakeDamageView()
{
imgTakeDamage.SetActive(true);
CancelInvoke();
Invoke("HideTakeDamageView", 2);
}
public void HideTakeDamageView()
{
imgTakeDamage.SetActive(false);
}
public void ShowOrHideWeaponUIView(bool show)
{
weaponUIViewGo.SetActive(show);
}
public void ChangeWeaponUIView(int id)
{
for (int i = 0; i < weaponUIGos.Length; i++)
{
weaponUIGos[i].SetActive(false);
}
weaponUIGos[id].SetActive(true);
}
/// <summary>
/// 血量更新
/// </summary>
public void UpdateHPValue(int value)
{
textHealth.text = value.ToString();
}
/// <summary>
/// 子弹更新
/// </summary>
public void UpdateBulletNum(int curretNum,int totalNum)
{
textBulletNum.text = curretNum.ToString() + "/" + totalNum.ToString();
}
public void ShowDeadUI()
{
imgDaeaGo.SetActive(true);
Invoke("PlayLossMusic", 2);
}
private void PlayLossMusic()
{
AudioSourceManager.Instance.playSound(lossClip);
Invoke("LoadCurrentScence", 3);
}
public void LoadCurrentScence()
{
SceneManager.LoadScene(0);
}
}
二十二、Weapon
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Weapon : MonoBehaviour
{
private DoctorController owner;
public int itemID = -1;
public int initAmount = -1;
private Animator animator;
public LineRenderer rayTrailPrefab;
public Transform shootPoint;
public WEAPONTYPE weaponType;
private List<ActiveTrail> activeTrails;
public Projectile projectilePrefab;
public float projectilelauchForce=200;
public AdvancedWeaponSettings advancedWeaponSettings;
public int clipSize = 4;//弹夹里子弹数量(当前可以往里放的最大数量)
public int clipContent;//当前弹夹里剩余子弹数
private WEAPONSTATE currentWeaponState;//当前武器状态
private int fireNameHash;
private int reloadNameHash;
public float reloadTime = 2.0f;//更换弹夹的速度
public float fireRete = 0.5f;//攻击频率(CD)
private float shotTimer = -1;//CD计时器
public AnimationClip fireAnimatortionClip;
public AnimationClip reloadAnimationClip;
public float damageValue = 1;//初始伤害值
public ParticleSystem raycastHitEffectPrefab;//粒子特效
public WEAPONMOOD weaponMode;
public bool hasTelesopicView;//有无倍镜功能
private TelesopicView telesopicView;
private float bulletSize;
public ParticleSystem bulletViewEffect;
private float currentDamageValue;//当前伤害值
public float decreaseSpeed;//减少量
public AudioClip shootClip;
public AudioClip hitWallClip;
public AudioClip reloadClip;//换弹夹
public AudioClip cockClip;
private float chargeTimer;
public AudioSource chargeAudio;
private float stopChargeTimer;//计时器
public AudioSource cockAudio;
private void Awake()
{
animator = GetComponentInChildren<Animator>();
}
// Start is called before the first frame update
void Start()
{
if (rayTrailPrefab!=null)
{
//如果当前武器可以发射激光,那么需要先生成几个备用的预制体
PoolManager.Instance.InitPool(rayTrailPrefab, 8);
}
activeTrails = new List<ActiveTrail>();
if (projectilePrefab!=null)
{
PoolManager.Instance.InitPool(projectilePrefab, 8);
}
currentWeaponState = WEAPONSTATE.IDLE;
fireNameHash=Animator.StringToHash("fire");
reloadNameHash = Animator.StringToHash("reload");
clipContent = clipSize;
if (raycastHitEffectPrefab!=null)
{
PoolManager.Instance.InitPool(raycastHitEffectPrefab, 8);
}
if (hasTelesopicView)
{
telesopicView = Camera.main.transform.GetComponent<TelesopicView>();
}
chargeTimer = 0;
chargeAudio = GetComponent<AudioSource>();
}
// Update is called once per frame
void Update()
{
UpdateController();
FireInput();
if (Input.GetMouseButtonDown(0))
{
Fire();
}
if (Input.GetButtonDown("Reload"))
{
Reload();
}
if (shotTimer>0)
{
shotTimer -= Time.deltaTime;
}
UpdateTrailState();
}
/// <summary>
/// 获取子弹数量
/// </summary>
/// <returns></returns>
public int GetInitAmount()
{
return initAmount;
}
/// <summary>
/// 获取武器的ID
/// </summary>
/// <returns></returns>
public int GetID()
{
return itemID;
}
/// <summary>
/// 选择当前武器
/// </summary>
public void Selected()
{
gameObject.SetActive(owner.GetAmmoAmount(itemID)!=0||clipContent!=0);
animator.SetTrigger("selected");
if (fireAnimatortionClip!=null)
{
animator.SetFloat("fireSpeed",fireAnimatortionClip.length/fireRete);
}
if (reloadAnimationClip!=null)
{
animator.SetFloat("reloadSpeed", reloadAnimationClip.length / reloadTime);
}
currentWeaponState = WEAPONSTATE.IDLE;
owner.decreaseSpeed = decreaseSpeed;
UIManager.Instance.ShowOrHideWeaponUIView(true);
UIManager.Instance.ChangeWeaponUIView(itemID);
UIManager.Instance.UpdateBulletNum(clipContent, owner.GetAmmoAmount(itemID));
if (reloadClip != null)
{
AudioSourceManager.Instance.playSound(reloadClip);
}
}
/// <summary>
/// 收起武器
/// </summary>
public void PutAway()
{
gameObject.SetActive(false);
if (weaponMode == WEAPONMOOD.ACCUMULATION)
{
InitAccumulationWeapon();
}
if (weaponType == WEAPONTYPE.RAYCAST)
{
for (int i = 0; i < activeTrails.Count; i++)
{
activeTrails[i].renderer.gameObject.SetActive(false);
}
activeTrails.Clear();
}
}
/// <summary>
/// 捡起武器,制定当前武器拥有者即DoctorController的引用
/// </summary>
public void PickUp(DoctorController doctorController)
{
owner = doctorController;
}
/// <summary>
/// 攻击方法
/// </summary>
public void Fire()
{
if (currentWeaponState!=WEAPONSTATE.IDLE||shotTimer>0)
{
return;
}
if (clipContent==0)
{
if (cockAudio.isPlaying)
{
cockAudio.Play();
}
return;
}
AudioSourceManager.Instance.playSound(shootClip);
shotTimer = fireRete;
clipContent -= 1;
UIManager.Instance.UpdateBulletNum(clipContent, owner.GetAmmoAmount(itemID));
animator.SetTrigger("fire");
owner.cameraShaker.SetShakeValue(advancedWeaponSettings.shakeTime, 0.05f * advancedWeaponSettings.shakeStrength);
currentWeaponState = WEAPONSTATE.FIRING;
if (weaponType==WEAPONTYPE.RAYCAST)
{
RayCastShot();
}
else
{
ProjectileShot();
}
}
/// <summary>
/// 发射激光类型枪的攻击
/// </summary>
private void RayCastShot()
{
//发散比例(当前单位长度)
float spreadRatio = advancedWeaponSettings.spreadAngle / Camera.main.fieldOfView;
Vector2 spread = spreadRatio*Random.insideUnitCircle;
Ray ray= Camera.main.ViewportPointToRay(Vector3.one*0.5f+(Vector3)spread);
RaycastHit hit;
if (Physics.Raycast(ray, out hit, 1000, ~(1 << 8), QueryTriggerInteraction.Ignore))
{
ParticleSystem ps= PoolManager.Instance.GetInstance<ParticleSystem>(raycastHitEffectPrefab);
ps.transform.position = hit.point;
ps.transform.forward= hit.normal;
ps.gameObject.SetActive(true);
ps.Play();
if (hit.collider.gameObject.layer==9)
{
Enemy enemy = hit.collider.GetComponent<Enemy>();
enemy.TakeDamage(damageValue);
}
else if (hit.collider.gameObject.layer==10)
{
Boss enemy = hit.collider.GetComponentInParent<Boss>();
enemy.TakeDamage(damageValue,transform.position);
}
else
{
AudioSourceManager.Instance.playSound(hitWallClip);
}
if (rayTrailPrefab!=null)
{
LineRenderer lineRenderer= PoolManager.Instance.GetInstance<LineRenderer>(rayTrailPrefab);
lineRenderer.gameObject.SetActive(true);
Vector3[] trailPos = new Vector3[] { shootPoint.position,hit.point};
lineRenderer.SetPositions(trailPos);
activeTrails.Add(
new ActiveTrail()
{
renderer=lineRenderer,
direction=(trailPos[1]-trailPos[0]).normalized,
remainningTime=0.3f
}
);
}
}
}
/// <summary>
/// 更新激光拖尾效果(模拟位移)
/// </summary>
private void UpdateTrailState()
{
Vector3[] pos = new Vector3[2];
for (int i = 0; i < activeTrails.Count; i++)
{
ActiveTrail activeTrail = activeTrails[i];
activeTrail.renderer.GetPositions(pos);
activeTrail.remainningTime -= Time.deltaTime;
pos[0] += activeTrail.direction * 50 * Time.deltaTime;
// pos[1] += activeTrail.direction * 50 * Time.deltaTime;
activeTrail.renderer.SetPositions(pos);
if (activeTrail.remainningTime<=0||Vector3.Distance(pos[0],pos[1])<=0.5F)
{
activeTrail.renderer.gameObject.SetActive(false);
activeTrails.RemoveAt(i);
i--;
}
}
}
/// <summary>
/// 发射子弹类型枪的攻击方式(包括投掷类武器比如手雷)
/// </summary>
private void ProjectileShot()
{
Projectile projectile=PoolManager.Instance.GetInstance<Projectile>(projectilePrefab);
projectile.gameObject.SetActive(true);
if (weaponMode==WEAPONMOOD.ACCUMULATION)
{
projectile.SetBulletDamageValue(currentDamageValue);
projectile.SetBulletSize(bulletSize);
}
Vector2 angleDir = Random.insideUnitCircle*Mathf.Sin(advancedWeaponSettings.spreadAngle*Mathf.Deg2Rad) ;
Vector3 dir = shootPoint.forward + (Vector3)angleDir;
dir.Normalize();
projectile.Launch(this, dir, projectilelauchForce);
}
public Transform GetShootPoint()
{
return shootPoint;
}
/// <summary>
/// 换弹夹
/// </summary>
public void Reload()
{
if (clipContent==clipSize||currentWeaponState!=WEAPONSTATE.IDLE)//弹夹子弹数已满
{
return;
}
int remainningBullet= owner.GetAmmoAmount(itemID);
if (remainningBullet==0)
{
if (itemID==2||itemID==6)
{
PutAway();
}
return;
}
int chargeInClip= Mathf.Min(remainningBullet,clipSize-clipContent);
clipContent += chargeInClip;
currentWeaponState = WEAPONSTATE.RELOADING;
owner.UpdateAmmoAmount(itemID, -chargeInClip);
UIManager.Instance.UpdateBulletNum(clipContent, owner.GetAmmoAmount(itemID));
animator.SetTrigger("reload");
if (weaponMode==WEAPONMOOD.ACCUMULATION)
{
bulletViewEffect.gameObject.SetActive(false);
bulletViewEffect.startSize = 0;
}
if (reloadClip!=null)
{
AudioSourceManager.Instance.playSound(reloadClip);
}
}
/// <summary>
/// 更新动画控制器以及武器的状态
/// </summary>
private void UpdateController()
{
animator.SetFloat("moveSpeed", owner.actualSpeed / 5);
AnimatorStateInfo animatorStateInfo= animator.GetCurrentAnimatorStateInfo(0);
WEAPONSTATE newState;
if (animatorStateInfo.shortNameHash==fireNameHash)
{
newState = WEAPONSTATE.FIRING;
}
else if (animatorStateInfo.shortNameHash==reloadNameHash)
{
newState = WEAPONSTATE.RELOADING;
}
else
{
newState = WEAPONSTATE.IDLE;
}
if (newState!=currentWeaponState)
{
WEAPONSTATE lastState= currentWeaponState;
currentWeaponState = newState;
if (lastState==WEAPONSTATE.FIRING&&clipContent==0)
{
Reload();
}
}
}
public bool HasBullet()
{
return clipContent > 0;
}
private void FireInput()
{
switch (weaponMode)
{
case WEAPONMOOD.NORMAL:
if (Input.GetMouseButtonDown(0))
{
Fire();
}
if (hasTelesopicView)
{
if (Input.GetMouseButton(1))
{
telesopicView.OpenTheTelesopicView();
}
else
{
telesopicView.OpenTheTelesopicView(false);
}
if (currentWeaponState == WEAPONSTATE.RELOADING)
{
telesopicView.OpenTheTelesopicView(false);
}
}
break;
case WEAPONMOOD.AUTO:
if (Input.GetMouseButton(0))
{
Fire();
}
break;
case WEAPONMOOD.ACCUMULATION:
if (Input.GetMouseButtonUp(0))//释放
{
Fire();
InitAccumulationWeapon();
}
else if (Input.GetMouseButtonDown(0)&&clipContent>0&¤tWeaponState!=WEAPONSTATE.RELOADING)//聚能
{
AccumulateEnergy();
}
break;
default:
break;
}
}
private void InitAccumulationWeapon()
{
chargeTimer = 0;
chargeAudio.Stop();
bulletViewEffect.gameObject.SetActive(false);
bulletViewEffect.startSize = 0;
}
/// <summary>
/// 聚能方法
/// </summary>
public void AccumulateEnergy()
{
bulletViewEffect.gameObject.SetActive(true);
if (bulletViewEffect.startSize<=0.3)
{
bulletViewEffect.startSize += Time.deltaTime;
}
if (currentDamageValue<=5*damageValue)
{
currentDamageValue += Time.deltaTime;
}
//第一次播放完整聚能音效
if (chargeTimer<=0)
{
chargeTimer = Time.deltaTime;
chargeAudio.time = 0;
chargeAudio.Play();
}
//后续播放持续循环的部分(即聚能)
if (Time.deltaTime-chargeTimer>=1.463f)
{
if (!chargeAudio.isPlaying)
{
//只持续播放某一个时间点到最后的音效
chargeAudio.time = 0.4f;
chargeAudio.Play();
stopChargeTimer = Time.time;
}
if (Time.time-stopChargeTimer>=0.5f)
{
chargeAudio.Stop();
}
}
}
}
public enum WEAPONTYPE
{
RAYCAST,
PROJECTILE
}
/// <summary>
/// 激光信息类
/// </summary>
public class ActiveTrail
{
public LineRenderer renderer;
public Vector3 direction;
public float remainningTime;
}
/// <summary>
/// 武器的额外设置
/// </summary>
[System.Serializable]
public class AdvancedWeaponSettings
{
public float spreadAngle;//偏移 (发散) 角度(单位不是传统意义上的度数,而是计量单位)
public float shakeTime;//震动时间
public float shakeStrength;//震动大小
}
public enum WEAPONSTATE
{
IDLE,
FIRING,
RELOADING
}
/// <summary>
/// 攻击模式
/// </summary>
public enum WEAPONMOOD
{
NORMAL,
AUTO,
ACCUMULATION
}