日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

基于Unity官方素材开发的3D二人沙漠坦克大战

發布時間:2023/12/10 编程问答 43 豆豆
生活随笔 收集整理的這篇文章主要介紹了 基于Unity官方素材开发的3D二人沙漠坦克大战 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

1.1前言

? ? ? ?本篇文章是對學習的知識內容進行總結和記錄下,前段時間跟著公開課程(沒收廣告費,就不說是哪個了)學習制作了第一個3D游戲—《3D坦克大戰》,是一款二人對戰的游戲,左邊玩家用WASD控制方向移動,空格鍵發射炮彈,右邊的玩家用方向鍵上下左右控制方向,Enter回車鍵發射炮彈。素材用的是UnityStore的官方的免費素材,基本的炮彈,坦克和地圖不需要自己去建模了,套進去用就行了。

**特別注意一下,這里我只放了腳本代碼,具體的幾個小點,包括Rigidbody組件和剛體組件的添加,Audio組件的添加,大小的調整介于時間問題就不放了,代碼部分最初變量的聲明可以到客戶端中進行更改,大家可以看看代碼來進行學習啥的**

? ? ? ?先放一張最后的完成圖

下面進入正題代碼部分

2.1TankControl

? ? ? ?第一部分是坦克控制的腳本,完成了坦克的初始化和幾個功能,Input獲取鍵盤輸入然后控制坦克的移動,這里倒退有bug所以多加了個一個判斷讓他倒退為反方向。包括開火的命令,這里進行了小優化,子彈射出的速度有初始速度和蓄力速度,最終的速度等于初速度+蓄力時間*蓄力速度,還有個最大值,大于最大值炮彈將自動發射。最后的炮彈的銷毀和特效的銷毀,這里注意下,特效銷毀的時候要把它拿出來放到父類,不然會和坦克一起被銷毀導致看不到特效或者聲音,最后面導入SceneManagement模塊來實現重開游戲,最后把腳本掛載到預制件上就可以了。

