[原创] 用Unity等比例制作广州地铁,广州加油,早日战胜疫情(Unity | 地铁地图 | 第三人称视角)

Posted 林新发

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[原创] 用Unity等比例制作广州地铁,广州加油,早日战胜疫情(Unity | 地铁地图 | 第三人称视角)相关的知识,希望对你有一定的参考价值。

文章目录

一、前言

嗨,大家好,我是新发。
最近比较忙,好几天没写文章了,这两天广州疫情的新闻频频上热搜,身在广州无时无刻不吊着个心,部分同事所在的区域已经封起来了,只能远程上班,我住的地区暂时还比较安全,不过也已经做好远程工作的准备了。
话说回来,我在广州上学、生活、工作了十年了,对广州有着特殊的情愫。
我这两天做了一个Demo,我在Unity中搭建了整个广州地铁路线地图,并做了第三人称视角相机跟随,双摇杆控制,可以登上广州塔鸟瞰整个广州。以此献给我热爱的大广州,效果如下:

注:今天广州大规模核酸检测,下午刚检测完毕,回家写这篇文章直到现在(2021-6-5 23:44),广州加油,早日战胜疫情!

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
下面我将讲解一下创作过程。

二、创建工程

1、创建工程

我用的Unity版本是2021.1.7f1c1 (64-bit),选择3D模板,工程名字起为GuangzhouGogo好了,点击创建。
在这里插入图片描述

创建成功,
在这里插入图片描述

2、创建目录

养成好习惯,先规范目录结构,创建一些文件夹,目录结构如下:
在这里插入图片描述

RawAssets目录主要是存放一些【生肉资源】或被场景依赖的资源;

注:关于【生肉资源】可以参见我之前写的这篇文章的说明:《Unity游戏开发——新发教你做游戏(三):3种资源加载方式》

Resources目录存放一些被代码动态加载 资源;
Scenes目录存放场景文件;
Scripts目录存放C#代码;
ThirdPart目录存放一些第三方工具或库;

三、地铁路线地图制作

1、广州地铁图

我们先找一下广州地铁图,我找到的最新版本是这个:
在这里插入图片描述
截止到2020年6月30日,广州市已经开通的地铁线路有14条,全市地铁站点有213个
先把这个图弄到Unity中,
在这里插入图片描述

图片格式设置为Sprite (2D and UI)
在这里插入图片描述
把地铁图拖到场景中,调整坐标、旋转、缩放,如下:
在这里插入图片描述
效果如下:
在这里插入图片描述

2、HexTiles:六变形3D瓦片工具的基本操作

2.1、HexTiles工具下载

地图我想做得比较风格化,于是我找了一个六变形3D瓦片工具:HexTiles,这个工具可以在GitHub上找到,地址:https://github.com/RoryDungan/HexTiles
在这里插入图片描述

注:2D瓦片工具可以参见我之前写的这篇文章:《[Unity 2D] 重温红白机经典FC游戏,顺便教你快速搭建2D游戏关卡(Tilemap | 场景 | 地图)》

下载下来后,放入工程的ThirdPart目录中,只需保留它的CodePlugins文件夹即可,如下:
在这里插入图片描述

2.2、创建瓦片材质球

我们要使用工具来绘制3D瓦片,我们需要先为瓦片制作材质球,我们先做一个绿色的材质球,在RawAssets/Materials/tiles目录右键点击菜单Create/Material,创建材质球;
在这里插入图片描述
材质球重命名为green,设置材质球的颜色为绿色,然后我们不想要有反光的效果,可以调整光滑度为0
在这里插入图片描述
以此类推,把地铁路线的颜色都做一个对应的材质球~
在这里插入图片描述

2.3、创建瓦片容器

Hierarchy视图空白处右键点击菜单Hex tile map,创建瓦片容器,
在这里插入图片描述
瓦片容器上会有个HexTileMap组件,可以看到对应的功能按钮,我们后续绘制的瓦片都会在这个Hex tile map的子节点下。
在这里插入图片描述

2.4、绘制瓦片

确保Scene视图的Gizmos按钮是激活状态的,
在这里插入图片描述
点击添加瓦片按钮,把要使用的材质球拖到Material槽中,
在这里插入图片描述
然后在场景中按住鼠标拖动即可绘制瓦片了,在这里插入图片描述

2.5、设置瓦片高度

