最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會(huì)員登陸 & 注冊(cè)

用Unity去釋放自己的惡意——我們來(lái)實(shí)現(xiàn)一下《掘地求升》

2018-08-31 21:37 作者:皮皮關(guān)做游戲  | 我要投稿

作者:沈琰

前言

好吧,這個(gè)標(biāo)題起的有些驚悚,但是對(duì)于這期要做的游戲來(lái)說(shuō)也并不算無(wú)的放矢。

相信大家對(duì)前段時(shí)間大火的《Getting Over It With Bennett Foddy》這款游戲并不陌生,如果不熟悉這個(gè)名字,那么《掘地求升》這個(gè)接地氣的名字想必有所耳聞。

這款游戲一反“游戲帶給人快樂(lè)”的這個(gè)主旨,而是以傷害別人為目的的。

甚至游戲制作人本人都坦誠(chéng)的把這句話寫(xiě)在了Steam商店的游戲簡(jiǎn)介上。

雖然是這么一款玩起來(lái)很痛苦的游戲,但是最后還是火了。

這款游戲雖然自己玩起來(lái)經(jīng)常有砸鼠標(biāo)的沖動(dòng),但是當(dāng)在直播平臺(tái)上看著別的主播玩卻意外的節(jié)目效果爆炸,因此該游戲迅速的占領(lǐng)各大直播平臺(tái)。

最有名的當(dāng)屬一位名叫kimdoe的韓國(guó)主播,當(dāng)他在直播中歷經(jīng)12小時(shí)的奮戰(zhàn)卻一個(gè)不慎從半山腰跌入起始點(diǎn),真的是隔著屏幕都能感覺(jué)到那散發(fā)出來(lái)的滿滿的絕望。

果然自己的快樂(lè)是建立在別人的痛苦之上的


我們這期就來(lái)嘗試著用Unity去復(fù)制一下這個(gè)充滿惡意游戲。


1.關(guān)于游戲?qū)崿F(xiàn)方式的猜想

老實(shí)說(shuō)當(dāng)一開(kāi)始思考這個(gè)游戲如何實(shí)現(xiàn)時(shí),我其實(shí)是懵逼的。因?yàn)榘凑宅F(xiàn)實(shí)中的物理學(xué)來(lái)看我們的爬山猛男擺出這么一個(gè)奇怪的姿勢(shì)明顯的不合理:

根據(jù)杠桿原理,猛男要斜著支撐起自己所要施加的力矩怕是這世界上最強(qiáng)壯的人也做不到。所幸這只是在游戲中模擬,既然真實(shí)物理學(xué)不太好用,我們就用代碼來(lái)實(shí)現(xiàn)。

要達(dá)到這種效果我們可以用代碼自己模擬一個(gè)絕世猛男,調(diào)整猛男手臂的力矩讓其拿著的錘子的錘頭始終指向鼠標(biāo)方向。

不過(guò)這么一來(lái)猛男手臂和手腕關(guān)節(jié)的扭矩的計(jì)算必然很復(fù)雜,即便能做到也是很繁瑣的做法。

首先我們過(guò)濾掉一切表象的東西,從根本的方向來(lái)思考這個(gè)問(wèn)題,為此我特意找到了這個(gè)游戲的原型SexyHiking:

萬(wàn)惡之源sexyhiking


根據(jù)原型的表現(xiàn)形式來(lái)看,我們可以把這個(gè)問(wèn)題再簡(jiǎn)化一下。


如上圖,如果我們把錘子運(yùn)動(dòng)的路徑看做一個(gè)圓,猛男身體的重心看做圓心,我們可以發(fā)現(xiàn)錘子相對(duì)于地面運(yùn)動(dòng)方向的向量剛好是這個(gè)圓的切線,而重心運(yùn)動(dòng)的方向則剛好相反,因此我們可以在錘子觸碰到任何碰撞體的時(shí)候給身體一個(gè)與錘子相反向量的力來(lái)模擬這個(gè)效果。

