新建项目,并在Assets下右键Import Package ->Custom Package导入资源。如下图所示。
地形工具的使用可以参考Unity官方文档
在Hierarchy中右键创建Terrain(地形),同时在Assets中会出现New Terrain(保存的是地形相关数据),将其重命名为Terrain Data并将其放入创建的Terrain文件夹中。
在Hierarchy视图中选中Terrain,可以在Inspector视图中看到Terrain游戏物体的组件Terrain,这时点击齿轮(Terrain Settings),在其中可以设置许多地形相关属性。找到Terrain Width 和Terrain Length以及Terrain Height(地形的最大高度),将他们的值设为50,每一个在修改后,按下回车将会生效。如下图所示。
其中
Brush size:用来调整刷子尺寸的
Opacity:地形强度,当Opacity越大时,地形升高的越快。
按住shift键并且用刷子刷地形可以降低地形。
详细的可以参考Unity官方文档
此工具可以将地形设计的更加平滑。
使用 Paint Texture 工具可将纹理(如草、雪或沙)添加到地形。如下图所示。
详情请参考Unity官方文档的Paint Texture和地形图层
通过点击Paint Trees图标可以在地形上摆放一些树,如下图所示。
详细请参考Unity官方文档
通过点击Paint Details图标,可以对草和其他一些细节进行处理,如下图所示。
详细的请参考Unity官方文档
此处自由发挥,就不描述了。
由于太亮,所以需要**调节光的强度。**如下图所示。
最终效果类似于如下图。
让相机和当前视野保持一致。选择Main Camera -> GameObject -> Align With View。如下图所示。
在场景中创建球体,运行游戏发现球体在半空中并没有掉落,这时就需要给球体游戏对象添加Rigidbody(刚体)组件来实现物理效果。
Mass:对象的质量(默认为千克)
Drag:阻力
Angular Drag:旋转时的阻力
Use Gravity:是否受重力影响
Freeze Position:锁定刚体沿世界 X、Y 和 Z 轴的移动。
Freeze Rotation:锁定刚体围绕局部 X、Y 和 Z 轴旋转。
有关刚体的更多信息,请参考Unity官方文档
碰撞器组件定义对象的形状以便用于物理碰撞。如下图所示,当禁用球体的碰撞器(Sphere Collider)后,球体会直接穿过地面,而没禁用碰撞器时,则会与地面发送物理碰撞效果,也就不会穿过地面。
注意:碰撞发生的条件,两个物体必须都有碰撞体(Collider),并且其中一个需要有刚体(Rigidbody)。
关于碰撞器的详细信息可以查看Unity官方文档
在Assets下创建Scripts文件夹,在Scripts中创建命名为Sphere的C#脚本,并将脚本挂载到Sphere游戏物体上。如下图所示。
可以使用以下方法(详情可以参考Unity官方文档)来进行碰撞检测,这些方法都是由Unity调用的:
//当该碰撞体/刚体已开始接触另一个刚体/碰撞体时,调用 OnCollisionEnter。(碰撞发生时调用)
private void OnCollisionEnter(Collision collision) {
}
//当该碰撞体/刚体已停止接触另一个刚体/碰撞体时,调用 OnCollisionExit。(碰撞结束时调用)
private void OnCollisionExit(Collision collision) {
}
//对应正在接触刚体/碰撞体的每一个碰撞体/刚体,每帧调用一次 :ref::OnCollisionStay。(碰撞过程中调用)
private void OnCollisionStay(Collision collision) {
}
//当该碰撞体/刚体已开始接触另一个刚体/碰撞体时,调用 OnCollisionEnter。(碰撞发生时调用)
private void OnCollisionEnter(Collision collision) {
//获取我们和哪一个碰撞器发生了碰撞
print(collision.collider);
//获取我们碰撞的游戏物体的名字
print(collision.collider.name);
//获取我们碰撞的游戏物体的标签
print(collision.collider.tag);
}
有时候我们需要做一些机关,当角色靠近机关时会触发一些效果,这时我们就可以将碰撞器设置为触发器(勾选 Is Trigger)。如下图所示。可以创建一个Cube,移除掉Mesh Filter和Mesh Renderer组件。
或者直接创建一个空游戏物体(Create Empty),然后给它添加碰撞器(Collider),并将碰撞器的Is Trigger勾选。并可以使用Edit Collider图标对Collider范围进行调节。如下图所示。
可以使用以下方法来进行触发检测,详情参考Unity官方文档
//当一个游戏物体开始进入到另一个游戏物体触发区域时,调用OnTriggerEnter
private void OnTriggerEnter(Collider other) {
}
//当一个游戏物体离开另一个游戏物体触发区域时,调用OnTriggerExit
private void OnTriggerExit(Collider other) {
}
//当一个游戏物体正在另一个游戏物体触发区域时,调用OnTriggerStay
private void OnTriggerStay(Collider other) {
}
//当一个游戏物体开始进入到另一个游戏物体触发区域时,调用OnTriggerEnter
private void OnTriggerEnter(Collider other) {
//获取触发器
print(other);
//获取触发器所在游戏物体的名字
print(other.name);
//获取触发器所在游戏物体的标签
print(other.tag);
}
关于光源的详细信息可以参考Unity官方文档
方向光对于在场景中创建诸如阳光的效果非常有用。方向光在许多方面的表现很像太阳光,可视为存在于无限远处的光源。方向光没有任何可识别的光源位置,因此光源对象可以放置在场景中的任何位置。
点光源位于空间中的一个点,并在所有方向上均匀发光。照射到表面的光线的方向是从接触点返回到光源对象中心的线。强度随着远离光源而衰减,在到达指定距离时变为零。点光源可用于模拟场景中的灯和其他局部光源。还可以用点光源逼真地模拟火花或爆炸照亮周围环境。
像点光源一样,聚光灯具有指定的位置和光线衰减范围。不同的是聚光灯有一个角度约束,形成锥形的光照区域。锥体的中心指向光源对象的发光 (Z) 方向。聚光灯锥体边缘的光线也会减弱。加宽该角度会增加锥体的宽度,并随之增加这种淡化的大小,称为“半影”。
聚光灯通常用于人造光源,例如手电筒、汽车前照灯和探照灯。
面光源是通过空间中的矩形来定义的。光线在表面区域上均匀地向所有方向上发射,但仅从矩形的所在的面发射。无法手动控制面光源的范围,但是当远离光源时,强度将按照距离的平方呈反比衰减。由于光照计算对处理器性能消耗较大,因此面光源不可实时处理,只能烘焙到光照贴图中。
给场景添加光源,并对光的颜色做一些设置。
给火堆添加光源,并将其设为Prefab(预制体)。
当我们在场景中不使用光照贴图的话,光对场景的影响是需要实时计算的,这样比较耗费性能。如果视野Lightmapping的话,会提前将光照信息计算好,然后烘焙成一个贴图,并将贴图贴到模型上,这样在实际运行中就不需要进行重复计算了。
由于火光会有动画效果,一闪一闪的,如果用Backed的话,效果是不变的。所以火堆这里需要用Mixed。
观察Scene视图,发现其中没有阴影。
选择Bonfire预制体,创建粒子系统。
调整形状。
调整Start LifeTime和Start Speed
比较详细的过程可以去看siki学院的视频
最终效果,如下图所示。
创建动画
给灯光添加运动动画效果,让火苗左右摇摆。
整个动画效果,如下图所示。
要使用导航系统需要进行导航网格的烘焙,在烘焙之前需要确定Environment的static是勾选上的(确保Navigation Static勾选上就行了)。
在Navigation -> Bake ->Bake按钮。可通过Agent Radius来调节代理半径(类似于在障碍物的范围不可进行导航)。
对于草之类的,角色应该是可以通过的,我们需要将其Navigation Static勾选取消掉,然后再重新进行烘焙。
对于不能到达的,要选中物体->Navigation->Object->AI->Navigation Area->Not Walkable。
想要控制角色在网格上行走,需要给角色添加导航组件Nav Mesh Agent,可通过Radius和Height调整宽度和高度。
通过鼠标点击地面,获取鼠标点击的点,将这个点设置为目的地,然后让角色到达目的地。
在Scripts文件夹下创建Hero脚本,并将其挂载到Hero游戏物体上。
void Update() {
//鼠标左键按下
if (Input.GetMouseButtonDown(0)) {
//射线检测
//ScreenPointToRay:将屏幕坐标转换为射线
//Input.mousePosition:鼠标位置
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
//用于从射线投射获取信息的结构。
RaycastHit hit;
//Physics.Raycast():向场景中的所有碰撞体投射射线,并返回有关命中对象的详细信息,信息保存在hit中。
if (Physics.Raycast(ray, out hit)) {
print(hit.point);
}
}
}
在Hero脚本中定义属性public NavMeshAgent agent;
并将组件Nav Mesh Agent拖入。如下图所示。
在判断代码中用agent调用SetDestination()方法
if (Physics.Raycast(ray, out hit)) {
//print(hit.point);
//设置目的地
agent.SetDestination(hit.point);
}
效果,如下图所示。
完整代码:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;
public class Hero : MonoBehaviour {
public NavMeshAgent agent;
// Start is called before the first frame update
void Start() {
}
// Update is called once per frame
void Update() {
//鼠标左键按下
if (Input.GetMouseButtonDown(0)) {
//射线检测
//ScreenPointToRay:将屏幕坐标转换为射线
//Input.mousePosition:鼠标位置
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
//用于从射线投射获取信息的结构。
RaycastHit hit;
//Physics.Raycast():向场景中的所有碰撞体投射射线,并返回有关命中对象的详细信息,信息保存在hit中。
if (Physics.Raycast(ray, out hit)) {
//print(hit.point);
//设置目的地
agent.SetDestination(hit.point);
}
}
}
}
调节相机到合适的角度,创建FollowTarget脚本,并将其挂载到Main Camera游戏物体上。
在FollowTarget脚本中定义属性public Transform hero;
保存,并在Unity中将Hero赋给hero。如下图所示。
代码:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class FollowTarget : MonoBehaviour {
//因为跟随目标,只需要目标的位置
public Transform hero;
//位置的偏移
private Vector3 offset;
// Start is called before the first frame update
void Start() {
//当前游戏物体位置减去角色hero的位置
offset = transform.position - hero.position;
}
// Update is called once per frame
void Update() {
//相机跟随
transform.position = offset + hero.position;
}
}
效果:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;
public class Hero : MonoBehaviour {
public NavMeshAgent agent;
public Animator anim;
// Start is called before the first frame update
void Start() {
}
// Update is called once per frame
void Update() {
//鼠标左键按下
if (Input.GetMouseButtonDown(0)) {
//射线检测
//ScreenPointToRay:将屏幕坐标转换为射线
//Input.mousePosition:鼠标位置
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
//用于从射线投射获取信息的结构。
RaycastHit hit;
//Physics.Raycast():向场景中的所有碰撞体投射射线,并返回有关命中对象的详细信息,信息保存在hit中。
if (Physics.Raycast(ray, out hit)) {
//print(hit.point);
//设置目的地
agent.SetDestination(hit.point);
}
}
//agent.velocity.magnitude:速度的大小
anim.SetFloat("speed",agent.velocity.magnitude);
}
}