双人3D坦克(unity+C#)

 

 

  

//TankManager.cs

using System;
using UnityEngine;

[Serializable]
public class TankManager
{
    public Color m_PlayerColor;            
    public Transform m_SpawnPoint;         
    [HideInInspector] public int m_PlayerNumber;             
    [HideInInspector] public string m_ColoredPlayerText;
    [HideInInspector] public GameObject m_Instance;          
    [HideInInspector] public int m_Wins;                     


    private MyTankMovement m_Movement;       
    private MyTankShooting m_Shooting;
    private GameObject m_CanvasGameObject;


    public void Setup()
    {
        m_Movement = m_Instance.GetComponent<MyTankMovement>();
        m_Shooting = m_Instance.GetComponent<MyTankShooting>();
        m_CanvasGameObject = m_Instance.GetComponentInChildren<Canvas>().gameObject;

        m_Movement.PlayerNumber = m_PlayerNumber;
        m_Shooting.PlayerNumber = m_PlayerNumber;

        m_ColoredPlayerText = "<color=#" + ColorUtility.ToHtmlStringRGB(m_PlayerColor) + ">PLAYER " + m_PlayerNumber + "</color>";

        MeshRenderer[] renderers = m_Instance.GetComponentsInChildren<MeshRenderer>();

        for (int i = 0; i < renderers.Length; i++)
        {
            renderers[i].material.color = m_PlayerColor;
        }
    }


    public void DisableControl()
    {
        m_Movement.enabled = false;
        m_Shooting.enabled = false;

        m_CanvasGameObject.SetActive(false);
    }


    public void EnableControl()
    {
        m_Movement.enabled = true;
        m_Shooting.enabled = true;

        m_CanvasGameObject.SetActive(true);
    }


    public void Reset()
    {
        m_Instance.transform.position = m_SpawnPoint.position;
        m_Instance.transform.rotation = m_SpawnPoint.rotation;

        m_Instance.SetActive(false);
        m_Instance.SetActive(true);
    }
}


//GameManagers.cs
using UnityEngine;
using System.Collections;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

//public class TankManager123
//{
//    public GameObject Tank;
//    public MyTankShooting my;
//    public MyTankMovement mymove;

//    public int m_RoundNumber;


//}




public class GameManager : MonoBehaviour
{
    public int m_NumRoundsToWin = 5;        
    public float m_StartDelay = 3f;         
    public float m_EndDelay = 3f;           
    public CameraControl m_CameraControl;   
    public Text m_MessageText;              
    public GameObject m_TankPrefab;         
    public TankManager[] m_Tanks;

    //private TankManager123 tk123;

    private int m_RoundNumber;              
    private WaitForSeconds m_StartWait;     
    private WaitForSeconds m_EndWait;       
    private TankManager m_RoundWinner;
    private TankManager m_GameWinner;       


    private void Start()
    {
        //tk123.m_RoundNumber = 123321;

        m_StartWait = new WaitForSeconds(m_StartDelay);
        m_EndWait = new WaitForSeconds(m_EndDelay);

        SpawnAllTanks();
        SetCameraTargets();

        StartCoroutine(GameLoop());//协程
    }


    private void SpawnAllTanks()
    {
        for (int i = 0; i < m_Tanks.Length; i++)
        {
            m_Tanks[i].m_Instance =
                Instantiate(m_TankPrefab, m_Tanks[i].m_SpawnPoint.position, m_Tanks[i].m_SpawnPoint.rotation) as GameObject;
            m_Tanks[i].m_PlayerNumber = i + 1;
            m_Tanks[i].Setup();
        }
    }


    private void SetCameraTargets()
    {
        Transform[] targets = new Transform[m_Tanks.Length];

        for (int i = 0; i < targets.Length; i++)
        {
            targets[i] = m_Tanks[i].m_Instance.transform;
        }

        m_CameraControl.m_Targets = targets;
    }


    private IEnumerator GameLoop()
    {
        yield return StartCoroutine(RoundStarting());
        yield return StartCoroutine(RoundPlaying());
        yield return StartCoroutine(RoundEnding());

        if (m_GameWinner != null)
        {
            SceneManager.LoadScene(0);
        }
        else
        {
            StartCoroutine(GameLoop());
        }
       }


    private IEnumerator RoundStarting()
    {
        ResetAllTanks();//?这是什么方法?
        DisableTankControl();//?

        m_CameraControl.SetStartPositionAndSize();//?

        m_RoundNumber++;//?

        m_MessageText.text = "ROUND_ "+m_RoundNumber ;



        yield return m_StartWait;//协程返回写yield,return
    }


    private IEnumerator RoundPlaying()
    {
        EnableTankControl();//?

        m_MessageText.text = " ";

        while (!OneTankLeft ())//?
        {
            yield return null;
        }

        
    }


    private IEnumerator RoundEnding()
    {
        DisableTankControl();


        m_RoundWinner = null;
        m_RoundWinner = GetRoundWinner();
        if (m_RoundWinner != null)
            m_RoundWinner.m_Wins++;

        m_GameWinner = GetGameWinner();

        string message = EndMessage();


        m_MessageText.text = message;

        yield return m_EndWait;
    }


    private bool OneTankLeft()
    {
        int numTanksLeft = 0;

        for (int i = 0; i < m_Tanks.Length; i++)
        {
            if (m_Tanks[i].m_Instance.activeSelf)
                numTanksLeft++;
        }

        return numTanksLeft <= 1;
    }


    private TankManager GetRoundWinner()
    {
        for (int i = 0; i < m_Tanks.Length; i++)
        {
            if (m_Tanks[i].m_Instance.activeSelf)
                return m_Tanks[i];
        }

        return null;
    }


    private TankManager GetGameWinner()
    {
        for (int i = 0; i < m_Tanks.Length; i++)
        {
            if (m_Tanks[i].m_Wins == m_NumRoundsToWin)
                return m_Tanks[i];
        }

        return null;
    }


    private string EndMessage()
    {
        string message = "DRAW!";

        if (m_RoundWinner != null)
            message = m_RoundWinner.m_ColoredPlayerText + " WINS THE ROUND!";

        message += "\n\n\n\n";

        for (int i = 0; i < m_Tanks.Length; i++)
        {
            message += m_Tanks[i].m_ColoredPlayerText + ": " + m_Tanks[i].m_Wins + " WINS\n";
        }

        if (m_GameWinner != null)
            message = m_GameWinner.m_ColoredPlayerText + " WINS THE GAME!";

        return message;
    }


    private void ResetAllTanks()
    {
        for (int i = 0; i < m_Tanks.Length; i++)
        {
            m_Tanks[i].Reset();
        }
    }


    private void EnableTankControl()
    {
        for (int i = 0; i < m_Tanks.Length; i++)
        {
            m_Tanks[i].EnableControl();
        }
    }


    private void DisableTankControl()
    {
        for (int i = 0; i < m_Tanks.Length; i++)
        {
            m_Tanks[i].DisableControl();
        }
    }
}

//CameraContral.cs
using UnityEngine;

public class CameraControl : MonoBehaviour
{
//m_DampTime就是过度时间,断定他是过渡时间是看SmoothDamp的传参
    public float m_DampTime = 0.2f;                 
    public float m_ScreenEdgeBuffer = 4f;           
    public float m_MinSize = 6.5f;                  
    [HideInInspector] public Transform[] m_Targets; //HideInInspector的作用是暂时将public的代码隐藏,就无法再unity检查器中看到
    //Targets是一个transform类型的数组,这里Targets作用是定义所有坦克对象的数组

    private Camera m_Camera;                        
    private float m_ZoomSpeed;                      
    private Vector3 m_MoveVelocity;  
    //两个坦克中心点(多个坦克中心点)
    private Vector3 m_DesiredPosition;              


    private void Awake()
    {
        m_Camera = GetComponentInChildren<Camera>();//GetComponent作用是获取物体组件,GetComponentInChildren的作用是在子物体中获取组件
    }


    private void FixedUpdate()
    {
        Move();
        Zoom();
    }


    private void Move()//Move就是遍历所有的坦克求出他们的平均点,然后将平均点给到CameraRig的position
    {
        FindAveragePosition();

        transform.position = Vector3.SmoothDamp(transform.position, m_DesiredPosition, ref m_MoveVelocity, m_DampTime);
    }


    private void FindAveragePosition()
    {
        Vector3 averagePos = new Vector3();
        int numTargets = 0;

        for (int i = 0; i < m_Targets.Length; i++)//循环所有坦克的位置
        {
            if (!m_Targets[i].gameObject.activeSelf)//sctiveSelf这个属性表示当前物体的Active状态
                continue;

            averagePos += m_Targets[i].position;
            numTargets++;
        }

        if (numTargets > 0)
            averagePos /= numTargets;

        averagePos.y = transform.position.y;

        m_DesiredPosition = averagePos;
    }


    private void Zoom()
    {
        float requiredSize = FindRequiredSize();
        m_Camera.orthographicSize = Mathf.SmoothDamp(m_Camera.orthographicSize, requiredSize, ref m_ZoomSpeed, m_DampTime);
    }


    private float FindRequiredSize()
    {
        Vector3 desiredLocalPos = transform.InverseTransformPoint(m_DesiredPosition);//这个方法InverseTransformPoint用来计算本地空间

        float size = 0f;

        for (int i = 0; i < m_Targets.Length; i++)
        {
            if (!m_Targets[i].gameObject.activeSelf)
                continue;

            Vector3 targetLocalPos = transform.InverseTransformPoint(m_Targets[i].position);

            Vector3 desiredPosToTarget = targetLocalPos - desiredLocalPos;

            size = Mathf.Max (size, Mathf.Abs (desiredPosToTarget.y));

            size = Mathf.Max (size, Mathf.Abs (desiredPosToTarget.x) / m_Camera.aspect);
        }
        
        size += m_ScreenEdgeBuffer;

        size = Mathf.Max(size, m_MinSize);

        return size;
    }


    public void SetStartPositionAndSize()
    {
        FindAveragePosition();

        transform.position = m_DesiredPosition;

        m_Camera.orthographicSize = FindRequiredSize();
    }
}

//MyShellExplosion.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using static UnityEngine.GraphicsBuffer;

public class MyShellExplosion : MonoBehaviour
{
    public LayerMask TankMask;
    public ParticleSystem ExplosionParticles;//接收粒子特效
    public AudioSource ExplosionAudio;
    public float MaxDamage = 100f;
    public float ExplisionForce = 1000f;
    public float MaxLifeTime = 2f;
    public float ExplosionRadius = 5f;//爆炸范围半径



    // Start is called before the first frame update
    void Start()
    {
        Destroy(transform.gameObject,MaxLifeTime );//重载就是两个方法,名字一样参数不一样(相当于一个行为的不同版本)



    }

    // Update is called once per frame
    void Update()
    {
        
    }
    private void OnTriggerEnter(Collider other)
    {
        //获取到半径为R的一个球型里面的所有物体
       Collider [] targetCollider= Physics.OverlapSphere(transform.position, ExplosionRadius, TankMask);

        for(int i = 0; i<targetCollider .Length;i ++)
        {
            //获取刚体组件
            Rigidbody target  = targetCollider[i].GetComponent<Rigidbody>();
            if (target == null)
                continue;
            //调用刚体组件下方的爆炸方法
            target.AddExplosionForce(ExplisionForce, transform.position, ExplosionRadius);

            //扣血
            TankHealth targetTankHealth=targetCollider[i].GetComponent<TankHealth>();

            //计算坦克与爆炸的距离
            float damage = CalculateDamage(target .gameObject .transform.position );


            targetTankHealth.TakeDamage(damage);

        }

        //TankHealth tankHealth= other.gameObject.GetComponent<TankHealth>();//通过坦克自身的Cokkider组件(other.gameObject),再通过gameObject来获取GetComponent来获取TankHealth。炮弹跟坦克发生碰撞之后,立马通过box collider来Getocmponent的血量组件
       
        //if(tankHealth !=null )

        //tankHealth.TakeDamage(20);//这句话就会使CurrentHealth血量减去20

        //other.gameObject.SetActive(true );//gameObject下有一个setActive方法设置true是启用,设置false是不启用,设置true和false时候会调用onenable方法
        //Debug.Log("触发了一个物体");
        //播放爆炸特效
        ExplosionParticles.transform.parent = null;
        ExplosionParticles.Play();

        //播放爆炸声音
        //void OnTriggerEnter(Collider other)
        //{
        //    // 检查音频源是否启用
        //    if (!ExplosionAudio .enabled)
        //    {
        //        // 如果未启用,则启用音频源
        //        ExplosionAudio.enabled = true;
        //    }

        //    // 播放音频
        //   // ExplosionAudio.Play();
        //}

        ExplosionAudio.Play();


        //销毁爆炸的特效
        Destroy(ExplosionParticles.gameObject,ExplosionParticles .main .duration );//main.duration获取粒子特效持续时间

        //销毁炮弹
        Destroy(gameObject );


    }


    /// <summary>
    /// 计算爆炸伤害
    /// </summary>
    /// <param name="targetPosition">爆炸的中心点</param>
    /// <returns></returns>
    private float CalculateDamage(Vector3  targetPosition)
    {
        //计算坦克与爆炸点的距离
        Vector3 v3 = targetPosition - transform.position;//计算两点间距离
        float distance = v3.magnitude;//获得它距离的具体的float值
        float damage = ((ExplosionRadius - distance) / ExplosionRadius) * MaxDamage;//通过float值和半径(distance)来计算出占比
        damage = Mathf.Max(0, damage);

        return damage;

    }

}

//TankHealth.cs
using UnityEngine;
using UnityEngine.UI;

public class TankHealth : MonoBehaviour
{
    public float m_StartingHealth = 100f;       //坦克初始血量   
    public Slider m_Slider;                //UI控件
        
    public Image m_FillImage;               //UI控件       
    public Color m_FullHealthColor = Color.green;  //ui控件
    public Color m_ZeroHealthColor = Color.red;    
    public GameObject m_ExplosionPrefab;//爆炸的预制体


    private AudioSource m_ExplosionAudio;//爆炸音效
    private ParticleSystem m_ExplosionParticles;//坦克爆炸的ParticleSystem粒子特效,
    //坦克当前血量
    private float m_CurrentHealth;

    private bool m_Dead;//表示坦克是否死亡,true是死亡,false是生存


    private void Awake()
    {
        //从爆炸预制体中获取爆炸粒子特效
        m_ExplosionParticles = Instantiate(m_ExplosionPrefab).GetComponent<ParticleSystem>();//Instantiate是实体化,Instantiate(m_ExplosionPrefab)初始化这样一个预制体。调用GetComponent<ParticleSystem>,从前面的预制体里面获取组件,获取OarticleSystem组件,并赋值给ExplosionParticles
        m_ExplosionAudio = m_ExplosionParticles.GetComponent<AudioSource>();//从ExplosionParticles这个粒子特效中,获取GetComponent的AudioSource声音播放组件

        m_ExplosionParticles.gameObject.SetActive(false);//坦克不会一场就爆炸,所有需要先将报纸粒子特效置为不可用
    }


    private void OnEnable()//物体可用时调用OnEnable方法
    {
        m_CurrentHealth = m_StartingHealth;//当前血量设置为初始血量
        m_Dead = false;

        SetHealthUI();//用来更新UI界面,血量实时显示在画面当中 
    }


    public void TakeDamage(float amount)
    {

        m_CurrentHealth -= amount;

        SetHealthUI();


        if (m_CurrentHealth<=0f&&!m_Dead )
        {
            OnDeath();
        }

        Debug.Log(m_CurrentHealth);

        // Adjust the tank's current health, update the UI based on the new health and check whether or not the tank is dead.
    }


    private void SetHealthUI()
    {
        //将当前的血量展示在UI界面中
        m_Slider.value = m_CurrentHealth;

        m_FillImage.color = Color.Lerp(m_ZeroHealthColor, m_FullHealthColor, m_CurrentHealth / m_StartingHealth); //颜色差值计算.解释(m_CureenHealth/m_StartingHealth是用来计算现有血量占总血量的比例,如果大于百分之50,则偏向于m_FullHealthColor绿色,小于百分之50偏向于m_ZeroHealthColor红色)
        //if (m_CurrentHealth > 40)//设置血量颜色,当大于40的时候是绿色,反之为红色
        //    m_FillImage.color = m_FullHealthColor;
        //else 
        //    m_FillImage.color = m_ZeroHealthColor;


        // Adjust the value and colour of the slider.
    }


    private void OnDeath()
    {
        m_Dead = true;

        //播放粒子特效
        m_ExplosionParticles.transform.position = transform.position;
        m_ExplosionParticles.gameObject.SetActive(true);

        m_ExplosionParticles.Play();



        //播放声音
        m_ExplosionAudio.Play();



        gameObject.SetActive(false);// Play the effects for the death of the tank and deactivate it.
    }
}

//MyTankShooting.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class MyTankShooting : MonoBehaviour
{
    public int PlayerNumber = 1;
    public GameObject FireTransform;
    public GameObject Shell;
    public Slider AimSlider;
    public AudioSource ShootingAudio;
    public AudioClip ChargingClip;
    public AudioClip FireClip;
    public float MinLaunchForce = 15f;
    public float MaxLunchForce = 30f;
    public float ChargeTime = 0.75f;


    // Start is called before the first frame update
    private string FireButton;
    private float ChargeSpeed;
    private float CurrentLaunchForce;
    private bool Fired;


    void Start()
    {
        FireButton = "Fire" + PlayerNumber;
        ChargeSpeed = (MaxLunchForce - MinLaunchForce) / ChargeTime;

    }

    // Update is called once per frame
    void Update()
    {
        //if (Input.GetButtonDown(FireButton ))
        //{
        //    GameObject gameObjectInstance = Instantiate(Shell, FireTransform.transform.position, FireTransform.transform.rotation);

        //    Rigidbody rigidbody = gameObjectInstance.GetComponent<Rigidbody>();
        //    rigidbody.velocity = FireTransform.transform.forward * 15;

        //}
        if (Input .GetButtonDown(FireButton))
        {
            CurrentLaunchForce = MinLaunchForce;
            ShootingAudio.clip = ChargingClip;
            ShootingAudio.Play();

            Fired = false;


        }
        else if (Input .GetButton (FireButton )&&!Fired )
        {
            CurrentLaunchForce += ChargeSpeed * Time.deltaTime;
            //if (ShootingAudio.clip == FireClip)
            //{
            //    ShootingAudio.clip = ChargingClip;
            //    ShootingAudio.Play();
            //}

            AimSlider.value = CurrentLaunchForce;


            if (CurrentLaunchForce >=MaxLunchForce )
            {
                CurrentLaunchForce = MaxLunchForce;

                Fire();


            }
        }
        else if (Input .GetButtonUp(FireButton )&&!Fired )
        {
            Fire();

        }
    }


    void Fire()
    {
        AimSlider.value = MinLaunchForce ;


        Fired = true;


        GameObject gameObjectInstance = Instantiate(Shell, FireTransform.transform.position, FireTransform.transform.rotation);

        Rigidbody rigidbody = gameObjectInstance.GetComponent<Rigidbody>();
        rigidbody.velocity = FireTransform.transform.forward * CurrentLaunchForce;
        CurrentLaunchForce = MinLaunchForce;

        ShootingAudio.clip = FireClip;
        ShootingAudio.Play();


    }
}

//MyTankMovement.cs
using UnityEngine;

public class MyTankMovement : MonoBehaviour
{
    public int PlayerNumber = 1;
    public float Speed = 12;
    public float TurnSpeed = 180;
    public AudioSource MovementAudio;
    public AudioClip EngineIdling;
    public AudioClip EngineDriving;
    public float PichRage = 0.2f;



    private Rigidbody rb;
    private string MovementAxisName;
    private string TurnAxisName;
    private float MovementInputValue;
    private float TurnInputValue;
    private float OriginalPitch;



    // Start is called before the first frame update

    public void Awake()
    {
        rb = GetComponent<Rigidbody>();
    }




    void Start()
    {
        MovementAxisName = "Horizontal"+PlayerNumber ;
        TurnAxisName = "Vertical"+PlayerNumber ;

        OriginalPitch = MovementAudio.pitch;


    }

    // Update is called once per frame
    void Update()//所有信息都可以获取
    {
        MovementInputValue = Input.GetAxis(TurnAxisName);
        TurnInputValue = Input.GetAxis(MovementAxisName);
        Debug.Log(MovementInputValue);

        //调用播放声音方法
        if (Mathf.Abs(MovementInputValue) < 0.1f &&Mathf.Abs(TurnInputValue) < 0.1)
            {
            if (MovementAudio .clip ==EngineDriving )
            {
                MovementAudio.clip = EngineIdling;
                MovementAudio.pitch =Random .Range(OriginalPitch - PichRage , OriginalPitch + PichRage );
                MovementAudio.Play();
            }


        }
        else
        {
            if (MovementAudio.clip == EngineIdling )
            {
                MovementAudio.clip = EngineDriving ;
                MovementAudio.pitch = Random.Range(OriginalPitch - PichRage, OriginalPitch  + PichRage);
                MovementAudio.Play();
            }
        }

        
        

        
    }

    private void FixedUpdate()//fixedupdate是固定帧率,间隔一段时间才会出发一次
    {

        Move();
        Turn();
    }

        void Move()
        {
            


            Vector3 movementV3 = transform.forward * MovementInputValue * Speed * Time.deltaTime;

            rb.MovePosition(rb.position + movementV3);
        }
        void Turn()
        {

            
            float turn = TurnInputValue * TurnSpeed * Time.deltaTime;

            Quaternion quaternion = Quaternion.Euler(0, turn, 0);

            rb.MoveRotation(rb.rotation * quaternion);//位置用加减法,旋转用惩罚,后面乘的是四元数
        }
    
}

  • 9
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值