首页 > 技术文章 > Survival Shooter 学习

revoid 2017-02-23 15:17 原文

 

using UnityEngine;
using System.Collections;

namespace CompleteProject
{
    /// <summary>
    /// 摄像机跟随
    /// </summary>
    public class CameraFollow : MonoBehaviour
    {
        /// <summary>
        /// 摄像机跟随的目标
        /// </summary>
        public Transform target;            
        /// <summary>
        /// 相机的移动速度
        /// </summary>
        public float smoothing = 5f;        

        /// <summary>
        /// 摄像机相对于目标的偏移
        /// </summary>
        Vector3 offset;                     


        void Start ()
        {
            //计算偏移
            offset = transform.position - target.position;
        }


        void FixedUpdate ()
        {
            //计算相机要移动到的位置
            Vector3 targetCamPos = target.position + offset;

            //移动相机
            transform.position = Vector3.Lerp (transform.position, targetCamPos, smoothing * Time.deltaTime);
        }
    }
}
CameraFollow

 

using UnityEngine;

namespace CompleteProject
{
    /// <summary>
    /// 敌人管理
    /// </summary>
    public class EnemyManager : MonoBehaviour
    {
        /// <summary>
        /// 玩家生命
        /// </summary>
        public PlayerHealth playerHealth;       
        /// <summary>
        /// 敌人预设
        /// </summary>
        public GameObject enemy;                
        /// <summary>
        /// 每次孵化敌人的间隔
        /// </summary>
        public float spawnTime = 3f;            
        /// <summary>
        /// 孵化敌人的位置
        /// </summary>
        public Transform[] spawnPoints;          


        void Start ()
        {
            InvokeRepeating ("Spawn", spawnTime, spawnTime);
        }

        /// <summary>
        /// 孵化敌人
        /// </summary>
        void Spawn ()
        {
            //如果玩家当前生命<=0,不处理
            if(playerHealth.currentHealth <= 0f)
            {
                return;
            }

            //随机找一个孵化点
            int spawnPointIndex = Random.Range (0, spawnPoints.Length);

            //在这个孵化点孵化敌人
            Instantiate (enemy, spawnPoints[spawnPointIndex].position, spawnPoints[spawnPointIndex].rotation);
        }
    }
}
EnemyManager
using UnityEngine;
using System.Collections;

namespace CompleteProject
{
    /// <summary>
    /// 玩家攻击
    /// </summary>
    public class EnemyAttack : MonoBehaviour
    {
        /// <summary>
        /// 每次攻击的时间间隔
        /// </summary>
        public float timeBetweenAttacks = 0.5f;     
        /// <summary>
        /// 每次攻击照成的伤害
        /// </summary>
        public int attackDamage = 10;               

        /// <summary>
        /// 敌人Animator
        /// </summary>
        Animator anim;                              
        /// <summary>
        /// 玩家
        /// </summary>
        GameObject player;                          
        /// <summary>
        /// 玩家生命
        /// </summary>
        PlayerHealth playerHealth;                  
        /// <summary>
        /// 敌人生命
        /// </summary>
        EnemyHealth enemyHealth;                    
        /// <summary>
        /// 玩家是否在攻击范围内
        /// </summary>
        bool playerInRange;                         
        /// <summary>
        /// 下次攻击的计时器
        /// </summary>
        float timer;                                                            


        void Awake ()
        {
            player = GameObject.FindGameObjectWithTag ("Player");
            playerHealth = player.GetComponent <PlayerHealth> ();
            enemyHealth = GetComponent<EnemyHealth>();
            anim = GetComponent <Animator> ();
        }


        void OnTriggerEnter (Collider other)
        {
            //如果碰到玩家
            if(other.gameObject == player)
            {
                //设置标志位为true
                playerInRange = true;
            }
        }


        void OnTriggerExit (Collider other)
        {
            //如果玩家离开
            if(other.gameObject == player)
            {
                //设置标志位为false
                playerInRange = false;
            }
        }


