[Unity官方教程]Tanks!单机双人坦克大战源码和素材
生活随笔
收集整理的這篇文章主要介紹了
[Unity官方教程]Tanks!单机双人坦克大战源码和素材
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
這是Unity官方案例Tanks的素材,讀者可以自行取閱。
鏈接:https://pan.baidu.com/s/1PSAZeT5zQOQJXNxzP9qs1A
提取碼:a57u
文章目錄
- 前言
- 腳本
- GameManager類
- TankManager類
- TankMovement類
- CameraControl類
- TankShooting類
- TankHealth類
- ShellExplosion類
- UIDirectionController類
前言
本篇文章是我在觀看了官方教程后寫的腳本,相較于官方的更為詳細,方便你們拿來直接查看引用。而且是適用于新版本的Unity。我這一版本的Unity是Version 2019.2.9f1 Persional。
建議多閱讀觀看官網的文檔和教程,一方面他們的代碼更加的規范,另一方面游戲開發的思想要更加的好。一個很明顯的區別在于,官方的代碼,每個模塊之間相互獨立,各司其職缺一不可。但又不會在一個腳本里塞入過多的內容顯得冗雜多余,整體上層次分明,架構清楚。各個類之間高內聚,低耦合。比國內所謂的一些學院教的要好得多,解釋的也更加的清楚。
官方中英機翻視頻教程點這里。個人建議不要開倍速一點點看完,適應一下老師的語速,而不要知其然不知其所以然。那么,廢話不多說,直接上代碼。英語水平有限,注釋能看懂就行,還望見諒。
腳本
GameManager類
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; using UnityEngine.SceneManagement;public class GameManager : MonoBehaviour {// Propertiespublic int m_NumRoundsToWin = 5; // number of rounds per startpublic float m_StartDelay = 3f; // time delay ecah startpublic float m_EndDelay = 3f; // time delay each endprivate int m_RoundNumber; // round in which you are// Referencepublic CameraContorller m_CameraContorl;public Text m_MessageText;public GameObject m_TankPrefab;public TankManager[] m_Tanks;private WaitForSeconds m_StartWait;private WaitForSeconds m_EndWait;private TankManager m_RoundWinner;private TankManager m_GameWinner;private void Start(){m_StartWait = new WaitForSeconds(m_StartDelay);m_EndWait = new WaitForSeconds(m_EndDelay);SpawnAllTanks();SetCameraTargets();StartCoroutine(GameLoop());}private void SpawnAllTanks(){// Spawn all the tanks for player, set their numbers from 1 to 2, and then call the function SetUp() to draw color for tanks.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);m_Tanks[i].m_PlayerNumber = i + 1;m_Tanks[i].SetUp();}}private void SetCameraTargets(){// Assign all the tank's transform to the GameraControl script.Transform[] targets = new Transform[m_Tanks.Length];for (int i = 0; i < targets.Length; ++i){targets[i] = m_Tanks[i].m_Instance.transform;}m_CameraContorl.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(){// When the game firstly starts or back to the scene(0) again, we should reset all tanks.// And then, we disable all tanks' control so that the player couldn't contorl them.// At the same time, we set the camera to the right position and size that calculated by the function in the CameraControl script.// Finally, we show the text "Round" and we wait for a while so that it won't flash by.ResetAllTanks();DisableTankControl();m_CameraContorl.SetStartPositionAndSize();m_RoundNumber++; // plus 1 to the round countm_MessageText.text = "ROUND " + m_RoundNumber;yield return m_StartWait;}private IEnumerator RoundPlaying(){// Ok now, we need to give the palyers control of their tank, or they will be angry.// So, we enable all tanks' control, and change the text to null.// You don't expect a big line blocking the palyers' view.// When two players fight out a winner, we can return the funcion.// Of course, their is nothing we could return.EnableTankControl();m_MessageText.text = string.Empty;while (!OneTankLeft()){yield return null;}}private IEnumerator RoundEnding(){// A winner came out. Winner whould like to run around for celebrating, but we don't think is a wise move.// So we disable tanks' control again, and we set winner for this round to null.// Because it still stores the reference of the winner of the last round.// And then, we need to find the reference of the winner of this round and we judge wether he is the game winner.// Again we show some "text" and wait for a while, then we get into the next round or start game again.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(){// Determine if any tanks are dead.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(){// Go through all the tanks, if find a TankManager who's Instance is active, then return it, or else return null.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(){// Go through all the tanks, if find a TankManager who's m_Wins is equal to 5, then return it, or else return null.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 = "DARW!";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 + " WIN THE GAME!";return message;}private void ResetAllTanks(){for (int i = 0; i < m_Tanks.Length; ++i){m_Tanks[i].Reset();}}private void DisableTankControl(){for (int i = 0; i < m_Tanks.Length; ++i){m_Tanks[i].DisableTankControl();}}private void EnableTankControl(){for (int i = 0; i < m_Tanks.Length; ++i){m_Tanks[i].EnableTankControl();}} }TankManager類
using System; using System.Collections; using System.Collections.Generic; using UnityEngine;[Serializable] public class TankManager {// Properties[HideInInspector] public int m_PlayerNumber;[HideInInspector] public int m_Wins;[HideInInspector] public string m_ColoredPlayerText;// Referencepublic Color m_PlayerColor;public Transform m_SpawnPoint;private TankMovement m_Movement;private TankShooting m_Shooting;private GameObject m_CanvasGameObject;[HideInInspector] public GameObject m_Instance;public void SetUp(){m_Movement = m_Instance.GetComponent<TankMovement>();m_Shooting = m_Instance.GetComponent<TankShooting>();m_CanvasGameObject = m_Instance.GetComponentInChildren<Canvas>().gameObject;m_Movement.m_PlayerNumber = m_PlayerNumber;m_Shooting.m_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 DisableTankControl(){// Turn off the script and turn off the canvas.m_Movement.enabled = false;m_Shooting.enabled = false;m_CanvasGameObject.SetActive(false);}public void EnableTankControl(){// Turn on the script and turn on the canvas.m_Movement.enabled = true;m_Shooting.enabled = true;m_CanvasGameObject.SetActive(true);}public void Reset(){// Set the Instance back to it's spawn point.m_Instance.transform.position = m_SpawnPoint.position;m_Instance.transform.rotation = m_SpawnPoint.rotation;// all of the tank's apart from the winner will be off, and we need to reset all of them// so we need to turn everything off first befor we can turn it back on againm_Instance.SetActive(false);m_Instance.SetActive(true);} }TankMovement類
using System.Collections; using System.Collections.Generic; using UnityEngine;public class TankMovement : MonoBehaviour {// Propertiespublic int m_PlayerNumber = 1;public float m_MoveSpeed = 12f;public float m_TurnSpeed = 180f;public float m_PitchRange = 0.2f;private float m_MovementInputValue;private float m_TurnInputValue;private float m_OriginalPitch;private string m_MovementAxisName;private string m_TurnAxisName;// Referencepublic AudioSource m_MovementAudio;public AudioClip m_EngineIdling;public AudioClip m_EngineDriving;private Rigidbody m_RigidBody;private void Awake(){m_RigidBody = GetComponent<Rigidbody>();}private void OnEnable(){// This function is called when this script is turned on.m_RigidBody.isKinematic = false;// Initialize inputvaluem_MovementInputValue = 0f;m_TurnInputValue = 0f;}private void OnDisable(){m_RigidBody.isKinematic = true;}private void Start(){// Get diffrent axisname input name for diffrent player.m_MovementAxisName = "Vertical" + m_PlayerNumber;m_TurnAxisName = "Horizontal" + m_PlayerNumber;// For now the originalPith's value equal to 1m_OriginalPitch = m_MovementAudio.pitch;}private void Update(){// Store the Player's input and make sure the audio for the engine is playing.m_MovementInputValue = Input.GetAxis(m_MovementAxisName);m_TurnInputValue = Input.GetAxis(m_TurnAxisName);EngineAudio();}private void EngineAudio(){// Play the correct audio clip based on whether or not the tank is moving and what audio is currently playing.if (Mathf.Abs(m_MovementInputValue) < 0.1f && Mathf.Abs(m_TurnInputValue) < 0.1f){if (m_MovementAudio.clip == m_EngineDriving){m_MovementAudio.clip = m_EngineIdling; // change the clipfloat lowPitch = m_OriginalPitch - m_PitchRange;float highPitch = m_OriginalPitch + m_PitchRange;m_MovementAudio.pitch = Random.Range(lowPitch, highPitch); // set the pitch between 0.8 and 1.2m_MovementAudio.Play();}}else{if (m_MovementAudio.clip == m_EngineIdling){m_MovementAudio.clip = m_EngineDriving;float lowPitch = m_OriginalPitch - m_PitchRange;float highPitch = m_OriginalPitch + m_PitchRange;m_MovementAudio.pitch = Random.Range(lowPitch, highPitch); // set the pitch between 0.8 and 1.2m_MovementAudio.Play();}}}private void FixedUpdate(){// MOve ang turn the tank.Move();Turn();}private void Move(){// Adjust the position of the tank based on the player's input.Vector3 movement = transform.forward * m_MovementInputValue * m_MoveSpeed * Time.deltaTime;m_RigidBody.MovePosition(m_RigidBody.position + movement);}private void Turn(){// Adjust the position of the tank based on the player's input.float turn = m_TurnInputValue * m_TurnSpeed * Time.deltaTime;Quaternion turnRotation = Quaternion.Euler(0f, turn, 0f);m_RigidBody.MoveRotation(m_RigidBody.rotation * turnRotation);} }CameraControl類
using System.Collections; using System.Collections.Generic; using UnityEngine;public class CameraContorller : MonoBehaviour {// Propertiespublic float m_DampTime = 0.2f; // time that the camera reach to the target will takepublic float m_ScreenEdgeBuff = 4f;public float m_MinSize = 6.5f;private float m_ZoomSpeed;// Reference[HideInInspector] public Transform[] m_Targets;private Camera m_Camera;private Vector3 m_MoveVelocity;private Vector3 m_DesiredPosition; // the position we want camera reach toprivate void Awake(){m_Camera = GetComponentInChildren<Camera>();}private void FixedUpdate(){Move();Zoom();}private void Move(){// Calculate the desiredposition.FindAveragePosition();// smooth the CamearRig's follow motiontransform.position = Vector3.SmoothDamp(transform.position, m_DesiredPosition, ref m_MoveVelocity, m_DampTime);}private void FindAveragePosition(){// Get the average position among all the tanks and the let the CameraRig reach to the position.Vector3 averagePos = new Vector3();int numTargets = 0;// check if the target' gameObject is active, wo don't need to zoom in on a deactivated tankfor (int i = 0; i < m_Targets.Length; ++i){if (!m_Targets[i].gameObject.activeSelf)continue;averagePos += m_Targets[i].position; // plus all tanks' position and count the total of active tank++numTargets;}if (numTargets > 0)averagePos /= numTargets;averagePos.y = transform.position.y; // Keep the CameraRig's height and Camera's height at a levelm_DesiredPosition = averagePos;}private void Zoom(){// Get the size and make Camera size reach to it smoothly.float requiredSize = FindRequiredSize();m_Camera.orthographicSize = Mathf.SmoothDamp(m_Camera.orthographicSize, requiredSize, ref m_ZoomSpeed, m_DampTime);}private float FindRequiredSize(){// Calculate the size.Vector3 desiredLocalPos = transform.InverseTransformPoint(m_DesiredPosition);float size = 0f;/* Go through all the tanks, find out all the size it could be.Pick the largest one, then the tanks are all definitely going to be on the screen. */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;/* For the Camera view, when tank is at Y axix, the Camera size is equal to absolute Y value of the Vector of the position of the tank, and itequalto X value / Camera.aspect while tank is at X axis.Finally we could find the furthest value. */size = Mathf.Max(size, Mathf.Abs(desiredPosToTarget.y));size = Mathf.Max(size, Mathf.Abs(desiredPosToTarget.x) / m_Camera.aspect);}size += m_ScreenEdgeBuff;size = Mathf.Max(size, m_MinSize); // set minimum size so that it won't zoom in so muchreturn size;}public void SetStartPositionAndSize(){// Set the scene to the right size and right position at every round.FindAveragePosition();transform.position = m_DesiredPosition;m_Camera.orthographicSize = FindRequiredSize();} }TankShooting類
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI;public class TankShooting : MonoBehaviour {// Propertiespublic int m_PlayerNumber = 1;public float m_MinLaunchForce = 15f;public float m_MaxLaunchForce = 30f;public float m_MaxChargeTime = 0.75f;private float m_CurrentLaunchForce;private float m_ChargeSpeed;private bool m_Fired; // record wether the player shoot the shell or notprivate string m_FireButtom;// Referencepublic Rigidbody m_Shell;public Transform m_FireTransform;public Slider m_AimSlider;public AudioSource m_ShootingAudio;public AudioClip m_ChargingClip;public AudioClip m_FireClip;private void OnEnable(){m_CurrentLaunchForce = m_MinLaunchForce;m_AimSlider.value = m_MinLaunchForce;}private void Start(){m_FireButtom = "Fire" + m_PlayerNumber;m_ChargeSpeed = (m_MaxLaunchForce - m_MinLaunchForce) / m_MaxChargeTime;}private void Update(){// Track the current state of the fire button and make decision based on the current launch force.m_AimSlider.value = m_MinLaunchForce;if (m_CurrentLaunchForce >= m_MaxLaunchForce && !m_Fired){// at max charge, not yet firedm_CurrentLaunchForce = m_MaxLaunchForce;Fire();}else if (Input.GetButtonDown(m_FireButtom)){// once we pressed fire buttom for the first timem_Fired = false;m_CurrentLaunchForce = m_MinLaunchForce;m_ShootingAudio.clip = m_ChargingClip;m_ShootingAudio.Play();}else if (Input.GetButton(m_FireButtom) && !m_Fired){// Holding the fire button, not yet firedm_CurrentLaunchForce += m_ChargeSpeed * Time.deltaTime;m_AimSlider.value = m_CurrentLaunchForce;}else if (Input.GetButtonUp(m_FireButtom) && !m_Fired){// we relased the button, having not fired yetFire();}}private void Fire(){// Instantiate and launch the shell.m_Fired = true;Rigidbody shellInstance = Instantiate(m_Shell, m_FireTransform.position, m_FireTransform.rotation) as Rigidbody;shellInstance.velocity = m_CurrentLaunchForce * m_FireTransform.forward;m_ShootingAudio.clip = m_FireClip;m_ShootingAudio.Play();m_CurrentLaunchForce = m_MinLaunchForce; // safety catch} }TankHealth類
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI;public class TankHealth : MonoBehaviour {// Propertiespublic float m_StartingHealth = 100f;private float m_CurrentHealth;private bool m_Dead;// Referencepublic Slider m_Slider;public Image m_FillImage;public Color m_FullHealthColor = Color.green;public Color m_ZeroHealthColor = Color.red;public GameObject m_ExplosionPrefab;private AudioSource m_ExplosionAudio;private ParticleSystem m_ExplosionParticles;private void Awake(){// We can get the reference of ParticleSystem by creating an ExplosionPrefab instance.m_ExplosionParticles = Instantiate(m_ExplosionPrefab).GetComponent<ParticleSystem>();m_ExplosionAudio = m_ExplosionParticles.GetComponent<AudioSource>();/* To avoid that always keep the particle system int the memory, keep them in the scene,we put it straight in to the game as soon as it starts, we swithc it off straight awayand we assign our references for when we need them. */m_ExplosionParticles.gameObject.SetActive(false);}private void OnEnable(){m_CurrentHealth = m_StartingHealth;m_Dead = false;SetHealthUI();}public void TankDamage(float amount){// Adjust the tank's current healt, update the UI based on the new health and check whether or not the tank is dead.m_CurrentHealth -= amount;SetHealthUI();if (m_CurrentHealth <= 0f && !m_Dead){OnDeath();}}private void SetHealthUI(){// Adjust the value and colour of the slider, to make sure it looks appropriate.m_Slider.value = m_CurrentHealth;m_FillImage.color = Color.Lerp(m_ZeroHealthColor, m_FullHealthColor, m_CurrentHealth / m_StartingHealth);}private void OnDeath(){// Play the effect for the death of the tank and deactive it.m_Dead = true;// move the particle system to the place where the playe died and turn on itm_ExplosionParticles.transform.position = transform.position;m_ExplosionParticles.gameObject.SetActive(true);m_ExplosionParticles.Play();m_ExplosionAudio.Play();gameObject.SetActive(false);} }ShellExplosion類
using System.Collections; using System.Collections.Generic; using UnityEngine;public class ShellExplosion : MonoBehaviour {//Propertiespublic float m_MaxDamage = 100f;public float m_ExplosionForce = 1000f;public float m_MaxLifeTime = 2f;public float m_ExplosionRadius = 5f;// Referencepublic LayerMask m_TankMask;public ParticleSystem m_ExplosionParticles;public AudioSource m_ExplosionAudio;private void Start(){Destroy(gameObject, m_MaxLifeTime);}private void OnTriggerEnter(Collider other){// Find all the tanks in an area around the shell and damage them.Collider[] colliders = Physics.OverlapSphere(transform.position, m_ExplosionRadius, m_TankMask);for (int i = 0; i < colliders.Length; i++){Rigidbody targetRigidbody = colliders[i].GetComponent<Rigidbody>();if (!targetRigidbody)continue;targetRigidbody.AddExplosionForce(m_ExplosionForce, transform.position, m_ExplosionRadius);// addressing a particular instance of TankHealth script so we can talk to it and reduce health with itTankHealth targetHealth = targetRigidbody.GetComponent<TankHealth>();if (!targetHealth)continue;float damage = CalculateDamage(targetRigidbody.position);targetHealth.TankDamage(damage);}/* When the shell is destoryed, its child is destoryed either, either is ParticleSystem;So we need to unparent them, so that they could still play even if the shell is destoyed.After that, they will explode and ducouple themsevles. */m_ExplosionParticles.transform.parent = null;m_ExplosionParticles.Play();m_ExplosionAudio.Play();Destroy(m_ExplosionParticles.gameObject, m_ExplosionParticles.main.duration);Destroy(gameObject); // remove the shell as well}private float CalculateDamage(Vector3 targetPosition){// Calculate the amout of damage a target should takes based on it's position.Vector3 explosionToTarget = targetPosition - transform.position;float explosionDistance = explosionToTarget.magnitude;float relativeDistance = (m_ExplosionRadius - explosionDistance) / m_ExplosionRadius;float damage = relativeDistance * m_MaxDamage;/* The damage starting off at 1 in the center going down to 0 at the edge and then they'll benegative beyond the edge. We don't want that, so we choose it from 0 and negative damage. */damage = Mathf.Max(0f, damage);return damage;} }UIDirectionController類
using System.Collections; using System.Collections.Generic; using UnityEngine;public class UIDirectionContorller : MonoBehaviour {// Propertiespublic bool m_UseRelativeRotation = true;// Referenceprivate Quaternion m_RelativeRotation;private void Start(){m_RelativeRotation = transform.parent.localRotation;}private void Update(){if (!m_UseRelativeRotation)transform.rotation = m_RelativeRotation;} }一定要自己寫一遍哦~~~
總結
以上是生活随笔為你收集整理的[Unity官方教程]Tanks!单机双人坦克大战源码和素材的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 变速积分pid控制器matlab,变速积
- 下一篇: 和合符放枕头下的作用_深度解析:记忆棉枕