18.综合练习题-改键练习
18.1 知识点
改键练习需求
- 请使用新输入系统制作改建功能(针对鼠标键盘设备)
- 默认行为有
- 移动(默认WASD)
- 开火(默认鼠标左键)
- 跳跃(默认空格键)
- 相关按键
- 我们可以为其自定义按键
改键功能制作方案
- 获取输入配置的Json信息(设备和按键信息)
- 修改Json数据
- 生成InputActionAsset对象
- 赋值给PlayerInput达到改建目的
注意:
改建的做法不止这一种,随着我们学习的知识增多,还可以使用别的方式来制作改建。
目前使用的方式只是基于现有知识点来进行制作的。
制作改建功能
编辑输入配置文件,并转存到Resource下新创建的json文件中
创建改键信息数据结构类,这个练习题有六个键可以被改,用6个字符串来记录设备路径数据,写入默认值
public class InputInfo
{
public string up = "<Keyboard>/w";
public string down = "<Keyboard>/s";
public string left = "<Keyboard>/a";
public string right = "<Keyboard>/d";
public string fire = "<Mouse>/leftButton";
public string jump = "<Keyboard>/space";
}
配置文件作为json拷贝一份到Resources下,要略作修改,自己定义规则改掉path:中的内容,比如用”<内容>”这样的格式,到时候使用string的替换api一键替换自己定义的内容就行,比如替换<fire>为<Mouse>/leftButton,之后使用str = str.Replace(“<fire>”, inputInfo.fire);替换。
{
"name": "Lesson18_综合练习题_改键练习",
"maps": [
{
"name": "Player",
"id": "14a39848-22b9-4050-8600-0abb93a82146",
"actions": [
{
"name": "Move",
"type": "Value",
"id": "ec6ae369-67af-47ef-a3be-90bfe79dc9ac",
"expectedControlType": "Vector2",
"processors": "",
"interactions": "",
"initialStateCheck": true
},
{
"name": "Fire",
"type": "Button",
"id": "01324b8c-7e30-46d0-9e83-ed67110227ac",
"expectedControlType": "Button",
"processors": "",
"interactions": "",
"initialStateCheck": false
},
{
"name": "Jump",
"type": "Button",
"id": "c076dec0-a0f2-47c1-962a-0381612a5084",
"expectedControlType": "Button",
"processors": "",
"interactions": "",
"initialStateCheck": false
}
],
"bindings": [
{
"name": "",
"id": "61f4c31e-d584-42a9-b4f2-dae262cef938",
"path": "<fire>",
"interactions": "",
"processors": "",
"groups": "",
"action": "Fire",
"isComposite": false,
"isPartOfComposite": false
},
{
"name": "",
"id": "3d9eec6f-3a8a-4e82-8197-d803af5734d7",
"path": "<jump>",
"interactions": "",
"processors": "",
"groups": "",
"action": "Jump",
"isComposite": false,
"isPartOfComposite": false
},
{
"name": "2D Vector",
"id": "d0c8d80d-3158-4eac-b920-2fbd3c2548f5",
"path": "2DVector",
"interactions": "",
"processors": "",
"groups": "",
"action": "Move",
"isComposite": true,
"isPartOfComposite": false
},
{
"name": "up",
"id": "1e1b2a6b-a5f6-4187-99ed-82c6194e3727",
"path": "<up>",
"interactions": "",
"processors": "",
"groups": "",
"action": "Move",
"isComposite": false,
"isPartOfComposite": true
},
{
"name": "down",
"id": "dcff145f-cd85-484d-a862-26253e4d7ed5",
"path": "<down>",
"interactions": "",
"processors": "",
"groups": "",
"action": "Move",
"isComposite": false,
"isPartOfComposite": true
},
{
"name": "left",
"id": "3218ba86-b48e-40af-8d70-548fe7f1ebb5",
"path": "<left>",
"interactions": "",
"processors": "",
"groups": "",
"action": "Move",
"isComposite": false,
"isPartOfComposite": true
},
{
"name": "right",
"id": "12678b81-d820-495d-8415-541187f6a3ef",
"path": "<right>",
"interactions": "",
"processors": "",
"groups": "",
"action": "Move",
"isComposite": false,
"isPartOfComposite": true
}
]
}
],
"controlSchemes": []
}
制作改建UI相关逻辑,改键的思路其实就是替换json自己定义的路径
拼面板,面板上创建六个对应的输入item,每个item包括输入操作,输入路径,修改按钮。点击修改按钮时要激活任意键输入检测。
创建ChangeInputPanel 脚本,创建各个路径文本和按钮变量,外部关联
public class ChangeInputPanel : MonoBehaviour
{
public Text txtUp; // 显示向上按钮的文本
public Text txtDown; // 显示向下按钮的文本
public Text txtLeft; // 显示向左按钮的文本
public Text txtRight; // 显示向右按钮的文本
public Text txtFire; // 显示射击按钮的文本
public Text txtJump; // 显示跳跃按钮的文本
public Button btnUp; // 向上按钮
public Button btnDown; // 向下按钮
public Button btnLeft; // 向左按钮
public Button btnRight; // 向右按钮
public Button btnFire; // 射击按钮
public Button btnJump; // 跳跃按钮
}
添加输入信息变量,Start初始化得到数据管理器中的输入信息(之后会写),添加更新按钮信息显示方法,方法中把文本逐个赋值为输入信息对象中的信息,Start中调用一次。注意inputInfo是得到之后要创立的DataManager中的inputInfo。
private InputInfo inputInfo; // 输入信息,用于记录键位信息
void Start()
{
inputInfo = DataManager.Instance.InputInfo; // 获取输入信息
UpdateBtnInfo(); // 更新按钮信息显示
}
/// <summary>
/// 更新按钮信息显示
/// </summary>
private void UpdateBtnInfo()
{
txtUp.text = inputInfo.up; // 更新向上按钮文本
txtDown.text = inputInfo.down; // 更新向下按钮文本
txtLeft.text = inputInfo.left; // 更新向左按钮文本
txtRight.text = inputInfo.right; // 更新向右按钮文本
txtFire.text = inputInfo.fire; // 更新射击按钮文本
txtJump.text = inputInfo.jump; // 更新跳跃按钮文本
}
添加按钮枚举和一个记录按钮枚举变量。添加改变按钮键位方法,要传入按钮枚举。在Start中为各个按钮添加点击事件监听。创建真正改建方法ChangeBtnReally。改变按钮键位方法中设置当前改哪一个键位,监听一次任意按钮的按下事件,调用 真正改变按钮键位的方法。真正改变按钮键位的方法中将路径按照 / 分割成字符串数组 ,转换格式为 <设备>/按钮。根据当前要改变的按钮类型更新键位信息到输入信息对象中,调用 更新按钮信息显示方法。
public enum BTN_TYPE
{
UP,
DOWN,
LEFT,
RIGHT,
FIRE,
JUMP,
}
public class ChangeInputPanel : MonoBehaviour
{
// 记录当前正在改哪一个键位
private BTN_TYPE nowType;
void Start()
{
// 为每个按钮添加点击事件监听
btnUp.onClick.AddListener(() =>
{
ChangeBtn(BTN_TYPE.UP);
});
btnDown.onClick.AddListener(() =>
{
ChangeBtn(BTN_TYPE.DOWN);
});
btnLeft.onClick.AddListener(() =>
{
ChangeBtn(BTN_TYPE.LEFT);
});
btnRight.onClick.AddListener(() =>
{
ChangeBtn(BTN_TYPE.RIGHT);
});
btnFire.onClick.AddListener(() =>
{
ChangeBtn(BTN_TYPE.FIRE);
});
btnJump.onClick.AddListener(() =>
{
ChangeBtn(BTN_TYPE.JUMP);
});
}
// 改变按钮键位
private void ChangeBtn(BTN_TYPE type)
{
nowType = type; // 设置当前改哪一个键位
// 监听一次任意按钮的按下事件,调用 ChangeBtnReally 方法
InputSystem.onAnyButtonPress.CallOnce(ChangeBtnReally);
}
// 真正改变按钮键位的方法
private void ChangeBtnReally(InputControl control)
{
print(control.path); // 打印控制器路径,用于调试 比如/Keyboard/y
string[] strs = control.path.Split('/'); // 将路径按照 / 分割成字符串数组
string path = "<" + strs[1] + ">/" + strs[2]; // 构建路径字符串,格式为 <设备>/按钮
//比如/Keyboard/y要自己转换成<Keyboard>/y
// 根据当前要改变的按钮类型更新键位信息
switch (nowType)
{
case BTN_TYPE.UP:
inputInfo.up = path;
break;
case BTN_TYPE.DOWN:
inputInfo.down = path;
break;
case BTN_TYPE.LEFT:
inputInfo.left = path;
break;
case BTN_TYPE.RIGHT:
inputInfo.right = path;
break;
case BTN_TYPE.FIRE:
inputInfo.fire = path;
break;
case BTN_TYPE.JUMP:
inputInfo.jump = path;
break;
}
UpdateBtnInfo(); // 更新按钮信息显示
}
}
实现改键逻辑
创建cube作为玩家,给玩家添加一个 PlayerInput 脚本,改成C#事件类型,不需要关联配置文件。新创建一个玩家脚本 Lesson18_综合练习题_改键练习Player
。
public class Lesson18_综合练习题_改键练习Player : MonoBehaviour
{
}
创建数据管理器脚本搞成单例。创建输入信息对象和 JSON 字符串对象。构造函数实例化输入信息对象和 JSON 字符串对象。提供一个获得在当前输入信息下的输入文件的方法,替换我们自己定义的 <占位符>
,通过 FromJson 方法传入 JSON 返回 InputActionAsset。
public class DataManager
{
private static DataManager instance = new DataManager(); // 创建一个私有的静态 DataManager 实例,用于实现单例模式
public static DataManager Instance => instance; // 公开的只读属性,用于获取 DataManager 实例
private InputInfo inputInfo; // 输入信息对象
public InputInfo InputInfo => inputInfo; // 公开的只读属性,用于获取输入信息对象
private string jsonStr; // JSON 字符串
private DataManager()
{
inputInfo = new InputInfo(); // 创建 InputInfo 对象,用于保存输入信息
jsonStr = Resources.Load<TextAsset>("Lesson18_综合练习题_改键练习Test").text; // 加载 Resources 文件夹下的名为 "Lesson18_综合练习题_改键练习Test" 的文本文件,并将其内容赋值给 jsonStr
}
public InputActionAsset GetActionAsset()
{
// 将 jsonStr 中的 "<up>" 替换为 inputInfo.up
string str = jsonStr.Replace("<up>", inputInfo.up);
// 将 str 中的 "<down>" 替换为 inputInfo.down
str = str.Replace("<down>", inputInfo.down);
// 将 str 中的 "<left>" 替换为 inputInfo.left
str = str.Replace("<left>", inputInfo.left);
// 将 str 中的 "<right>" 替换为 inputInfo.right
str = str.Replace("<right>", inputInfo.right);
// 将 str 中的 "<fire>" 替换为 inputInfo.fire
str = str.Replace("<fire>", inputInfo.fire);
// 将 str 中的 "<jump>" 替换为 inputInfo.jump
str = str.Replace("<jump>", inputInfo.jump);
return InputActionAsset.FromJson(str); // 将替换后的字符串转换为 InputActionAsset 对象并返回
}
}
玩家脚本中,得到PlayerInput组件对象action
从数据管理器 DataManager.Instance.GetActionAsset()
得到配置文件。要进行启用。最后注册 playerInput
的 onActionTriggered
事件回调函数,处理熟路逻辑。提供一个让玩家产生真正改键效果的方法。重新获得配置文件赋值并启用。
public class Lesson18_综合练习题_改键练习Player : MonoBehaviour
{
private PlayerInput playerInput; // PlayerInput 组件对象
void Start()
{
playerInput = this.GetComponent<PlayerInput>(); // 获取当前游戏对象上的 PlayerInput 组件
playerInput.actions = DataManager.Instance.GetActionAsset(); // 将 DataManager 返回的 InputActionAsset 赋值给 playerInput 的 actions 属性
playerInput.actions.Enable(); // 启用 playerInput 的 actions
playerInput.onActionTriggered += (context) => // 注册 onActionTriggered 事件回调函数
{
if (context.phase == InputActionPhase.Performed) // 判断输入动作的阶段是否为 Performed
{
switch (context.action.name) // 根据输入动作的名称进行不同的处理
{
case "Fire":
print("开火"); // 打印 "开火"
break;
case "Jump":
print("跳跃"); // 打印 "跳跃"
break;
case "Move":
print("移动"); // 打印 "移动"
break;
}
}
};
}
//让玩家产生改建效果
public void ChangeInput()
{
//改建就是改变 我们 PlayerInput 上关联的输入配置信息嘛
playerInput.actions = DataManager.Instance.GetActionAsset(); // 将 DataManager 返回的 InputActionAsset 赋值给 playerInput 的 actions 属性
playerInput.actions.Enable(); // 启用 playerInput 的 actions
}
}
假如发现输入没有效果,可以查看InputDebug窗口中的信息。可能是未启用的原因。
ChangeInputPanel脚本中关联一个Player脚本,外部关联,在真正改变的时候调用一次玩家的真正改变键位方法,注意可以重写得到一个配置文件赋值给玩家的PlayerInput。
public Lesson18_综合练习题_改键练习Player player; // 玩家对象
// 真正改变按钮键位的方法
private void ChangeBtnReally(InputControl control)
{
...
// 让玩家对象产生改键位的效果
player.ChangeInput();
}
遗留的问题
可能键位冲突,自己判断一下
重新运行不会保存改键信息,可以用数据持久化保存
18.2 知识点代码
Lesson18_综合练习题_改键练习
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Lesson18_综合练习题_改键练习 : MonoBehaviour
{
void Start()
{
#region 知识点一 改键功能制作方案
//1.获取输入配置的Json信息(设备和按键信息)
//2.修改Json数据
//3.生成InputActionAsset对象
//4.赋值给PlayerInput达到改建目的
//注意:
//改建的做法不止这一种,随着我们学习的知识增多,还可以使用别的方式来制作改建
//目前使用的方式只是基于现有知识点来进行制作的
#endregion
#region 知识点二 制作改建功能
//1.编辑输入配置文件,并转存到文本文件中
//2.创建改键信息数据结构类
//3.编辑文本文件中的可变字符串
//4.制作改建UI相关逻辑
//5.实现改建
#endregion
}
}
InputInfo
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class InputInfo
{
public string up = "<Keyboard>/w";
public string down = "<Keyboard>/s";
public string left = "<Keyboard>/a";
public string right = "<Keyboard>/d";
public string fire = "<Mouse>/leftButton";
public string jump = "<Keyboard>/space";
}
DataManager
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.InputSystem;
public class DataManager
{
private static DataManager instance = new DataManager(); // 创建一个私有的静态 DataManager 实例,用于实现单例模式
public static DataManager Instance => instance; // 公开的只读属性,用于获取 DataManager 实例
private InputInfo inputInfo; // 输入信息对象
public InputInfo InputInfo => inputInfo; // 公开的只读属性,用于获取输入信息对象
private string jsonStr; // JSON 字符串
private DataManager()
{
inputInfo = new InputInfo(); // 创建 InputInfo 对象,用于保存输入信息
jsonStr = Resources.Load<TextAsset>("Lesson18_综合练习题_改键练习Test").text; // 加载 Resources 文件夹下的名为 "Lesson18_综合练习题_改键练习Test" 的文本文件,并将其内容赋值给 jsonStr
}
public InputActionAsset GetActionAsset()
{
// 将 jsonStr 中的 "<up>" 替换为 inputInfo.up
string str = jsonStr.Replace("<up>", inputInfo.up);
// 将 str 中的 "<down>" 替换为 inputInfo.down
str = str.Replace("<down>", inputInfo.down);
// 将 str 中的 "<left>" 替换为 inputInfo.left
str = str.Replace("<left>", inputInfo.left);
// 将 str 中的 "<right>" 替换为 inputInfo.right
str = str.Replace("<right>", inputInfo.right);
// 将 str 中的 "<fire>" 替换为 inputInfo.fire
str = str.Replace("<fire>", inputInfo.fire);
// 将 str 中的 "<jump>" 替换为 inputInfo.jump
str = str.Replace("<jump>", inputInfo.jump);
return InputActionAsset.FromJson(str); // 将替换后的字符串转换为 InputActionAsset 对象并返回
}
}
Lesson18_综合练习题_改键练习Player
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.InputSystem;
public class Lesson18_综合练习题_改键练习Player : MonoBehaviour
{
private PlayerInput playerInput; // PlayerInput 组件对象
void Start()
{
playerInput = this.GetComponent<PlayerInput>(); // 获取当前游戏对象上的 PlayerInput 组件
playerInput.actions = DataManager.Instance.GetActionAsset(); // 将 DataManager 返回的 InputActionAsset 赋值给 playerInput 的 actions 属性
playerInput.actions.Enable(); // 启用 playerInput 的 actions
playerInput.onActionTriggered += (context) => // 注册 onActionTriggered 事件回调函数
{
if (context.phase == InputActionPhase.Performed) // 判断输入动作的阶段是否为 Performed
{
switch (context.action.name) // 根据输入动作的名称进行不同的处理
{
case "Fire":
print("开火"); // 打印 "开火"
break;
case "Jump":
print("跳跃"); // 打印 "跳跃"
break;
case "Move":
print("移动"); // 打印 "移动"
break;
}
}
};
}
//让玩家产生改建效果
public void ChangeInput()
{
//改建就是改变 我们PlayerInput上关联的输入配置信息嘛
playerInput.actions = DataManager.Instance.GetActionAsset(); // 将 DataManager 返回的 InputActionAsset 赋值给 playerInput 的 actions 属性
playerInput.actions.Enable(); // 启用 playerInput 的 actions
}
}
ChangeInputPanel
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Utilities;
using UnityEngine.UI;
public enum BTN_TYPE
{
UP,
DOWN,
LEFT,
RIGHT,
FIRE,
JUMP,
}
public class ChangeInputPanel : MonoBehaviour
{
public Text txtUp; // 显示向上按钮的文本
public Text txtDown; // 显示向下按钮的文本
public Text txtLeft; // 显示向左按钮的文本
public Text txtRight; // 显示向右按钮的文本
public Text txtFire; // 显示射击按钮的文本
public Text txtJump; // 显示跳跃按钮的文本
public Button btnUp; // 向上按钮
public Button btnDown; // 向下按钮
public Button btnLeft; // 向左按钮
public Button btnRight; // 向右按钮
public Button btnFire; // 射击按钮
public Button btnJump; // 跳跃按钮
private InputInfo inputInfo; // 输入信息,用于记录键位信息
// 记录当前正在改哪一个键位
private BTN_TYPE nowType;
public Lesson18_综合练习题_改键练习Player player; // 玩家对象
void Start()
{
inputInfo = DataManager.Instance.InputInfo; // 获取输入信息
UpdateBtnInfo(); // 更新按钮信息显示
// 为每个按钮添加点击事件监听
btnUp.onClick.AddListener(() =>
{
ChangeBtn(BTN_TYPE.UP);
});
btnDown.onClick.AddListener(() =>
{
ChangeBtn(BTN_TYPE.DOWN);
});
btnLeft.onClick.AddListener(() =>
{
ChangeBtn(BTN_TYPE.LEFT);
});
btnRight.onClick.AddListener(() =>
{
ChangeBtn(BTN_TYPE.RIGHT);
});
btnFire.onClick.AddListener(() =>
{
ChangeBtn(BTN_TYPE.FIRE);
});
btnJump.onClick.AddListener(() =>
{
ChangeBtn(BTN_TYPE.JUMP);
});
}
// 改变按钮键位
private void ChangeBtn(BTN_TYPE type)
{
nowType = type; // 设置当前改哪一个键位
// 监听一次任意按钮的按下事件,调用 ChangeBtnReally 方法
InputSystem.onAnyButtonPress.CallOnce(ChangeBtnReally);
}
// 真正改变按钮键位的方法
private void ChangeBtnReally(InputControl control)
{
print(control.path); // 打印控制器路径,用于调试 比如/Keyboard/y
string[] strs = control.path.Split('/'); // 将路径按照 / 分割成字符串数组
string path = "<" + strs[1] + ">/" + strs[2]; // 构建路径字符串,格式为 <设备>/按钮
//比如/Keyboard/y要自己转换成<Keyboard>/y
// 根据当前要改变的按钮类型更新键位信息
switch (nowType)
{
case BTN_TYPE.UP:
inputInfo.up = path;
break;
case BTN_TYPE.DOWN:
inputInfo.down = path;
break;
case BTN_TYPE.LEFT:
inputInfo.left = path;
break;
case BTN_TYPE.RIGHT:
inputInfo.right = path;
break;
case BTN_TYPE.FIRE:
inputInfo.fire = path;
break;
case BTN_TYPE.JUMP:
inputInfo.jump = path;
break;
}
UpdateBtnInfo(); // 更新按钮信息显示
// 让玩家对象产生改键位的效果
player.ChangeInput();
}
/// <summary>
/// 更新按钮信息显示
/// </summary>
private void UpdateBtnInfo()
{
txtUp.text = inputInfo.up; // 更新向上按钮文本
txtDown.text = inputInfo.down; // 更新向下按钮文本
txtLeft.text = inputInfo.left; // 更新向左按钮文本
txtRight.text = inputInfo.right; // 更新向右按钮文本
txtFire.text = inputInfo.fire; // 更新射击按钮文本
txtJump.text = inputInfo.jump; // 更新跳跃按钮文本
}
}
Lesson18_综合练习题_改键练习.json
{
"name": "Lesson18_综合练习题_改键练习",
"maps": [
{
"name": "Player",
"id": "14a39848-22b9-4050-8600-0abb93a82146",
"actions": [
{
"name": "Move",
"type": "Value",
"id": "ec6ae369-67af-47ef-a3be-90bfe79dc9ac",
"expectedControlType": "Vector2",
"processors": "",
"interactions": "",
"initialStateCheck": true
},
{
"name": "Fire",
"type": "Button",
"id": "01324b8c-7e30-46d0-9e83-ed67110227ac",
"expectedControlType": "Button",
"processors": "",
"interactions": "",
"initialStateCheck": false
},
{
"name": "Jump",
"type": "Button",
"id": "c076dec0-a0f2-47c1-962a-0381612a5084",
"expectedControlType": "Button",
"processors": "",
"interactions": "",
"initialStateCheck": false
}
],
"bindings": [
{
"name": "",
"id": "61f4c31e-d584-42a9-b4f2-dae262cef938",
"path": "<fire>",
"interactions": "",
"processors": "",
"groups": "",
"action": "Fire",
"isComposite": false,
"isPartOfComposite": false
},
{
"name": "",
"id": "3d9eec6f-3a8a-4e82-8197-d803af5734d7",
"path": "<jump>",
"interactions": "",
"processors": "",
"groups": "",
"action": "Jump",
"isComposite": false,
"isPartOfComposite": false
},
{
"name": "2D Vector",
"id": "d0c8d80d-3158-4eac-b920-2fbd3c2548f5",
"path": "2DVector",
"interactions": "",
"processors": "",
"groups": "",
"action": "Move",
"isComposite": true,
"isPartOfComposite": false
},
{
"name": "up",
"id": "1e1b2a6b-a5f6-4187-99ed-82c6194e3727",
"path": "<up>",
"interactions": "",
"processors": "",
"groups": "",
"action": "Move",
"isComposite": false,
"isPartOfComposite": true
},
{
"name": "down",
"id": "dcff145f-cd85-484d-a862-26253e4d7ed5",
"path": "<down>",
"interactions": "",
"processors": "",
"groups": "",
"action": "Move",
"isComposite": false,
"isPartOfComposite": true
},
{
"name": "left",
"id": "3218ba86-b48e-40af-8d70-548fe7f1ebb5",
"path": "<left>",
"interactions": "",
"processors": "",
"groups": "",
"action": "Move",
"isComposite": false,
"isPartOfComposite": true
},
{
"name": "right",
"id": "12678b81-d820-495d-8415-541187f6a3ef",
"path": "<right>",
"interactions": "",
"processors": "",
"groups": "",
"action": "Move",
"isComposite": false,
"isPartOfComposite": true
}
]
}
],
"controlSchemes": []
}
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com