        void Update ()
        {
            //每帧增加计时器的时间
            timer += Time.deltaTime;

            //当计时器的时间大于等于每次攻击的时间间隔
            //玩家在攻击范围内
            //敌人的当前血量大于0
            if(timer >= timeBetweenAttacks && playerInRange && enemyHealth.currentHealth > 0)
            {
                //攻击
                Attack ();
            }

            //如果玩家的生命值小于等于0
            if(playerHealth.currentHealth <= 0)
            {
                //animator触发PlayerDead动画
                anim.SetTrigger ("PlayerDead");
            }
        }

        /// <summary>
        /// 攻击
        /// </summary>
        void Attack ()
        {
            //重置攻击计时器
            timer = 0f;

            //如果玩家当前生命大于0
            if(playerHealth.currentHealth > 0)
            {
                //伤害玩家
                playerHealth.TakeDamage (attackDamage);
            }
        }
    }
}
EnemyAttack
using UnityEngine;

namespace CompleteProject
{
    public class EnemyHealth : MonoBehaviour
    {
        /// <summary>
        /// 敌人初始生命
        /// </summary>
        public int startingHealth = 100;            
        /// <summary>
        /// 敌人当前生命
        /// </summary>
        public int currentHealth;                   
        /// <summary>
        /// 敌人死亡后下沉的速度
        /// </summary>
        public float sinkSpeed = 2.5f;              
        /// <summary>
        /// 被玩家击杀时,玩家获得的得分
        /// </summary>
        public int scoreValue = 10;                 
        /// <summary>
        /// 死亡声音
        /// </summary>
        public AudioClip deathClip;                 


        Animator anim;                              
        AudioSource enemyAudio;                     
        /// <summary>
        /// 敌人受到伤害时的粒子系统
        /// </summary>
        ParticleSystem hitParticles;                
        CapsuleCollider capsuleCollider;            
        /// <summary>
        /// 敌人是否死亡
        /// </summary>
        bool isDead;                                
        /// <summary>
        /// 敌人是否下沉
        /// </summary>
        bool isSinking;                                            


        void Awake ()
        {

            anim = GetComponent <Animator> ();
            enemyAudio = GetComponent <AudioSource> ();
            hitParticles = GetComponentInChildren <ParticleSystem> ();
            capsuleCollider = GetComponent <CapsuleCollider> ();

            //设置当前生命为初始生命
            currentHealth = startingHealth;
        }


        void Update ()
        {
            //如果敌人在下沉
            if(isSinking)
            {
                //移动敌人在y轴的位置
                transform.Translate (-Vector3.up * sinkSpeed * Time.deltaTime);
            }
        }
        
        /// <summary>
        /// 受到伤害
        /// </summary>
        /// <param name="amount">伤害数值</param>
        /// <param name="hitPoint">受到伤害的位置</param>

        public void TakeDamage (int amount, Vector3 hitPoint)
        {
            //如果敌人死亡,不处理
            if(isDead)
                return;

            //播放受伤声音
            enemyAudio.Play ();

            //减少当前血量
            currentHealth -= amount;
            
            //将受伤的粒子系统的位置设置为受到伤害的位置
            hitParticles.transform.position = hitPoint;

            //播放粒子系统
            hitParticles.Play();

            //如果当前血量<=0
            if(currentHealth <= 0)
            {
                //调用死亡函数
                Death ();
            }
        }

        /// <summary>
        /// 死亡
        /// </summary>
        void Death ()
        {
            //修改死亡标志位
            isDead = true;

           //将碰撞器改为触发器
            capsuleCollider.isTrigger = true;

            //触发animator的dead动画
            anim.SetTrigger ("Dead");
            
            //播放敌人死亡声音
            enemyAudio.clip = deathClip;
            enemyAudio.Play ();
        }

