Unity实现按键设置功能代码
一、前言
最近在学习unity2D,想做一个横版过关游戏,需要按键设置功能,让用户可以自定义方向键与攻击键等。
自己写了一个,总结如下。
二、界面效果图
这个是一个csv文件,准备第一列是中文按键说明,第二列是英文,第三列是日文(还没有翻译);第四列是默认按键名称,第五列是默认按键ascII码(如果用户选了恢复默认设置,就会用到);第六列是用户自己设置的按键名称,第七列是用户自己设置的按键ascII码;第八列保留,暂未使用。
这个是首页,如果选到了这个按钮,就是按键设置页面。
这个是按键设置页面,实现了按 上下/用户设置的上下
移动光标,按回车/ 开始
键确定,然后按另一个键进行按键修改。
(图中灰色块就是光标,还没有找图片;最后3行按钮进行了特殊处理)
三、主要部分代码
unity代码比较多,总结下主要用到的3个类。
1.FileManager.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.IO;
using UnityEngine.SceneManagement;
using System.Text;
public class FileManager : MonoSingleTon<FileManager>
{
private bool isInit =false;
public TextAsset keyConfigCsv;
//012列是语言,3是默认名称,4是默认ascii,56是用户设置的
//这个要装第二行的数据
/*
w
s
a
d
j
k
l
i
q
e
u
o
c
v
j
k
*/
//语言,这个不能修改,只能读取
public static string[] LanguageWasd = new string[16];
private static string[] LanguageWasd0 = new string[16];
private static string[] LanguageWasd1 = new string[16];
private static string[] LanguageWasd2 = new string[16];
//默认的key的str与int,这个不能修改,只能读取
public static int[] DefaultWasdNumKeys = new int[16];
public static string[] DefaultWasdNumKeysStr = new string[16];
//实际用的key的str与int
public static int[] WasdNumKeys = new int[16];
public static string[] WasdNumKeysStr = new string[16];
//临时用的key的str与int,因为可能只修改不保存,所以用
public static int[] WasdNumKeysTemp = new int[16];
public static string[] WasdNumKeysStrTemp = new string[16];
// Start is called before the first frame update
// 这个方法会在脚本被启用时注册监听事件
void OnEnable()
{
//Debug.Log("进入这个场景playerData");
//Debug.Log("执行完毕这个场景playerData");
}
private void Awake()
{
if (!isInit)
{
InitKeyConfig();
}
}
void Start()
{
}
// Update is called once per frame
void Update()
{
}
private bool InitKeyConfig()
{
string path = GetKeyConfigPath();
Debug.Log(path);
if (File.Exists(path))
{
//用用户的初始化
LoadKeyConfig(File.ReadAllText(path).Split("\n"));
Debug.Log("使用用户的初始化");
isInit = true;
return false;
}
else
{
string text = keyConfigCsv.text;
//把默认的文件写进本地
File.WriteAllText(path, text, Encoding.UTF8);
//用默认的初始化
LoadKeyConfig(text.Split("\n"));
//string[] dataRow = text.Split("\n");
//File.WriteAllLines(path, dataRow);
Debug.Log("使用默认的初始化");
isInit = true;
return true;
}
}
//读取到临时变量里
public void LoadKeyConfig(string[] dataRow)
{
int language = LanguageManager.GetLanguage();
//File.WriteAllLines(path, dataRow);
for(int i = 0; i < dataRow.Length; i++)
{
string[] dataCell = dataRow[i].Split(",");
if (i >= 16)
{
break;
}
else
{
LanguageWasd[i] = dataCell[language];
LanguageWasd0[i] = dataCell[0];
LanguageWasd1[i] = dataCell[1];
LanguageWasd2[i] = dataCell[2];
DefaultWasdNumKeysStr[i] = dataCell[3];
DefaultWasdNumKeys[i] = int.Parse(dataCell[4]);
WasdNumKeysStr[i] = dataCell[5];
WasdNumKeysStrTemp[i] = dataCell[5];
WasdNumKeys[i] = int.Parse(dataCell[6]);
WasdNumKeysTemp[i] = int.Parse(dataCell[6]);
}
}
}
private static void SaveKeyConfig(string[] dataRow)
{
//Application.dataPath
//这个打包后就不能用了,可能没权限
string path = GetKeyConfigPath();
if (File.Exists(path))
{
//删了重新创建
File.Delete(path);
File.CreateText(path).Close();
}
else
{
File.CreateText(path).Close();
}
//List<string> datas2 = new List<string>();
//datas.Add("coins,1");
Debug.Log("写入数据");
File.WriteAllLines(path, dataRow, Encoding.UTF8);
Debug.Log("写入数据完毕");
}
public void ResetKeyConfig()
{
string text = keyConfigCsv.text;
//用默认的初始化
LoadKeyConfig(text.Split("\n"));
//并且保存
SaveKeyConfig(text.Split("\n"));
//SceneManager.LoadScene(0);
}
public static string GetKeyConfigPath()
{
return Application.persistentDataPath + "/KeyConfig.csv";
}
public static void SaveKeyConfig()
{
string[] newData = new string[16];
//保存文件
for(int i=0; i<16; i++)
{
newData[i] = LanguageWasd0[i] + "," + LanguageWasd1[i] + "," + LanguageWasd2[i] + ","
+ DefaultWasdNumKeysStr[i] + "," + DefaultWasdNumKeys[i] + "," + WasdNumKeysStr[i] + "," + WasdNumKeys[i] + "," + ";";
}
SaveKeyConfig(newData);
Debug.Log("保存键盘信息完毕");
}
}
这个类负责操作配置文件,要把内置的配置文件保存到本地配置文件中,以及读取配置文件。(要区分读取内置的还是本地的)
2.TitleScreen.cs
using System;
using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
public class TitleScreen : MonoBehaviour
{
TextMeshProUGUI tmpTitleText;
bool calledNextScene;
bool inputDetected = false;
bool isTitle = true;
bool isKeySetting = false;
bool isLanguageSetting = false;
int alphaKeyPressText = 255;
bool alphaKeyPressTextShow = true;
public AudioClip keyPressClip;
public AudioClip keyChangeClip;
[SerializeField] Image[] buttons = new Image[5];
[SerializeField] Text[] buttonsText = new Text[5];
private int buttonsChoosedNum = 0;
private enum TitleScreenStates { WaitForInput, NextScene };
TitleScreenStates titleScreenState = TitleScreenStates.WaitForInput;
#if UNITY_STANDALONE
string insertKeyPressText = "PRESS ANY KEY";
#endif
#if UNITY_ANDROID || UNITY_IOS
string insertKeyPressText = "TAP TO START";
#endif
string titleText =
@"<size=18><color=#ffffff{0:X2}>{1}</color></size>";
private void Awake()
{
//tmpTitleText = GameObject.Find("TitleText").GetComponent<TextMeshProUGUI>();
}
// Start is called before the first frame update
void Start()
{
//tmpTitleText.alignment = TextAlignmentOptions.Center;
//tmpTitleText.alignment = TextAlignmentOptions.Midline;
//tmpTitleText.fontStyle = FontStyles.UpperCase;
titleScreenState = TitleScreenStates.WaitForInput;
if (isHaveSavePoint())
{
initButton(1);
}
else {
initButton(0);
}
}
// Update is called once per frame
void Update()
{
switch(titleScreenState)
{
case TitleScreenStates.WaitForInput:
//buttonsText[buttonsChoosedNum].text = String.Format(titleText, alphaKeyPressText, buttonsText[buttonsChoosedNum].text);
buttonsText[buttonsChoosedNum].enabled = alphaKeyPressTextShow;
if (!inputDetected)
{
ChooseButton();
SelectButton();
}
break;
case TitleScreenStates.NextScene:
if (!calledNextScene)
{
GameManager.Instance.StartNextScene();
calledNextScene = true;
}
break;
}
}
private IEnumerator FlashTitleText() {
for(int i = 0; i < 5; i++)
{
alphaKeyPressTextShow = true;
alphaKeyPressText = 0;
yield return new WaitForSeconds(0.1f);
alphaKeyPressTextShow = false;
alphaKeyPressText = 255;
yield return new WaitForSeconds(0.1f);
}
alphaKeyPressTextShow = true;
alphaKeyPressText = 0;
yield return new WaitForSeconds(0.1f);
titleScreenState = TitleScreenStates.NextScene;
}
private IEnumerator FlashTitleTextAndOpenKeyConfig()
{
for (int i = 0; i < 5; i++)
{
alphaKeyPressTextShow = true;
alphaKeyPressText = 0;
yield return new WaitForSeconds(0.1f);
alphaKeyPressTextShow = false;
alphaKeyPressText = 255;
yield return new WaitForSeconds(0.1f);
}
alphaKeyPressTextShow = true;
alphaKeyPressText = 0;
yield return new WaitForSeconds(0.1f);
//退出的时候,需要改为接受输入
KeyConfigScript.Instance.Show(() => { inputDetected = false;Debug.Log("回调方法"); });
}
private bool isHaveSavePoint() {
return false;
}
private void ChooseButton() {
if ( Input.GetKeyDown(KeyCode.UpArrow) || Input.GetKeyDown((KeyCode)FileManager.WasdNumKeys[0]) )
{
if (buttonsChoosedNum > 0)
{
buttons[buttonsChoosedNum].enabled = false;
buttonsChoosedNum--;
buttons[buttonsChoosedNum].enabled = true;
PlayChangeButton();
}
else
{
PlayChangeButton();
}
}
if (Input.GetKeyDown(KeyCode.DownArrow) || Input.GetKeyDown((KeyCode)FileManager.WasdNumKeys[1]) )
{
if (buttonsChoosedNum < buttons.Length - 1)
{
buttons[buttonsChoosedNum].enabled = false;
buttonsChoosedNum++;
buttons[buttonsChoosedNum].enabled = true;
PlayChangeButton();
}
else
{
PlayChangeButton();
}
}
}
private void SelectButton() {
if( Input.GetKeyDown(KeyCode.Return) || Input.GetKeyDown((KeyCode)FileManager.WasdNumKeys[13]) )
{
if (buttonsChoosedNum == 0)
{
inputDetected = true;
StartCoroutine(FlashTitleText());
SoundManager.Instance.Play(keyPressClip);
} else if (buttonsChoosedNum == 1) {
inputDetected = true;
SoundManager.Instance.Play(keyPressClip);
}
else if (buttonsChoosedNum == 2)
{
inputDetected = true;
SoundManager.Instance.Play(keyPressClip);
}
else if (buttonsChoosedNum == 3)
{
inputDetected = true;
SoundManager.Instance.Play(keyPressClip);
StartCoroutine(FlashTitleTextAndOpenKeyConfig());
}
else if (buttonsChoosedNum == 4)
{
Application.Quit();
}
}
}
private void PlayChangeButton() {
if(keyChangeClip != null)
{
SoundManager.Instance.Play(keyChangeClip);
}
}
private void initButton(int n) {
buttonsChoosedNum = n;
for(int i = 0; i < buttons.Length; i++)
{
if (i == n)
{
buttons[i].enabled = true;
}
else {
buttons[i].enabled = false;
}
}
}
}
这个是游戏首页用的类,主要管是否显示按键设置页面;
按键设置页面也在首页,是个Canvas
对象,刚开始会隐藏,选择按键设置按钮并确定后,才会展示。
3.KeyConfigScript.cs
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.UIElements;
public class KeyConfigScript : MonoBehaviour
{
bool flashText = false;
public static KeyConfigScript Instance = null;
[SerializeField] Canvas canvas;
public AudioClip keyChangeClip;
public AudioClip keySelectClip;
/*
w
s
a
d
j
k
l
i
q
e
u
o
c
v
j
k
*/
public UnityEngine.UI.Image[] buttons = new UnityEngine.UI.Image[19];
[SerializeField] Text[] buttonsText = new Text[19];
[SerializeField] ScrollRect scrollRect;
private Action nowScreenAction;
private int buttonsChoosedNum = 0;
bool alphaKeyPressTextShow = true;
//检测按键
bool canInput = false;
bool canMove = true;
private void Awake()
{
// If there is not already an instance of SoundManager, set it to this.
if (Instance == null)
{
Instance = this;
}
//If an instance already exists, destroy whatever this object is to enforce the singleton.
else if (Instance != this)
{
Destroy(gameObject);
}
//Set SoundManager to DontDestroyOnLoad so that it won't be destroyed when reloading our scene.
DontDestroyOnLoad(gameObject);
//默认隐藏
Close();
}
private void OnEnable()
{
}
private void initKeysObjects() {
//读取下当前配置文件,获得每个按键配置的名称和值
for (int i = 0; i < buttons.Length; i++)
{
if (i >= 16)
{
buttons[i].enabled = false;
}
else
{
buttonsText[i].text = FileManager.LanguageWasd[i] + ":" + FileManager.WasdNumKeysStr[i];
//初始化方法,只有0才是
if (i == 0)
{
buttons[i].enabled = true;
}
else
{
buttons[i].enabled = false;
}
}
}
}
private void ChooseButton()
{
if ( Input.GetKeyDown(KeyCode.UpArrow) || Input.GetKeyDown((KeyCode)FileManager.WasdNumKeys[0]) )
{
if (buttonsChoosedNum > 0)
{
buttons[buttonsChoosedNum].enabled = false;
buttonsChoosedNum--;
buttons[buttonsChoosedNum].enabled = true;
PlayChangeButton();
ScrollUpOrDown(buttonsChoosedNum, false);
}
else
{
//移动到最后一个
buttons[buttonsChoosedNum].enabled = false;
buttonsChoosedNum = buttons.Length - 1;
buttons[buttonsChoosedNum].enabled = true;
PlayChangeButton();
}
}
if (Input.GetKeyDown(KeyCode.DownArrow) || Input.GetKeyDown((KeyCode)FileManager.WasdNumKeys[1]) )
{
if (buttonsChoosedNum < buttons.Length - 1)
{
buttons[buttonsChoosedNum].enabled = false;
buttonsChoosedNum++;
buttons[buttonsChoosedNum].enabled = true;
PlayChangeButton();
ScrollUpOrDown(buttonsChoosedNum, true);
}
else
{
//移动到第一个
buttons[buttonsChoosedNum].enabled = false;
buttonsChoosedNum = 0;
buttons[buttonsChoosedNum].enabled = true;
PlayChangeButton();
}
}
}
private void SelectButton()
{
//如果是可以移动状态
if (canMove)
{
//如果按下选择键
if ( Input.GetKeyDown(KeyCode.Return) || Input.GetKeyDown((KeyCode)FileManager.WasdNumKeys[13]) )
{
//播放声音
PlaySelectButton();
//关闭移动
canMove = false;
//如果是这些键,特殊处理然后返回
switch (buttonsChoosedNum)
{
case 16:
StartCoroutine(FlashTitleTextSecond(Reset));
return;
case 17:
StartCoroutine(FlashTitleTextSecond(SaveAndClose));
return;
case 18:
StartCoroutine(FlashTitleTextSecond(Close));
return;
}
//如果是普通键
//闪烁标志位打开
flashText = true;
//字幕闪烁
StartCoroutine(FlashTitleText());
}
}
else
{
BeforeSettingKey();
}
}
private void BeforeSettingKey()
{
if (Input.anyKeyDown)
{
//需要看这个键能不能设置
bool canSetting = false;
foreach (KeyCode key in System.Enum.GetValues(typeof(KeyCode)))
{
if (Input.GetKeyDown(key))
{
// 检查按键是否为可打印字符
if ((key >= KeyCode.A && key <= KeyCode.Z) || (key >= KeyCode.Alpha0 && key <= KeyCode.Alpha9))
{
// 将字符转换为 ASCII 码
string keyStr = key.ToString();
//char keyChar = keyStr[0];
int asciiValue = (int)key;
Debug.Log($"按下的键 {key} 对应的 ASCII 码值是:{asciiValue}");
SettingKey(keyStr, asciiValue);
canSetting = true;
break;
}
else
{
// 非字母数字键(你可以根据需求处理这些按键)
Debug.Log($"按下了非字符键:{key}");
if (key == KeyCode.Space)
{
SettingKey("Space", 32);
canSetting = true;
break;
}
else if (key == KeyCode.Return)
{
SettingKey("Enter", 13);
canSetting = true;
break;
}
else if (key == KeyCode.UpArrow)
{
SettingKey("Up", 273);
canSetting = true;
break;
}
else if (key == KeyCode.DownArrow)
{
SettingKey("Down", 274);
canSetting = true;
break;
}
else if (key == KeyCode.LeftArrow)
{
SettingKey("Left", 276);
canSetting = true;
break;
}
else if (key == KeyCode.RightArrow)
{
SettingKey("Right", 275);
canSetting = true;
break;
}
}
}
}
//如果可以设置,再进行后续操作
if (canSetting)
{
//停止闪烁
StartCoroutine(ResetTitleText());
}
}
}
private void SettingKey(string str, int ascII)
{
//更新设置的信息
FileManager.WasdNumKeysStrTemp[buttonsChoosedNum] = str;
FileManager.WasdNumKeysTemp[buttonsChoosedNum] = ascII;
//更新按键文本信息
buttonsText[buttonsChoosedNum].text = FileManager.LanguageWasd[buttonsChoosedNum] + ":"+ str;
}
private IEnumerator FlashTitleText()
{
while (flashText) {
alphaKeyPressTextShow = true;
yield return new WaitForSeconds(0.1f);
alphaKeyPressTextShow = false;
yield return new WaitForSeconds(0.1f);
}
}
private IEnumerator FlashTitleTextSecond(Func<bool> func)
{
for (int i=0; i<5; i++)
{
alphaKeyPressTextShow = true;
yield return new WaitForSeconds(0.1f);
alphaKeyPressTextShow = false;
yield return new WaitForSeconds(0.1f);
}
alphaKeyPressTextShow = true;
yield return new WaitForSeconds(0.1f);
//闪烁完毕后才能移动
canMove = true;
//闪烁完毕后执行
func();
}
private IEnumerator ResetTitleText()
{
flashText = false;
yield return new WaitForSeconds(0.2f);
alphaKeyPressTextShow = true;
buttonsText[buttonsChoosedNum].enabled = true;
canMove = true;
}
private void PlayChangeButton()
{
if (keyChangeClip != null)
{
SoundManager.Instance.Play(keyChangeClip);
}
}
private void PlaySelectButton()
{
if (keySelectClip != null)
{
SoundManager.Instance.Play(keySelectClip);
}
}
//先从配置文件读取配置信息
void Start()
{
}
// Update is called once per frame
void Update()
{
if (canInput)
{
buttonsText[buttonsChoosedNum].enabled = alphaKeyPressTextShow;
if (canMove)
{
ChooseButton();
}
SelectButton();
}
}
//翻页,现在不用
private void ScrollUpOrDown(int count, bool isDown)
{
/*
if(count == 8 && isDown)
{
scrollRect.normalizedPosition = new Vector2(0, 0);
}
else if (count == 7 && !isDown)
{
scrollRect.normalizedPosition = new Vector2(0, 1);
}
*/
}
public void Show(Action action) {
//翻页,现在不用
//scrollRect.normalizedPosition = new Vector2(0, 0);
alphaKeyPressTextShow = true;
buttonsChoosedNum = 0;
initKeysObjects();
this.nowScreenAction = action;
Debug.Log("show Key Config");
canvas.enabled = true;
canInput = true;
}
private bool Reset()
{
for (int i = 0; i < 16; i++)
{
//先把text内容改了
buttonsText[i].text = FileManager.LanguageWasd[i] + ":" + FileManager.DefaultWasdNumKeysStr[i];
//然后把临时数组改了
FileManager.WasdNumKeysStrTemp[i] = FileManager.DefaultWasdNumKeysStr[i];
FileManager.WasdNumKeysTemp[i] = FileManager.DefaultWasdNumKeys[i];
}
return true;
}
private bool Save()
{
for (int i = 0; i < 16; i++)
{
//给实际用的赋值
FileManager.WasdNumKeysStr[i] = FileManager.WasdNumKeysStrTemp[i];
FileManager.WasdNumKeys[i] = FileManager.WasdNumKeysTemp[i];
}
//写入文件
FileManager.SaveKeyConfig();
return true;
}
private bool SaveAndClose()
{
Save();
Close();
return true;
}
public bool Close() {
canvas.enabled = false;
canInput = false;
//上一个屏幕,改为接受输入
if(nowScreenAction != null)
{
nowScreenAction();
}
return true;
//nowScreenManager.GetComponent<TitleScreen>().inputDetected = false;
}
}
这个类就是按键设置页面用的类,有光标上下移动功能、按钮选择后让字幕闪烁的功能、再次按键后设置新按键功能。
四、备注
unity代码比较复杂,直接复制粘贴是无法使用的,每人的场景元素也不一样,很多变量会不存在,仅供参考。
以上就是个人编写的设置按键的代码,大致逻辑如下:
1.游戏启动后,先判断有没有本地配置文件,如果有、就读取本地配置文件并初始化;如果没有,就用内置配置文件初始化,并把内置配置文件保存到本地。
2.进入按键选择页面时,上下方向键移动光标,按回车可以选择按键,此时按键闪烁,再按另一个键可以设置为新按键。
3.有恢复默认设置功能,有保存并退出功能,有直接退出功能;如果选保存并退出,才把设置的临时按键数组赋值到实际使用的按键数组,并保存到本地配置文件。