文章目錄
- 一、前言
- 二、最終效果
- 三、Blender建模
- 1、Blender下載安裝
- 2、傳送門建模
- 3、房子建模
- 4、公司大樓建模
- 5、文字模型
- 四、Blender匯出FBX
- 五、FBX匯入Unity中
- 六、制作材質
- 1、傳送門
- 1.1、ShaderGraph準備
- 1.2、創建ShaderGraph
- 1.3、編輯ShaderGraph
- 1.4、材質球使用ShaderGraph
- 1.5、模型參考材質球
- 2、房子材質球
- 3、公司大樓材質球
- 七、地面
- 1、廣州地鐵圖
- 2、地面(Plane)
- 八、主角
- 1、主角資源
- 2、雙搖桿制作
- 3、主角移動控制
- 3.1、影片控制
- 3.2、移動控制
- 3.3、主角腳本代碼
- 3.4、主角移動測驗
- 4、攝像機跟隨
- 5、攝像頭角度控制
- 九、導航烘焙
- 十、傳送門觸發器
- 十一、特效
- 十二、最終效果
- 十三、工程原始碼
- 十四、完畢
一、前言
嗨,大家好,我是新發,
現在每天上班的通勤時間是一個多小時,加上下班的通勤時間,每天在路上就是兩個半小時,在廣州早高峰坐地鐵簡直要命,這里我不得不吐槽一下廣州21號線,人流量超多,發車頻率還低,導致每趟都堆積特別多人,每次都要等至少兩三趟才能擠上,而且都好暴力,太瘋狂了,這樣真的容易出事,每次出地鐵心里都在重復一句話:下次不搭21號線了!
還好有熱心同事經常開車搭我上下班,老麻煩別人也很不好意思,不過,搭了幾次車又感覺臉皮厚了-_-
要是有一個任意門可以連接家門口和公司門口就好了,現實中沒有,那就在虛擬世界里做一個吧~
二、最終效果
我做的Demo最終效果如下,
家門口:

從家門口穿過傳送門:

從公司經過傳送門回家:

下面,我就來講講我的制作程序吧~
三、Blender建模
看過我前面兩篇文章的同學應該知道,我最近自學了Blender建模,感興趣的同學可以看下我之前兩篇文章,
【游戲開發創新】當我學了Blender 建模,自制3D電腦桌面,回收站爆發了,把我做的模型都吐了出來(Blender | Unity | FBX),
【游戲開發創新】自學Blender建模,自制孔明燈,在Unity中點亮整個星空,愿新年,勝舊年(Unity | 建模 | 粒子系統 | 預設)
1、Blender下載安裝
Blender官網:https://www.blender.org/
Blender中國社區:https://www.blendercn.org/
Blender中文手冊:https://docs.blender.org/manual/zh-hans/2.79/about/introduction.html
我使用的Blender版本是2.93.4,

注:關于
Blender的教程網上蠻多的,這里我就不過多講了,掌味訓本操作和快捷鍵,很快就可以上手建模啦~
2、傳送門建模
傳送門最終模型如下:

3、房子建模
房子最終模型如下:

4、公司大樓建模
公司大樓最終模型如下:

5、文字模型
再做一些文字模型,

四、Blender匯出FBX
在Blender中點擊選單File / Export / FBX,將模型匯出成FBX格式,

如下:

五、FBX匯入Unity中
將FBX檔案匯入到Unity工程中,

把模型放入場景中,現在都是默認的材質,所以都是灰白色的,不著急,下面我們就來做材質~

六、制作材質
1、傳送門
材質球使用的shader我打算使用ShaderGraph來制作,我之前寫過一篇ShaderGraph的文章:《ShaderGraph使用教程與各種特效案例:Unity2020》,推薦先看下這篇文章,
1.1、ShaderGraph準備
安裝Universal RP插件,

在Project視圖中右鍵滑鼠,點擊選單Create / Rendering / Universal Render Pipeline / Pipeline Asset (Forward Renderer),

創建UniversalRenderPipelineAsset,如下

點擊選單Edit / Project Settings...,打開Project Settings視窗,選擇Graphics分頁,把UniversalRenderPipelineAsset拖到Scriptable Render Pipeline Settings中,

1.2、創建ShaderGraph
在Project視圖中右鍵滑鼠,點擊選單Create / Shader / Universal Render Pipeline / Lit Shader Graph,創建一個PBR的ShaderGraph,

重命名為PortalCenter,作為傳送門中心的shader,