2.實(shí)現(xiàn)方法

1.場(chǎng)景搭建

說(shuō)干就干,我們來(lái)實(shí)際操作一下。

新建一個(gè)場(chǎng)景,用簡(jiǎn)易的3D物體來(lái)組裝成猛男和錘子,掛上不同的材質(zhì)用顏色區(qū)分一下。

然后分別掛上Rigibody,因?yàn)槲覀兿氲氖亲?D平面運(yùn)行,因此在Rigibody上分別鎖了Z軸的移動(dòng)和X,Y軸的旋轉(zhuǎn),順便把錘子上的重力去掉,因?yàn)榇龝?huì)要用代碼去控制錘子的位置。

2.控制錘頭

新建一個(gè)腳本掛載在父節(jié)點(diǎn)上,分別獲取身體和錘子。

我們想要的效果是錘子能一直跟隨鼠標(biāo)移動(dòng)且錘頭一直指向鼠標(biāo),思路是在錘頭添加一個(gè)空的子節(jié)點(diǎn)作為錘頭的錨點(diǎn),然后讓整個(gè)錘子圍繞著這個(gè)錨點(diǎn)的Z軸旋轉(zhuǎn)讓錘柄指向身體的方向。


public GameObject body;

public GameObject hammer;

 

Rigidbody body_rig;

Rigidbody hammer_rig;

Transform hammer_anchor;

 

void Start()

{

        body_rig = body.GetComponent<Rigidbody>();

        hammer_rig = hammer.GetComponent<Rigidbody>();

        hammer_anchor = hammer.transform.GetChild(2);

}

//物理相關(guān)的操作一般最好放在FixedUpdate里進(jìn)行,與系統(tǒng)的物理計(jì)算保持同步

 private void FixedUpdate()

 {

        HammerControl();

 }

void HammerControl()

{

        //獲取鼠標(biāo)在屏幕上的坐標(biāo)并轉(zhuǎn)換為世界坐標(biāo)

        Vector2 MousePosition = Camera.main.ScreenToWorldPoint(Input.mousePosition);

 

        //通過(guò)修改錘子身上剛體的速度讓錘子移動(dòng),向量的起點(diǎn)用錘頭的坐標(biāo)

        //注意這里要讓錘頭的錨點(diǎn)與錘子本身的坐標(biāo)的Z值相等,為了讓旋轉(zhuǎn)軸與世界坐標(biāo)的Z軸平行,同理鼠標(biāo)坐標(biāo)的Z值也直接使用錘子的坐標(biāo)的Z

        hammer_rig.velocity = (new Vector3(MousePosition.x, MousePosition.y, hammer.transform.position.z) - hammer_anchor.position) * 10;

 

        //獲取身體到鼠標(biāo)的方向

        Vector3 direction = (new Vector3(MousePosition.x, MousePosition.y, hammer_anchor.position.z) - new Vector3(body.transform.position.x, body.transform.position.y, hammer_anchor.position.z)).normalized;

 

        //讓錘子沿著錘頭錨點(diǎn)轉(zhuǎn)向身體的方向

        hammer.transform.RotateAround(hammer_anchor.position, Vector3.Cross(hammer_anchor.up, direction), Vector3.Angle(hammer_anchor.up, direction));

 }



效果如圖:

但是現(xiàn)在問(wèn)題來(lái)了,因?yàn)橹皇呛?jiǎn)單模擬功能的效果,所以猛男并沒(méi)有手,但是最終我們是希望錘子活動(dòng)的范圍離猛男有一個(gè)最大距離的限制,這樣看起來(lái)好像是有一只無(wú)形的手來(lái)操作錘子一樣,該如何計(jì)算這個(gè)范圍呢?