我们想要在更低一层绘制瓦片,可以调整Height offset,比如我调整为-0.5,顺便把材质球改成白色,
在这里插入图片描述
现在我们绘制瓦片,可以看到是在原来的瓦片的下层绘制了,并且边缘衔接处会自动补上,
在这里插入图片描述

2.6、擦除瓦片

我们想把绘制的瓦片擦除,可以点击擦除瓦片按钮,
在这里插入图片描述
然后在场景中点击要擦除的瓦片,
在这里插入图片描述

2.7、瓦片材质绘制:材质替换

点击材质绘制按钮,然后目标材质,
在这里插入图片描述
然后点击要绘制的瓦片即可,
在这里插入图片描述

2.8、大面积刷瓦片

上面我们是一个瓦片一个瓦片刷的,我们可以调整刷子的尺寸(Brush size),比如我调整为3
在这里插入图片描述

这样就可以大面积刷瓦片了,
在这里插入图片描述

2.9、大面积擦除瓦片

同样,我们也可以大面积擦除瓦片,
在这里插入图片描述

在这里插入图片描述

3、搭建地铁路线

开始沿着地铁路线铺路,
在这里插入图片描述
铺啊铺,铺啊铺,
在这里插入图片描述
把所有地铁路线图都铺好,在这里插入图片描述

四、地铁站点制作

1、地铁站点模型

站点就用一个简单的柱体就好了,创建一个Cube
在这里插入图片描述
拉长,
在这里插入图片描述
由两个柱体(底部和顶部)组成一个站点的模型,
在这里插入图片描述

2、地铁站点材质球

创建两个材质球,分别作为站点底部和顶部的材质,
在这里插入图片描述
将材质球赋值给上面的柱体,
在这里插入图片描述

3、地铁站点名称:TextMeshPro

地铁站点的名字我使用了TextMeshPro来显示,它的好处是在3D空间下近距离观察文字也是很清晰的。

注:关于TextMeshPro的使用教程可以参见我之前的这篇文章:《手把手教,Unity使用TextMeshPro显示字体》

下面我讲下操作步骤。

3.1、导入字体文件

找一个你喜欢的字体(TTF格式),比如我找的是免费的思源字体,
在这里插入图片描述
将其放入Unity工程中,

在这里插入图片描述

3.2、安装TextMeshPro

点击菜单Window / Package,打开Package Manger窗口,
在这里插入图片描述
Packages选择Unity Registry,然后搜索textmeshpro,选择TextMeshPro,点击Install按钮,如果你已经安装过,则没有Install按钮了。
在这里插入图片描述
安装成功后,可以看到Window菜单中多了一个TextMeshPro菜单,
在这里插入图片描述

3.3、制作字符集

我们需要为TextMeshPro创建一个字符集(一个txt文件),把我们需要用到的字放在这个字符集文件里,如下,在TTF同级目录中创建一个txt文件(characters.txt),
在这里插入图片描述
把广州市所有的地铁站名字都放在这个characters.txt文件中,
在这里插入图片描述

3.4、制作Font Asset

