12.PlayerInput-PlayerInput行为执行模式
12.1 知识点
Send Messages
在自定义脚本中申明名为 “On+行为名” 的函数,没有参数或者参数类型为 InputValue。将该自定义脚本挂载到 PlayerInput 依附的对象上,当触发对应输入时会自动调用函数。
并且还有默认的 3 个和设备相关的函数可以调用:
- 设备注册(当控制器从设备丢失中恢复并再次运行时会触发):
OnDeviceRegained(PlayerInput input)
- 设备丢失(玩家失去了分配给它的设备之一,例如,当无线设备耗尽电池时):
OnDeviceLost(PlayerInput input)
- 控制器切换:
OnControlsChanged(PlayerInput input)
public void OnMove(InputValue value)
{
print("Move");
print("Move" + value.Get<Vector2>());
}
public void OnLook(InputValue value)
{
print("Look");
print("Look" + value.Get<Vector2>());
}
public void OnFire(InputValue value)
{
print("Fire");
if (value.isPressed)
{
print("Fire按下");
}
}
public void OnDeviceLost(PlayerInput input)
{
print("设备丢失");
}
public void OnDeviceRegained(PlayerInput input)
{
print("设备注册");
}
public void OnControlsChanged(PlayerInput input)
{
print("控制器切换");
}
挂载脚本在 Player 对象上后,这种模式下会自动寻找对象身上的脚本是否有对应名字的函数。
Broadcast Messages
基本和 SendMessage 规则一致,唯一的区别是,自定义脚本不仅可以挂载在 PlayerInput 依附的对象上,还可以挂载在其子对象下。
Invoke Unity Events
该模式可以让我们在 Inspector 窗口上通过拖拽的形式关联响应函数,但是注意:响应函数的参数类型需要改为 InputAction.CallbackContext
。
切换成Invoke Unity Events后 下方展开可以看到事件关联列表
在脚本中声明函数并拖拽关联进行测试:
public void MyFire(InputAction.CallbackContext context)
{
print("开火1");
}
public void MyMove(InputAction.CallbackContext context)
{
print("移动1");
}
public void MyLook(InputAction.CallbackContext context)
{
print("Look1");
}
这种模式的好处是可以关联别的对象身上的脚本函数。
Invoke C Sharp Events
- 获取 PlayerInput 组件:
PlayerInput input = this.GetComponent<PlayerInput>();
- 获取对应事件进行委托函数添加:
input.onDeviceLost += OnDeviceLost;
input.onDeviceRegained += OnDeviceRegained;
input.onControlsChanged += OnControlsChanged;
// 输入触发事件,任何输入都会触发该事件
input.onActionTriggered += OnActionTrigger;
可以通过 PlayerInput 中的 currentActionMap
属性得到对应行为的值:
input.currentActionMap["Move"].ReadValue<Vector2>();
- 当触发输入时会自动触发事件调用对应函数:
public void OnActionTrigger(InputAction.CallbackContext context)
{
print("触发OnActionTrigger");
switch (context.action.name)
{
case "Fire":
// 输入阶段的判断 触发阶段 才去做逻辑
if (context.phase == InputActionPhase.Performed)
print("开火");
break;
case "Look":
print("看向");
print(context.ReadValue<Vector2>());
break;
case "Move":
print("移动");
print(context.ReadValue<Vector2>());
break;
}
}
关键参数InputValue和InputAction.CallbackContext
InputValue
- 判断是否按下单个按键或按钮:
inputValue.isPressed
- 获取
Vector2
类型的值:inputValue.Get<Vector2>()
InputAction.CallbackContext
- 获取
Vector2
类型的值的x
和y
属性:callbackContext.ReadValue<Vector2>().x
、callbackContext.ReadValue<Vector2>().y
- 获取输入行为的名字:
callbackContext.action.name
,例如 Fire、Move - 获取输入控件的名字:
callbackContext.control.name
,例如 wasd - 获取输入控件所属的设备的名字:
callbackContext.control.device.name
,例如 Keyboard - 获取输入阶段的枚举值:
callbackContext.phase
,例如InputActionPhase.Performed
、InputActionPhase.Started
- 获取输入阶段的字符串表示:
callbackContext.phase.ToString()
,例如 “Performed”、”Started”
总结
建议使用Invoke Unity Events或Invoke CSharp Events的方式,效率更高。
12.2 知识点代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.InputSystem;
public class Lesson12_PlayerInput_PlayerInput行为执行模式 : MonoBehaviour
{
void Start()
{
#region 知识点一 Send Messages
//在自定义脚本中
//申明名为 "On+行为名" 的函数
//没有参数 或者 参数类型为InputValue
//将该自定义脚本挂载到PlayerInput依附的对象上
//当触发对应输入时 会自动调用函数
//并且还有默认的3个和设备相关的函数可以调用
//设备注册(当控制器从设备丢失中恢复并再次运行时会触发):OnDeviceRegained(PlayerInput playerInput)
//设备丢失(玩家失去了分配给它的设备之一,例如,当无线设备耗尽电池时):OnDeviceLost(PlayerInput playerInput)
//控制器切换:OnControlsChanged(PlayerInput playerInput)
#endregion
#region 知识点二 Broadcast Messages
//基本和SendMessage规则一致
//唯一的区别是,自定义脚本不仅可以挂载在PlayerInput依附的对象上
//还可以挂载在其子对象下
#endregion
#region 知识点三 Invoke Unity Events
//该模式可以让我们在Inspector窗口上通过拖拽的形式关联响应函数
//但是注意:响应函数的参数类型 需要改为 InputAction.CallbackContext
#endregion
#region 知识点四 Invoke C Sharp Events
//1.获取PlayerInput组件
PlayerInput input = this.GetComponent<PlayerInput>();
//2.获取对应事件进行委托函数添加
input.onDeviceLost += OnDeviceLost;
input.onDeviceRegained += OnDeviceRegained;
input.onControlsChanged += OnControlsChanged;
//输入触发事件 任何输入都会触发该事件
input.onActionTriggered += OnActionTrigger;
//可以通过PlayerInput中的currentActionMap属性得到对应行为的值
input.currentActionMap["Move"].ReadValue<Vector2>();
//3.当触发输入时会自动触发事件调用对应函数
#endregion
#region 知识点五 关键参数InputValue和InputAction.CallbackContext
//InputValue
//判断是否按下单个按键或按钮 inputValue.isPressed
//获取Vector2类型的值 inputValue.Get<Vector2>()
//InputAction.CallbackContext
//获取Vector2类型的值的x和y属性 callbackContext.ReadValue<Vector2>().x callbackContext.ReadValue<Vector2>().y
//获取输入行为的名字 callbackContext.action.name //例如Fire Move
//获取输入控件的名字 callbackContext.control.name //例如wasd
//获取输入控件所属的设备的名字 callbackContext.control.device.name //例如Keyboard
//获取输入阶段的枚举值 callbackContext.phase //例如InputActionPhase.Performed InputActionPhase.Started
//获取输入阶段的字符串表示 callbackContext.phase.ToString() //例如"Performed" "Started"
#endregion
#region 总结
//建议使用第三第四中方式 效率更高
#endregion
}
#region 知识点一 Send Messages / 知识点二 Broadcast Messages
public void OnMove(InputValue value)
{
print("Move");
print("Move"+value.Get<Vector2>());
}
public void OnLook(InputValue value)
{
print("Look");
print("Look" + value.Get<Vector2>());
}
public void OnFire(InputValue value)
{
print("Fire");
if(value.isPressed)
{
print("Fire按下");
}
}
public void OnDeviceLost( PlayerInput input)
{
print("设备丢失");
}
public void OnDeviceRegained(PlayerInput input)
{
print("设备注册");
}
public void OnControlsChanged(PlayerInput input)
{
print("控制器切换");
}
#endregion
#region 知识点三 Invoke Unity Events
public void MyFire(InputAction.CallbackContext context)
{
print("开火1");
}
public void MyMove(InputAction.CallbackContext context)
{
print("移动1");
}
public void MyLook(InputAction.CallbackContext context)
{
print("Look1");
}
#endregion
#region 知识点四 Invoke C Sharp Events
public void OnActionTrigger(InputAction.CallbackContext context)
{
print("触发OnActionTrigger");
switch (context.action.name)
{
case "Fire":
//输入阶段的判断 触发阶段 才去做逻辑
if(context.phase == InputActionPhase.Performed)
print("开火");
break;
case "Look":
print("看向");
print(context.ReadValue<Vector2>());
break;
case "Move":
print("移动");
print(context.ReadValue<Vector2>());
break;
}
}
#endregion
}
12.3 练习题
请使用上一道小球练习题制作的输入配置文件,用PlayerInput为一个对象处理移动、跳跃、开火的逻辑,分别用4种行为执行模式处理一次
移除之前的小球控制脚本,给小球添加PlayerInput组件,新创建一个配置文件进行关联
创建脚本,添加必要的变量,外部关联
public GameObject bullet; //子弹预制体
public Rigidbody body; //刚体组件 外部拖拽
private Vector3 dir; //移动方向
public PlayerInput playerInput; //玩家输入组件
修改PlayerInput为SendMessages模式,声明对应的On行为函数,移动代码要在Update调用,因为OnMove只会在改变时调用一次不会每帧调用
public void OnJump(InputValue value)
{
body.AddForce(Vector3.up * 200); //给刚体添加向上的力
}
public void OnFire(InputValue value)
{
//鼠标位置的射线检测
RaycastHit info;
if (Physics.Raycast(Camera.main.ScreenPointToRay(Mouse.current.position.ReadValue()), out info)) //从摄像机到鼠标位置发射一条射线,如果碰到物体,返回碰撞信息
{
//得到子弹飞出去的向量
Vector3 point = info.point; //获取碰撞点坐标
point.y = this.transform.position.y; //调整y轴坐标和自身一致
Vector3 dir = point - this.transform.position; //计算子弹方向向量
//创建子弹 飞出去
Instantiate(bullet, this.transform.position, Quaternion.LookRotation(dir)); //实例化子弹,并设置位置和旋转角度
}
}
//需要获取值的 这种函数 需要注意 只会在改变时进入函数
public void OnMove(InputValue value)
{
dir = value.Get<Vector2>(); //获取Vector2类型的值
dir.z = dir.y; //转换为Vector3类型的值
dir.y = 0;
Debug.Log("OnMove dir" + dir);
}
void Update()
{
Debug.Log("Update前 dir" + dir);
if (dir == Vector3.zero) return;
Debug.Log("Update后 dir" + dir);
body.AddForce(dir); //给刚体添加移动方向的力
}
修改PlayerInput为BroadcastMessages模式下 ,把脚本挂载到子对象上,也能实现输入。代码复用SendMessages的就行
修改PlayerInput为Invoke Unity Events模式,声明有不同参数的重载On行为函数,要手动拖对象进行脚本函数关联
public void OnJump(InputAction.CallbackContext context)
{
if (context.phase != InputActionPhase.Performed) //判断输入阶段是否为Performed
return;
body.AddForce(Vector3.up * 200); //给刚体添加向上的力
}
public void OnFire(InputAction.CallbackContext context)
{
if (context.phase != InputActionPhase.Performed) //判断输入阶段是否为Performed
return;
//鼠标位置的射线检测
RaycastHit info;
if (Physics.Raycast(Camera.main.ScreenPointToRay(Mouse.current.position.ReadValue()), out info)) //从摄像机到鼠标位置发射一条射线,如果碰到物体,返回碰撞信息
{
//得到子弹飞出去的向量
Vector3 point = info.point; //获取碰撞点坐标
point.y = this.transform.position.y; //调整y轴坐标和自身一致
Vector3 dir = point - this.transform.position; //计算子弹方向向量
//创建子弹 飞出去
Instantiate(bullet, this.transform.position, Quaternion.LookRotation(dir)); //实例化子弹,并设置位置和旋转角度
}
}
//需要获取值的 这种函数 需要注意 只会在改变时进入函数
public void OnMove(InputAction.CallbackContext context)
{
dir = context.ReadValue<Vector2>(); //获取Vector2类型的值
dir.z = dir.y; //转换为Vector3类型的值
dir.y = 0;
}
修改PlayerInput为Invoke C Sharp Events模式,在Start时给onActionTriggered 注册监听,判断行为名字且在触发阶段才执行逻辑
playerInput.onActionTriggered += (context) =>
{
switch (context.action.name)
{
case "Move":
dir = context.ReadValue<Vector2>(); //获取Vector2类型的值
dir.z = dir.y; //转换为Vector3类型的值
dir.y = 0;
break;
case "Jump":
if (context.phase == InputActionPhase.Performed) //判断输入阶段是否为Performed
body.AddForce(Vector3.up * 200); //给刚体添加向上的力
break;
case "Fire":
//鼠标位置的射线检测
//不是按键触发事件 就不处理后面的内容了
if (context.phase != InputActionPhase.Performed) //判断输入阶段是否为Performed
return;
RaycastHit info;
if (Physics.Raycast(Camera.main.ScreenPointToRay(Mouse.current.position.ReadValue()), out info)) //从摄像机到鼠标位置发射一条射线,如果碰到物体,返回碰撞信息
{
//得到子弹飞出去的向量
Vector3 point = info.point; //获取碰撞点坐标
point.y = this.transform.position.y; //调整y轴坐标和自身一致
Vector3 dir = point - this.transform.position; //计算子弹方向向量
//创建子弹 飞出去
Instantiate(bullet, this.transform.position, Quaternion.LookRotation(dir)); //实例化子弹,并设置位置和旋转角度
}
break;
}
};
12.4 练习题代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.InputSystem;
public class Lesson12_练习题 : MonoBehaviour
{
public GameObject bullet; //子弹预制体
public Rigidbody body; //刚体组件 外部拖拽
private Vector3 dir; //移动方向
public PlayerInput playerInput; //玩家输入组件
void Start()
{
//body = this.GetComponent<Rigidbody>();
#region Invoke C Sharp Events
playerInput.onActionTriggered += (context) =>
{
switch (context.action.name)
{
case "Move":
dir = context.ReadValue<Vector2>(); //获取Vector2类型的值
dir.z = dir.y; //转换为Vector3类型的值
dir.y = 0;
break;
case "Jump":
if (context.phase == InputActionPhase.Performed) //判断输入阶段是否为Performed
body.AddForce(Vector3.up * 200); //给刚体添加向上的力
break;
case "Fire":
//鼠标位置的射线检测
//不是按键触发事件 就不处理后面的内容了
if (context.phase != InputActionPhase.Performed) //判断输入阶段是否为Performed
return;
RaycastHit info;
if (Physics.Raycast(Camera.main.ScreenPointToRay(Mouse.current.position.ReadValue()), out info)) //从摄像机到鼠标位置发射一条射线,如果碰到物体,返回碰撞信息
{
//得到子弹飞出去的向量
Vector3 point = info.point; //获取碰撞点坐标
point.y = this.transform.position.y; //调整y轴坐标和自身一致
Vector3 dir = point - this.transform.position; //计算子弹方向向量
//创建子弹 飞出去
Instantiate(bullet, this.transform.position, Quaternion.LookRotation(dir)); //实例化子弹,并设置位置和旋转角度
}
break;
}
};
#endregion
}
void Update()
{
Debug.Log("Update前 dir" + dir);
if (dir == Vector3.zero) return;
Debug.Log("Update后 dir" + dir);
body.AddForce(dir); //给刚体添加移动方向的力
}
#region Send Messages 和 Broadcast Messages
public void OnJump(InputValue value)
{
body.AddForce(Vector3.up * 200); //给刚体添加向上的力
}
public void OnFire(InputValue value)
{
//鼠标位置的射线检测
RaycastHit info;
if (Physics.Raycast(Camera.main.ScreenPointToRay(Mouse.current.position.ReadValue()), out info)) //从摄像机到鼠标位置发射一条射线,如果碰到物体,返回碰撞信息
{
//得到子弹飞出去的向量
Vector3 point = info.point; //获取碰撞点坐标
point.y = this.transform.position.y; //调整y轴坐标和自身一致
Vector3 dir = point - this.transform.position; //计算子弹方向向量
//创建子弹 飞出去
Instantiate(bullet, this.transform.position, Quaternion.LookRotation(dir)); //实例化子弹,并设置位置和旋转角度
}
}
//需要获取值的 这种函数 需要注意 只会在改变时进入函数
public void OnMove(InputValue value)
{
dir = value.Get<Vector2>(); //获取Vector2类型的值
dir.z = dir.y; //转换为Vector3类型的值
dir.y = 0;
Debug.Log("OnMove dir" + dir);
}
#endregion
#region Invoke Unity Events
public void OnJump(InputAction.CallbackContext context)
{
if (context.phase != InputActionPhase.Performed) //判断输入阶段是否为Performed
return;
body.AddForce(Vector3.up * 200); //给刚体添加向上的力
}
public void OnFire(InputAction.CallbackContext context)
{
if (context.phase != InputActionPhase.Performed) //判断输入阶段是否为Performed
return;
//鼠标位置的射线检测
RaycastHit info;
if (Physics.Raycast(Camera.main.ScreenPointToRay(Mouse.current.position.ReadValue()), out info)) //从摄像机到鼠标位置发射一条射线,如果碰到物体,返回碰撞信息
{
//得到子弹飞出去的向量
Vector3 point = info.point; //获取碰撞点坐标
point.y = this.transform.position.y; //调整y轴坐标和自身一致
Vector3 dir = point - this.transform.position; //计算子弹方向向量
//创建子弹 飞出去
Instantiate(bullet, this.transform.position, Quaternion.LookRotation(dir)); //实例化子弹,并设置位置和旋转角度
}
}
//需要获取值的 这种函数 需要注意 只会在改变时进入函数
public void OnMove(InputAction.CallbackContext context)
{
dir = context.ReadValue<Vector2>(); //获取Vector2类型的值
dir.z = dir.y; //转换为Vector3类型的值
dir.y = 0;
}
#endregion
}
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com