




版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報或認(rèn)領(lǐng)
文檔簡介
本科畢業(yè)設(shè)計(論文)基于Unity3D的狩獵模擬器的設(shè)計與實現(xiàn)DesignanddevelopmentoftankcombatsurvivalgamebasedonUnity3D院(系)計算機(jī)學(xué)院專業(yè)軟件工程班級7班學(xué)號16210120727學(xué)生姓名楊天天指導(dǎo)教師王千秋提交日期
內(nèi)容摘要因為國內(nèi)能夠打獵的狩獵基地大多不對外開放,再加上國家對于野生動物的保護(hù),人們極少能夠見到野生的動物,更別說與野生動物互動和打獵了。因此,為了人們能夠體會到在遠(yuǎn)離人群的森林中,體驗真實的狩獵快感和體會自然森林的原始韻味,特此制作了這款模擬類型的游戲《狩獵模擬器》。模擬類型的游戲重點在于現(xiàn)實的還原,但是一味照搬現(xiàn)實是枯燥無味的,所以在玩法上應(yīng)該以玩家能夠更容易體驗到狩獵的樂趣為主。以狩獵為核心的游戲目前出現(xiàn)的也不在少數(shù),比如《荒野的召喚》可以說是這個類型游戲的標(biāo)桿,但是即使這樣的大作也不可能讓所有玩家喜歡,其中更是有些許的不足之處,其中引用b站一部分玩家的評論“這個游戲過于枯燥“、”大部分時間都在走路“和”看風(fēng)景模擬器“,這些是部分玩家的觀點,我認(rèn)為出現(xiàn)這種重心從狩獵偏移到看風(fēng)景的情況,不是因為說這個游戲的狩獵玩法很硬核,而是這個游戲節(jié)奏太慢,不適合現(xiàn)在國內(nèi)部分喜歡快節(jié)奏游戲人群的玩家,當(dāng)我自己去體驗了這個游戲之后感覺也確實如此。所以我開發(fā)的這個狩獵游戲?qū)L試解決這個問題:在盡量保留狩獵的真實性的情況下,將一個以狩獵為核心的游戲從跑圖看風(fēng)景中解放出來,回歸到暢快淋漓的狩獵之中。
AbstractBecausemosthuntingbasesthatcanbehuntedinChinaarenotopentotheoutsideworld,coupledwiththecountry'sprotectionofwildanimals,peoplerarelyseewildanimals,letaloneinteractwithandhuntwildanimals.Therefore,inordertoallowpeopletoexperiencetherealhuntingpleasureintheforestfarfromthecrowdandtheoriginalcharmofthenaturalforest,thissimulation-typegame"HuntingSimulator"wasproduced.Simulation-typegamesfocusonclosenessandreality,butcopyingrealityblindlyisdull,sothegameplayshouldbebasedontheplayer'sabilitytoexperiencethefunofhuntingmoreeasily.Hunting-basedgamesarenotuncommonatpresent,suchas"CalloftheWild"canbesaidtobethebenchmarkforthistypeofgame,butevensuchamasterpieceisnotlikelytobelikedbyallplayers,amongwhichtherearesomeshortcomings,Whichquotesthecommentsofsomeplayersinstationb,"Thisgameistooboring","Ispendmostofmytimewalking,"and"seeingthescenerysimulator."Thesearesomeoftheplayers’views.IthinkthisfocusshiftsfromhuntingtoLookingatthescenery,itisnotbecausethehuntinggameplayofthisgameisveryhard-core,butbecausethepaceofthegameistooslow,anditisnotsuitableforsomedomesticplayerswholikefast-pacedgamers.ItfeelstruewhenIexperiencethegameinthisway.SothishuntinggameIdevelopedwilltrytosolvethisproblem:whileretainingtheauthenticityofhuntingasmuchaspossible,liberateagamewithhuntingasthecorefromrunningpicturestoseethelandscape,andreturntothesmoothandvigoroushunting.
本科畢業(yè)設(shè)計(論文) 緒論選題的目的和意義前幾年最火的游戲類型無疑是競技類型的游戲,比如《英雄聯(lián)盟》和《王者榮耀》等,但是近兩年玩家的喜好又偏向模擬真實類型的游戲,比如《模擬飛行》、《模擬修仙》等,其中有大廠也有新人的制作,未來模擬類型的游戲可能成為比較火的游戲類型。模擬類型的游戲非常注重玩家的體驗,這是該類型游戲的核心,不管模擬什么,最終的目的就是要玩家感受到該游戲所模擬的東西。游戲中設(shè)計模擬的東西可以是生活中十分平常的東西也可以是普通人觸及不到又想體驗的東西,更可以是設(shè)計者幻想架空的夢幻之物,但是最終一定要玩家體驗到這個游戲所模擬的東西,并真正融入進(jìn)去?!夺鳙C模擬器》這個游戲就和名字一樣,這是一款模擬狩獵的游戲,狩獵這個詞對于任何人都不陌生,但是狩獵在中國幾乎是不可能的事情,偷獵更是會將牢低坐穿。因為狩獵這個早在幾十萬年前就作為我們祖先獲得食物的方式,后面的封建時代更是發(fā)展為娛樂,可以說這是刻在我們骨子里的行為,所以大部分人應(yīng)該對狩獵有一種熱衷。為了能讓這部分人可以體驗到原汁原味到叢林狩獵而不會犯法,這款模擬狩獵的游戲就此誕生。模擬類游戲的誕生和發(fā)展模擬器類游戲是誕生自一些軍用的模擬器,例如防空3D模擬器等,也正因為這一特性,最早的模擬器類游戲就是以軍事為題材的,據(jù)說第一款模擬器類游戲內(nèi)容非常簡單,模擬發(fā)射一枚導(dǎo)彈來命中目標(biāo),玩家可以通過旋鈕來控制導(dǎo)彈的速度和弧度。大家公認(rèn)的“世界上第一款電子游戲”雙人網(wǎng)球就是世界所知的第二款模擬器類游戲,兩個玩家通過游戲手柄控制游戲,這其實就是一個模擬網(wǎng)球游戲,但值得可喜的是示波器上能顯示游戲畫面了。到了1970和80年代,計算機(jī)圖像技術(shù)雖然逐漸成熟,但是游戲作品少有模擬器類的擬真元素存在。到了80和90年代,隨著計算機(jī)科學(xué)的不斷發(fā)展,慢慢的帶有模擬類的游戲就開始泛行了,其中模擬人生、模擬城市和命令與征服系列,直到今天仍然受到眾多玩家的喜愛。由于受到計算機(jī)的限制,那個時候的人們都是用在科研等領(lǐng)域,因為那時候計算機(jī)的計算能力還不足以支撐實時的模擬驗算在上個世紀(jì)計算機(jī)模擬技術(shù)主要用于科學(xué)、電影特效等領(lǐng)域。這其中就包含了一些古老的模擬游戲,在經(jīng)過一番畫面升級和游戲內(nèi)容豐富而煥然一新,模擬城市游戲里甚至每一個市民都變得有血有肉,而不再僅僅是游戲中的一個數(shù)字。在2012年前后全球范圍內(nèi)曾掀起了一陣模擬類游戲熱潮,除了模擬城市、都市天際線等一系列的城建模擬游戲,還有一些模擬駕駛游戲,能讓玩家體驗到日常生活中很難體驗到的駕駛體驗。隨著計算機(jī)技術(shù)的發(fā)展,模擬這一元素幾乎成為了每一款游戲中不可缺少的部分,模擬元素在游戲中的存在讓玩家有了更好的沉浸感。有一句話就叫做學(xué)習(xí)知識最好的方法就是實踐,游戲就提供了一個很好的平臺,隨著Unity3D、UE等游戲引擎的出現(xiàn),制作游戲難度日益降低,有越來越多的人出于個人興趣,去嘗試著做一款屬于獨屬于自己的游戲,有些玩家能在游戲中進(jìn)行實踐和學(xué)習(xí);有的在各種千奇百怪的模擬器類游戲,甚至還有什么做飯模擬器等,享受更多的是一種娛樂;有的則可以在游戲中體驗不一樣的人生,更多是實現(xiàn)了在現(xiàn)實生活中實現(xiàn)不了的東西!論文組織結(jié)構(gòu)游戲以學(xué)校所教的unity3d引擎為基礎(chǔ),利用自己和老師教的知識進(jìn)行游戲開發(fā)。論文記錄游戲構(gòu)思、設(shè)計和制作的過程,并且完美展示自己的所見所學(xué),證明自己的獨到觀點。第一部分:緒論,主要介紹設(shè)計該游戲的背景和目的,并且討論模擬類游戲的過去,從而希望別人理解為什么會設(shè)計這款游戲第二部分:介紹開發(fā)這款游戲所做的準(zhǔn)備、所需要的開發(fā)環(huán)境和開發(fā)工具第三部分:詳細(xì)介紹游戲各個模塊的設(shè)計和實現(xiàn),根據(jù)設(shè)計這款游戲程序的模式,將這部分的說明分成不同小塊來進(jìn)行說明第四部分:游戲界面設(shè)計、聲效設(shè)計第五部分:白盒測試、黑盒測試,打包后運(yùn)行測試第六部分:從新論述一遍自己原先設(shè)計該游戲的目的和背景,回應(yīng)開題
開發(fā)工具Unity3D引擎關(guān)于Unity3D軟件功能的介紹Unity3D是一款3D跨平臺次時代游戲引擎。它提供了非常完美的跨平臺系統(tǒng)。平臺之間的差別常常會影響到產(chǎn)品的開發(fā)速度和進(jìn)度,因此大部分開發(fā)者們要在這上面花費(fèi)大量的時間,而Unity3D則能夠在近10種主流平臺之間完美移植。同時Unity3D也提供了一個交互良好的操作界面,能夠完美地在Windows和MacOS下部署。該軟件自帶的五個工程視圖框架能夠很好地幫助設(shè)計者分類游戲中的對象及觀察制作游戲的進(jìn)程。其中包括project視圖,該視圖主要存放資源文件,hierarchy視圖主要放置場景中具體的游戲?qū)ο?,inspector視圖主要用來描述游戲資源的信息,scene視圖主要存放游戲中模型資源,game視圖是用來觀察已經(jīng)完成的游戲的運(yùn)行成果。Unity3D還為我們提供了多種腳本語言編譯的常用環(huán)境,用戶可以根據(jù)需求將默認(rèn)的編譯環(huán)境做出調(diào)整。Unity3D支持C#、javascript、boo三種腳本語言。API接口Unity3D豐富的API能夠幫你完成各種想要完成的需求。API是一些先定義好的函數(shù),為開發(fā)人員基于某軟件或硬件無需訪問源碼就能提供訪問一組例程的能力,亦或理解內(nèi)部工作機(jī)制的細(xì)節(jié)。Unity3D就有一套完整的API函數(shù)庫,通過這些API函數(shù),開發(fā)人員可以很方便地實現(xiàn)項目的初始化,功能模塊的每幀調(diào)用,如何進(jìn)行觸發(fā)檢測并進(jìn)行觸發(fā)事件的響應(yīng),如何判斷是否進(jìn)入觸發(fā)區(qū),是否在觸發(fā)區(qū),是否離開觸發(fā)區(qū),如何實現(xiàn)拖動事件的響應(yīng)等。物理引擎任何一個游戲的設(shè)計都需要其物體特性的合理性,這樣才顯得真實可信。U3D為開發(fā)人員提供了大量組件,可以對對象進(jìn)行渲染,顏色的誘明度,平行光,點光源,法線貼圖,圖片,文字,動畫,聲音,材質(zhì)等一系列組件可以帶給我們很逼真的視覺效果,而在物理特性上添加的剛體,碰撞器等組件,可以實現(xiàn)使對象受到重力,摩擦力,空氣阻力等自然物理特性的影響,而爬坡的坡度設(shè)定,碰撞后的一切物理變化,逼真的體驗讓你覺得這就是一個現(xiàn)實中的物體。Unity3D腳本生命周期Unity3D游戲引擎不像常規(guī)的程序直接在Main函數(shù)入口運(yùn)行,而是在內(nèi)部實現(xiàn)了自己的生命周期事件。通過對這些生命周期的事件進(jìn)行寫入,Unity內(nèi)部就會不斷地迭代這些生命周期函數(shù)。下面按照腳本的執(zhí)行順序介紹游戲中比較常用的Unity事件函數(shù)。Awake():當(dāng)游戲?qū)ο蟊怀跏蓟臅r候調(diào)用,無論該對象是否已被激活。Start():在Awake事件后調(diào)用,但只有被激活的時候才能夠執(zhí)行。Update():游戲中的幀事件,因為游戲大部分都是按幀率來執(zhí)行邏輯的。FixedUpdate():游戲的固定幀事件,基本同Update(),但該事件可由開發(fā)者去控制執(zhí)行頻率。LateUpdate():這個函數(shù)是在Update()函數(shù)在每幀執(zhí)行結(jié)束后才會執(zhí)行的函數(shù),和Update類似,每幀都會執(zhí)行一次,一般用來處理跟隨邏輯,比如OnGUI():一般寫UI邏輯,不過NGUI出來后一般不使用,因為優(yōu)化很差OnEnable():在Start事件后調(diào)用,只有被激活的時候才會執(zhí)行。OnDisable():當(dāng)游戲?qū)ο蟊唤辜せ畹臅r候會調(diào)用。OnDestroy():當(dāng)游戲?qū)ο蟊讳N毀的時候執(zhí)行。Unity3D的腳本基本上都會繼承自于MonoBehavior基類。一般不繼承于MonoBehavior的類會用來寫一些工具類。通常來說,它在整個程序運(yùn)行的過程中都是存在的,除非自己手動GC釋放內(nèi)存。圖形用戶界面本文主要使用NGUI和UGUI進(jìn)行UI界面的開發(fā),GUI是圖形用戶界面的英文簡寫。Unity的GUI系統(tǒng)被稱作UnityGUI,UnityGUI能使你非??旖莺啽愕奶砑庸δ荦R備且種類繁多的界面元素,通過在GUI控件的創(chuàng)建操作中同時包括實例化,定位和功能定義。使你只需要一次性寫出很少量的代碼就能同時完成創(chuàng)建一個GUI界面元素實例,并定位實例在屏幕的位置和描述界面元素被激活時所要執(zhí)行的腳本三種工作。UGUI是Unity內(nèi)置的,NGUI則是一款為Unity游戲引擎開發(fā)的工具功能擴(kuò)展的UI插件,它也能夠為開發(fā)者提供方便快捷的UI設(shè)計方法,加快設(shè)計游戲的速度。BehaviorDesignerBehaviorDesigner提供了強(qiáng)大的API,可以讓你根據(jù)自己游戲內(nèi)部的AI需求編寫自己的AI腳本,而且還提供了一個直觀的可視化編輯器,該可視化編輯器具有廣泛的第三方集成,可以創(chuàng)建復(fù)雜的AI,而無需編寫任何代碼。AssetStoreUnityTechnologies和其他社區(qū)成員會不斷地創(chuàng)作免費(fèi)的和商用的資源,而Unity資源商店則是這些資源收錄的寶庫。里面有各種各樣的資源可供使用,包含了從紋理,模型和動畫到整個示例項目,教程和編輯器插件等內(nèi)容。在Unity編輯器的內(nèi)置接口中可以訪問到這些資源,它們可供下載并直接導(dǎo)入到你的項目中去。這里我們游戲中大部分使用的模型都會從資源商店中下載,而不是使用建模軟件自己動手做,那樣花費(fèi)大量的時間。C#語言C#是微軟公司發(fā)布的一種運(yùn)行于.NETFramework之上,面向?qū)ο蟮母呒壋绦蛟O(shè)計語言。C#與Java類似,是一種語言,一種工具。但這里需要知道Unity是跨平臺的,而C#并不是一種跨平臺的語言。只是由于Mono的的重新實現(xiàn),使得Unity能夠使用C#來開發(fā),因此,我們可以在Unity上寫C#的代碼。語法明了,類庫使用方便,是C#的優(yōu)點,也是我們在Unity開發(fā)中首先考慮的語言。VisualStudio2017VisualStudio2017是微軟公司推出的開發(fā)環(huán)境。它支持最新的集成環(huán)境開發(fā)。VisualStudio也帶來了NETFramework4.6、MicrosoftVisualStudio2017CTP,同時也支持開發(fā)者開發(fā)Windows的應(yīng)用程序。并且支持SQLSever,IBM,Oracle數(shù)據(jù)庫等。
游戲玩法設(shè)計場景設(shè)計主要分為場景1和場景2,場景1主要是游戲開始界面,主要是給帶來玩家一個引導(dǎo)作用,當(dāng)玩家點擊開始游戲跳轉(zhuǎn)進(jìn)入場景2,場景2是游戲的主體玩法部分。場景1主要組成部分:開始游戲按鈕、退出游戲按鈕、加載界面場景2主要組成部分:平地、高山、花、草、樹木、竹林、動物場景的制作Unity中自帶地形創(chuàng)建工具,我們可以在Hierarchy面板中Create->3DObject->Terrain這樣來創(chuàng)建一個平面的地形,然后在檢查器界面對地形進(jìn)行改造、改變地面材質(zhì)和添加花草樹木。圖3-1地形繪制改造界面狩獵模擬器游戲地圖,為了經(jīng)量模擬真實世界環(huán)境,使用了9塊地形來拼成一個大地圖,這里只有中間的地形作為玩家可以進(jìn)入的地形,而游戲也圍繞這里開展,其他8塊地形只作為遠(yuǎn)景地形,也就是玩家只能看到而不能走進(jìn)去,中間那一塊四周將會使用空氣墻把玩家隔離,防止玩家走進(jìn)其他只是作為景色的地形。圖3-2創(chuàng)建好的地形
為了讓玩家更加身臨其境,我們可以為地形添加一些樹木和花草,讓森林變的更加真實,這里我們直接使用unity自帶的地形編輯器,為我們的地形進(jìn)行批量的添加花草樹木圖3-3環(huán)境繪制Player制作物理抖動為了模擬現(xiàn)實中人物在移動和瞄準(zhǔn)的時候明顯的感覺自己在抖動,這個抖動會分別使用相機(jī)和手的左右抖動來模擬,也就是當(dāng)Player在游戲里進(jìn)行移動的時候,我們的鏡頭和手也會跟著抖動,而進(jìn)入瞄準(zhǔn)狀態(tài)的時候玩家會更明顯的感覺到瞄準(zhǔn)鏡的抖動。 上面說明了物理抖動的實現(xiàn)原理,接下來我們來編寫腳本以實現(xiàn)我們鏡頭抖動。首先create->script創(chuàng)建腳本FPSController,接下來實現(xiàn)如下:localCameraRotationOffset
=
Vector3.Lerp(localCameraRotationOffset,
Vector3.zero,
Time.deltaTime
*
3);
//相機(jī)本地旋轉(zhuǎn)值還原
float
swayY
=
(Mathf.Cos(Time.fixedTime
*
10
*
swaySpeed)
*
0.3f)
*
sizeY;
float
swayX
=
(Mathf.Sin(Time.fixedTime
*
5
*
swaySpeed)
*
0.2f)
*
sizeX;
FPSCamera.gameObject.transform.localPosition
=
Vector3.Lerp(FPSCamera.gameObject.transform.localPosition,
localCameraPositionTemp
+
new
Vector3(swayX,
swayY,
0),
Time.deltaTime
*
3);
//瞄準(zhǔn)鏡頭位置設(shè)置
FPSCamera.gameObject.transform.localRotation
=
Quaternion.Lerp(FPSCamera.gameObject.transform.localRotation,
Quaternion.Euler(localCameraRotationTemp.eulerAngles
+
localCameraRotationOffset),
Time.deltaTime
*
3);//瞄準(zhǔn)鏡頭旋轉(zhuǎn)角度設(shè)置
接下來是手的抖動,因為該游戲里面手是連著槍的,所以我們直接在槍的腳本里寫手的物理抖動,同樣create->script先創(chuàng)建腳本Gun,然后實現(xiàn)如下:float
swayY
=
(Mathf.Cos(Time.time
*
10
*
swaySpeed)
*
0.3f)
*
sizeY;
float
swayX
=
(Mathf.Sin(Time.time
*
5
*
swaySpeed)
*
0.2f)
*
sizeX;
this.transform.localPosition
=
Vector3.Lerp(this.transform.localPosition,
positionTemp
+
new
Vector3(swayX,
swayY,
0),
Time.fixedDeltaTime
*
4);
this.transform.localRotation
=
Quaternion.Lerp(this.transform.localRotation,
Quaternion.Euler((rotationTemp.eulerAngles.x
+
(FPSmotor.rotationDif.x)),
(rotationTemp.eulerAngles.y
+
(FPSmotor.rotationDif.y)),
(rotationTemp.eulerAngles.z
+
(FPSmotor.direction.x
*
7))),
Time.fixedDeltaTime
*
3);
FPS功能FPS功能就是指開鏡功能,當(dāng)我們使用FPS功能時,就會舉起槍打開單筒望遠(yuǎn)鏡,而鏡頭會切換到瞄準(zhǔn)鏡頭,玩家也就可以看的更遠(yuǎn)。這里我們創(chuàng)建兩個相機(jī)create->camera,一個是正常的相機(jī),深度為1,可以看到自己和槍,也就是模擬正常人觀看視角的相機(jī),另一個相機(jī)將深度設(shè)置為2,設(shè)置為只能看到槍,當(dāng)我們點擊鼠標(biāo)右鍵時就會渲染第二個相機(jī)用來覆蓋第一個相機(jī),再次點擊鼠標(biāo)右鍵則可以關(guān)閉渲染,變回原來的鏡頭。圖3-4正常相機(jī)鏡頭圖3-5FPS相機(jī)鏡頭功能我們在腳本FPSController里面實現(xiàn),下面實現(xiàn)開鏡時人物動畫的播放判斷處理,代碼如下:if
(HideGunWhileZooming
&&
FPSmotor
&&
NormalCamera.GetComponent<Camera>().enabled)
{
FPSmotor.HideGun(!Zooming);
}
if
(!GetComponent<Animation>()
||
!Active)
return;
switch
(gunState)
{
case
0:
if
(AmmoIn
<=
0)
{
Zooming
=
false;
if
(Clip
>
0)
{
GetComponent<Animation>().clip
=
GetComponent<Animation>()[BoltPose].clip;
GetComponent<Animation>().CrossFade(BoltPose,
0.5f,
PlayMode.StopAll);
gunState
=
2;
if
(FPSmotor
&&
Zooming)
{
FPSmotor.CameraForceRotation(new
Vector3(0,
0,
20));
FPSmotor.Stun(0.2f);
}
}
}
break;
case
1:
if
(Time.time
>=
cooldowntime
+
CooldownTime)
{
gunState
=
0;
}
break;
case
2:
GetComponent<Animation>().Play();
if
(GetComponent<Animation>()[BoltPose].normalizedTime
>
BoltTime)
{
if
(Shell
&&
ShellSpawn)
{
if
(!boltout)
{
if
(FPSmotor
&&
Zooming)
{
FPSmotor.CameraForceRotation(new
Vector3(0,
0,
-5));
FPSmotor.Stun(0.1f);
}
}
}
}
break;
}
這里對鏡頭的切換進(jìn)行平緩處理,開鏡的時候可以明顯的看到鏡頭向前平移,然后再覆蓋掉正常鏡頭,代碼實現(xiàn)如下:if
(FPSmotor)
{
if
(Zooming)
{
FPSmotor.sensitivityXMult
=
MouseSensitiveZoom;
FPSmotor.sensitivityYMult
=
MouseSensitiveZoom;
FPSmotor.Noise
=
true;
}
else
{
FPSmotor.sensitivityXMult
=
MouseSensitive;
FPSmotor.sensitivityYMult
=
MouseSensitive;
FPSmotor.Noise
=
false;
}
}
if
(Zooming)
{
if
(ZoomFOVLists.Length
>
0)
{
MouseSensitiveZoom
=
((MouseSensitive
*
0.16f)
/
10)
*
ZoomFOVLists[IndexZoom];
NormalCamera.GetComponent<Camera>().fieldOfView
+=
(ZoomFOVLists[IndexZoom]
-
NormalCamera.GetComponent<Camera>().fieldOfView)
/
10;
}
}
else
{
NormalCamera.GetComponent<Camera>().fieldOfView
+=
(fovTemp
-
NormalCamera.GetComponent<Camera>().fieldOfView)
/
10;
}
開鏡的UI處理,這里我們直接在OnGUI函數(shù)里面編寫實現(xiàn),代碼如下:void
OnGUI()
{
if
(!Active)
return;
if
(NormalCamera.GetComponent<Camera>().enabled)
{
if
(!Zooming)
{
if
(CrosshairImg)
{
GUI.color
=
new
Color(1,
1,
1,
0.8f);
GUI.DrawTexture(new
Rect((Screen.width
*
0.5f)
-
(CrosshairImg.width
*
0.5f),
(Screen.height
*
0.5f)
-
(CrosshairImg.height
*
0.5f),
CrosshairImg.width,
CrosshairImg.height),
CrosshairImg);
GUI.color
=
Color.white;
}
}
else
{
scale.x
=
Screen.width
/
originalWidth;
//
calculate
hor
scale
scale.y
=
Screen.height
/
originalHeight;
//
calculate
vert
scale
scale.z
=
1.0f;
var
svMat
=
GUI.matrix;
GUI.matrix
=
Matrix4x4.TRS(Vector3.zero,
Quaternion.identity,
scale);
if
(CrosshairZoom)
{
float
scopeSize
=
(Screen.height
*
1.1f);
GUI.DrawTexture(new
Rect(0,
0,
originalWidth,
originalHeight),
CrosshairZoom);
}
GUI.matrix
=
svMat;
}
}
}
射擊功能本游戲?qū)⒅皇褂靡话褬屵M(jìn)行游戲,這把槍可以設(shè)置射出的子彈速度、發(fā)射間隔、攻擊力等。圖3-6玩家使用的武器槍主要的功能就是發(fā)射子彈,接下來我們就來實現(xiàn)這個功能,我們直接打開Gun腳本來進(jìn)行編寫,因為發(fā)射子彈是單獨的一個功能,我們可以寫一個發(fā)射子彈的方法,命名為Shoot(),代碼實現(xiàn)如下:public
void
Shoot()
{
if
(!Active)
return;
if
(timefire
+
FireRate
<
Time.time)
{
if
(gunState
==
0)
{
if
(AmmoIn
>
0)
{
if
(FPSmotor)
FPSmotor.Stun(KickPower);
if
(SoundGunFire
&&
audiosource
!=
null)
{
if
(PlayerPrefs.GetInt("FX")
==
1)
audiosource.PlayOneShot(SoundGunFire);
}
for
(int
i
=
0;
i
<
BulletNum;
i++)
{
if
(Bullets)
{
Vector3
point
=
NormalCamera.GetComponent<Camera>().ScreenToWorldPoint(new
Vector3(0,
0,
NormalCamera.GetComponent<Camera>().nearClipPlane));
GameObject
bullet
=
(GameObject)Instantiate(Bullets,
point,
NormalCamera.gameObject.transform.rotation);
//生成子彈
bullet.transform.forward
=
NormalCamera.transform.forward;
//設(shè)置子彈的z軸
Destroy(bullet,
LifeTimeBullet);
//刪除子彈
}
}
boltout
=
false;
GetComponent<Animation>().Stop();
GetComponent<Animation>().Play(ShootPose,
PlayMode.StopAll);
timefire
=
Time.time;
cooldowntime
=
Time.time;
if
(!SemiAuto)
{
gunState
=
1;
AmmoIn
-=
1;
}
else
{
if
(Shell
&&
ShellSpawn)
{
GameObject
shell
=
(GameObject)Instantiate(Shell,
ShellSpawn.position,
ShellSpawn.rotation);
shell.GetComponent<Rigidbody>().AddForce(ShellSpawn.transform.right
*
2);
shell.GetComponent<Rigidbody>().AddTorque(Random.rotation.eulerAngles
*
10);
GameObject.Destroy(shell,
5);
}
if
(Clip
>
0)
{
AmmoIn
=
1;
Clip
-=
1;
}
else
{
gunState
=
3;
}
}
}
}
}
}
子彈單列在寫子彈前,我們必須清楚怎么處理子彈的和其他物體的碰撞,在這個問題上,我是先上百度搜索來一下,看看網(wǎng)友們的意見,最后我找了一些有意思的討論,如下圖:圖3-7評論1圖3-8評論2這里我們可以看到直接做碰撞會浪費(fèi)資源造成卡頓,而且發(fā)生碰撞的時候還會有問題,并給出了解決方法,那就是使用射線來檢測。在這個游戲里我們會在子彈上面發(fā)射一條射線來獲取子彈即將擊中的目標(biāo),并根據(jù)子彈和即將碰撞的物體的距離和子彈速度,求出在多久后會擊中目標(biāo),create->script創(chuàng)建一個bullet_AS的腳本,然后代碼實現(xiàn)如下:public
bool
RayShoot(bool
first)
{
bool
res
=
false;
RaycastHit[]
hits;
//射線集合
float
ray
=
BulletRaylength
*
Time.timeScale;
if
(ray
<
0.5f)
{
ray
=
0.5f;
}
hits
=
Physics.RaycastAll(transform.position,
transform.forward,
ray,
ignoreWalkThru);
for
(var
i
=
0;
i
<
hits.Length;
i++)
{
RaycastHit
hit
=
hits[i];
if
(hit.collider)
{
if
(hit.collider.tag
!=
"Player"
&&
hit.collider.tag
!=
this.gameObject.tag)
//射線的碰撞器不是player和子彈
{
targetLookat
=
null;
TargetLocked
=
false;
if
(!hittedObjectCheck(hit.collider.gameObject))
//當(dāng)objectHittedList集合里面沒有射線拿到的gameobject時,將其添加進(jìn)來
{
res
=
true;
addHitedObject(hit.collider.gameObject);
GameObject
hitparticle
=
null;
this.transform.position
=
hit.point;
if
(hit.rigidbody)
{
hit.rigidbody.AddForceAtPosition(this.transform.forward
*
HitForce,
hit.point);
//子彈打到物體給于物體一個力
}
if
(hitparticle
!=
null)
{
if
(hit.collider.GetComponent<Terrain>())
{
hitparticle.transform.forward
=
hit.normal;
}
else
{
hitparticle.transform.forward
=
this.transform.forward;
}
GameObject.Destroy(hitparticle,
5);
//
Debug.Log("second
called");
}
if
(DestroyWhenHit
||
hitcount
>=
HitCountMax
||
hit.collider.GetComponent<Terrain>())
{
//Debug.Log(hit.collider.tag);
hitArea
=
hit.collider.tag;
Debug.Log(hitArea);
hited
=
true;
//刪除子彈開關(guān)
}
HitTarget(hit.collider);
}
}
}
}
if
(hited)
//刪除子彈判斷
{
GameObject.Destroy(this.gameObject,0.04f);
}
return
res;
}
上里我們說bullet_AS寫成一個單列,是因為在擊中目標(biāo)后,我們要知道到底擊中了哪個部位,所以這里要返回?fù)糁心繕?biāo)部位的名字,這樣動物的傷害判定就可以使用,代碼實現(xiàn)如下:public
static
Bullet_AS
Instance()
{
return
_instance;
}
public
void
Awake()
{
_instance
=
this;
}
public
string
HitArea()
{
if
(hitArea
!=
null)
{
return
hitArea;
}
return
null;
}
子彈的飛行是物理運(yùn)算,為了防止掉幀的時候子彈的運(yùn)動出現(xiàn)異常,如一開始我們說的那樣,這里不使用Update函數(shù)而是使用FixedUpdate函數(shù),代碼實現(xiàn)如下:void
FixedUpdate()
{
if
(TargetLocked
&&
GetComponent<Rigidbody>())
{
if
(targetLookat
!=
null)
{
this.transform.LookAt((targetLookat.transform.position
+
targetLookatOffset));
float
lateralSpeed
=
GetComponent<Rigidbody>().velocity.magnitude;
this.GetComponent<Rigidbody>().velocity
=
new
Vector3(transform.forward.x,
transform.forward.y,
transform.forward.z)
*
lateralSpeed;
}
}
}
人物移動創(chuàng)建一個空的物體,給空的物體添加碰撞器和Rigidbody,我們將在這個空的物體里面實現(xiàn)Player的移動邏輯,到時候?qū)⒃镜腜layer作為這個空物體的子物體就可以實現(xiàn)Player的移動了。 這部分的功能分為移動、快速跑和下蹲,移動功能是指玩家可以根據(jù)WSAD鍵來控制Player進(jìn)行上下左右移動,快速跑的是指點擊左Shift鍵就可以加快速度奔跑,而下蹲鍵是玩家可以根據(jù)點擊左Ctrl鍵降低身位。這些功能我們直接調(diào)用unity里面的函數(shù)來實現(xiàn),而不用自己手動寫。首先create->script創(chuàng)建腳本FirstPersonCharacter,物理運(yùn)動邏輯自然還是在FixedUpdate函數(shù)里面實現(xiàn),代碼如下:float
speed
=
runSpeed;
float
h
=
Input.GetAxis("Horizontal");
float
v
=
Input.GetAxis("Vertical");
input
=
new
Vector2(
h,
v
);
bool
walkOrRun
=
Input.GetKey(KeyCode.LeftShift);
speed
=
walkByDefault
?
(walkOrRun
?
runSpeed
:
walkSpeed)
:
(walkOrRun
?
walkSpeed
:
runSpeed);
if
(input.sqrMagnitude
>
1)
input.Normalize();
Vector3
desiredMove
=
transform.forward
*
input.y
*
speed
+
transform.right
*
input.x
*
strafeSpeed;
因為我們是第一人稱射擊的玩法,只有移動、快速跑動還是不夠的,還要有camera跟隨鼠標(biāo)轉(zhuǎn)動,接下來我們來實現(xiàn),繼續(xù)創(chuàng)建腳本SimpleMouseRotator,代碼實現(xiàn)如下: transform.localRotation
=
originalRotation;
float
inputH
=
0;
float
inputV
=
0;
if
(relative)
{
inputH
=
Input.GetAxis("Mouse
X");
inputV
=
Input.GetAxis("Mouse
Y");
if
(targetAngles.y
>
180)
{
targetAngles.y
-=
360;
followAngles.y
-=
360;
}
if
(targetAngles.x
>
180)
{
targetAngles.x
-=
360;
followAngles.x-=
360;
}
if
(targetAngles.y
<
-180)
{
targetAngles.y
+=
360;
followAngles.y
+=
360;
}
if
(targetAngles.x
<
-180)
{
targetAngles.x
+=
360;
followAngles.x
+=
360;
}
targetAngles.y
+=
inputH
*
rotationSpeed;
targetAngles.x
+=
inputV
*
rotationSpeed;
targetAngles.y
=
Mathf.Clamp
(
targetAngles.y,
-rotationRange.y
*
0.5f,
rotationRange.y
*
0.5f
);
targetAngles.x
=
Mathf.Clamp
(
targetAngles.x,
-rotationRange.x
*
0.5f,
rotationRange.x
*
0.5f
);
}
else
{
inputH
=
Input.mousePosition.x;
inputV
=
Input.mousePosition.y;
targetAngles.y
=
Mathf.Lerp
(
-rotationRange.y
*
0.5f,
rotationRange.y
*
0.5f,
inputH/Screen.width
);
targetAngles.x
=
Mathf.Lerp
(
-rotationRange.x
*
0.5f,
rotationRange.x
*
0.5f,
inputV/Screen.height
);
}
followAngles
=
Vector3.SmoothDamp(
followAngles,
targetAngles,
ref
followVelocity,
dampingTime
);
transform.localRotation
=
originalRotation
*
Quaternion.Euler(
-followAngles.x,
followAngles.y,
0
);
動物的AI動物AI比較明顯的分為兩大板塊,一種是食草動物,一種是食肉動物,食草動物AI之間只有一些細(xì)節(jié)上的區(qū)別,食肉動物AI也是這樣,只會對一些特殊點的動物AI進(jìn)行改進(jìn)。鹿的AI我們先來分析大自然中的鹿有哪些行為,比如:行走、奔跑、吃草、休息、被攻擊會逃跑和死亡,我們將這些行為設(shè)計為一個個狀態(tài),鹿會根據(jù)特定的條件在這些狀態(tài)之間切換。首先我們將行走、奔跑、吃草、休息、逃跑和死亡等行為設(shè)計為行走狀態(tài)、奔跑狀態(tài)、吃草狀態(tài)、休息狀態(tài)、逃跑狀態(tài)和死亡狀態(tài),鹿正常情況下會隨機(jī)休息、行走和吃草,而被攻擊和看到敵人后就會逃跑,當(dāng)血量降低為0的時候,鹿就會進(jìn)入死亡狀態(tài),這里我們可以將鹿的行為歸類為兩個比較大的行為,也就是沒有被攻擊的狀態(tài)和被攻擊的狀態(tài),鹿沒有被攻擊的狀態(tài)設(shè)計為行為樹1可以進(jìn)行自由行走、休息、吃草、看敵人逃跑和死亡等狀態(tài),被攻擊的狀態(tài)設(shè)計為行為樹2可以逃跑和逃跑,然后根據(jù)這些狀態(tài)和跳轉(zhuǎn)條件繪制行為樹圖,下面是行為樹1圖:圖3-9行為樹1圖3-10行為樹2首先我們先拿到鹿的模型,導(dǎo)入包->自定義包,導(dǎo)入鹿的模型和動畫,這里鹿的動畫是幀動畫,也就是每幀對模型進(jìn)行微調(diào)整,制作出來的動畫,這里我們根據(jù)行為樹的狀態(tài)制作了6個動畫,代表鹿的6個行為,分別是死亡、被攻擊、吃草、跑動、走動和休息。圖3-11鹿的幀動畫和模型接下來我們根據(jù)設(shè)計行為樹圖來制作鹿的行為樹,這里我們會使用到行為樹插件。首先AddBehaviorTree創(chuàng)建一個名為notHitBehaviorTree的行為樹,然后根據(jù)行為樹1設(shè)計圖構(gòu)建行為樹,先看完成后的行為樹,如圖:圖3-12行為樹1完成圖這里我們將鹿的探查范圍設(shè)計為兩個,分別上視覺和聽覺,視覺只能看到前方90度位置的敵人,不過比聽覺的范圍更大,聽覺范圍是一個圓圈距離較小。圖3-13鹿的探查范圍AddBehaviorTree創(chuàng)建一個名為hitBehaviorTree的行為樹,根據(jù)行為樹2設(shè)計圖構(gòu)建行為樹,看完成后的行為樹,如圖:圖3-14行為樹2完成圖接下來我們更加詳細(xì)的了解鹿各個行為的實現(xiàn),首先是自由行走行為,鹿會隨機(jī)挑選一個自己巡邏的地點,向那個地點前進(jìn),行走一段時間后,自由行走狀態(tài)會自行打斷,跳轉(zhuǎn)到休息狀態(tài)或者吃草狀態(tài)。Create->script創(chuàng)建腳本DeerPatrol,代碼實現(xiàn)如下:if(startTime
+
restDuration
<
Time.time)
{
isIdle
=
true;
startTime
=
Time.time;
restDuration
=
restTime.Value;
return
TaskStatus.Inactive;
}
if
(!navMeshAgent.pathPending)
{
var
thisPosition
=
transform.position;
thisPosition.y
=
navMeshAgent.destination.y;
if
(Vector3.SqrMagnitude(thisPosition
-
navMeshAgent.destination)
<
arriveDistance.Value)
{
if
(randomPatrol.Value)
{
waypointIndex
=
Random.Range(0,
waypoints.Value.Count);
}
else
{
waypointIndex
=
(waypointIndex
+
1)
%
waypoints.Value.Count;
}
navMeshAgent.destination
=
Target();
}
}
transform.GetComponent<Animation>().Play("walk");
return
TaskStatus.Running;
接下來實現(xiàn)休息狀態(tài)和吃草狀態(tài),休息狀態(tài)和吃草狀態(tài)都是禁止的,所以我們可以將他們寫在一個狀態(tài)里,根據(jù)停留的時間來判斷執(zhí)行哪個狀態(tài),這里停留的時間是隨機(jī)的。create->script創(chuàng)建腳本DeerWait,代碼實現(xiàn)如下:public
override
void
OnStart()
{
startTime
=
Time.time;
if
(randomWait.Value)
{
waitDuration
=
Random.Range(randomWaitMin.Value,
randomWaitMax.Value);
}
else
{
waitDuration
=
waitTime.Value;
}
}
public
override
TaskStatus
OnUpdate()
{
if
(startTime
+
waitDuration
<
Time.time)
{
return
TaskStatus.Inactive;
}
if
(randomWait.Value)
{
if
(waitDuration
<
randomWaitMax.Value
/
2)
{
transform.GetComponent<Animation>().Play("idle2");
}
else
{
transform.GetComponent<Animation>().Play("idle1");
}
}
return
TaskStatus.Running;
}
public
override
void
OnPause(bool
paused)
{
if
(paused)
{
pauseTime
=
Time.time;
}
else
{
startTime
+=
(Time.time
-
pauseTime);
}
}
這里我們來實現(xiàn)逃跑狀態(tài),逃跑是在看到敵人或者聽到敵人在附近的時候就會切換進(jìn)入的狀態(tài),所以這里我們要拿到看到的物體或者聽到的物體,然后逃離這個物體,接下來我們先創(chuàng)建腳本DeerFlee,然后代碼實現(xiàn)如下:public
override
void
OnAwake()
{
navMeshAgent
=
gameObject.GetComponent<UnityEngine.AI.NavMeshAgent>();
}
public
override
void
OnStart()
{
dynamicTarget
=
(targetTransform
!=
null
&&
targetTransform.Value
!=
null);
navMeshAgent.speed
=
speed.Value;
navMeshAgent.angularSpeed
=
angularSpeed.Value;
navMeshAgent.enabled
=
true;
navMeshAgent.destination
=
Target();
}
public
override
TaskStatus
OnUpdate()
{
if
(!navMeshAgent.pathPending
&&
Vector3.SqrMagnitude(transform.position
-
Target())
>
fleedDistance.Value)
{
return
TaskStatus.Success;
}
navMeshAgent.destination
=
FleePosition();
transform.GetComponent<Animation>().Play("run");
return
TaskStatus.Running;
}
public
override
void
OnEnd()
{
navMeshAgent.enabled
=
false;
}
private
Vector3
Target()
{
if
(dynamicTarget)
{
return
targetTransform.Value.position;
}
return
targetPosition.Value;
}
private
Vector3
FleePosition()
{
return
transform.position
+
(transform.position
-
Target()).normalized
*
lookAheadDistance.Value;
}
鹿的AI已經(jīng)制作完成,接下來鹿還需要一個管理器,管理行為樹間的切換、HP、被攻擊扣除的血量、被攻擊的部位和死亡狀態(tài)所要處理的事項,Create->script創(chuàng)建腳本DeerController,實現(xiàn)鹿被攻擊之后關(guān)閉行為樹1,打開行為樹2,并完成扣血進(jìn)入流血狀態(tài),當(dāng)鹿逃跑一段時間后,流血狀態(tài)取消,關(guān)閉行為樹2打開行為樹1。HP為0的時候行為樹1和行為樹2全部關(guān)閉,執(zhí)行鹿死亡的動畫。這里子彈單列返回的擊中部位可以使用到,我們可以根據(jù)這個來判斷對鹿造成多少傷害,會進(jìn)入什么狀態(tài)。下面是代碼:void
Update()
{
if
(isDeath
==
false)
{
if
(Bullet_AS.Instance()
!=
null)
{
if
(Bullet_AS.Instance().HitArea()
!=
null)
{
Debug.Log(Bullet_AS.Instance().HitArea());
if
(Bullet_AS.Instance().HitArea()
==
"DeerHead")
{
hitHead();
}
if
(Bullet_AS.Instance().HitArea()
==
"DeerNeck")
{
hitNeck();
}
if
(Bullet_AS.Instance().HitArea()
==
"DeerBody")
{
hitBody();
}
}
}
if
(HP
>=
0
&&
bleedTime
>=
0)
{
if
(bleedInterval
<=
0)
{
HP
-=
1;
bleedInterval
=
bleedIntervale
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 社區(qū)勞動服務(wù)項目經(jīng)驗分享如何培養(yǎng)志愿者團(tuán)隊
- 租山田合同范本
- 二零二五年度學(xué)生電子產(chǎn)品使用行為規(guī)范及家長責(zé)任協(xié)議
- 買賣賒欠合同范本
- 2025年度車輛過戶與二手車交易市場合作合同協(xié)議
- 二零二五年度城市配送司機(jī)勞務(wù)合同書
- 二零二五年度烘焙店員工勞動合同與員工激勵計劃
- 二零二五年度干股合作協(xié)議及項目進(jìn)度管理
- 2025至2030年中國移動式泡沫滅火裝置數(shù)據(jù)監(jiān)測研究報告
- 2025至2030年中國磨毛油數(shù)據(jù)監(jiān)測研究報告
- 《文化人類學(xué)電子》課件
- 教育專家報告合集:年度得到:沈祖蕓全球教育報告(2023-2024)
- 兒童尿道黏膜脫垂介紹演示培訓(xùn)課件
- 靜壓樁施工技術(shù)交底
- 2023發(fā)電企業(yè)防汛工作管理辦法
- 《酒店客房管理課件》
- 服裝市場調(diào)研報告
- 食品安全風(fēng)險評估的課件
- 醫(yī)院維修施工方案施工方案
- 第四單元細(xì)胞的物質(zhì)輸入和輸出(單元教學(xué)設(shè)計)高一生物(人教版2019必修1)
- 《公路路基路面現(xiàn)場測試規(guī)程》(3450-2019)
評論
0/150
提交評論