        /// <summary>
        /// 敌人开始下沉
        /// </summary>
        public void StartSinking ()
        {
            //关闭NavMeshAgent
            GetComponent <NavMeshAgent> ().enabled = false;

            //将Rigidbody设置为kinematic
            GetComponent <Rigidbody> ().isKinematic = true;

            //将下沉标志位设置为true
            isSinking = true;

            //添加玩家的得分
            ScoreManager.score += scoreValue;

            //销毁敌人
            Destroy (gameObject, 2f);
        }
    }
}
EnemyHealth
using UnityEngine;
using System.Collections;

namespace CompleteProject
{
    /// <summary>
    /// 敌人移动
    /// </summary>
    public class EnemyMovement : MonoBehaviour
    {
        /// <summary>
        /// 玩家的位置
        /// </summary>
        Transform player;               
        /// <summary>
        /// 玩家的生命
        /// </summary>
        PlayerHealth playerHealth;      
        /// <summary>
        /// 敌人生命
        /// </summary>
        EnemyHealth enemyHealth;        
        /// <summary>
        /// NavMeshAgent
        /// </summary>
        NavMeshAgent nav;                   


        void Awake ()
        {
            player = GameObject.FindGameObjectWithTag ("Player").transform;
            playerHealth = player.GetComponent <PlayerHealth> ();
            enemyHealth = GetComponent <EnemyHealth> ();
            nav = GetComponent <NavMeshAgent> ();
        }


        void Update ()
        {
            //如果敌人生命和玩家生命都大于0
            if(enemyHealth.currentHealth > 0 && playerHealth.currentHealth > 0)
            {
                //敌人移动到玩家位置
                nav.SetDestination (player.position);
            }
            //否则关闭NavMeshAgent
            else
            {
                nav.enabled = false;
            }
        }
    }
}
EnemyMovement

 

using UnityEngine;
using UnityEngine.UI;
using System.Collections;
using UnityEngine.SceneManagement;

namespace CompleteProject
{
    /// <summary>
    /// 玩家生命
    /// </summary>
    public class PlayerHealth : MonoBehaviour
    {
        /// <summary>
        /// 游戏开始时玩家的生命
        /// </summary>
        public int startingHealth = 100;                          
        /// <summary>
        /// 玩家的当前生命
        /// </summary>
        public int currentHealth;                                  
        /// <summary>
        /// 生命滑动条
        /// </summary>
        public Slider healthSlider;                                
        /// <summary>
        /// 玩家受到伤害时的图片
        /// </summary>
        public Image damageImage;                                  
        /// <summary>
        /// 玩家死亡的声音剪辑
        /// </summary>
        public AudioClip deathClip;                                
        /// <summary>
        /// 伤害图片变透明的速度
        /// </summary>
        public float flashSpeed = 5f;                              
        /// <summary>
        /// 伤害图片的颜色
        /// </summary>
        public Color flashColour = new Color(1f, 0f, 0f, 0.1f);        

        Animator anim;                          
        AudioSource playerAudio;                                  
        PlayerMovement playerMovement;                            
        PlayerShooting playerShooting;

        /// <summary>
        /// 玩家是否死亡
        /// </summary>                               
        bool isDead;
        /// <summary>
        /// 玩家是否受到伤害
        /// </summary>                                                
        bool damaged;                                                    


        void Awake ()
        {

            anim = GetComponent <Animator> ();
            playerAudio = GetComponent <AudioSource> ();
            playerMovement = GetComponent <PlayerMovement> ();
            playerShooting = GetComponentInChildren <PlayerShooting> ();

            //设置当前血量为起始血量
            currentHealth = startingHealth;
        }


        void Update ()
        {
            //如果玩家受到伤害,修改伤害图片的颜色
            if(damaged)
            {
                damageImage.color = flashColour;
            }
            //没有受到伤害,伤害图片渐隐
            else
            {
                damageImage.color = Color.Lerp (damageImage.color, Color.clear, flashSpeed * Time.deltaTime);
            }

            //设置伤害标志位false
            damaged = false;
        }