1.3、編輯ShaderGraph
雙擊PortalCenter打開編輯器,編輯節點如下,核心就是對泰森多邊形(Voronio)進行UV旋渦旋轉(Twirl),

1.4、材質球使用ShaderGraph
創建一個材質球Material, 重命名為PortalCenter,

設定材質球的shader為剛剛的ShaderGraph檔案,

1.5、模型參考材質球
將材質球賦值給傳送門模型,

效果如下,

2、房子材質球
同理,制作房子的材質,

效果如下,

3、公司大樓材質球
制作房子的材質,

效果如下,

七、地面
1、廣州地鐵圖
找一張廣州最新的地鐵地圖,我找到的是下面這張,

2、地面(Plane)
在場景中創建一個Plane平面,制作材質并參考這張圖片,


效果如下:

八、主角
1、主角資源
主角我在AssetStore上找到了一個心儀的模型,推薦給大家,
AssetStore地址:https://assetstore.unity.com/packages/3d/characters/humanoids/sci-fi/stylized-astronaut-114298

將模型下載匯入Unity中,

2、雙搖桿制作
主角的移動和攝像頭的角度旋轉我想通過搖桿來控制,我們做一個雙搖桿功能,
在Canvas節點上右鍵點擊選單UI / Panel,創建一個Panel,

把Image組件禁用掉,因為我們不需要Panel顯示出來,

在Panel下創建一個Image,重命名為leftJointedArm,作為左搖桿的父節點,

設定它的錨點為bottom - left,即螢屏左下角,調整坐標和寬高,

像這樣子,

把它的Color的alpha調為0,因為我們只需要利用它的區域來檢測觸碰,我們不需要肉眼看見它,

接著在它的子節點下創建兩個Image,分別命名為bg和center,

它們的Source Image都設定為搖桿的圖片資源,

分別調整下bg和center的大小和顏色透明度,效果如下:

同理再做一個右搖桿,

效果如下:

接下來需要給搖桿加上邏輯,Unity的UGUI提供了ScrollRect組件,非常適合用來制作搖桿,我們繼承ScrollRect然后實作OnDrag和OnEndDrag方法,可以很方便地獲取到搖桿的遙控資料,另外,為了檢測區域點擊,我們再實作IPointerDownHandler介面,

創建搖桿腳本JointedArm.cs,代碼如下:
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
using System;
/// <summary>
/// 搖桿
/// </summary>
public class JointedArm : ScrollRect, IPointerDownHandler
{
public Action<Vector2> onDragCb;
public Action onStopCb;
protected float radius = 0f;
private Transform trans;
private RectTransform bgTrans;
private Camera uiCam;
private Vector3 originalPos;
protected override void Awake()
{
base.Awake();
trans = transform;
bgTrans = trans.Find("bg") as RectTransform;
uiCam = GameObject.Find("UICamera").GetComponent<Camera>();
originalPos = trans.localPosition;
}
void Update()
{
if (Input.GetMouseButtonUp(0))
{
//松手時,搖桿復位
trans.localPosition = originalPos;
this.content.localPosition = Vector3.zero;
}
}
protected override void Start()
{
base.Start();
//計算搖桿塊的半徑
radius = bgTrans.sizeDelta.x * 0.5f;
}
public override void OnDrag(PointerEventData eventData)
{
base.OnDrag(eventData);
var contentPostion = this.content.anchoredPosition;
if (contentPostion.magnitude > radius)
{
contentPostion = contentPostion.normalized * radius;
SetContentAnchoredPosition(contentPostion);
}
// Debug.Log("搖桿滑動,方向:" + contentPostion);
if(null != onDragCb)
onDragCb(contentPostion);
}
public override void OnEndDrag(PointerEventData eventData)
{
base.OnEndDrag(eventData);
// Debug.Log("搖桿拖動結束");
if (null != onStopCb)
onStopCb();
}
public void OnPointerDown(PointerEventData eventData)
{
//點擊到搖桿的區域,搖桿移動到點擊的位置
trans.position = uiCam.ScreenToWorldPoint(eventData.position);
trans.localPosition = new Vector3(trans.localPosition.x, trans.localPosition.y, 0);
}
}
把JointedArm.cs分別掛到leftJointedArm和rightJointedArm上,賦值對應的center,

運行Unity,搖桿測驗效果如下:

接下來我們要實作左搖桿控制主角移動并播放跑的影片,右搖桿控制攝像機角度旋轉,
3、主角移動控制
3.1、影片控制
注:關于Animator組件的詳細使用可以參見我之前寫的這篇文章:《Unity影片狀態機Animator使用》
打開角色的影片控制器檔案CharacterController,