由圖可見(jiàn)這個(gè)問(wèn)題的思路是,當(dāng)錘頭錨點(diǎn)在最大活動(dòng)范圍里面時(shí)是跟著鼠標(biāo)的坐標(biāo)走,當(dāng)處于最大活動(dòng)范圍外的時(shí)候是跟著錘頭錨點(diǎn)與身體坐標(biāo)的向量和最大移動(dòng)范圍形成的圓的交點(diǎn)走。這個(gè)思路用于代碼上就是當(dāng)鼠標(biāo)移動(dòng)到最大距離范圍之外時(shí)得到當(dāng)前身體到鼠標(biāo)位置的方向向量,然后讓這個(gè)向量的長(zhǎng)度等于最大距離的長(zhǎng)度。

那么,我們?cè)诖嘶A(chǔ)上修改一下代碼。


public float MaxDistance;

 float RelativeDistance;

 

//單獨(dú)抽出一個(gè)函數(shù)獲取最大距離以外的轉(zhuǎn)換后的鼠標(biāo)坐標(biāo)

    Vector2 GetConfinedPosition(Vector2 mouseposition)

    {

 

        Vector2 Confined_MousePosition;

 

        //去掉body坐標(biāo)的Z,避免計(jì)算距離時(shí)的影響

        Vector2 body_position = new Vector2(body.transform.position.x, body.transform.position.y);

 

        //計(jì)算當(dāng)前鼠標(biāo)位置和身體位置的相對(duì)距離

        RelativeDistance = Vector2.Distance(mouseposition, body_position);

 

        if (RelativeDistance > MaxDistance)

        {

            //當(dāng)相對(duì)距離大于自己設(shè)置的最大距離時(shí),獲取轉(zhuǎn)換以后的目標(biāo)坐標(biāo)

            //這里的思路需要稍稍轉(zhuǎn)一個(gè)彎,一開(kāi)始是獲取的是長(zhǎng)度為最大距離,方向?yàn)樯眢w到鼠標(biāo)方向的向量。

            //在這個(gè)基礎(chǔ)上加上身體的當(dāng)前坐標(biāo),其等于是將這個(gè)向量的起始點(diǎn)設(shè)置為身體的坐標(biāo)。

            //最后向量與坐標(biāo)點(diǎn)可以直接相互轉(zhuǎn)換,此時(shí)轉(zhuǎn)換后的目標(biāo)坐標(biāo)就等于這個(gè)向量。

            Confined_MousePosition = (mouseposition - body_position).normalized * MaxDistance + body_position;

        }

        else

        {

            //若相對(duì)距離小于最大距離那么鼠標(biāo)當(dāng)前坐標(biāo)就是目標(biāo)坐標(biāo)

            Confined_MousePosition = mouseposition;

        }    

        return Confined_MousePosition;

    }

 

然后把HammerControl()函數(shù)內(nèi)的MousePosition的賦值修改一下:


//獲取鼠標(biāo)在屏幕上的坐標(biāo)并轉(zhuǎn)換為世界坐標(biāo),若相對(duì)距離大于最大距離則獲得轉(zhuǎn)換后的坐標(biāo)

Vector2 MousePosition = GetConfinedPosition(Camera.main.ScreenToWorldPoint(Input.mousePosition));

 

最后調(diào)整MaxDistance到一個(gè)合適的值,效果如下:

3.控制身體的逆向運(yùn)動(dòng)

現(xiàn)在離實(shí)現(xiàn)就差最后一步了,前面思考實(shí)現(xiàn)方式的時(shí)候說(shuō)過(guò),錘頭的移動(dòng)方向與理論上身體移動(dòng)的方向剛好是相反的,并且同為以身體重心為原點(diǎn),錘子的最大移動(dòng)距離為半徑的圓的切線。那么我們順著這個(gè)思路去做。

首先還有一個(gè)先決條件,錘子不能憑空受力,必須是杵在地上或者掛在障礙物上才行。

我們先在錘頭上加一個(gè)碰撞盒子并在錘子上添加一個(gè)新建腳本,用幾句簡(jiǎn)單的代碼用來(lái)檢測(cè)錘頭是否碰撞到東西。