        /// <summary>
        /// 玩家受到伤害
        /// </summary>
        /// <param name="amount">受到伤害的数值</param>
        public void TakeDamage (int amount)
        {
            //设置伤害标志位true
            damaged = true;

            //减少当前血量
            currentHealth -= amount;

            //将当前血量应用到血量滑动条上
            healthSlider.value = currentHealth;

            //播放受伤的声音
            playerAudio.Play ();

            //如果当前血量<=0并且玩家死亡的标志位位true.
            if(currentHealth <= 0 && !isDead)
            {
                Death ();
            }
        }

        /// <summary>
        /// 玩家死亡
        /// </summary>
        void Death ()
        {
            //修改死亡标志位
            isDead = true;

            //关闭射击特效
            playerShooting.DisableEffects ();

            //触发死亡动画
            anim.SetTrigger ("Die");

            //播放死亡声音
            playerAudio.clip = deathClip;
            playerAudio.Play ();

            //关闭移动和射击脚本
            playerMovement.enabled = false;
            playerShooting.enabled = false;
        }

        /// <summary>
        /// 重新开始
        /// </summary>
        public void RestartLevel ()
        {
            //加载场景
            SceneManager.LoadScene (0);
        }
    }
}
PlayerHealth
using UnityEngine;
using UnitySampleAssets.CrossPlatformInput;

namespace CompleteProject
{
    /// <summary>
    /// 玩家移动
    /// </summary>
    public class PlayerMovement : MonoBehaviour
    {
        /// <summary>
        /// 玩家移动速度
        /// </summary>
        public float speed = 6f;            

        /// <summary>
        /// 玩家移动方向
        /// </summary>
        Vector3 movement;                   
        /// <summary>
        /// Animator
        /// </summary>
        Animator anim;                      
        /// <summary>
        /// Rigidbody
        /// </summary>
        Rigidbody playerRigidbody;          

#if !MOBILE_INPUT
        /// <summary>
        /// 地面
        /// </summary>
        int floorMask;                      
        /// <summary>
        /// 射线的长度
        /// </summary>
        float camRayLength = 100f;                                    
#endif

        void Awake ()
        {
#if !MOBILE_INPUT

            floorMask = LayerMask.GetMask ("Floor");
#endif

            anim = GetComponent <Animator> ();
            playerRigidbody = GetComponent <Rigidbody> ();
        }


        void FixedUpdate ()
        {
            //获取水平和垂直的输入
            float h = CrossPlatformInputManager.GetAxisRaw("Horizontal");
            float v = CrossPlatformInputManager.GetAxisRaw("Vertical");

            //处理玩家的移动
            Move (h, v);

            //处理玩家的旋转
            Turning ();

            //处理玩家的动画
            Animating (h, v);
        }

        /// <summary>
        /// 移动
        /// </summary>
        /// <param name="h"></param>
        /// <param name="v"></param>
        void Move (float h, float v)
        {
            movement.Set (h, 0f, v);
            
            //设置移动速度
            movement = movement.normalized * speed * Time.deltaTime;

            //刚体移动
            playerRigidbody.MovePosition (transform.position + movement);
        }

        /// <summary>
        /// 旋转
        /// </summary>
        void Turning ()
        {
#if !MOBILE_INPUT
            //从鼠标点击的屏幕位置创建射线
            Ray camRay = Camera.main.ScreenPointToRay (Input.mousePosition);

            //接收射线撞击到地面的数据
            RaycastHit floorHit;

            //发射射线
            if(Physics.Raycast (camRay, out floorHit, camRayLength, floorMask))
            {
                //创建一个玩家到撞击点的向量
                Vector3 playerToMouse = floorHit.point - transform.position;

                //保证这个向量的y为0
                playerToMouse.y = 0f;

                //根据这个向量创建四元数
                Quaternion newRotatation = Quaternion.LookRotation (playerToMouse);

                //刚体旋转
                playerRigidbody.MoveRotation (newRotatation);
            }
#else

            Vector3 turnDir = new Vector3(CrossPlatformInputManager.GetAxisRaw("Mouse X") , 0f , CrossPlatformInputManager.GetAxisRaw("Mouse Y"));

            if (turnDir != Vector3.zero)
            {
                Vector3 playerToMouse = (transform.position + turnDir) - transform.position;

                playerToMouse.y = 0f;

                Quaternion newRotatation = Quaternion.LookRotation(playerToMouse);

                playerRigidbody.MoveRotation(newRotatation);
            }
#endif
        }

