版权申明:

  • 本文原创首发于以下网站:
  1. 博客园『优梦创客』的空间:https://www.cnblogs.com/raymondking123
  2. 优梦创客的官方博客:https://91make.top
  3. 优梦创客的游戏讲堂:https://91make.ke.qq.com
  4. 『优梦创客』的微信公众号:umaketop
  • 您可以自由转载,但必须加入完整的版权声明

搭建场景:

  • 场景布局,设置static属性,设置Layer层,调整属性(Carmera,Lighting)

  • 一般复杂的场景为了渲染效果都会做的很精细(有很多三角形构成),但是碰撞检测这种内部事件就可以简单点,会有场景的一个低模mesh来表征碰撞,降低性能消耗 //mesh collider的convex选项表示该模型是个凸多边形,(一般我们创建的都是凸多边形,像山洞啊什么的可能就为凹多边形)

  • 把场景都设置为Static //光照 导航 遮挡都是静态的 //在unity中参与Lightmaps烘焙的物体必须都是静态的对象

  • 增加灯光,对场景进行烘焙,把烘焙后的内容作为贴图,贴到物体上

  • 设置light的效果:


添加灯光:

  • Lighting烘焙: 把Light的Baking属性设置为Baked, 在需要烘焙的物体设置为static,可以在Lighting->Scene中把Precomputed realtime GI(预计算的实时光照去掉) //烘焙完成后会生成物体的光照贴图的信息(会有一个Scene同名的文件夹)贴到被设置为Static的物体上

  • Light中backed Shadow Radius决定了back类型的光照照出来的阴影的散射半径,但其值为0时,表示不散射,此时hard Shadow和soft shadow没有差别,但该值越大,soft shadow的阴影散射的就越厉害

  • Light中选择realtime下soft shadow的表现形式受到反射次数等参数影响,且需要在Lighting->scene中打开precomputed realtime GI,关闭Baked GI的选项

  • Light中的Mixed光照模式,包含了实时计算和Baked两个功能,当物体没有动,光照没变化时,采用baked的光照信息;当物体运动时,使用precomputer teatime 实时计算光照shadows //听说用的不多?实现的是静态物体透视到动态物体上的阴影,因为静态物体不会动,所以阴影基本是固定的,bake好阴影贴图,动态的贴到动态物体上?

  • Light中的Culling Mask表示有灯光作用的物体标签类型,不选择中的标签不受光照影响

  • 增加一盏场景灯和报警灯,场景灯提供场景关照,报警灯当发现玩家后进行报警的视觉和声音效果:

  • 报警灯脚本:

public class AlarmLight : MonoBehaviour
{
public float fadeSpeed = 2f; //灯光报警fade的速度(默认2s变化一次)
public float hightIntensity = 4f; //最高最低亮度
public float lowIntensity = 0.5f;
public float changeMargin = 0.2f; //插值阈值
public bool alarmOn; private float targetIntensity; //目标亮度值
private Light alarmLight;
private AudioSource audioSource; private void Awake()
{
alarmLight = GetComponent<Light>();
audioSource = GetComponent<AudioSource>();
alarmLight.intensity = 0;
targetIntensity = hightIntensity;
} // Update is called once per frame
void Update()
{
if (alarmOn)
{
if (!audioSource.isPlaying)
audioSource.Play(); alarmLight.intensity = Mathf.Lerp(alarmLight.intensity, targetIntensity, fadeSpeed * Time.deltaTime); if (Mathf.Abs(targetIntensity - alarmLight.intensity) < changeMargin)
{
if (targetIntensity == hightIntensity)
targetIntensity = lowIntensity;
else
targetIntensity = hightIntensity;
}
}
else
{
if (audioSource.isPlaying)
audioSource.Stop(); alarmLight.intensity = Mathf.Lerp(alarmLight.intensity, 0, fadeSpeed * Time.deltaTime);
}
}
}

