导入资源,把地图拖到hierarchy中,创建gameobject,命名为env,把地图拖到env中,该地图只是显示了出来,还需要添加碰撞属性collider,在地图属性中添加mesh collider,在资源model中有地图的collider文件,拖动到地图mesh collider属性中的mesh当中,选中地图,在视图中看到有绿色的框框,那就是碰撞器。由于里面有个车子的碰撞器没有放车子,需要把车子放到地图中,将车放到env中,创建名为Lights的gameobject,放directional light,背景为黑色,再放一个point light,背景黄色,在地图中放入多个point light,添加报警音乐,在地图中找到所有报警灯,在属性中add---->audio source,将资源的报警声拖动到audio source中,并把play on awake勾勾去掉。将警报的tag全部设置成siren。添加一个directional light命名为alermLight,改成红色背景,当警报响起时,调整他的intensity即可,添加一个alermLight脚本到灯光中,
public bool alermOn = false;
private float minIntensity = 0;
private float maxIntensity = 0.5f;
private float targetIntensity;
public float animationSpeed = 1;
public static alermLight _instance;
void Awake()
{
targetIntensity = maxIntensity;
alermOn = false;
_instance = this;
}
void Update () {
if (alermOn)
{
//通过插值函数渐变灯光的强度
GetComponent<Light>().intensity=Mathf.Lerp(GetComponent<Light>().intensity,
targetIntensity,Time.deltaTime* animationSpeed);
if (Mathf.Abs(targetIntensity- GetComponent<Light>().intensity) < 0.05f)
{
if (targetIntensity==maxIntensity)
{
targetIntensity = minIntensity;
}
else
{
targetIntensity = maxIntensity;
}
}
}
else
{
GetComponent<Light>().intensity = Mathf.Lerp(GetComponent<Light>().intensity,
0, Time.deltaTime * animationSpeed);
}
}
创建gameobject名为GameController,给他添加一个脚本
public bool alermOn = false;
private GameObject[] sirens;
void Awake()
{
alermOn = false;
sirens = GameObject.FindGameObjectsWithTag("siren");
}
void Update () {
alermLight._instance.alermOn = this.alermOn;
if (alermOn)
{
PlayAudio();
}
else
{
StopAudio();
}
}
void PlayAudio()
{
foreach(GameObject i in sirens)
{
if (!i.GetComponent<AudioSource>().isPlaying)
{
i.GetComponent<AudioSource>().Play();
}
}
}
void StopAudio()
{
foreach (GameObject i in sirens)
{
i.GetComponent<AudioSource>().Stop();
}
}
测试时发现alermLight的alerm on勾勾无法点击,我们把所有音频属性的load type中compressed in memory改成decompress on load即可
在地图中添加摄像机小物体,给摄像机添加light,在light的type改成spot,背景红色,intensity最大,在资源中找到cctvcam_collision,拖动到左侧的cctvcam_body中,可以实现摄像机检测附件是否有人,给cctvcam_collision添加mesh collider,把mesh rendener移除掉,在地图中添加多个摄像机,要实现摄像机的左右晃动的动画,步骤如下:
选中摄像机,点击Window--->animation,创建动画,保存位置自定,add property,点击transform里面的rotation,先设0毫秒时rotation.y为270, 60毫秒时rotation.y为90 120毫秒时rotation.y为90,其他全部是0,点击播放,可以在scene查看动画效果,摄像机0-1秒rotation.y从270转到90度,1-2秒转了回来,让其他摄像机也拥有此动画只需添加animator,把第一个相机的animator--->controller复制过去即可
要让相机添加碰撞警报,在gamecontroller脚本中添加
public static GameController _instance;
private Vector3 lastPlayerPosition = Vector3.zero;
在awake函数中添加_instance = this;这样创建一个单例并且设置一个vector3保存检测到人物的时候那个坐标
创建脚本名为cctvCollider放到摄像机collider中,代码如下
void OnTriggerStay(Collider other)
{
if (other.tag=="Player")
{
GameController._instance.alermOn = true; //发现人物,开启警报
GameController._instance.lastPlayerPosition = other.transform.position;//记录人物的当前坐标
}
}
接下来该添加光栅了,拖动到地图中,属性添加box collider(勾选is trigger)和light和audio source(勾选loop),添加Laser脚本
public float ontime;
public float offtime;
private float calctime=0;
public bool isflash=false; //光栅是否开始闪
void Update()
{
if (isflash)
{
calctime += Time.deltaTime;
if (GetComponent<Renderer>().enabled) //光栅是亮的
{
if (calctime>ontime) //到了光栅灭的时间
{
GetComponent<Renderer>().enabled = false;
calctime = 0;
}
}
else //光栅是灭的
{
if (calctime > offtime) //到了光栅灭的时间
{
GetComponent<Renderer>().enabled = true;
calctime = 0;
}
}
}
}
void OnTriggerStay(Collider other)
{
if (other.tag == "Player")
{
GameController._instance.alermOn = true;
GameController._instance.lastPlayerPosition = other.transform.position;
}
}
接下来该弄人物了,拖动人物模型到地图中,创建一个animator controller名为GameController拖动到人物的animator中,选中GameController,打开Window-->animator,拖动站立动画(Idle)到animator中,添加locomotion的blend tree,在其中添加run和walk动画,创建speed参数,blend tree的parameter选择speed,idel动画连接locomotion,条件为speed大于0.1,设置bool参数sneak(缓慢行动),拖动sneak动画到animator,从idel到sneak条件为speed>0.1和sneak=true。两个动作连接到idel动画的条件时speed<0.1。locomotion到sneak的来回连接条件就不说了
给player添加player脚本
public float moveSpeed = 3;
public float stopSpeed = 7;
public float rotateSpeed = 7;
private Animator anim;
void Awake()
{
anim = GetComponent<Animator>();
}
void Update () {
float h = Input.GetAxis("Horizontal");
float v = Input.GetAxis("Vertical");
if (Mathf.Abs(h)>0.1|| Mathf.Abs(v) > 0.1)
{
float newSpeed = Mathf.Lerp(anim.GetFloat("Speed"), 5.6f, moveSpeed * Time.deltaTime);
anim.SetFloat("Speed", newSpeed);
Vector3 targetDir = new Vector3(h,0,v);
Vector3 nowDir = transform.forward;
float angle = Vector3.Angle(targetDir, nowDir);
if (angle > 180)
{
angle = 360 - angle;
angle = -angle;
}
transform.Rotate(Vector3.up*angle*Time.deltaTime*rotateSpeed);
}
else
{
anim.SetFloat("Speed", 0);
}
发现人物会穿墙而过,人物添加capsule collider和rigidbody
如上代码发现angle>180的判断还是会出现向左转时,主角向右转一大圈绕回来的情况,所以采用四元数:
float newSpeed = Mathf.Lerp(anim.GetFloat("Speed"), 5.6f, moveSpeed * Time.deltaTime);
anim.SetFloat("Speed", newSpeed);
Vector3 targetDir = new Vector3(h,0,v);
Quaternion newRotation = Quaternion.LookRotation(targetDir, Vector3.up);
transform.rotation = Quaternion.Lerp(transform.rotation,newRotation,rotateSpeed*Time.deltaTime);
最后给sneak潜行添加代码
if (Input.GetKey(KeyCode.LeftShift))
{
anim.SetBool("Sneak", true);
}
else
{
anim.SetBool("Sneak", false);
}
要让潜行速度快点,在状态机sneak动画里面speed参数指的是该动画播放速度参数,改成2即可
给角色添加player标签,给gamecontroller添加角色进场背景音乐(勾选loop和play on awake,把volume音量调到最大),和触发警报时音乐(勾选play on awake 勾选loop把volume调成0)。在gameController声明musicNormal和musicPanic两个audioSuorce,拖动两个audiosource组件到脚本中(是组件不是音频源文件)。
这里注意的是:要把musicPanic勾选play on awake,音量调到0正常状态不响,如果不勾选play on awake,触发警报它不会响起来。