using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.SceneManagement; using UnityEngine.UI;public enum TankType {Tank_One = 1,Tank_Two = 2,Tank_Enemy = 3, } public class TankControl : MonoBehaviour {public TankType TankType = TankType.Tank_One;public string inputHorizontalStr;public string inputVerticalStr;public string inputFireStr;public Rigidbody _rigidbody;public float h_Value;public float v_Value;public float speed = 40;public float rotateSpeed = 60;//Firepublic GameObject shell;public Transform shellPos;public float MinSpeed = 10;public float MaxSpeed = 30;public float currentSpeed = 10;public float speedChange = 10;public bool IsFire = false;//PHpublic float PH = 20;public Slider phSlider; //血條的那個滑塊,同時這是一個UI要導入UI的包//tank爆炸public ParticleSystem tankExplosion; //ParticleSystem 粒子特效//public AudioSource tankFire;//先舍棄// Start is called before the first frame updatevoid Start(){TankInitilization();}void TankInitilization() //坦克的初始化{//將Tank1和Tank2區分開來,用Tank2你按wasd是沒有效果的,必須要按方向鍵的上下左右_rigidbody = this.gameObject.GetComponent<Rigidbody>();inputHorizontalStr = inputHorizontalStr + (int)TankType;inputVerticalStr = inputVerticalStr + (int)TankType;inputFireStr = inputFireStr + (int)TankType;//血量初始化一下phSlider.maxValue = PH;phSlider.value = PH;}// Update is called once per framevoid Update(){//input 控制器h_Value = Input.GetAxis(inputHorizontalStr);v_Value = Input.GetAxis(inputVerticalStr);if (v_Value != 0){_rigidbody.MovePosition(this.transform.position + v_Value*this.transform.forward * speed * Time.deltaTime);//Time.delataTime上一幀執行的時間,可理解為間隔時間}if(h_Value != 0){if (v_Value < 0) //倒退有bug,讓他方向為反的時候左右方向為反{h_Value = - h_Value;}this.gameObject.transform.Rotate(Vector3.up* h_Value * rotateSpeed * Time.deltaTime);//旋轉方向}//按下的瞬間if (Input.GetButtonDown(inputFireStr)){IsFire = true;currentSpeed = MinSpeed;}//按下的狀態if (Input.GetButton(inputFireStr)&& IsFire){currentSpeed += speedChange * Time.deltaTime; //當前時間=改變的速度與蓄力的時間的乘積if (currentSpeed >=MaxSpeed) {currentSpeed = MaxSpeed;OpenFire(currentSpeed);currentSpeed = MinSpeed;IsFire = false;}}if (Input.GetButtonUp(inputFireStr)&&IsFire) //這里是不蓄力的{//FireOpenFire(currentSpeed);currentSpeed = MinSpeed;IsFire = false;}}private void FixedUpdate(){}void OpenFire(float shellSpeed) //開火的方法,等著被調用{ //step1:先克隆一個炮彈//step2:給炮彈一個速度if (shell!=null){//AudioMangager._audioManagerInstance.tankExplosionAudioPlay();//調用AudioManager中的方法,不能重復多次,暫時舍棄 /* if (tankFire!=null){tankFire.Play();}*///同上先舍棄GameObject shellObj = Instantiate(shell, shellPos.position, shellPos.transform.rotation); //克隆后面的炮彈Rigidbody shellRegidbody = shellObj.GetComponent<Rigidbody>(); //把Rigidbody下賦值給shellRegidbodyif (shellRegidbody != null){shellRegidbody.velocity = shellPos.forward * shellSpeed;}}}public void ShellDamage(float damgae) //炮彈的銷毀{if (PH > 0){PH -= damgae;phSlider.value = PH;}if (PH < 0){//Dieif (tankExplosion != null){AudioMangager._audioManagerInstance.tankExplosionAudioPlay();//調用AudioManager中的方法tankExplosion.transform.parent = null;tankExplosion.Play();Destroy(tankExplosion.gameObject, tankExplosion.main.duration);}//Destroy(this.transform.gameObject);this.gameObject.SetActive(false); Invoke("RelodTankBattleScence",2);}}void RelodTankBattleScence(){SceneManager.LoadScene("3D TankBattle");//坦克損毀重開} }

? ? ? ? ? ? ? 腳本掛載到坦克預制件上

2.2ShellControl

? ? ? ? 掛載在子彈預制件上,主要去實現碰撞和傷害,擊中對方的坦克進行范圍和傷害的判斷然后掉血,這里要注意一下,坦克的兩個Rigidbody如果是使用兩個的話,可以刪除一個,否則因為爆炸有范圍傷害,扣血會雙倍,特效的話和上面類同,這里就不展開敘述了

using System.Collections; using System.Collections.Generic; using UnityEngine;public class ShellControl : MonoBehaviour {public ParticleSystem shellExplosion;public float explosionRadius = 2;//爆炸半徑public LayerMask tankMask;public float explosionForce = 5;//爆炸力//Damage 損害public float MaxDamage = 5;public AudioSource shellFireAudio;public AudioSource shellExplosionAudio;// Start is called before the first frame updatevoid Start(){if (shellFireAudio != null){if (!shellFireAudio.isPlaying){shellFireAudio.Play();}}}// Update is called once per framevoid Update(){}private void OnCollisionEnter(Collision collision){Collider[] tankColliders = Physics.OverlapSphere(this.transform.position,explosionRadius,tankMask); //爆炸特效的地點和范圍for(int i =0; i<tankColliders.Length; i++){var tankRigidbody = tankColliders[i].gameObject.GetComponent<Rigidbody>(); //判斷爆炸范圍內是否有tank,有的話,獲取他的collierif (tankRigidbody!=null){tankRigidbody.AddExplosionForce(explosionForce,this.transform.position,explosionRadius); //傳入爆炸力和爆炸中心點,爆炸半徑float distance = (this.transform.position - tankRigidbody.position).magnitude; //獲取范圍大小float currentDamage = distance / explosionRadius * MaxDamage; //當前傷害量的計算方法var tankControl = tankColliders[i].gameObject.GetComponent<TankControl>(); //把tankControl賦值給一個變量//穩妥起見,再判斷一下if (tankControl!=null){tankControl.ShellDamage(currentDamage); //這里擊中坦克就可以掉血了}}}// AudioMangager._audioManagerInstance.shellExplosionAudioPlay();//調用AudioManager中的方法,無法重復,舍棄if (shellExplosionAudio != null){shellExplosionAudio.gameObject.transform.parent = null;if (!shellExplosionAudio.isPlaying){shellExplosionAudio.Play();Destroy(shellExplosionAudio.gameObject, 1);}}//炮彈爆炸特效if (shellExplosion!=null) //發送爆炸{shellExplosion.transform.parent = null; //把爆炸父體給消除,把爆炸特效放在外面(上一級目錄),防止跟隨炮彈被銷毀shellExplosion.Play();//爆炸特效Destroy(shellExplosion.gameObject,shellExplosion.main.duration); //銷毀爆炸特效以及物體本身}Destroy(this.gameObject);} }

? ? ?完成了兩個主體預制件腳本的掛載,下面終于進入了整體游戲控制管理腳本的編寫

3.1GameMange

? ? ? ?背景音樂的播放,這里暫時不提,后面有專門的腳本;由于是雙人游戲,GameMange腳本完成了一個很重要的任務,克隆兩個新的坦克,我們可以在場景里新創建兩個空的組件,在你預期想產生新坦克的位置,在對預制件進行使用Instantiate克隆的時候,克隆位置那里可以選擇這兩個組件他所在的位置,克隆兩次,就會有兩個和預制件設置一模一樣的坦克產生,記得將它們的控制改為Horizontal1和Horizontal2,否則你按WASD兩個克隆體都會同時移動。還可以通過定義Color變量來調整兩個坦克的顏色等。

using System.Collections; using System.Collections.Generic; using UnityEngine;public class GameMange : MonoBehaviour {//兩個位置生成坦克;初始化坦克設置public Transform PosOne;public Transform PosTwo;public GameObject tankProfab;public Color TankOneColor;public Color TankTwoColor;public GameCameraControl camerControl;public AudioMangager audioMangager;// Start is called before the first frame updatevoid Start(){audioMangager = GameObject.FindWithTag("AudioManager").GetComponent<AudioMangager>(); //通過腳本找到游戲對象if (audioMangager != null){audioMangager.bgAudioPlay();}TankSpawn();if (camerControl!= null){camerControl.tanks = GameObject.FindGameObjectsWithTag("Player"); }}// Update is called once per framevoid Update(){}void TankSpawn() //產生新的坦克,用原來的坦克克隆{GameObject tankOne = Instantiate(tankProfab, PosOne.position,PosOne.transform.rotation);//克隆一號var tankOneControl = tankOne.GetComponent<TankControl>(); //坦克的控制if (tankOneControl != null){tankOneControl.TankType = TankType.Tank_One;MeshRenderer[] tankOnerenderers = tankOne.GetComponentsInChildren<MeshRenderer>();for (int i = 0; i < tankOnerenderers.Length; i++){tankOnerenderers[i].material.color = TankOneColor;}/* foreach (var renderer in renderers){renderer.material.color = tankOneColor;}*/}GameObject tankTwo = Instantiate(tankProfab, PosTwo.position, PosTwo.transform.rotation);//克隆二號var tankTwoControl = tankTwo.GetComponent<TankControl>();if (tankTwoControl != null){tankTwoControl.TankType = TankType.Tank_Two;MeshRenderer[] tankTworenderers = tankTwo.GetComponentsInChildren<MeshRenderer>();for(int i=0;i < tankTworenderers.Length; i++){tankTworenderers[i].material.color = TankTwoColor;}}} }

3.2GameCameraControl

? ? ? ? 這里個人覺得是最難理解的一個腳本,負責照相機視角的移動,基本原理就是相機的位置始終保持在兩個坦克的中心點,通過計算坦克與相機目標中心位置的X,Z的差值,來判斷計算機的Size為多少。(小知識,x,z的值一般只有整個游戲畫面分辨率的一半)。

? ? ? ?如果你想強行玩2D的話,也有個小技巧,把相機Rotation也就是旋轉角度調為90°,這樣相機就會以垂直90°俯視整個地圖,就變成了2D游戲了,但是我們這里當時是先計算好了90°時中心點的值,后來想讓相機以45°角去看地圖以達到3D視角的目的,但是一開始視角會產生偏移(視角不一樣),后來我們采取將相機組件放入另一個空的組件中并重新命名,在游戲開始后調整組件的視角與位置以到達我們想要的效果,記錄下改變后的(當時空組件改變了的)位置參數,然后在Main Camera中改參數,其實主要是Position中Z軸的函數,然后腳本通過計算加上一點點時延,就可以達到根據兩個坦克的移動平滑移動相機視角的目的。不多說了,上代碼。

using System.Collections; using System.Collections.Generic; using UnityEngine;public class GameCameraControl : MonoBehaviour {public GameObject[] tanks;public Vector3 targetCameraPos = Vector3.zero; //相機移動的目標位置,也就是所有坦克的中心點public Camera mainCamera;public GameObject cameraParent; public Vector3 currentVelocity = Vector3.zero;public float smoothTime = 1;public float maxSoothSpeed = 2;public float sizeOffset = 4;//public Vector3 cameraOffsetPos = Vector3.zero;//每次去重置它的位置 // Start is called before the first frame updatevoid Start(){mainCamera = Camera.main;}// Update is called once per framevoid Update(){ResetCameraPos();ResetCameraSize();}void ResetCameraPos(){//Step1 計算相機移動目標位置//Step2 平滑移動相機Vector3 sumPos = Vector3.zero;foreach (var tank in tanks){sumPos += tank.transform.position;//所有坦克位置加起來}if (tanks.Length > 0){targetCameraPos = sumPos / tanks.Length;//中心點targetCameraPos.y = cameraParent.transform.position.y;//避免y坐標有偏差 // targetCameraPos += cameraOffsetPos; //每次讓他的位置偏移量加上一個數,如果你想垂直視角游玩,請注釋本行cameraParent.transform.position = Vector3.SmoothDamp(cameraParent.transform.position, targetCameraPos, ref currentVelocity, smoothTime, maxSoothSpeed);//當前位置到目標位置,平滑移動}}void ResetCameraSize(){//通過計算坦克與相機目標中心位置的X,Z的差值,來判斷計算機的Size為多少float size = 0;foreach (var tank in tanks){Vector3 offsetPos = tank.transform.position - targetCameraPos; //這里是坦克位置與相機目標中心位置的差值float z_Value = Mathf.Abs(offsetPos.z); //取絕對值保證為正數size = Mathf.Max(size, z_Value); //z方向,取兩個之間最大的值float x_Value = Mathf.Abs(offsetPos.x);//mainCamera.aspect 寬度除以高度,用這個來求長寬比,最后和分辨率聯系size = Mathf.Max(size, x_Value / mainCamera.aspect);}size += sizeOffset;mainCamera.orthographicSize = size; //正交攝像機的大小} }

3.3AudioMangager

? ? ? ? 最后是聲音控件的部分了,這里沒什么要重點講的,我甚至當時連注釋都沒怎么標(霧)不是懶,很簡單的代碼,一般都能看懂,不細說了。主要包括一個背景音樂和三個音效的添加,需要注意的是,這里的代碼主要是在特定位置和時間進行觸發,除了循環的BGM,一般寫好了會在比如說坦克爆炸后調用,子彈發射后調用,子彈爆炸后調用等,唯一注意下就是別跟著別的函數跟組件一起被銷毀就行,要不然到時候看不到現象。

using System.Collections; using System.Collections.Generic; using UnityEngine;public class AudioMangager : MonoBehaviour {public AudioSource bg;public AudioSource tankExplosion;public AudioSource shellExplosion;public AudioSource tankFire;public static AudioMangager _audioManagerInstance;private void Awake()//喚醒并進行實例化。簡省后面的聲明,減少代碼重復性{_audioManagerInstance = this;}// Start is called before the first frame updatevoid Start(){}// Update is called once per framevoid Update(){}public void bgAudioPlay(){if (bg != null){if (!bg.isPlaying){bg.loop = true; bg.Play();}}}public void tankExplosionAudioPlay(){if (tankExplosion != null){if (!tankExplosion.isPlaying){tankExplosion.Play();}}}public void shellExplosionAudioPlay(){if (shellExplosion != null){if (!shellExplosion.isPlaying){shellExplosion.Play();}}}public void tankFireAudioPlay(){if (tankFire != null){if (!tankFire.isPlaying){tankFire.Play();}}} }

四、總結

? ? ? ?個人的第一個3D游戲,目前還在學習中,也肯定會有很多不足很多值得改進的地方,包括UI的設計,開始結束界面也可以添加,暫不支持聯網功能等等,這些知識在學了,在學了,我會努力在下一個2.0或者3.0版本進行更新和加入的,希望各位有別的新的想法或者建議,或者錯誤啥的可以跟我探討,也請各位大佬斧正,歡迎私信交流探討,有時間一定會回復的。

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?最后再來兩張實物圖

?

總結

以上是生活随笔為你收集整理的基于Unity官方素材开发的3D二人沙漠坦克大战的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: a视频在线观看 | 特级黄毛片 | v天堂在线观看 | 成人一区二区三区四区 | 国产女人毛片 | 日韩中文无| 免费的黄色网 | 日韩精品1区2区 | 性折磨bdsm欧美激情另类 | 污视频网站在线播放 | 日韩精品aaa | 国产精品羞羞答答在线 | 欧美一区二区三区在线观看视频 | 欧美中文| 熟妇人妻av无码一区二区三区 | 日韩av一区二区在线观看 | 九月色婷婷 | 不卡的av片 | 50度灰在线| 亚洲欧洲日产av | 亚州男人天堂 | 中文字幕精品三区 | 免费男女乱淫真视频免费播放 | 欧美色综合网站 | av片手机在线观看 | 69激情网| 99热com| 国产亚洲欧美精品久久久久久 | 亚洲精品一线二线三线 | 国产va亚洲va在线va | 日韩成人综合 | 国产精品1区2区3区4区 | 亚洲精品一区二区三区在线观看 | 青青在线视频观看 | 国产精品丝袜视频 | 国产女人和拘做受视频免费 | 亚洲成人动漫在线观看 | 大又大又粗又硬又爽少妇毛片 | 青青久视频 | 色偷偷免费费视频在线 | 蜜桃传媒一区二区亚洲av | 国精品人妻无码一区二区三区喝尿 | www.久久久.com | 日本亚洲综合 | 国产一区二区三区四区五区六区 | 91精品欧美一区二区三区 | 不卡中文一二三区 | 国产www色| 欧美久久精品 | 爱av在线 | 爽妇综合网 | 国产亚洲二区 | 自拍偷拍精品视频 | 尤物视频在线 | 亚洲狠狠| 欧洲免费av | 亚洲国产成人精品91久久久 | 色婷婷777 | 91视频网页 | 精精国产xxxx视频在线播放 | 国产精选一区 | www操| 侵犯亲女在线播放视频 | 国产熟妇另类久久久久 | 中国浓毛少妇毛茸茸 | 一本一道久久综合狠狠老精东影业 | 国产精品第十页 | 亚洲av电影天堂男人的天堂 | 老司机福利院 | 日本色www | 亚洲图片视频小说 | a三级黄色片 | 清纯唯美亚洲激情 | 国产探花一区 | 特极毛片 | 久久av导航| 狂野欧美性猛交xxxxhd | 在线观看亚洲区 | 美女黄色小视频 | 禁果av一区二区三区 | 亚洲欧美综合另类自拍 | av女优天堂在线观看 | 狠狠狠狠狠干 | av影视在线 | 色污网站 | 一区二区片 | 清草视频 | 欧美视频在线观看 | 美女扒开尿口给男人捅 | 亚洲一二三精品 | 韩日精品在线观看 | 成人久久毛片 | av综合导航 | 91在线视频免费播放 | 国产成a人亚洲精v品无码 | 超碰在线观看免费 | 免费毛片播放 | 97在线视频免费 | 白丝校花扒腿让我c |