烘焙场景Lightmaps

  • //烘焙场景是使用bake类型的灯光对场景进行烘焙,生成贴图贴到物体表面,用于表现灯光效果

  • Unity的官方解释:

  • 烘焙的意义:单独使用 Unity 实时光源的光线时,这些光线不会自动进行反射。为了使用全局光照等技术创建更逼真的场景,我们需要启用 Unity 的预计算光照解决方案;Unity 可以计算复杂的静态光照效果(使用称为全局光照(简称 GI)的技术)并将它们存储在称为光照贴图的纹理贴图中作为参考。这一计算过程称为烘焙。对光照贴图进行烘焙时,会计算光源对场景中静态对象的影响,并将结果写入纹理中,这些纹理覆盖在场景几何体上以营造出光照效果。

  • (这些光照贴图既可以包括照射到表面的直射光,也可以包括从场景内其他物体或表面反射的间接光。该光照纹理可与颜色(反照率)和浮雕(法线)之类的对象表面信息材质相关联的着色器一起使用。

  • 使用烘焙光照时,这些光照贴图在游戏过程中无法改变,因此称为“静态”。实时光源可以重叠并可在光照贴图场景上叠加使用,但不能实时改变光照贴图本身。

  • 通过这种方法,我们可在游戏中移动我们的光照,通过降低实时光计算量潜在提高性能,适应性能较低的硬件,如移动平台)

  • 预计算实时全局光照

  • 虽然静态光照贴图无法对场景中的光照条件变化作出反应,但预计算实时 GI 确实为我们提供了一种可以实时更新复杂场景光照的技术。

  • 通过这种方法,可创建具有丰富全局光照和反射光的光照环境,能够实时响光照变化。这方面的一个典型例子是一天的时间系统:光源的位置和颜色随时间变化。如果使用传统的烘焙光照,这是无法实现的

  • 优势和代价

  • 虽然可以同时使用烘焙 GI 光照和预计算实时 GI,但要注意,同时渲染两个系统的性能开销也是各自开销的总和。我们不仅需要在视频内存中存储两组光照贴图,而且还要在着色器中进行解码的处理。

  • 在什么情况下选择什么光照方法取决于项目的性质和目标硬件的性能。例如,在视频内存和处理能力局限性更大的移动端,烘焙 GI 光照方法可能具有更高性能。在具有专用图形硬件的独立计算机或最新款的游戏主机上,很可能可以使用预计算实时 GI,甚至同时使用这两个系统。

  • 必须根据特定项目和所需目标平台的性质来决定采用哪种方法。请记住,在面向一系列不同硬件时,通常情况下,性能最低的硬件将决定选取哪种方法。

  • 添加灯光:

  • Render Mode表示渲染的类型:Important表示逐像素渲染灯光,Not Impritant表示逐顶点渲染灯光,Auto表示自动

  • 设置渲染质量:Project Setting -> Quality面板,因为我们游戏场景中的资源偏多,可能存在效率问题,所以我们我们渲染质量选择Good; 设置Rendering中 Pixel Light Cout(逐像素渲染的灯光的数量),因为我们灯光都选择的Auto的类型,所有应该Unity会挑6盏灯逐像素渲染,其他都逐顶点渲染(Unity怎么挑的???)

  • 设置光照贴图的设置,之后就可以开始Bake了:

  • 具体参数可以参考Unity官方文档,2018开始Unity的Lightmapper中提供了一种新的烘焙方式Progressive,此方式进行渐进式的光照贴图烘焙,一个简单的场景烘焙时间都到10个小时起

  • 烘焙完成后可以在Scenes下找到和场景同名的文件夹中存放的就是烘焙后的数据,会作为贴图,贴到物体上

  • 烘焙完成后的贴图自动贴到了场景物体上,此时关闭所有灯光,物体依旧具有关照的效果


添加Tag的管理类 //用来定义Tag的字符串的静态变量

  • 添加转场效果 //加载场景逐渐变亮,退出场景逐渐变暗
  • 使用RawImage遮挡这个画面实现,代码部分:
public class ScreenFadeInOut : MonoBehaviour
{
public float fadeSpeed = 1.5f; private bool sceneStarting;
private RawImage rawImage; // Start is called before the first frame update
void Start()
{
sceneStarting = true;
rawImage = this.GetComponent<RawImage>();
} // Update is called once per frame
void Update()
{
if (sceneStarting)
{
rawImage.color = Color.Lerp(rawImage.color, Color.clear, fadeSpeed * Time.deltaTime); if (rawImage.color.a <= 0.05f)
{
rawImage.color = Color.clear;
sceneStarting = false;
rawImage.enabled = false;
}
}
} public void EndScene()
{
rawImage.enabled = true;
rawImage.color = Color.Lerp(rawImage.color, Color.black, fadeSpeed * Time.deltaTime);
if (rawImage.color.a > 0.95f)
SceneManager.LoadScene(0);
}
}
  • 添加游戏控制器GameController //负责控制背景音乐播放,角色位置管理
  • 为GameController添加Audio文件, //这里我们添加了两个Audio都设置play ON Awake和loop,区别在于normal主音量值为1 ,Panic被发现时的音量值为0 //我们要实现的效果就是当玩家被发现时,主音量逐渐降低,Panic音量逐渐提高
  • 具体脚本:
public class LastPlayerSighting : MonoBehaviour
{
public Vector3 position = new Vector3(1000f, 1000f, 1000f); //表示玩家最后一次被发现的位置,如果没有被发现,就设置为默认值
public Vector3 resetPosition = new Vector3(1000f, 1000f, 1000f);
public float lightHighIntensity = 0.25f; //主灯光的亮度范围
public float lightLowIntensity = 0f;
public float lightFadeSpeed = 7f;
public float musicFadeSpeed = 1f; //音乐变化的fade速率
public bool isPlayerFound = false; private AlarmLight alarmLightScript;
private Light mainLight; //主灯光
private AudioSource mainMusic; //主音乐和panic时播放的音乐
private AudioSource panicMusic;
private AudioSource[] sirens; //报警音乐
private const float muteVolume = 0f; //音乐的变化范围
private const float normalVolume = 0.8f; // Start is called before the first frame update
void Start()
{
alarmLightScript = GameObject.FindGameObjectWithTag(Tags.ALARM_LIGHT).GetComponent<AlarmLight>();
mainLight = GameObject.FindGameObjectWithTag(Tags.MAIN_LIGHT).GetComponent<Light>();
mainMusic = this.GetComponent<AudioSource>();
panicMusic = this.transform.Find("Secondary_music").GetComponent<AudioSource>();
//sirens = new AudioSource[];
} // Update is called once per frame
void Update()
{
isPlayerFound = (position != resetPosition); //当玩家被发现时,调低主灯光,打开报警灯,淡出主音乐,淡入panic音乐, 但玩家脱离危险后恢复;
mainLight.intensity = Mathf.Lerp(mainLight.intensity, isPlayerFound ? lightLowIntensity : lightHighIntensity, lightFadeSpeed * Time.deltaTime);
alarmLightScript.alarmOn = isPlayerFound;
mainMusic.volume = Mathf.Lerp(mainMusic.volume, isPlayerFound ? muteVolume : normalVolume, musicFadeSpeed);
panicMusic.volume = Mathf.Lerp(panicMusic.volume, isPlayerFound ? normalVolume : muteVolume, musicFadeSpeed);
}
}

添加CCTV Carmera

  • //CCTV闭路电视 //碰撞的触发依赖于Riggdbody组件,没有Riggdbody就不会触发trigger和collision(为了防止两个单独的collider互相触发)
  • 添加Carmera的模型
  • 添加mesh collider和spot光源
  • 添加碰撞脚本:
public class CCTVCollision : MonoBehaviour
{ private LastPlayerSighting lastPlayerSighting; private void Start()
{
lastPlayerSighting = GameObject.FindGameObjectWithTag(Tags.GAMECONTROLLER).GetComponent<LastPlayerSighting>();
} private void OnTriggerStay(Collider other)
{
if (other.tag == Tags.PLAYER)
{
lastPlayerSighting.position = other.transform.position;
}
} private void OnTriggerExit(Collider other)
{
if (other.tag == Tags.PLAYER)
{
lastPlayerSighting.position = lastPlayerSighting.resetPosition;
}
}
}
  • 添加Animator使其旋转, 注意Animator中可以设置curves调整动画变化的速率

添加Laser Grid(激光栅栏)

  • //一般Main Camera上会挂一个Audio Listener用于收音(一个场景只允许由一个Audio Listener)
  • //在unity当前版本中Audio Source要设置Spatial Blend到3D,下面的3D Sound Settings才会生效(会有一个空间衰减的相关)
  • 添加laser的模型(并使其和场景匹配),box collider,Light(红色点光源)和Audio Source
  • 为Laser添加脚本,控制激光栅栏的开关和碰撞检测:
  • 激光栅栏的开关:
public class LasterBlinking : MonoBehaviour
{
public float onTime; //灯灭的时间间隔
public float offTime; //灯亮的时间间隔 private float timer; //流逝的时间
private Renderer laserRenerer;
private Light laserLight; // Start is called before the first frame update
void Start()
{
laserRenerer = GetComponent<Renderer>();
laserLight = GetComponent<Light>();
timer = 0;
} // Update is called once per frame
void Update()
{
timer += Time.deltaTime;
if (laserRenerer.enabled && timer >= onTime)
{
laserRenerer.enabled = false;
laserLight.enabled = false;
timer = 0;
}
else if (!laserRenerer.enabled && timer >= offTime)
{
laserRenerer.enabled = true;
laserLight.enabled = true;
timer = 0;
}
}
}
  • 碰撞检测:
public class LaserPlayerDetection : MonoBehaviour
{
private void OnTriggerStay(Collider other)
{
if (other.tag == Tags.PLAYER && this.GetComponent<Renderer>().enabled)
{
LastPlayerSighting.Instance.position = other.transform.position;
}
}
}

