准备工作
旁白改进
在上一次的制作中,旁白的字幕有些些微的问题,如果Text在三维空间中与摄像机之间有物体遮挡,字母的一部分会被遮住,我尝试了改变Canvas的Sorting Layers,但是仍有字幕遮挡现象发生,在搜索了很久后,我终于找到了解决办法,需要重新编写着色器,来让字幕总是优先显示于最上层。
首先新建Stantard Surface Shader文件,并在其中写入以下代码:
Shader "Custom/UIShader"
{
Properties
{
[PerRendererData] _MainTex("Font Texture", 2D) = "white" {
}
_Color("Tint", Color) = (1,1,1,1)
_StencilComp("Stencil Comparison", Float) = 8
_Stencil("Stencil ID", Float) = 0
_StencilOp("Stencil Operation", Float) = 0
_StencilWriteMask("Stencil Write Mask", Float) = 255
_StencilReadMask("Stencil Read Mask", Float) = 255
_ColorMask("Color Mask", Float) = 15
}
SubShader
{
LOD 100
Tags
{
"Queue" = "Transparent"
"IgnoreProjector" = "True"
"RenderType" = "Transparent"
"PreviewType" = "Plane"
"CanUseSpriteAtlas" = "True"
}
Stencil
{
Ref[_Stencil]
Comp[_StencilComp]
Pass[_StencilOp]
ReadMask[_StencilReadMask]
WriteMask[_StencilWriteMask]
}
Cull Off
Lighting Off
ZWrite Off
ZTest Always
Offset -1,-1
Blend SrcAlpha OneMinusSrcAlpha
ColorMask[_ColorMask]
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "UnityUI.cginc"
struct appdata_t
{
float4 vertex : POSITION;
float2 texcoord : TEXCOORD0;
float4 color : COLOR;
};
struct v2f
{
float4 vertex : SV_POSITION;
half2 texcoord : TEXCOORD0;
fixed4 color : COLOR;
};
sampler2D _MainTex;
float4 _MainTex_ST;
fixed4 _Color;
fixed4 _TextureSampleAdd;
v2f vert(appdata_t v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.texcoord = TRANSFORM_TEX(v.texcoord, _MainTex);
o.color = v.color * _Color;
#ifdef UNITY_HALF_TEXEL_OFFSET
o.vertex.xy += (_ScreenParams.zw - 1.0) * float2(-1,1);
#endif
return o;
}
fixed4 frag(v2f i) : SV_Target
{
fixed4 col = (tex2D(_MainTex, i.texcoord) + _TextureSampleAdd) * i.color;
clip(col.a - 0.01);
return col;
}
ENDCG
}
}
}
之后新建Material材质球,修改其着色器为刚刚编写的着色器,并将其赋给Text,之后字幕就会永远显示在画面的最外层了。
玩法具体描述
为了让工作内容与目标变得更加清晰,需要重新具体地描绘一遍玩法以及特殊机制:
- 整个场景没有全局光照,只有玩家自带的点光源;
- 在接触到(互动)部分物体后,这部分物体会变亮;
- 因为视觉受限,寻找物体的主要方式依靠听觉,所以会为特殊的物品添加声源;
- 与特殊物品互动后,会得到相应的道具或者触发机关推动关卡的进行;
- 特殊物体发光后,可以照亮部分黑暗地区,让原本模糊的关键道具变得可见;
- 设置一些隐藏房间和对通关无关紧要的奖励/彩蛋房间,设置成就;
- 在完成这些的基础上,添加关卡计时系统与存档/读档功能,添加主界面UI。
第一关
作为游戏的第一个关卡,其内容一定要简单明了,让玩家在轻松通关的同时掌握游戏的玩法,如果说出生时的房间是为了让玩家学会走路,那么第一关的职责就是让玩家认识这个世界,因此我并没有采取特别复杂的编排方式,我绘制了一个简单的关卡草图来描述第一个关卡:
第一关没有什么需要抉择的难题,玩家前往十字岔路时,过关的道路看似死胡同,而另一侧的门是上锁的,玩家只能前往右侧的房间。
因为保险柜不会发出声音而且环境幽暗,所以即使玩家进入左边的房间也不会有收获,这时候需要用旁白来引导玩家离开这个房间,在玩家进入另一个房间后,会循着声音找到喷泉与水瓶架子,玩家自然会明白用水瓶去装水。
但是这时候问题来了,装起来的水有什么用呢?
在玩家迷茫之际,泉水和架子的干扰声消失,只要玩家触碰过这两个物体,保险柜就会发出金属声响,引导玩家前往另一个房间,此时保险柜已经被喷泉点亮的灯光照耀现行,因此玩家就进入了输入密码的环节,而唯一的提示就是架子的水平数量,当玩家解开谜题后就会获得钥匙,之后的解密就变得一气呵成,玩家会经过开门——倒水——触发机关——根据声音找到打开的机关门——沿着楼梯过关的顺序通过第一关,第一关的初步设计就这样做好了。
脚本编写
道具
经过刚才的设计,很明显我们要为玩家设计一个道具系统,使用道具栏的方式会有很多局限性,例如彩蛋物品放在哪里、道具持有达到上限卡关等问题,因此不予采纳。
而背包使用起来较为繁琐,代码编写也比较复杂,我们仍然不去采用。
考虑到游戏本身操作并不复杂,涉及不到多个道具同时使用的情况,我决定把道具栏设为滚动式,只有一个当前道具栏位,使用切换键可以切换当前道具。
而道具的使用方式和互动键可以是同一按键,所以在编写道具脚本的同时,也要改变一下物体之间的交互方式。
因为代码过长,我将分区域进行展示:
[Header("ImgInstance")]
public GameObject itemNow;
public GameObject itemNext;
public GameObject itemPrev;
public GameObject itemTmp;
[Header("Parameters of Long Click")]
public float startChange;
public float changeSpan;
[Header("Flames")]
public Image flameNow;
public Image flameNext;
public Image flamePrev;
public Image flameTmp;
[Header("MoveAnimation")]
public float moveTime;
[Range(0,1f)]
public float moveSpeed;
[Range