可以看到,兩個動作,一個idle(站立)一個Run(跑),

到Parameters(引數)里面有一個AnimationPar引數,這個引數就是用來控制站立與跑著兩個影片的過渡條件的,

從Idle過渡到Run的條件是AnimationPar等于1,

從Run過渡到Idle的條件是AnimationPar等于0,

這樣,我們就可以在代碼中通過這個引數來控制影片的過渡了,例:
// public Animator anim;
// 站立 -> 跑
anim.SetInteger("AnimationPar", 1);
// 跑 -> 站立
anim.SetInteger("AnimationPar", 0);
3.2、移動控制
主角的移動控制包括坐標和角度的變化,當搖桿向左滑,主角向左移動,同時主角的朝向也跟著轉向左邊,
移動我們可以設定transfrom的position屬性來實作,轉向我們可以設定transfrom的forward屬性來實作,例:
// Vector3 moveDirection; 搖桿向量
// float speed; 移動速度
// float turnSpeed; 轉向速度
transform.position += moveDirection * speed * Time.deltaTime;
transform.forward = Vector3.Lerp(transform.forward, moveDirection, turnSpeed * Time.deltaTime);
3.3、主角腳本代碼
綜上,我們封裝一個主角腳本Player.cs,代碼如下:
// Player.cs
using System.Runtime.InteropServices;
using UnityEngine;
using UnityEngine.AI;
/// <summary>
/// 主角腳本
/// </summary>
public class Player : MonoBehaviour
{
// 移動速度
public float speed = 1f;
// 轉向速度
public float turnSpeed = 20f;
public Animator anim;
// 跟節點
public Transform rootTrans;
// 模型節點
public Transform modelTrans;
// 導航Agent
public NavMeshAgent navAgent;
// 是否在移動
private bool moving = false;
// 移動向量
private Vector3 moveDirection = Vector3.zero;
// 是否可移動
private bool canMove = true;
private void Awake() {
companyPosParticle.Stop();
homePosParticle.Stop();
}
void Update()
{
if (canMove && moving)
{
anim.SetInteger("AnimationPar", 1);
rootTrans.position += moveDirection * speed * Time.deltaTime;
modelTrans.forward = Vector3.Lerp(modelTrans.forward, moveDirection, turnSpeed * Time.deltaTime);
}
else
{
anim.SetInteger("AnimationPar", 0);
}
}
public void Move(Vector3 direction)
{
moveDirection = direction;
moving = true;
}
public void Stand()
{
moving = false;
}
將Player.cs腳本掛到主角物體上,在Inspector面板賦值腳本的成員變數,

我們再創建一個GameMgr.cs腳本來調度,
// GameMgr.cs
public Player player;
public JointedArm leftJointedArm;
private Transform playerTrans;
private Transform camTrans;
// ...
// 左搖桿 -------------------------------------------
leftJointedArm.onDragCb = (direction) =>
{
var realDirect = camTrans.localToWorldMatrix * new Vector3(direction.x, 0, direction.y);
realDirect.y = 0;
realDirect = realDirect.normalized;
player.Move(realDirect);
};
leftJointedArm.onStopCb = () => { player.Stand(); };
創建一個空物體重命名為GameMgr,把GameMgr.cs掛到這個物體上,并在Inspector面板中賦值腳本的成員變數,
3.4、主角移動測驗
運行測驗效果如下:

4、攝像機跟隨
我們創建一個CameraControler.cs腳本,實作攝像機跟隨主角的邏輯,代碼如下:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// 攝像機控制器
/// </summary>
public class CameraControler : MonoBehaviour
{
// 限制攝像機角度范圍
private const float Y_ANGLE_MIN = 10f;
private const float Y_ANGLE_MAX = 50.0f;
// 攝像機看向的物體
public Transform lookAt;
// 攝像機Transform
public Transform camTransform;
// 攝像機距離目標物體的距離
public float distance = 1.2f;
// 原始距離
private float originalDistance;
// 旋轉速度
public float rotateSpeed = 0.01f;
public float currentX = 0.0f;
public float currentY = 20.0f;
private void Start()
{
camTransform = transform;
originalDistance = distance;
}
private void Update()
{
if (rotating)
{
currentX += rotateDelta.x;
currentY += rotateDelta.y;
currentY = Mathf.Clamp(currentY, Y_ANGLE_MIN, Y_ANGLE_MAX);
}
}
private void LateUpdate()
{
Vector3 dir = new Vector3(0, 0, -distance);
Quaternion rotation = Quaternion.Euler(currentY, currentX, 0);
camTransform.position = lookAt.position + rotation * dir;
camTransform.LookAt(lookAt.position);
}
}
把CameraControler.cs腳本掛到主攝像機上,在Inspector面板賦值腳本的成員變數,運行Unity,可以看到有跟隨效果了,

5、攝像頭角度控制
我們在上面的CameraControler.cs腳本中添加兩個方法,如下:
// CameraControler.cs
private bool rotating;
private Vector2 rotateDelta;
public void RotateCam(Vector2 delta)
{
rotateDelta = delta * rotateSpeed;
rotating = true;
}
public void StopRotate()
{
rotating = false;
}
然后在GameMgr.cs中添加右搖桿的調度,
// GameMgr.cs
// 右搖桿 ------------------------------------------
rightJointedArm.onDragCb = (direction) =>
{
camCtrler.RotateCam(direction);
};
rightJointedArm.onStopCb = () => { camCtrler.StopRotate(); };
運行Unity,可以控制攝像頭角度旋轉了,

九、導航烘焙
如果我們想要實作點擊地圖某個位置,讓主角走到目標點,可以使用Unity的尋路導航功能,另外,這個功能也可以限制主角的移動區域,為了防止主角走到地圖外面,我們使用Navigation對場景進行導航烘焙,
首先選中地面,把地面設定為Static,

然后點擊選單Window / AI / Navigation,

點擊Bake分頁,點擊Bake按鈕,

看到地面蒙上了一層藍色的網,就說明烘焙成功了,

你可以在場景檔案所在目錄中看到它生成了一個NavMesh檔案,

另外,我們需要給主角添加NavMeshAgent組件,并根據主角模型大小設定Radius和Height,

如下:

運行Unity,測驗一下移動到地面邊界的效果,

十、傳送門觸發器
傳送門傳送,我使用了觸發器,檢測主角是否通過了傳送門,然后出發傳送邏輯,
給傳送門的前后添加兩個碰撞體,并勾選Is Trigger,

如下:

分別在兩個沖送門位置添加一個標記傳送目標位置的空物體,


給主角腳本Player.cs添加傳送的邏輯,
// Player.cs
public Transform homePos;
public Transform companyPos;
private string lastTrigger;
private void OnTriggerEnter(Collider other)
{
if("trigger1" == other.name || "trigger3" == other.name)
{
lastTrigger = other.name;
}
else
{
if("trigger1" == lastTrigger && "trigger2" == other.name)
{
// 執行傳送,從公司到家
rootTrans.position = homePos.position;
}
else if("trigger3" == lastTrigger && "trigger4" == other.name)
{
// 執行傳送,從家到公司
rootTrans.position = companyPos.position;
}
}
}
這樣,當主角傳過傳送門的時候就會按順序觸發觸發器,最終指向傳送邏輯,
十一、特效
傳送時加多一個特效吧,使用PhotoShop畫一個菱形,如下:

再畫個菱形邊框,

使用粒子系統制作特效,效果如下:

主要利用的是粒子系統的color over Lifttime和Size over Lifetime來達到上面的效果,

關于粒子系統的相關教程,可以參見我之前寫的這幾篇文章:
【游戲開發實戰】權游紅袍女在火中看到了什么,我看到了…(Unity | 粒子系統 | 火焰特效 | ParticleSystem | 手把手制作)
【游戲開發實戰】Unity使用ShaderGraph配合粒子系統,制作子彈拖尾特效(Fate/stay night金閃閃的大招效果)
【學Unity的貓】——第十五章:Unity粒子系統ParticleSystem,下雪啦下雪啦
【游戲開發實戰】手把手教你使用Unity制作一個飛機噴射火焰尾氣的粒子效果
十二、最終效果
家門口:

從家門口穿過傳送門:

從公司經過傳送門回家:

十三、工程原始碼
本工程我已上傳到CODE CHINA,感興趣的同學可自行下載學習,
地址:https://codechina.csdn.net/linxinfa/UnityPortalDemo
注:我使用的Unity版本為Unity 2021.1.9f1c1 (64-bit),

十四、完畢
好了,就到這里吧,最后希望廣州地鐵21號線高峰期可以提高發車頻率,不然真的很痛苦,
我是林新發:https://blog.csdn.net/linxinfa
原創不易,若轉載請注明出處,感謝大家~
喜歡我的可以點贊、關注、收藏,如果有什么技術上的疑問,歡迎留言或私信,我們下期見~
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/302830.html
標籤:其他