public class CollisionDetection : MonoBehaviour

{

    public bool IsCollision;

 

    private void OnCollisionEnter(Collision collision)

    {

        IsCollision = true;

    }

    private void OnCollisionExit(Collision collision)

    {

        IsCollision = false;

    }

}

 

這里要注意的是錘子是錘頭的父物體,所以即便碰撞盒子不在錘子上,作為父物體的錘子依然能檢測(cè)到子物體上的碰撞信息,反過(guò)來(lái)由于錘頭上并沒(méi)有剛體組件,所以腳本掛在錘頭上是檢測(cè)不到碰撞信息的。

然后當(dāng)檢測(cè)到碰撞時(shí)給予身體一個(gè)相反的速度值。


void HammerControl()

{

 

  //在給hammer_rig.velocity賦值后添加下面的代碼

  if(hammer.GetComponent<CollisionDetection>().IsCollision)

        {

            BodyControl(hammer_rig.velocity);

        }

}

 

void BodyControl(Vector3 velociy)

 {

    body_rig.velocity = -velociy;  

 }

 

最后效果如下:

4.后續(xù)優(yōu)化思路

到這里功能已經(jīng)基本實(shí)現(xiàn)了,但是并沒(méi)有結(jié)束,在手感上依然有很多可以調(diào)整的地方。比如原作中快速的杵地可以把自己“甩”飛起來(lái),通過(guò)調(diào)整障礙物上碰撞盒子的物理材質(zhì)達(dá)到原作中那詭異的摩擦力,添加物理關(guān)節(jié)模擬手的效果等等。

不過(guò)這些調(diào)整相當(dāng)?shù)穆闊┡c耗時(shí),想必原作者為了能給大家?guī)?lái)痛苦在最后調(diào)整參數(shù)與設(shè)計(jì)關(guān)卡的時(shí)候也是花費(fèi)了巨大的心力,這究竟是一種怎么樣的精神...

限于篇幅在這里就不展開(kāi)了,大家可以盡情發(fā)揮自己的想象力去坑自己的朋友。

結(jié)束

在本期文章里我們用相對(duì)簡(jiǎn)單的方法實(shí)現(xiàn)了《Getting Over It With Bennett Foddy》里的功能,并沒(méi)有用到什么復(fù)雜的組件,代碼量也很少,初學(xué)者也能較為容易實(shí)現(xiàn)。觀看到這里的同學(xué)大可自己動(dòng)手試試。原作也不見(jiàn)得就是用的同樣的方法,大家有什么其他的實(shí)現(xiàn)方法也歡迎在評(píng)論區(qū)留言。

工程連接:https://github.com/tank1018702/unity_001/tree/master/BennettFoodyMustDie


最后想系統(tǒng)學(xué)習(xí)游戲開(kāi)發(fā)的童鞋,歡迎訪問(wèn) http://levelpp.com/
游戲開(kāi)發(fā)攪基QQ群:869551769
微信公眾號(hào):皮皮關(guān)



用Unity去釋放自己的惡意——我們來(lái)實(shí)現(xiàn)一下《掘地求升》的評(píng)論 (共 條)

分享到微博請(qǐng)遵守國(guó)家法律
惠水县| 普定县| 鄂温| 乐山市| 凤翔县| 吉木萨尔县| 深水埗区| 内丘县| 五寨县| 尉氏县| 崇义县| 尤溪县| 连江县| 正镶白旗| 青龙| 武平县| 涪陵区| 林口县| 韩城市| 桐城市| 同仁县| 禹城市| 柯坪县| 宁国市| 永仁县| 崇仁县| 新田县| 乌鲁木齐市| 阿克| 炎陵县| 乌拉特后旗| 顺平县| 方城县| 陈巴尔虎旗| 西宁市| 和林格尔县| 灯塔市| 怀柔区| 广宗县| 迭部县| 富源县|