Unity实现按键设置功能代码

一、前言

最近在学习unity2D,想做一个横版过关游戏,需要按键设置功能,让用户可以自定义方向键与攻击键等。

自己写了一个,总结如下。

二、界面效果图

这个是一个csv文件,准备第一列是中文按键说明,第二列是英文,第三列是日文(还没有翻译);第四列是默认按键名称,第五列是默认按键ascII码(如果用户选了恢复默认设置,就会用到);第六列是用户自己设置的按键名称,第七列是用户自己设置的按键ascII码;第八列保留,暂未使用。

这个是首页,如果选到了这个按钮,就是按键设置页面。

这个是按键设置页面,实现了按 上下/用户设置的上下移动光标,按回车/ 开始键确定,然后按另一个键进行按键修改。

(图中灰色块就是光标,还没有找图片;最后3行按钮进行了特殊处理)

三、主要部分代码

unity代码比较多,总结下主要用到的3个类。

1.FileManager.cs

a 复制代码
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

a 复制代码
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

a 复制代码
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.有恢复默认设置功能,有保存并退出功能,有直接退出功能;如果选保存并退出,才把设置的临时按键数组赋值到实际使用的按键数组,并保存到本地配置文件。

相关推荐
WarPigs18 小时前
游戏签到系统
unity
小拉达不是臭老鼠20 小时前
Unity中的UI系统之UGUI
学习·ui·unity
万兴丶20 小时前
Coplay适用于 Unity 的“Al 代理”使用指南
unity·游戏引擎·ai编程
魔士于安1 天前
Unity材质球大合集
unity·游戏引擎·材质
mxwin1 天前
Unity Shader 冰面 Shader 制作原理与流程
unity·游戏引擎·shader
小拉达不是臭老鼠1 天前
Unity中的UI系统之UGUI_登陆面板实现
ui·unity
郝学胜-神的一滴1 天前
[简化版 GAMES 101] 计算机图形学 11:频域·卷积·抗锯齿
c++·unity·图形渲染·opengl·three·unreal
元气少女小圆丶2 天前
SenseGlove Nova 2+Unity开发笔记2
笔记·unity·游戏引擎
想不明白的过度思考者2 天前
Unity学习笔记——虚拟摇杆实现笔记(事件触发器的使用、UGUI 坐标转换)
笔记·学习·unity
魔士于安2 天前
unity volumefog带各种demo第一人称 wsad 穿墙控制
游戏·unity·游戏引擎·贴图·模型