点击菜单 Window / TextMeshPro / Font Asset Creator
在这里插入图片描述
首次打开会弹出下面这个窗口,点击Import TMP Essentials按钮,
在这里插入图片描述
Font Asset Creator窗口中,设置Source Font File为我们的字体TTF文件,设置Character SetCharacters from File,设置Character File为我们的字符集文件characters.txt,最后点击Generate Font Atlas按钮,
在这里插入图片描述
此时会生成一个纹理,我们点击Save保存,
在这里插入图片描述
保存到RawAssets/Fonts目录中,
在这里插入图片描述
如下(font SDF.asset
在这里插入图片描述

3.5、显示地铁站名字

在地铁站节点下创建一个空物体,重命名为name
在这里插入图片描述
给这个name节点添加TextMeshPro - Text组件,
在这里插入图片描述
Text Input中输入地铁站的名字,比如珠江新城
设置Font Asset为我们上面生成的font SDF
在这里插入图片描述
此时效果如下:
在这里插入图片描述
设置一下字号,设置一下对其方式,
在这里插入图片描述
设置一下坐标和显示区域大小,
在这里插入图片描述
效果如下:
在这里插入图片描述
为了能在四个面都看得到地铁站名字,我们再复制出另外三份,
在这里插入图片描述
调整下坐标和旋转角度,效果如下:
在这里插入图片描述

3.6、保存站点预设

养成好习惯,需要重复使用的物体(模板)我们最好保存成预设,将其保存到RawAssets/Prefabs目录中,如下:
在这里插入图片描述

4、排放地铁站点

按照地铁线路,依次摆放地铁站点,
在这里插入图片描述
摆呀摆,
在这里插入图片描述
终于把地铁站全部弄好了,
在这里插入图片描述
把站点按地铁路线收纳好,方便管理,
在这里插入图片描述

5、地平面

创建一个Plan作为地平面,这样影子可以投射到地面上,
在这里插入图片描述
给地面创建一个材质球,材质球赋值给Plan
在这里插入图片描述
调整材质球颜色,
在这里插入图片描述

五、导航系统:NevMesh烘焙

地铁路线有了,接下来就给它烘焙NevMesh吧,以便后面支持导航功能。

1、设置烘焙对象为Static

因为NevMesh只对场景中的静态对象进行烘焙,所以我们需要先把地铁路线设置为Static的。
选择HexTileMap节点,将其设置为Static
在这里插入图片描述
点击Yes, change children,即所有的子节点都设置为Static
在这里插入图片描述

2、NevMesh烘焙

点击菜单Window / AI / Navigation
在这里插入图片描述
Navigation窗口中,点击Bake标签页,
调节一下Agent Radius,因为我们的地铁路面比较窄,所以这里的Agent Radius需要调小一点,
最后点击Bake按钮即可,
在这里插入图片描述
烘焙成功后,可以看到路面上出现了蓝色的网格,
在这里插入图片描述
同时,在场景文件目录中,会看到生成了一个与场景同名的文件夹,里面的NavMesh.asset保存的就是场景的导航烘焙信息,
在这里插入图片描述

六、摇杆制作

地图有了,接下来就是主角了,不过在做主角之前,我们先把摇杆做一下吧~

1、摇杆图片

摇杆的图片很简单,一个圆就可以了,
在这里插入图片描述

2、Canvas与UICamera

创建一个Canvas,作为后面UI的父节点,
在这里插入图片描述

在这里插入图片描述

在创建一个Camera来专门渲染Canvas
在这里插入图片描述

将其重命名为UICamera
在这里插入图片描述

设置UICameraClear FlagsDepth only,并设置Culling MaskUI,这样它就只会渲染UI层,把Projection设置为Orthographic(正交),
在这里插入图片描述

接着把CanvasRender Mode(渲染模式)改为Screen Space - Camera(即由摄像机来渲染),然后把Render Camera设置为刚刚的UICamera
接着再设置下分辨率适配,把Canvas Scale组件的UI Scale Mode设置为Scale with Screen Size,把分辨率设置为1280, 720
在这里插入图片描述
另外,因为UI已交给UICamera来渲染,所以Main Camera不需要再渲染UI层了,把Main CameraCulling MaskUI勾选去掉,
在这里插入图片描述

3、摇杆UI制作

Canvas节点上右键点击菜单UI / Panel,创建一个Panel
在这里插入图片描述
Image组件禁用掉,因为我们不需要Panel显示出来,
在这里插入图片描述
Panel下创建一个Image,重命名为leftJointedArm,作为左摇杆的父节点,
在这里插入图片描述

设置它的锚点为bottom - left,即屏幕左下角,调整坐标和宽高,
在这里插入图片描述
像这样子,
在这里插入图片描述

把它的Coloralpha调为0,因为我们只需要利用它的区域来检测触碰,我们不需要肉眼看见它,
在这里插入图片描述
接着在它的子节点下创建两个Image,分别命名为bgcenter
在这里插入图片描述
它们的Source Image都设置为摇杆的图片资源,
在这里插入图片描述
分别调整下bgcenter的大小和颜色透明度,效果如下:
在这里插入图片描述
同理再做一个右摇杆,
在这里插入图片描述
效果如下:
在这里插入图片描述

3、摇杆逻辑代码

UnityUGUI提供了ScrollRect组件,非常适合用来制作摇杆,我们继承ScrollRect然后实现OnDragOnEndDrag方法,可以很方便地获取到摇杆的遥控数据,另外,为了检测区域点击,我们再实现IPointerDownHandler接口。
在这里插入图片描述

创建摇杆脚本JointedArm.cs,
在这里插入图片描述
JointedArm .cs代码如下:

using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
using System;

public class JointedArm : ScrollRect, IPointerDownHandler
{
    public Action<Vector2> onDragCb;
    public Action onStopCb;

    protected float mRadius = 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();
        //计算摇杆块的半径
        mRadius = bgTrans.sizeDelta.x * 0.5f;
    }

    public override void OnDrag(PointerEventData eventData)
    {
        base.OnDrag(eventData);
        var contentPostion = this.content.anchoredPosition;
        if (contentPostion.magnitude > mRadius)
        {
            contentPostion = contentPostion.normalized * mRadius;
            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);
    }
}

