一、 概述
1.1 項(xiàng)目類型
3D多人坦克大戰(zhàn)
1.2 開發(fā)工具
·Unity2019.4.29??
·Visual Studio 2019
二、 設(shè)計(jì)題目實(shí)現(xiàn)
2.1 概要設(shè)計(jì)
雙人坦克游戲:
- 坦克:可移動(dòng)旋轉(zhuǎn),發(fā)射炮彈
- 子彈:按一定方向一定速度發(fā)射;炮彈周圍會(huì)產(chǎn)生沖擊波,擊中坦克或接觸地面后爆炸
- 坦克生命:坦克被擊中后血條相應(yīng)變化;血條不隨坦克的旋轉(zhuǎn)而旋轉(zhuǎn);血條減到小于等于0后爆炸
- 相機(jī):攝像機(jī)的大小及位置實(shí)時(shí)變化
- 音效:游戲背景音;坦克開動(dòng)時(shí)的引擎聲音;坦克靜止時(shí)發(fā)動(dòng)機(jī)待機(jī)的聲音;炮彈爆炸時(shí)的的聲音;炮彈蓄力的聲音;炮彈發(fā)射時(shí)的聲音;坦克爆炸的聲音
玩家編號(hào) |
移動(dòng)旋轉(zhuǎn)按鍵 |
炮彈發(fā)射按鍵 |
1 |
W??S??A??D |
space |
2 |
↑??↓??←?→ |
enter |
一方死亡,則一輪游戲結(jié)束
2.2 詳細(xì)設(shè)計(jì)與實(shí)現(xiàn)?
2.2.1 項(xiàng)目場(chǎng)景設(shè)置
2.2.1.1項(xiàng)目工程設(shè)置
下載坦克大戰(zhàn)資源包,并導(dǎo)入到Unity中.
在右上角選擇Layout-->2 by 3.
Project面板右鍵選擇OneColumn Layout.
在Game窗口選擇分辨率16 :9.
2.2.2.2場(chǎng)景設(shè)置
創(chuàng)建Scence場(chǎng)景:Project面板選中Assets右鍵Create/Scene,命名MainScene,雙擊激活并保存。
將Hierarchy面板中的Directional Light刪除
將Project/Prefabs/LevelArt拖如Hierarchy面板中
打開Window/Rendering/Lighting Set 面板打開??吭趇nspector面板旁邊。
2.2.2.3光線設(shè)置
去掉勾選Lighting/Debug Settings/Auto Generate
設(shè)置 Indirect Resolution 為 0.5 (合適的烘焙渲染幀率值)
修改Environment Lighting/Source 為Color
設(shè)置 Ambient Color( 72,?62,?113 )
點(diǎn)擊Generate Lighting開始烘焙場(chǎng)景
2.2.2.4?鏡頭設(shè)置
選中Main Camera在Inspector面板中。
修改Main Camera的Transform組件Position屬性為( -43,?42,?-25 )
修改Rotation( 40,?60,?0 )
攝像機(jī)組件修改Projection屬性為OrthograPhic
正交0rthographic(無消失點(diǎn)投影):無法判斷距離
透視Perspective(有消失點(diǎn)投影):透視視圖和我們從眼睛看到的視圖是一樣的
修改Clear Flags 屬性為 Solid Color
修改Background顏色為 ( 80,?60,?50 )
保存場(chǎng)景。
2.2.2 坦克的移動(dòng)旋轉(zhuǎn)
2.2.2.1坦克創(chuàng)建
1. 將坦克模型Project/Models/Tank拖拽到Hierarchy面板中。
2. 制作坦克預(yù)制體
在_Completed-Assets文件夾下新建文件夾Prefabs,將坦克模型拖拽到該文件夾下。
- 在Hoierarchy中選中Tank
- 添加Rigidbody剛體組件(實(shí)現(xiàn)物理碰撞的關(guān)鍵組件)
給Tank添加Rigidbody 組件通過Rigidbody/Constraints 約束坦克的移動(dòng)Y軸和旋轉(zhuǎn)X,Z軸
- 添加Box Collider碰撞盒子(實(shí)現(xiàn)物理碰撞的關(guān)鍵組件)
給Tank添加Box Collider組件,修改Center( 0, 0.85, 0 ),Size( 1.5, 1.7, 1.6 ),使其完美地包裹住坦克
- 添加坦克運(yùn)動(dòng)時(shí)車輪后的煙霧效果
將Project面板中Prefabs/DushTrail拖拽到HierarcHy面板中Tank下,作為tank的子物體
復(fù)制一個(gè)DushTrail,分別重命名:LeftDustTrail,RightDustTrail,分別設(shè)置Position做標(biāo)
(-0.5,0,-0.75),(0.5,0,-0.75)。
選中Hierarchy面板里的Tank,在Inspector面板, 設(shè)置標(biāo)簽Tag
3. 點(diǎn)擊Aplay all,應(yīng)用到預(yù)制體中。刪除Hierarchy面板里的Tank,將_Completed-Assets/Prefabs下的預(yù)制體拖入Hierarchy面板中。
2.2.2.3添加腳本
1. 在_Completed-Assets文件夾下新建文件夾Scripts,在Scripts下新建Tank文件夾,以后與坦克有關(guān)的代碼均放置在該文件夾下。在該文件夾下新建C# Scripts,命名為Tank Movement。將該腳本拖拽到Tank上。
2. 在Project Setting-->input Manager中設(shè)置按鈕映射
即:
玩家編號(hào) |
移動(dòng)旋轉(zhuǎn)按鍵 |
炮彈發(fā)射按鍵 |
1 |
W??S??A??D |
space |
2 |
?↑??↓??←?→ |
enter |
3. 打開腳本,完成腳本編寫
2.2.2.4腳本編寫
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class TankMovement : MonoBehaviour
{
private Rigidbody m_Rigidbody;
private float turnValue;//獲取坦克左右旋轉(zhuǎn)的輸入值
private float moveValue;//獲取坦克前后移動(dòng)的輸入值
[HideInInspector]
public int m_PlayerNumber = 1;//設(shè)置玩家編號(hào) 不同玩家用不同的按鈕控制坦克實(shí)現(xiàn)移動(dòng)
public float m_MoveSpeed = 0.5f;//設(shè)置坦克的前后移動(dòng)速度
public float m_TurnSpeed = 3.6f;//設(shè)置坦克的左右旋轉(zhuǎn)速度
private float m_MovementInputValue;//獲取坦克前后移動(dòng)的輸入值
private float m_TurnInputValue;//獲取坦克左右旋轉(zhuǎn)的輸入值
private string m_MovementAxisName;//前后移動(dòng)軸名稱
private string m_TurnAxisName;//左右旋轉(zhuǎn)軸名稱
/*----------------------------音效---------------------------------*/
public AudioSource m_MovementAudio;
public AudioClip m_EngineIdling;
public AudioClip m_EngineDriving;
public float m_PitchRange = 0.2f; //引擎頻率區(qū)間
private float m_OriginalPitch;//引擎頻率
/*---------------------------粒子特效--------------------------------*/
private ParticleSystem[] m_ParticleSystems;
private void Awake()
{
m_Rigidbody = this.gameObject.GetComponent<Rigidbody>();//獲取Rigidbody組件
m_ParticleSystems = GetComponentsInChildren<ParticleSystem>();//獲取粒子特效
}
void Start()
{
m_MovementAxisName = "Vertical" + m_PlayerNumber;
m_TurnAxisName = "Horizontal" + m_PlayerNumber;
//初始化引擎頻率
m_OriginalPitch = m_MovementAudio.pitch;
}
void Update()
{
//更新輸入軸信息
m_MovementInputValue = Input.GetAxis(m_MovementAxisName);
m_TurnInputValue = Input.GetAxis(m_TurnAxisName);
Sound();
}
private void FixedUpdate()
{
//移動(dòng)
Vector3 mowement = transform.forward * m_MovementInputValue * m_MoveSpeed;
m_Rigidbody.MovePosition(m_Rigidbody.position + mowement);
//旋轉(zhuǎn)
Quaternion turnRotate = Quaternion.Euler(0, m_TurnInputValue * m_TurnSpeed, 0);
m_Rigidbody.MoveRotation(m_Rigidbody.rotation * turnRotate);
}
private void OnEnable()//激活坦克的時(shí)候調(diào)用
//OnEnable():初始化移動(dòng)旋轉(zhuǎn)輸入?yún)?shù);關(guān)閉是否被物理引擎驅(qū)動(dòng)開關(guān):Is Kinematic=false;打開粒子特效
{
m_MovementInputValue = 0;
m_TurnInputValue = 0;
m_Rigidbody.isKinematic = false;//不禁用物理引擎效果
//打開粒子特效
for (int i = 0; i < m_ParticleSystems.Length; i++)
{
m_ParticleSystems[i].Play();
}
}
private void OnDisable()
//OnDisable();關(guān)閉是否被物理引擎驅(qū)動(dòng)開關(guān):Is Kinematic=true;關(guān)閉粒子特效
{
m_Rigidbody.isKinematic = true;
for (int i = 0; i < m_ParticleSystems.Length; i++)
{
m_ParticleSystems[i].Stop();
}
}
private void Sound()
{
/*------------------------切換待機(jī)音效和移動(dòng)音效------------------------------*/
if (Mathf.Abs(m_MovementInputValue) < 0.1f && Mathf.Abs(m_TurnInputValue) < 0.1f)
{
//停了
if (m_MovementAudio.clip == m_EngineDriving)
{
m_MovementAudio.clip = m_EngineIdling;
m_MovementAudio.pitch = Random.Range(m_OriginalPitch - m_PitchRange, m_OriginalPitch + m_PitchRange);
m_MovementAudio.Play();
}
}
else
{
//在移動(dòng)中
if (m_MovementAudio.clip == m_EngineIdling)
{
m_MovementAudio.clip = m_EngineDriving;
m_MovementAudio.pitch = Random.Range(m_OriginalPitch - m_PitchRange, m_OriginalPitch + m_PitchRange);
m_MovementAudio.Play();
}
}
}
}
2.2.3 炮彈射擊
2.2.3.1炮彈創(chuàng)建
1. 場(chǎng)景添加子彈。將Models文件夾中的Shell模型拖拽到Hierarchy面板中。
2. 制作炮彈預(yù)制體
添加Rigidbody剛體
添加Light Component組件,設(shè)置參數(shù):Position(-0.003,0.4,0.2)
添加Capsule Collider組件,勾選上Is Trigger觸發(fā)器選項(xiàng)。設(shè)置參數(shù):
Center(0,0,0.2) ,Radius為0.15 ,Height為0.55,Direction:Z-Axis
添加炮彈爆炸粒子特效.
給ShellExplosion添加AudioSource組件,設(shè)置AudioClip音效資源選擇
ShellExplosion.mp3, 取消勾選Play On Awake。
3. 添加發(fā)射點(diǎn)。選中Hierarchy面板里的Tank,創(chuàng)建一個(gè)GameObject,重命名為FireTransform,設(shè)置Position為(0,1.7,1.35),Rotation為(350,0,0) 。
4. 添加UI發(fā)射條。作為坦克蓄力的可視化顯示。
選中Hierarchy面板里的Tank,創(chuàng)建Canvas,選中Canvas對(duì)象上在Canvas組件上修改屬性Render Mode為WorldSpace。
新建Slider,命名為AimSlider
將Canavs和Slider都Reset一下,將Canavs的Rotation設(shè)置為(90,0,0),修改Width,Height為3.5。打開AimSlider所有子物體,刪HandleSlideArea和Background。選中AimSlider設(shè)置Position(1,-9,-1?)
2.2.3.2添加腳本
創(chuàng)建Tank Shotting腳本,將腳本拖到CompletedTank中。
創(chuàng)建ShellExplosion腳本,將腳本拖到CompletedShell中
2.2.3.3腳本編寫
·Tank Shotting
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class TankShooting : MonoBehaviour
{
[HideInInspector]
public int m_PlayerNumber = 1; //坦克編號(hào)
public Rigidbody m_Shell; //炮彈剛體對(duì)象
public float m_MaxLaunchForce = 35f; //最大力度
public float m_MinLaunchForce = 15f; //最小力度
public float m_MaxChargeTime = 0.75f; //換彈時(shí)間
public Slider m_AimSlider; //Slider組件
private Transform m_FireTransform; 發(fā)射點(diǎn)的Transform組件
private string m_FireButton; //發(fā)射按鈕名稱
private float m_ChargeSpeed; //發(fā)射速度
private float m_CurrentLaunchForce; //發(fā)射力度
private bool m_Fired; //發(fā)射狀態(tài)
public AudioSource m_ShootingAudio; //發(fā)射音效組件
private AudioClip m_FireClip; //發(fā)射音效
public AudioClip m_ChargingClip; //發(fā)射準(zhǔn)備音效
void Start()
{
m_FireTransform = transform.Find("FireTransform");//初始化角色發(fā)射按鈕輸入軸名稱
m_FireButton = "Fire" + m_PlayerNumber;
m_ChargeSpeed = (m_MaxLaunchForce - m_MinLaunchForce) / m_MaxChargeTime;//初始化發(fā)射蓄力箭頭速度為最小
m_FireClip = ResourcesManager.Instance.Load<AudioClip>("AudioClips/ShotFiring");
}
private void OnEnable()//初始化發(fā)射力度。
{
m_CurrentLaunchForce = m_MinLaunchForce;
m_AimSlider.value = m_MinLaunchForce;
}
void Update()//1):當(dāng)發(fā)射力度達(dá)到最大值的時(shí)候:如果沒有處于發(fā)射中, 就直接發(fā)射;
//2):否則如果是剛剛按下:初始化發(fā)射狀態(tài)為false,發(fā)射力度為最小,發(fā)射音效為發(fā)射準(zhǔn)備音效
//3):持續(xù)按下中:蓄力過程中需要累加發(fā)射力度,同時(shí)更新UI。
//4):抬起發(fā)射按鈕:則直接發(fā)射。
{
if (m_CurrentLaunchForce >= m_MaxLaunchForce && !m_Fired)
{
m_CurrentLaunchForce = m_MaxLaunchForce;
Fire();
}
else if (Input.GetButtonDown(m_FireButton))
{
//剛剛按下
m_Fired = false;
m_CurrentLaunchForce = m_MinLaunchForce;
//音效
m_ShootingAudio.clip = m_ChargingClip;
m_ShootingAudio.Play();
}
else if (Input.GetButton(m_FireButton) && !m_Fired)
{
m_CurrentLaunchForce += m_ChargeSpeed * Time.deltaTime; //Time.deltaTime:上一幀執(zhí)行的時(shí)長(zhǎng)
m_AimSlider.value = m_CurrentLaunchForce;
}
else if (Input.GetButtonUp(m_FireButton) && !m_Fired)
{
Fire();
}
}
private void Fire()
{
m_Fired = true;
Rigidbody shellInstiate = Instantiate(m_Shell,
m_FireTransform.position,
m_FireTransform.rotation);//通過剛體組件給炮彈施加一個(gè)初速度
shellInstiate.velocity = m_CurrentLaunchForce * m_FireTransform.forward;
/*---------設(shè)置音效并且播放音效;初始化發(fā)射力度最小---------*/
//切換音效
m_ShootingAudio.clip = m_FireClip;
m_ShootingAudio.Play();
/*-------------初始化一下參數(shù)------------*/
m_CurrentLaunchForce = m_MinLaunchForce;
m_AimSlider.value = m_MinLaunchForce;
}
}
·ShellExplosion
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ShellExplosion : MonoBehaviour
{
[SerializeField]
private GameObject shellExplosion;
public float m_MaxLifeTime = 1.5f;//設(shè)置最長(zhǎng)飛行時(shí)間
public float m_ExplosionRadius = 5f;//沖擊波半徑
public LayerMask m_TankMask;//分層,以區(qū)別炮彈打到哪個(gè)上了
public float m_ExplosionForce = 1000f; //爆炸推力大小
public ParticleSystem m_ExplosionParticle;//子彈爆炸特效
public float m_MaxDamage = 40f;
/*------音效-------*/
public AudioSource m_ExplosionAudio;//子彈爆炸音效
private void Start()
{
Destroy(gameObject, m_MaxLifeTime);//經(jīng)過一定時(shí)間,炮彈自動(dòng)銷毀
}
private void OnTriggerEnter(Collider other)//射線尋找目標(biāo);給目標(biāo)施加推力;給目標(biāo)施加傷害;
//將特效從炮彈上移除;
//播放特效,音效,銷毀特效,銷毀炮彈。
{
Collider[] colliders = Physics.OverlapSphere(transform.position, m_ExplosionRadius, m_TankMask);
for (int i = 0; i < colliders.Length; i++)
{
/*——--------------—坦克血條扣分—------------------——*/
//施加推力
Rigidbody targetRigidbody = colliders[i].GetComponent<Rigidbody>();
targetRigidbody.AddExplosionForce(m_ExplosionForce, transform.position, m_ExplosionRadius);
//施加傷害
TankHealth targetHealth = colliders[i].GetComponent<TankHealth>();
targetHealth.TankDamage(CaculateDamage(colliders[i].transform.position));
}
/*—————子彈爆炸特效——————*/
shellExplosion.transform.parent = null;
//m_ExplosionParticle.transform.parent = null;
m_ExplosionParticle = shellExplosion.GetComponent<ParticleSystem>();
m_ExplosionParticle.Play();//播放子彈爆炸特效
m_ExplosionAudio.Play();//播放子彈爆炸音效
//延時(shí)銷毀特效:特效不銷毀會(huì)一直存在。
Destroy(shellExplosion, m_ExplosionParticle.main.duration);
Destroy(gameObject);
}
//計(jì)算爆炸的方法
private float CaculateDamage(Vector3 targetPosition)//根據(jù)炮彈爆炸的位置來計(jì)算(位置離目標(biāo)越近傷害越大)。
{
float explosionDistance = (targetPosition - transform.position).magnitude;
float relativeDistance = (m_ExplosionRadius - explosionDistance) / m_ExplosionRadius;
float damage = relativeDistance * m_MaxDamage;
damage = Mathf.Max(0, damage);
return damage;
}
}
2.2.4 鏡頭調(diào)整
2.2.4.1相機(jī)設(shè)置
1.在Hierarchy面板中右鍵創(chuàng)建空物體,重命名為CameraRig,在Inspector面板中設(shè)置位置:
Position(0,0,0) 旋轉(zhuǎn)角度:Rotation(40,60,0)。
在Hierarchy面板中將Main Camera拖拽到CameraRig下作為其子物體,同時(shí)設(shè)置Main Camera位置 Position(0,0,-65) 旋轉(zhuǎn)角度:Rotation(0,0,0)
2.2.4.2添加腳本
1.在Project面板的Scripts/Camera/創(chuàng)建腳本CameraControl.cs.
2.將腳本添加到Hierachy面板里CameraRig上。
3.打開CameraControl.cs腳本編輯:
1)移動(dòng)相機(jī)CameraControl.Move()
2)縮放相機(jī)CameraControl.Zoom()
2.2.4.3腳本編寫
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CameraControl : MonoBehaviour
{
public Transform[] m_Targets;
public float m_DampTime = 0.2f; //相機(jī)移動(dòng)時(shí)間
public float m_MinSize = 6.5f;//視口最小值
public float m_ScreenEdyeBuffer = 4f;//緩沖區(qū)大小
private Vector3 m_DesiredPosition;//目標(biāo)位置
private Vector3 m_MoveVelocity;
private float m_ZoomSpeed;
private Camera m_Camera;
void Start()
{
m_Camera = GetComponentInChildren<Camera>();//獲取Camera組件。
}
private void FixedUpdate()//移動(dòng)相機(jī),改變相機(jī)視口大小
{
FindAveragePosition();//計(jì)算目標(biāo)點(diǎn)
//移動(dòng)相機(jī)(父物體)
transform.position = Vector3.SmoothDamp(transform.position, m_DesiredPosition, ref m_MoveVelocity, m_DampTime);
//改變相機(jī)視口大小
m_Camera.orthographicSize = Mathf.SmoothDamp(m_Camera.orthographicSize, FindReguireSize(), ref m_ZoomSpeed, m_DampTime);
}
private void FindAveragePosition()//計(jì)算相機(jī)當(dāng)前應(yīng)該到達(dá)的位置。
{
int numTargets = 0;//Player數(shù)量
//累加坐標(biāo)
Vector3 averagePos = new Vector3();
for (int i = 0; i < m_Targets.Length; i++)
{
//判斷Player是否顯示
if (!m_Targets[i].gameObject.activeSelf)
{
continue;
}
numTargets++;
averagePos = averagePos + m_Targets[i].position;
}
//求平均位置
if (numTargets > 0)
{
averagePos.y = transform.position.y;
m_DesiredPosition = averagePos / numTargets;
}
}
private float FindReguireSize()//計(jì)算視口大小
{
float size = 0;//初始化視口大小
Vector3 desiredLocalPos = transform.InverseTransformPoint(m_DesiredPosition);
for (int i = 0; i < m_Targets.Length; i++)
{
//判斷Player是否被禁用
if (!m_Targets[i].gameObject.activeSelf)
{
continue;
}
//將Player從世界坐標(biāo)轉(zhuǎn)到以CameraRig為中心的本地坐標(biāo)系
Vector3 targetLocalPos = transform.InverseTransformPoint(m_Targets[i].position);
//計(jì)算目標(biāo)點(diǎn)到Player的向量
Vector3 desirePosToTarget = targetLocalPos - desiredLocalPos;
size = Mathf.Max(size, desirePosToTarget.y);
size = Mathf.Max(size, desirePosToTarget.x / m_Camera.aspect);//aspect:寬/高
}
//給相機(jī)邊緣加一個(gè)緩沖區(qū)
size += m_ScreenEdyeBuffer;
//限制視口最小值
size = Mathf.Max(size, m_MinSize);
return size;
}
//重置位置的方法
public void SetStartPositionAndSize()//回合結(jié)束的時(shí)候初始化方法。
{
FindAveragePosition();
transform.position = m_DesiredPosition;
m_Camera.orthographicSize = FindReguireSize();
}
}
2.2.5 坦克生命值
2.2.5.1坦克血條制作
1. Scence面板左上角的Toggle Tool Handle Position設(shè)置成Pivot而不是Center。
2. 選中Hierarchy面板里的Tank,創(chuàng)建一個(gè)Slider,重命名為HealthSlider。
3. 打開HealthSlider所有子物體,刪HandleSlideArea。選中其他所有子對(duì)象,將其設(shè)置為平鋪。
4. 選中Background,將Source Image設(shè)置為圖片資源Health Wheel。選中Fill,將Image Type設(shè)置為Filed,實(shí)現(xiàn)360°填充效果,選中將Source Image設(shè)置為圖片資源Health Wheel,顏色設(shè)置為綠色。
2.2.5.2添加腳本
1. 在Scripts/Tank目錄下創(chuàng)建腳本TankHealth
2. 雙擊點(diǎn)開CompletedTank預(yù)制體,將腳本拖入
2.2.5.3腳本編寫
·TankHealth
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class TankHealth : MonoBehaviour
{
public Slider m_Slider;
private float m_CurrentHealth;//定義血量值
public int m_StartingHealth = 100;//血量初始值
//顏色
public Image m_FillImage;
public Color m_ZeroHealthColor;//血量為0顏色
public Color m_FullHealthColor;//滿血顏色
[SerializeField]
private GameObject tankExplosion;
//public GameObject m_Explosion; //死亡特效
//private ParticleSystem m_ExplosionEffect;
//private GameObject m_ExplosionInstance;//對(duì)象(在場(chǎng)景中顯示)
private AudioSource m_ExplosionAudio;//音效
private void Awake()//初始化:特效組件;特效音效組件;
{
//m_ExplosionInstance = Instantiate(m_Explosion);//實(shí)例化坦克爆炸特效
//m_ExplosionEffect = m_ExplosionInstance.GetComponent<ParticleSystem>();//獲取ParticleSystem
///*------音效------*/
//m_ExplosionAudio = m_ExplosionInstance.GetComponent<AudioSource>();
}
private void OnEnable()//初始化血量為100;更新血量UI值以及UI顏色。
{
m_CurrentHealth = m_StartingHealth;
SetHealthUI();
}
//坦克受傷 amount:傷害值
public void TankDamage(float amount)//血量減少;
//更新到UI上;
//判斷是否已經(jīng)死亡。
{
//血量減少
m_CurrentHealth -= amount;
//更新UI
SetHealthUI();
//小于等于0調(diào)用死亡方法
if (m_CurrentHealth <= 0)
{
OnDeath();
}
}
private void SetHealthUI()//更新UI:設(shè)置UI血量;
//修改顏色(根據(jù)血量多少設(shè)置)。
{
m_Slider.value = m_CurrentHealth;
//lerp:差值變化,從a變到b,后面是比例
m_FillImage.color = Color.Lerp(m_ZeroHealthColor, m_FullHealthColor, m_CurrentHealth / m_StartingHealth);
}
//死亡方法
private void OnDeath()//顯示死亡特效;
//播放死亡特效;
//播放死亡音效;
//隱藏關(guān)閉坦克。
{
//m_ExplosionInstance.transform.position = transform.position;//爆炸放到坦克的位置上去
//m_ExplosionEffect.Play();//播放坦克爆炸特效
//m_ExplosionAudio.Play();//播放音效
//Destroy(m_ExplosionInstance, m_ExplosionEffect.main.duration);//爆炸效果播放完后刪除爆炸實(shí)例
GameObject tankExplosionInstance = Instantiate(tankExplosion); //實(shí)例化坦克爆炸特效
tankExplosionInstance.transform.position = transform.position;//爆炸放到坦克的位置上去
ParticleSystem tankExplosionEffect = tankExplosionInstance.GetComponent<ParticleSystem>();//獲取ParticleSystem
tankExplosionEffect.Play();//播放坦克爆炸特效
Destroy(tankExplosionInstance, tankExplosionEffect.main.duration);//爆炸效果播放完后刪除爆炸實(shí)例
m_ExplosionAudio = tankExplosionInstance.GetComponent<AudioSource>();
m_ExplosionAudio.Play();//播放音效
gameObject.SetActive(false);//坦克血量小于等于0后,坦克消失
}
/*點(diǎn)擊按鈕就會(huì)執(zhí)行傷害方法(20點(diǎn)傷害)
private void OnGUI()
{
if (GUI.Button(new Rect(10, 10, 150, 50), "攻擊"))
{
TankDamage(20);
}
}*/
}
·UIDirectionControl
實(shí)現(xiàn)坦克血條不隨著坦克的旋轉(zhuǎn)而旋轉(zhuǎn)
using UnityEngine;
public class UIDirectionControl : MonoBehaviour
{
public bool m_UseRelativeRotation = true;
private Quaternion m_RelativeRotation;
private void Start()
{
m_RelativeRotation = transform.parent.localRotation;
}
private void Update()
{
if (m_UseRelativeRotation)
transform.rotation = m_RelativeRotation;
}
}
2.2.6 游戲管理系統(tǒng)
2.2.6.1創(chuàng)建游戲管理系統(tǒng)
1. 創(chuàng)建空物體作為坦克生成點(diǎn),分別重命名為:SpawnPoint1:Position(-3,0,30)
Rotation(0,180,0) ??SpawnPoint2:Position(13,0,-5) Rotation(0,0,0)
2. 創(chuàng)建顯示UI:右鍵創(chuàng)建UI/Cavas重命名為MessageCanvas,在Cavas下右鍵UI>Text創(chuàng)建Text文本組件,錨點(diǎn)選擇Alt+最右下模式, 參數(shù)為(0,0,0,0,0)Anchors:Min(0.1,0.1),Max(0.9,0.9)
3. 文本內(nèi)容:TANKS!,字體:BowlbyOne-Regular,大?。?0,文本格式選擇居中顯示,勾選Best Fit,Max Size:60, Color(255,255,255,255)。在Text上添加組件Shadow,設(shè)置參數(shù):Color(114,71,40,128),Effect Distance(-3,-3)
4.?創(chuàng)建GameManger:
1):在Hierarchy面板中創(chuàng)建空物體重命名為GameManger,添加腳本組件GameManager.cs
2):設(shè)置腳本面板參數(shù):Camera Control:拖拽CameraRig ?Message Text:拖拽消息面板Text, ?Tank Prefab:拖拽預(yù)制件Tank ?Tanks:Size選擇2,第一個(gè)坦克Color(42,100,178),第二個(gè)坦克Color(229,46,40),Spawn Point選擇對(duì)應(yīng)的出生點(diǎn)。
3):給GameManger添加組件Audio Source,AudioClip:BackgroundMusic ,勾選Play On
Awake, 勾選:Loop。
2.2.6.2添加腳本
1. 將Resources.cs直接拖入Scripts/Manager目錄中;在Scripts/Manager目錄下創(chuàng)鍵GameManager腳本;在Scripts/Tank目錄下創(chuàng)鍵TankItem腳本
2. 將GameManager腳本拖到Hierarchy面板中的GameManager上;
2.2.6.3腳本編寫
·GameManager
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
//管理游戲主要流程
public class GameManger : MonoBehaviour
{
public GameObject m_TankPrefab; //坦克預(yù)制體
public CameraControl m_CameraControl; //攝像機(jī)控制腳本組件
public TankItem[] m_Tanks; //所有坦克屬性集
public Text m_MessageText; //UI顯示組件對(duì)象
public float m_StartDelay = 3f; //開始準(zhǔn)備時(shí)長(zhǎng)
public float m_EndDelay = 3f; //結(jié)束等待時(shí)長(zhǎng)
public int m_NumRoundsToWin = 3; //贏得游戲需要贏的局?jǐn)?shù)
private TankItem m_GameWinner; //游戲贏家
private TankItem m_RoundWinner; //回合贏家
private int m_RoundNumber; //當(dāng)前回合數(shù)
void Start()//初始化:創(chuàng)建并初始化所有坦克;獲取所有坦克的位置組件對(duì)象賦給CameraControl.Targets;開始游戲
{
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].Init();
}
Transform[] targets = new Transform[m_Tanks.Length];
for (int i = 0; i < m_Tanks.Length; i++)
{
targets[i] = m_Tanks[i].m_Instance.transform;
}
m_CameraControl.m_Targets = targets;
StartCoroutine(GameLoop());
}
private IEnumerator GameLoop()//使用協(xié)程:開始循環(huán)游戲
{
yield return null;
yield return StartCoroutine(RoundStarting());
yield return StartCoroutine(RoundPlaying());
yield return StartCoroutine(RounEnding());
if (m_GameWinner != null)
{
//重新加載場(chǎng)景
SceneManager.LoadScene("MainScence");
}
else
{
//回合結(jié)束,又開始
StartCoroutine(GameLoop());
}
}
private IEnumerator RoundStarting()//回合初始化:調(diào)用坦克對(duì)象的初始化方法進(jìn)行初始化;
//調(diào)用坦克對(duì)象的凍結(jié)方法凍結(jié)坦克控制;
//調(diào)用攝像機(jī)初始化相機(jī)位置和視口大?。? //初始化回合界面顯示文本;
//等待一定時(shí)間再進(jìn)行下一步。
{
for (int i = 0; i < m_Tanks.Length; i++)
{
m_Tanks[i].Reset();
m_Tanks[i].DisableControl();
}
m_CameraControl.SetStartPositionAndSize();
m_RoundNumber++;
m_MessageText.text = "ROUND" + m_RoundNumber + "!";
yield return new WaitForSeconds(m_StartDelay);
}
private IEnumerator RoundPlaying()//開始游戲:調(diào)用坦克對(duì)象的激活方法激活坦克控制;
//清除UI界面顯示內(nèi)容;
{
//回合初始化
for (int i = 0; i < m_Tanks.Length; i++)
{
m_Tanks[i].EnableControl();
}
m_MessageText.text = string.Empty;
while (!OneTankLeft())
{
yield return null; //等待一幀
}
}
private IEnumerator RounEnding()//回合結(jié)束:調(diào)用坦克對(duì)象的凍結(jié)方法凍結(jié)坦克控制
{
for (int i = 0; i < m_Tanks.Length; i++)
{
m_Tanks[i].DisableControl();
}
m_RoundWinner = GetRoundWinner();
m_GameWinner = GetGameWinner();
if(m_RoundWinner!=null)
m_RoundWinner.m_Wins++;
m_MessageText.text = EndingMessage();
yield return new WaitForSeconds(m_EndDelay);
}
private bool OneTankLeft()//判斷是否只剩下一個(gè)坦克(OneTankLeft()),如是則回合結(jié)束否則游戲一直進(jìn)行。
{
int numTankLeft = 0;
for (int i = 0; i < m_Tanks.Length; i++)
{
if (m_Tanks[i].m_Instance.activeSelf)
numTankLeft++;
}
return numTankLeft<=1;
}
private TankItem GetRoundWinner()//獲取回合贏家(GetRoundWinner());增加贏家分?jǐn)?shù);
{
for (int i = 0; i < m_Tanks.Length; i++)
{
if (m_Tanks[i].m_Instance.activeSelf)
return m_Tanks[i];
}
return null;
}
private TankItem GetGameWinner()//獲取游戲贏家(GetGameWinner()判斷是否進(jìn)行下一回合。)
{
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 EndingMessage()//顯示回合結(jié)束的信息(EndingMessage());
//等待一定時(shí)間用于顯示信息。判斷游戲是否結(jié)束;如果沒有則繼續(xù)循環(huán)進(jìn)行下一回合;如果結(jié)束了重置一下游戲場(chǎng)景。
{
string message = "DRAW!";
if (m_RoundWinner != null)
message = m_RoundWinner.m_ColoredPlayerText+ "WINS THE ROUND";
message = "\n" + message + "\n\n\n";
for (int i = 0; i < m_Tanks.Length; i++)
{
message += m_Tanks[i].m_ColoredPlayerText + ":" + m_Tanks[i].m_Wins + " WIN\n";
}
if (m_GameWinner != null)
message = m_GameWinner.m_ColoredPlayerText + "WINS THE GAME!";
return message;
}
}
·TankItem
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//代表一個(gè)坦克對(duì)象的信息和提供一些方法
[Serializable] //序列化:序列化后可以在控制面板中查看其公開屬性
public class TankItem
{
public Color m_PlayerColor; //顏色
public Transform m_SpawnPoint; //出生點(diǎn)
[HideInInspector] public int m_PlayerNumber; //編號(hào)
[HideInInspector] public GameObject m_Instance; //坦克實(shí)例
[HideInInspector] public int m_Wins; //贏得的回合數(shù)
[HideInInspector] public string m_ColoredPlayerText; //贏家信息
private TankMovement m_Movement; //移動(dòng)組件
private TankShooting m_Shooting; //射擊組件
private GameObject m_CanvasGO; //Canvas節(jié)點(diǎn)對(duì)象
//游戲開始游戲?qū)ο蟪跏蓟椒? public void Init()
{
//初始化組件
m_Movement = m_Instance.GetComponent<TankMovement>();
m_Shooting = m_Instance.GetComponent<TankShooting>();
m_CanvasGO = m_Instance.GetComponentInChildren<Canvas>().gameObject;
//初始化編號(hào)
m_Movement.m_PlayerNumber = m_PlayerNumber;
m_Shooting.m_PlayerNumber = m_PlayerNumber;
//初始化玩家信息顏色 PLAYER1
m_ColoredPlayerText = "<color=#" + ColorUtility.ToHtmlStringRGB(m_PlayerColor) + ">PLAYER" + m_PlayerNumber + "</color>";
//將顏色應(yīng)用到坦克上
MeshRenderer[] renderers = m_Instance.GetComponentsInChildren<MeshRenderer>();
for (int i = 0; i < renderers.Length; i++)
{
renderers[i].material.color = m_PlayerColor;
}
}
//激活本對(duì)象控制狀態(tài):移動(dòng),射擊
public void EnableControl()
{
m_Movement.enabled = true;
m_Shooting.enabled = true;
m_CanvasGO.SetActive(true);
}
//凍結(jié)本對(duì)象的行為
public void DisableControl()
{
m_Movement.enabled = false;
m_Shooting.enabled = false;
m_CanvasGO.SetActive(false);
}
//回合結(jié)束初始化方法
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);
}
}
2.2.7 音效管理
混音器
1. 在Project/Assets下創(chuàng)建文件夾AudioMixers,選中AudioMixers右鍵Create/AudioMixer,重命名MainMix.雙擊打開Audio Mixer面板,拖放到Scene面板旁邊。
2. 在Groups欄,點(diǎn)擊加號(hào)創(chuàng)建三個(gè)組,分別命名為Music,SFX,Driving。
3. 賦值:
·選中Tank預(yù)制件,第一個(gè)Audio Source的Output選擇Driving.第二個(gè)選擇SFX;
·選中Shell預(yù)制件雙擊打開,選中其子物體ShellExplosion,AudioSource組件的Output屬性選擇為SFX.
·在Hierarchy面板中選中GameManager.AudioSource的Output屬性選則Music.
·選中TankExplosion預(yù)制件,AudioSource組件上Output屬性設(shè)置SFX.
4. 回到Audio Mixer Window面板,設(shè)置Music相對(duì)強(qiáng)度為-12,Driving為-28。
5. Music面板下點(diǎn)擊Add/Duck Volume,設(shè)置參數(shù)Threshold:-45dB. Rotio:250%。Attack Time:0.
6. SFX面板選中Add/Send.參數(shù)選則,Receive:Music\Duck Volume ?Send level:0dB.文章來源:http://www.zghlxwxcb.cn/news/detail-860909.html
2.3 使用說明
不同玩家通過不同的按鈕控制對(duì)應(yīng)坦克的前后移動(dòng)左右旋轉(zhuǎn)以及炮彈發(fā)射。當(dāng)擊中對(duì)方坦克時(shí),對(duì)方坦克扣血。當(dāng)血條扣完后,坦克爆炸,一輪游戲結(jié)束。一輪游戲結(jié)束后可新開始一輪。游戲結(jié)束后,玩家可通過屏幕上的信息知道各自輸贏了幾次。文章來源地址http://www.zghlxwxcb.cn/news/detail-860909.html
到了這里,關(guān)于Unity游戲程序設(shè)計(jì)——3D雙人坦克大戰(zhàn)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!