Stealth——01场景的基本搭建以及基础逻辑的更多相关文章

  1. php从入门到放弃系列-01.php环境的搭建

    php从入门到放弃系列-01.php环境的搭建 一.为什么要学习php 1.php语言适用于中小型网站的快速开发: 2.并且有非常成熟的开源框架,例如yii,thinkphp等: 3.几乎全部的CMS ...

  2. ionic搭建与基础

    ionic搭建与基础 一.环境搭建 安装 npm install -g cordova npm install -g ionic 创建 项目 ionic start MyProject blank i ...

  3. 搭建webpack基础配置

    搭建webpack基础步骤: 1.去官方网站下载node.js(根据自己电脑的系统类型选择) 2.安装node.js完成后打开cmd命令提示符: 出现版本号证明安装成功 3.cd到工程目录下 npm ...

  4. 测试那些事儿—Linux搭建环境基础步骤

    Linux搭建环境基础步骤 准备工具:SecureCRT工具(Linux工具,连接服务器)FTP传输工具(上传文件到服务器)MySQL连接工具 安装包(以下文件均为压缩包rpm格式和tar.gz):J ...

  5. 搭建Istio基础环境

    需求 搭建istio基础环境(基于1.5.1版本) 安装步骤 在安装 Istio 之前,需要一个运行着 Kubernetes 的环境,安装步骤可以参考前面的文章 下载istio,然后解压,然后将 is ...

  6. 搭建SSM基础环境>基于idea

    目录 搭建SSM基础环境 创建一个Web项目 导入所需要的jar包 在项目目录下创建一个Resources文件夹并设置为类路径 在src目录下创建项目的初始文件夹目录 在resources文件夹下创建 ...

  7. 黑马程序员_java基础笔记(01)...java的环境搭建

    —————————— ASP.Net+Android+IOS开发..Net培训.期待与您交流!——————————  JavaSE(Java Standard Edtion java标准版)技术概况 ...

  8. JavaSE 学习笔记01丨开发前言与环境搭建、基础语法

    本蒟蒻学习过C/C++的语法,故在学习Java的过程中,会关注于C++与Java的区别.开发前言部分,看了苏星河教程中的操作步骤.而后,主要阅读了<Java核心技术 卷1 基础知识>(第8 ...

  9. 零基础Android学习笔记-01 安卓开发环境搭建

    安卓开发环境搭建. 1.首先准备JDK,从官网找到JDK下载地址,原来做.NET不熟悉JAVA,干脆用最新的,下载了JDK 1.7的版本.原来装过1.5还要配置环境变量什么的.但1.7好像很给力,装好 ...

随机推荐

  1. 基于opencv,开发摄像头播放程序

    前言 Windows下实现摄像视频捕捉有多种实现方式:各种方式的优劣,本文不做对比.但是,opencv是一款老牌开发库,在图像处理领域声名显赫.采用opencv来处理摄像视频,在性能和稳定性上,是有保 ...

  2. ccf 201809-4 再卖菜

    这题一开始不知道剪枝这种操作,只会傻傻地dfs. 然后dfs递归写80分超时,非递归写70分超时(纳尼?我一直以为非递归算法在时间上会更优秀一些,为什么会这样?!!) 剪一下枝就都能过了 #inclu ...

  3. mysql复制那点事(2)-binlog组提交源码分析和实现

    mysql复制那点事(2)-binlog组提交源码分析和实现 [TOC] 0. 参考文献 序号 文献 1 MySQL 5.7 MTS源码分析 2 MySQL 组提交 3 MySQL Redo/Binl ...

  4. 【CodeForces - 1200A】Hotelier(水题、模拟)

    Hotelier 直接翻译了 Descriptions Amugae的酒店由10人组成10客房.房间从0开始编号0到99 从左到右. 酒店有两个入口 - 一个来自左端,另一个来自右端.当顾客通过左入口 ...

  5. PKI机制总结

    PKI,全称是Public Key Infrastructure,可译为公钥基础设施.它是因特网中节点通信的安全保障机制,HTTPS中的‘S’就来源于PKI. 要去学习一个技术,首先要从它的源头考虑— ...

  6. PL/SQL 调用 JAVA代码

    1.直接在 SQL Developer中写入代码 create or replace and compile java source named "HelloWorld" as p ...

  7. WebSocket和HTTP协议的区别

    HTTP: 1,无状态协议. 2,短连接.(Ajax轮询方式或Long  poll方式实现“持久连接”状态) 2,被动型.  客户端请求->服务器端响应.服务端不能主动联系客户端,只能有客户端发 ...

  8. redis 有没有ACID事务

    看redis官网的介绍: redis确实是有事务的,但是和传统的ACID是否相同呢? 原子性(Atomicity) 原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生. 一致 ...

  9. StudyAndroid.1

    目标: 手动创建第一个Activity 开发环境: Android Studio 3.3.1 Build #AI-182.5107.16.33.5264788, built on January 29 ...

  10. 《Java 8 in Action》Chapter 2:通过行为参数化传递代码

    你将了解行为参数化,这是Java 8非常依赖的一种软件开发模式,也是引入 Lambda表达式的主要原因.行为参数化就是可以帮助你处理频繁变更的需求的一种软件开发模式.一言以蔽之,它意味 着拿出一个代码 ...