4、挂摇杆逻脚本

JointedArm .cs分别挂到leftJointedArmrightJointedArm上,赋值对应的center
在这里插入图片描述

5、摇杆测试

运行Unity,摇杆测试效果如下:
在这里插入图片描述

七、角色、动画与控制

1、角色模型下载

主角我在AssetStore上找到了一个心仪的模型,推荐给大家,
AssetStore地址:https://assetstore.unity.com/packages/3d/characters/humanoids/sci-fi/stylized-astronaut-114298
在这里插入图片描述

注:更多模型下载可以参见我之前写的这篇文章:
《Unity游戏开发——新发教你做游戏(二):60个Unity免费资源获取网站》

将模型下载导入Unity中,
在这里插入图片描述

2、动画控制器

注:关于Animator组件的详细使用可以参见我之前写的这篇文章:《Unity动画状态机Animator使用》

打开角色的动画控制器文件CharacterController
在这里插入图片描述
可以看到,两个动作,一个idle(站立)一个Run(跑),
在这里插入图片描述
Parameters(参数)里面有一个AnimationPar参数,这个参数就是用来控制站立与跑着两个动画的过渡条件的,
在这里插入图片描述
Run过渡到Idle的条件是AnimationPar等于1
在这里插入图片描述
Idle过渡到Run的条件是AnimationPar等于0
在这里插入图片描述

这样,我们就可以在代码中通过这个参数来控制动画的过渡了,例:

// public Animator anim; 

// 站立 -> 跑
anim.SetInteger("AnimationPar", 1);
// 跑 -> 站立
anim.SetInteger("AnimationPar", 0);

3、主角出场

在场景中创建一个空物体,重命名为Player
在这里插入图片描述
把主角模型拖到Player子节点中,把主角模型也命名为Player
在这里插入图片描述
这样,场景中出现了我们的主角了,
在这里插入图片描述
因为主角需要在地铁路线上跑,我们用了导航系统NevMesh,所以主角需要挂NevMeshAgent组件,
在这里插入图片描述
调节Radius(半径)与Height(高度)使之与主角模型匹配,
在这里插入图片描述
在这里插入图片描述

4、摇杆控制主角

写一个Player.cs脚本,主要逻辑如下,

// Player.cs 

using UnityEngine;

public class Player : MonoBehaviour
{
	public float speed = 1f;
	public float turnSpeed = 20f;
	
	public Animator anim;
	public Transform rootTrans;
	public Transform modelTrans;
	
	private bool moving = false;
	private Vector3 moveDirection = Vector3.zero;
	
	// ...
	
	void Update()
	{
	    if (moving)
	    {
	    	// 播放跑动画
	        anim.SetInteger("AnimationPar", 1);
			// 更新主角坐标
	        rootTrans.position += moveDirection * speed * Time.deltaTime;
	        // 更新主角朝向,使用Vector3.Lerp进行插值运算,使得角度变化不那么生硬
	        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父节点上,赋值对应的变量,
在这里插入图片描述
为了方便管理,我们再封装一个游戏管理器GameMgr.cs,由游戏管理器来调度摇杆与主角,

// GameMgr.cs

public Player player;
// 左摇杆
public JointedArm leftJointedArm;
// 摄像机的Transform
private Transform camTrans;

// ...

leftJointedArm.onDragCb = (direction) =>
	{
		// 摇杆向量转世界坐标系下的向量
	    var以上是关于[原创] 用Unity等比例制作广州地铁,广州加油,早日战胜疫情(Unity | 地铁地图 | 第三人称视角)的主要内容,如果未能解决你的问题,请参考以下文章

广州软件公司最有名气的是哪几家

基于Unity开发广州幻境的Handy结合HTC Tracker的开发坑

12个保养卵巢的最好方法

广州热门摄影特色景点有哪些?

三日小计

GDKOI2018游记