目标:游戏角色在可拾取物体前按下F键播放拾取动画,完成对目标物体的拾取
如下动图所示(速度加快了,主要看个效果,别介意)
实现方法
触发器和帧事件添加
主要通过的是GameObject的触发器以及动画的帧事件。
首先对游戏物体增加一个Sphere Collider组件,将该组件的Is Trigger勾选上,制作一个简易的触发器,如下图所示
,然后再在角色拾取动画中添加一个帧事件(这个游戏角色是在资源商店免费获取的,搜索关键字为:Character Pack),如下图所示
之后编辑C#脚本进行操作
C#脚本
我创建了一个ItemBehavior类专门管理这些可拾取的物体,一个BoyBehavior类来管理游戏角色类,这里只展示实现该功能的部分代码
BoyBehavior类
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class BoyBehavior : MonoBehaviour
{
//.........
//标志位
public bool isPickUp ;
void Start
{
isPickUp =false;
}
//这里定义了在动画PickUp中的添加的帧方法
public void PickUpThings()
{
isPickUp = true;
}
//.....
}
ItemBehavior类
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ItemBehavior : MonoBehaviour
{
public GameBehavior gameManager;
public BoyBehavior boy;
//这里使用onTriggerStay,即角色呆在触发器中
private void OnTriggerStay(Collider other)
{
if (other.name == "Boy" && boy.isPickUp)
{
Destroy(this.transform.parent.gameObject);
Debug.Log("Item collected!");
gameManager.Items += 1;
boy.isPickUp = false;
}
}
void Start()
{
gameManager = GameObject.Find("Game Manager").GetComponent<GameBehavior>();
//注意这里要对boy进行初始化,否则会报错
boy = GameObject.Find("Boy").GetComponent<BoyBehavior>();
}
}
代码解释:
首先在BoyBehavior类中创建了一个标志位isPickUp并将其初始化为false,当角色执行动画时会触发帧事件,即PickUpThings,在该方法中将isPickUp赋值为true。然后在ItemBehavior中写了onTriggerStay事件,在该事件中添加一个条件判断,当人物处在触发器范围内并且标志位isPickUp为true时,摧毁游戏事件,并将拾取的物体数加1。此外还要注意,在进行完这些操作之后要将isPickUp标志位重新置为false,否则人物一旦进入其他可拾取物体的触发器范围内将自动拾取,这是我们不希望看到的。
犯的错误:
将onTriggerStay误用成了onTriggerEnter,这一错误导致在运行游戏之后,人物在物体面前按下F键触发动画之后物体并没有消失,而是在人物重新进入触发器范围内物体才消失。原因就是两个函数的机制不同,onTriggerStay是在角色呆在触发器范围内才触发,而onTriggerEnter是在角色开始进入触发器范围内才会触发。
综上完成了初始目标要求
本文是对《Unity和C#游戏编程入门》该书籍中的Hero Born案例的改进,希望对大家有所帮助。
更正:
BoyBehavior类中的标志位的访问权限是public,这样的设置存在一定的问题,其权限为public时,在Unity编辑界面可以直接设置,如下图所示
但是这个标志位并不应该在此处随意修改。所以对代码进行优化:
BoyBehavior类
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class BoyBehavior : MonoBehaviour
{
//.........
//标志位
private bool isPickUp ;
public bool PickUpFlag
{
get { return isPickUP;}
set { isPickUp = value;}
}
void Start
{
isPickUp =false;
}
//这里定义了在动画PickUp中的添加的帧方法
public void PickUpThings()
{
isPickUp = true;
}
//.....
}
ItemBehavior类
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ItemBehavior : MonoBehaviour
{
public GameBehavior gameManager;
public BoyBehavior boy;
//这里使用onTriggerStay,即角色呆在触发器中
private void OnTriggerStay(Collider other)
{
if (other.name == "Boy" && boy.PickUpFlag)
{
Destroy(this.transform.parent.gameObject);
Debug.Log("Item collected!");
gameManager.Items += 1;
boy.PickUpFlag = false;
}
}
// Start is called before the first frame update
void Start()
{
gameManager = GameObject.Find("Game Manager").GetComponent<GameBehavior>();
boy = GameObject.Find("Boy").GetComponent<BoyBehavior>();
}
// Update is called once per frame
void Update()
{
}
}
这里主要使用了get和set属性对代码进行了优化
此外,由于需要可拾取的触发器来触发拾取操作,因此每个可拾取物体上都要挂载该脚本,因此这样的方法存在一定的缺陷,游戏物体和脚本之间的耦合性太高,如果某个物体有什么独特的拾取效果,比如这个物体是个假的宝石,拾取时会爆炸伤害玩家,那么单一修改该物体的脚本会导致其他物体的脚本全部被修改,从而所有的物体都变成了爆炸物品。因此可以换个思路。
该思路就是在游戏角色上添加一个触发器,当物体进入游戏角色的触发器范围内且游戏角色按下拾取键后拾取物体,该方法读者可以自己先实现,并不困难。