【Unity】动作游戏开发实战详细分析-08-场景互动
基本思路
在游戏场景中操控机关,物件,进行攀爬,使用钩索等等行为,都是与环境的互动行为,这些都被归纳为场景交互的范畴。我们需要对这些重复性和操控性较强的部分进行系统设计
代码实现
角色冻结
角色冻结逻辑比较简单,禁用动画状态机,并设置刚体IsKinematic
属性,停用动画状态机的瞬间角色会瞬间停止在动作的瞬间可以起到冻结的作用。
其次,可以根据需要进行角色冻结外观的修改
using UnityEngine;
namespace ACTBook
{
public class PlayerFreezeTest : MonoBehaviour
{
public Cloth[] characterCloths;//角色的布料对象数组
public Animator animator;
public Rigidbody selfRigidbody;//角色刚体组件
void Freeze(bool isFreeze)
{
if (isFreeze)//进入冻结逻辑
{
selfRigidbody.isKinematic = true;
animator.enabled = false;
}
else//退出冻结逻辑
{
for (int i = 0; i < characterCloths.Length; i++)
characterCloths[i].ClearTransformMotion();//清空布料运动信息
selfRigidbody.isKinematic = false;//恢复刚体
animator.enabled = true;//恢复Animator
animator.Rebind();//重置根运动
}
}
}
}
场景互动组件
场景可交互组件接口
-
ID属性
-
初始化函数
-
获得控制权柄
-
强制退出组件
该接口的作用是,所有可交互的场景组件都需要实现它
public interface ISceneComponent
{
int ID { get; }
void Initialization(object interfaceObj);
object GetHandler();
void ForceExitComponent();
}
我们列举一个例子来更好的理解这个接口,现在我们有一个可交互组件:Ladder
那么,我们首先要定义ILadderUser接口,所有使用他的角色都需要实现这个接口
其次,我们还需要定义ILadderHandler接口,作为外部使用接口来让角色们能够调用组件做出行为
然后就是Ladder类,它是一个场景交互类,它继承了MonoBehaviour,实现了场景组件接口与ILadderHandler接口,并实现了所有方法。
如果想要使用,这样的设计还不够完整
public interface ILadderUser//使用角色需要实现它
{
Transform Transform { get; }
void ToggleLadderAnimation(bool enable);//改变动画变量
}
public interface ILadderHandler
{
void MoveLeave();//用于AI,NPC直接调用爬梯子结束
void MoveUp();//用于玩家,控制向上移动
void MoveDown();//用于玩家,控制向下移动
}
class Ladder : MonoBehaviour, ISceneComponent, ILadderHandler
{
ILadderUser mLadderUser;
int ISceneComponent.ID { get { return SceneComponentConst.LADDER; } }
void ISceneComponent.Initialization(object interfaceObj)
{
mLadderUser = interfaceObj as ILadderUser;
//do something...
}
void ISceneComponent.ForceExitComponent()
{
//do something...
}
object ISceneComponent.GetHandler()
{
return this;
}
void ILadderHandler.MoveLeave()
{
//do something...
}
void ILadderHandler.MoveUp()
{
//do something...
}
void ILadderHandler.MoveDown()
{
//do something...
}
}
这是场景组件接收类,所有要使用组件的角色还需要挂载这个类
它包括几个字段
- SCENE_COMPONENT_MAX 组件数量
- mReceiveComponentIdentities 组件数组
- OnReceived 接收事件
在Awake中,我们对组件数组进行初始化,所有可以使用的组件会赋予true
其次,我们通过触发器对其进行监听,并获取对应的接口,判断是否能够使用,然后接收事件
public class SceneComponentReceiver : MonoBehaviour
{
const int SCENE_COMPONENT_MAX = 64;//最多64个组件
BitArray mReceiveComponentIdentities;//筛选的位数组
public string compareTag;//标签筛选
public int[] receiveIdentities;//编辑面板配置接收ID
public event Action<SceneComponentReceiver, ISceneComponent> OnReceived;//接收事件
void Awake()
{
mReceiveComponentIdentities = new BitArray(SCENE_COMPONENT_MAX);//创建位数组
for (int i = 0; i < receiveIdentities.Length; i++)
mReceiveComponentIdentities[receiveIdentities[i]] = true;//接收ID都设为true
}
void OnTriggerEnter(Collider other)//碰撞事件触发
{
if (!other.CompareTag(compareTag)) return;//标签不一致跳出
var sceneComponent = other.GetComponent<ISceneComponent>();
if (mReceiveComponentIdentities[sceneComponent.ID])//进入筛选
{
if (OnReceived != null)
OnReceived(this, sceneComponent);//触发接收事件
}
}
}
当然,仅仅如此仍然不够,我们还需要对事件进行注册,因此,我们需要一个角色的部分类来进行实现
角色部分类,实现ILadderUser接口,并自行实现事件注册与注销函数,并实现事件绑定函数,初始化函数在Awake中进行调用
public partial class Player : MonoBehaviour, ILadderUser//玩家的部分类
{
Transform ILadderUser.Transform { get { return transform; } }//user实现字段
void SceneComponent_Ladder_Initialization()//初始化函数,在主类里调用
{
sceneComponentReceiver.OnReceived += OnSceneComponentLadderReceived;//事件绑定
}
void SceneComponent_Ladder_Destroy()//销毁函数,在主类里调用
{
sceneComponentReceiver.OnReceived -= OnSceneComponentLadderReceived;//事件绑定注销
}
void OnSceneComponentLadderReceived(SceneComponentReceiver arg1, ISceneComponent arg2)
{
if (arg2.ID == SceneComponentConst.LADDER)//id判断
{
arg2.Initialization(this);//传入接口
var ladderHandler = arg2.GetHandler() as ILadderHandler;
//do something...
Debug.Log("Ladder Component Process!");
}
}
void ILadderUser.ToggleLadderAnimation(bool enable)//user实现函数
{
animator.SetBool("Ladder", enable);//动画变量修改
}
}