        /// <summary>
        /// 播放动画
        /// </summary>
        /// <param name="h">水平输入值</param>
        /// <param name="v">垂直输入值</param>
        void Animating (float h, float v)
        {
            bool walking = h != 0f || v != 0f;

            anim.SetBool ("IsWalking", walking);
        }
    }
}
PlayerMovement
using UnityEngine;
using UnitySampleAssets.CrossPlatformInput;

namespace CompleteProject
{
    /// <summary>
    /// 玩家射击
    /// </summary>
    public class PlayerShooting : MonoBehaviour
    {
        /// <summary>
        /// 没发子弹造成的伤害
        /// </summary>
        public int damagePerShot = 20;                  
        /// <summary>
        /// 每发子弹的时间间隔
        /// </summary>
        public float timeBetweenBullets = 0.15f;        
        /// <summary>
        /// 射击范围
        /// </summary>
        public float range = 100f;                      

        /// <summary>
        /// 发射子弹的计时器
        /// </summary>
        float timer;                                    
        /// <summary>
        /// 射击射线
        /// </summary>
        Ray shootRay;                                   
        /// <summary>
        /// 射击击中点
        /// </summary>
        RaycastHit shootHit;                            
        /// <summary>
        /// 可以受到射击的层
        /// </summary>
        int shootableMask;                              
        /// <summary>
        /// 
        /// </summary>
        ParticleSystem gunParticles;                    
        /// <summary>
        /// 枪的线条渲染器
        /// </summary>
        LineRenderer gunLine;                           
        /// <summary>
        /// 枪的声音
        /// </summary>
        AudioSource gunAudio;                           
        /// <summary>
        /// 枪的灯光
        /// </summary>
        Light gunLight;                                 
        /// <summary>
        /// 
        /// </summary>
        public Light faceLight;                            
        /// <summary>
        /// 效果的显示时间
        /// </summary>
        float effectsDisplayTime = 0.2f;                                                                                                                 


        void Awake ()
        {
            //获取可以射击的层
            shootableMask = LayerMask.GetMask ("Shootable");

            gunParticles = GetComponent<ParticleSystem> ();
            gunLine = GetComponent <LineRenderer> ();
            gunAudio = GetComponent<AudioSource> ();
            gunLight = GetComponent<Light> ();
            //faceLight = GetComponentInChildren<Light> ();
        }


        void Update ()
        {
            //每帧增加计时器的时间
            timer += Time.deltaTime;

#if !MOBILE_INPUT
            //如果按下发射按键,计时器大于两次发射子弹的间隔时,时间比例不等于0
            if(Input.GetButton ("Fire1") && timer >= timeBetweenBullets && Time.timeScale != 0)
            {
                //射击
                Shoot ();
            }
#else
            if ((CrossPlatformInputManager.GetAxisRaw("Mouse X") != 0 || CrossPlatformInputManager.GetAxisRaw("Mouse Y") != 0) && timer >= timeBetweenBullets)
            {
                Shoot();
            }
#endif
            //如果计时器超过特效显示的时间
            if(timer >= timeBetweenBullets * effectsDisplayTime)
            {
                //关闭射击效果
                DisableEffects ();
            }
        }


        public void DisableEffects ()
        {
            gunLine.enabled = false;
            faceLight.enabled = false;
            gunLight.enabled = false;
        }

        /// <summary>
        /// 射击
        /// </summary>
        void Shoot ()
        {
            //重置计时器
            timer = 0f;

            //播放射击声音
            gunAudio.Play ();

            
            gunLight.enabled = true;
            faceLight.enabled = true;

            //先停止粒子特效,然后再播放特效
            gunParticles.Stop ();
            gunParticles.Play ();

            //设置线条渲染器的第一个位置
            gunLine.enabled = true;
            gunLine.SetPosition (0, transform.position);

            //设置 射击射线的 起点和方向
            shootRay.origin = transform.position;
            shootRay.direction = transform.forward;

            //发射射线,碰到敌人
            if(Physics.Raycast (shootRay, out shootHit, range, shootableMask))
            {
                //敌人受到伤害
                EnemyHealth enemyHealth = shootHit.collider.GetComponent <EnemyHealth> ();
                if(enemyHealth != null)
                {
                    enemyHealth.TakeDamage (damagePerShot, shootHit.point);
                }

                //设置线条渲染器的第二个位置为射击点的位置
                gunLine.SetPosition (1, shootHit.point);
            }
            //没有碰到敌人,设置线条渲染器的第2个位置
            else
            {
                gunLine.SetPosition (1, shootRay.origin + shootRay.direction * range);
            }
        }
    }
}
PlayerShooting

 

using UnityEngine;

namespace CompleteProject
{
    /// <summary>
    /// 游戏结束管理
    /// </summary>
    public class GameOverManager : MonoBehaviour
    {
        /// <summary>
        /// 玩家生命
        /// </summary>
        public PlayerHealth playerHealth;       

        /// <summary>
        /// Animator
        /// </summary>
        Animator anim;                          


        void Awake ()
        {
            anim = GetComponent <Animator> ();
        }


        void Update ()
        {
            //如果玩家生命值小于等于0
            if(playerHealth.currentHealth <= 0)
            {
                //Animator设置GameOver触发器
                anim.SetTrigger ("GameOver");
            }
        }
    }
}
GameOverManager
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
using UnityEngine.Audio;
#if UNITY_EDITOR
using UnityEditor;
#endif

/// <summary>
/// 暂停管理
/// </summary>
public class PauseManager : MonoBehaviour {
    
    public AudioMixerSnapshot paused;
    public AudioMixerSnapshot unpaused;
    
    /// <summary>
    /// Canvas
    /// </summary>
    Canvas canvas;
    
    void Start()
    {
        canvas = GetComponent<Canvas>();
    }
    
    void Update()
    {
        //按esc键 切换游戏状态
        if (Input.GetKeyDown(KeyCode.Escape))
        {
            canvas.enabled = !canvas.enabled;
            Pause();
        }
    }
    
    /// <summary>
    /// 暂停游戏
    /// </summary>
    public void Pause()
    {
        Time.timeScale = Time.timeScale == 0 ? 1 : 0;
        Lowpass ();
        
    }
    
    /// <summary>
    /// 调整声音
    /// </summary>
    void Lowpass()
    {
        if (Time.timeScale == 0)
        {
            paused.TransitionTo(.01f);
        }
        
        else
            
        {
            unpaused.TransitionTo(.01f);
        }
    }
    
    /// <summary>
    /// 退出游戏
    /// </summary>
    public void Quit()
    {
        //调用编辑器的退出
        #if UNITY_EDITOR 
        EditorApplication.isPlaying = false;
        //调用正常的退出
        #else 
        Application.Quit();
        #endif
    }
}
PauseManager
using UnityEngine;
using UnityEngine.UI;
using System.Collections;

namespace CompleteProject
{
    /// <summary>
    /// 玩家分数管理
    /// </summary>
    public class ScoreManager : MonoBehaviour
    {
        /// <summary>
        /// 玩家分数
        /// </summary>
        public static int score;        

        /// <summary>
        /// 显示玩家分数的文本
        /// </summary>
        Text text;                      
                                                                             

        void Awake ()
        {
            text = GetComponent <Text> ();

            //重置玩家分数
            score = 0;
        }


        void Update ()
        {
            //更新玩家分数的显示
            text.text = "Score: " + score;
        }
    }
}
ScoreManager

视频:https://pan.baidu.com/s/1geIxFs3

项目:https://pan.baidu.com/s/1gfDonkf

推荐阅读