XNA播放器输入过程

时间:2011-12-03 00:43:12

标签: c# input xna

我有一个代码可以让玩家输入他们的名字(是的,老式的) 声明变量:

bool hiScore = false; 

string[] alphabet = new string[] { "_", "a", "b", "c", "d", "e", "f", "g", "h",
       "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", 
       "x", "y", "z" }; 

int ltrCounter1, ltrCounter2, ltrCounter3, ltrCounter4, ltrCounter5; 

string plyrNamePos1, plyrNamePos2, plyrNamePos3, plyrNamePos4, plyrNamePos5; 

int letterPosition = 1;

我在课程的开头将它设为零

ltrCounter1 = 0;
ltrCounter2 = 0; 
ltrCounter3 = 0;
ltrCounter4 = 0;
ltrCounter5 = 0;
plyrNamePos1 = alphabet[0];
plyrNamePos2 = alphabet[0];
plyrNamePos3 = alphabet[0];
plyrNamePos4 = alphabet[0];
plyrNamePos5 = alphabet[0]

并且在update()方法中我放置了这个代码来抓取箭头键并使用它们,更改字母。

keyboardState = Keyboard.GetState();

if (CheckKey(Keys.Up))
{
    hiScoreUp();
}
else if (CheckKey(Keys.Down))
{
    hiScoreDown();
}

if (CheckKey(Keys.Right))
{
    letterPosition++;
    if (letterPosition > 5)
    {
        letterPosition = 5;
    }
}
else if (CheckKey(Keys.Left))
{
    letterPosition--;
    if (letterPosition < 1)
    {
        letterPosition = 1;
    }
}
else if (CheckKey(Keys.Enter))
{
    saveHighScores();
    QuizScreen.score = 100;
    ActionScreen.halaman = 1;
    kembalikemenuutama = true;
}

和这个方法来改变字母

private void hiScoreUp() 
{ 
    switch (letterPosition) 
    { 
        case 1: 
            ltrCounter1++; 
            if (ltrCounter1 > 26) 
            { 
                ltrCounter1 = 0; 
            } 
            plyrNamePos1 = alphabet[ltrCounter1]; 
            break; 
        case 2: 
            ltrCounter2++; 
            if (ltrCounter2 > 26) 
            { 
                ltrCounter2 = 0; 
            } 
            plyrNamePos2 = alphabet[ltrCounter2]; 
            break; 
        case 3: 
            ltrCounter3++; 
            if (ltrCounter3 > 26) 
            { 
                ltrCounter3 = 0; 
            } 
            plyrNamePos3 = alphabet[ltrCounter3]; 
            break; 
        case 4: 
            ltrCounter4++; 
            if (ltrCounter4 > 26) 
            { 
                ltrCounter4 = 0; 
            } 
            plyrNamePos4 = alphabet[ltrCounter4]; 
            break; 
        case 5: 
            ltrCounter5++; 
            if (ltrCounter5 > 26) 
            { 
                ltrCounter5 = 0; 
            } 
            plyrNamePos5 = alphabet[ltrCounter5]; 
            break; 
    } 
} 

private void hiScoreDown() 
{ 
    switch (letterPosition) 
    { 
        case 1: 
            ltrCounter1--; 
            if (ltrCounter1 < 0) 
            { 
                ltrCounter1 = 26; 
            } 
            plyrNamePos1 = alphabet[ltrCounter1]; 
            break; 
        case 2: 
            ltrCounter2--; 
            if (ltrCounter2 < 0) 
            { 
                ltrCounter2 = 26; 
            } 
            plyrNamePos2 = alphabet[ltrCounter2]; 
            break; 
        case 3: 
            ltrCounter3--; 
            if (ltrCounter3 < 0) 
            { 
                ltrCounter3 = 26; 
            } 
            plyrNamePos3 = alphabet[ltrCounter3]; 
            break; 
        case 4: 
            ltrCounter4--; 
            if (ltrCounter4 < 0) 
            { 
                ltrCounter4 = 26; 
            } 
            plyrNamePos4 = alphabet[ltrCounter4]; 
            break; 
        case 5: 
            ltrCounter5--; 
            if (ltrCounter5 < 0) 
            { 
                ltrCounter5 = 26; 
            } 
            plyrNamePos5 = alphabet[ltrCounter5]; 
            break; 
    } 
}

这就是我画它的方式

hiScoreName = plyrNamePos1 + " " + plyrNamePos2 + " " + plyrNamePos3 + " " + 
      plyrNamePos4 + " " + plyrNamePos5;

if (gameTime.TotalGameTime.Milliseconds % 1000 < 500)
{
    spriteBatch.DrawString(spriteFont, hiScoreName, new Vector2(350, 280), 
    Color.Blue);
}

我想问的是:

  1. 如何使该输入字段的活动字段闪烁? 使用当前代码,我只能使所有字段闪烁(使用gameTime.TotalGameTime.Milliseconds % 1000 < 500作为抽绳)。

  2. 我正在使用"_"填写空格" "。保存播放器名称数据后,"_"也会保存在数据库中。当"_"保存到外部来源时,如何将" "更改为"_"

  3. 当玩家尝试在所有字段中保存带有{{1}}字符的玩家名称时,如何向玩家显示警告消息...?

  4. 任何人都可以帮忙......?

    对不起,如果这很长。 谢谢你

2 个答案:

答案 0 :(得分:0)

好了...

1) 您可以绘制彼此相邻的单个字符,而不是显示包含所有字符的字符串

hiScoreName = plyrNamePos1 + plyrNamePos2 + plyrNamePos3 + plyrNamePos4 + plyrNamePos5;

int characterSpacing = 20; //might be more or less for you

for(int i=1; i<=5; i++){
    if(i==letterposition){
        if(gameTime.TotalGameTime.Miliseconds % 1000 < 500){
            spriteBatch.DrawString(spriteFont, hiScoreName[i-1], new Vector(350 + (i-1)*characterSpacing, 280), Color.Blue);
        }
    }else{
        spriteBatch.DrawString(spriteFont, hiScoreName[i-1], new Vector(350 + (i-1)*characterSpacing, 280), Color.Blue);
    }
}

注意:我刚刚在这里输入了这段代码,从未对它进行过测试,因此可能会有一些错误:)

2) 您可以使用String.Replace()方法。

String stringToSave = hiScoreName.Replace("_"," ");

3) 当你检测到所有字符都是_时,你应该在你的程序中设置一个标志(bool变量可以说),表明玩家试图保存所有_。然后在draw方法中,如果设置了此标志,则可以显示消息。当玩家随后更改角色时,应重置该标志。

bool _invalidHiScoreName = false; //somewhere outside methods.

void SavePlayerScore(String hiScoreName, int score){
    bool allUnderscore = true;
    foreach(char c in hiScoreName){
        if(c!='_')
            allUnderscore = false;
    }

    if(allUnderscore){
        _invalidHiScoreName = true;
    }else{
        String stringToSave = hiScoreName.Replace("_"," "); //from step 2)
        //your saving logic.
    }
}

然后,在您的绘制方法中,spriteBatch.Begin()spriteBatch.End()之间可以放置:

if(_invalidHiScoreName){
    spriteBatch.DrawString(spriteFont, "Hi Score Name must have at least one character!", Vector2.Zero, Color.White); //Might wanna position it.
}

当角色被更改时,别忘了重置标志_invalidHiScoreName=false;

除此之外,我可以告诉你,这是一个非常糟糕的方式来获取输入并建议其他更简单/更好的方法,但这并没有真正回答这个问题,当人们不回答时我也讨厌它问题:))

玩得开心。

答案 1 :(得分:0)

以下是基于输入的输入解决方案,而不是选择字符

此代码使用本机win32 API来检测击键(包括控制字符)。

代码由gamedev.net用户Promit in this post提供。

在项目中创建一个新类,将其命名为CharEventArgs,并使用以下内容替换内部的所有内容(来自上面链接的代码):

using System;
using System.Runtime.InteropServices;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;

namespace EventInput
{
    public class CharacterEventArgs : EventArgs
    {
        private readonly char character;
        private readonly int lParam;

        public CharacterEventArgs(char character, int lParam)
        {
            this.character = character;
            this.lParam = lParam;
        }

        public char Character
        {
            get { return character; }
        }

        public int Param
        {
            get { return lParam; }
        }

        public int RepeatCount
        {
            get { return lParam & 0xffff; }
        }

        public bool ExtendedKey
        {
            get { return (lParam & (1 << 24)) > 0; }
        }

        public bool AltPressed
        {
            get { return (lParam & (1 << 29)) > 0; }
        }

        public bool PreviousState
        {
            get { return (lParam & (1 << 30)) > 0; }
        }

        public bool TransitionState
        {
            get { return (lParam & (1 << 31)) > 0; }
        }
    }

    public class KeyEventArgs : EventArgs
    {
        private Keys keyCode;

        public KeyEventArgs(Keys keyCode)
        {
            this.keyCode = keyCode;
        }

        public Keys KeyCode
        {
            get { return keyCode; }
        }
    }

    public delegate void CharEnteredHandler(object sender, CharacterEventArgs e);
    public delegate void KeyEventHandler(object sender, KeyEventArgs e);

    public static class EventInput
    {
        /// <summary>
        /// Event raised when a character has been entered.
        /// </summary>
        public static event CharEnteredHandler CharEntered;

        /// <summary>
        /// Event raised when a key has been pressed down. May fire multiple times due to keyboard repeat.
        /// </summary>
        public static event KeyEventHandler KeyDown;

        /// <summary>
        /// Event raised when a key has been released.
        /// </summary>
        public static event KeyEventHandler KeyUp;

        delegate IntPtr WndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);

        static bool initialized;
        static IntPtr prevWndProc;
        static WndProc hookProcDelegate;
        static IntPtr hIMC;

        //various Win32 constants that we need
        const int GWL_WNDPROC = -4;
        const int WM_KEYDOWN = 0x100;
        const int WM_KEYUP = 0x101;
        const int WM_CHAR = 0x102;
        const int WM_IME_SETCONTEXT = 0x0281;
        const int WM_INPUTLANGCHANGE = 0x51;
        const int WM_GETDLGCODE = 0x87;
        const int WM_IME_COMPOSITION = 0x10f;
        const int DLGC_WANTALLKEYS = 4;

        //Win32 functions that we're using
        [DllImport("Imm32.dll")]
        static extern IntPtr ImmGetContext(IntPtr hWnd);

        [DllImport("Imm32.dll")]
        static extern IntPtr ImmAssociateContext(IntPtr hWnd, IntPtr hIMC);

        [DllImport("user32.dll")]
        static extern IntPtr CallWindowProc(IntPtr lpPrevWndFunc, IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);

        [DllImport("user32.dll")]
        static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);

        /// <summary>
        /// Initialize the TextInput with the given GameWindow.
        /// </summary>
        /// <param name="window">The XNA window to which text input should be linked.</param>
        public static void Initialize(GameWindow window)
        {
            if (initialized)
                throw new InvalidOperationException("TextInput.Initialize can only be called once!");

            hookProcDelegate = new WndProc(HookProc);
            prevWndProc = (IntPtr)SetWindowLong(window.Handle, GWL_WNDPROC,
                (int)Marshal.GetFunctionPointerForDelegate(hookProcDelegate));

            hIMC = ImmGetContext(window.Handle);
            initialized = true;
        }

        static IntPtr HookProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam)
        {
            IntPtr returnCode = CallWindowProc(prevWndProc, hWnd, msg, wParam, lParam);

            switch (msg)
            {
                case WM_GETDLGCODE:
                    returnCode = (IntPtr)(returnCode.ToInt32() | DLGC_WANTALLKEYS);
                    break;

                case WM_KEYDOWN:
                    if (KeyDown != null)
                        KeyDown(null, new KeyEventArgs((Keys)wParam));
                    break;

                case WM_KEYUP:
                    if (KeyUp != null)
                        KeyUp(null, new KeyEventArgs((Keys)wParam));
                    break;

                case WM_CHAR:
                    if (CharEntered != null)
                        CharEntered(null, new CharacterEventArgs((char)wParam, lParam.ToInt32()));
                    break;

                case WM_IME_SETCONTEXT:
                    if (wParam.ToInt32() == 1)
                        ImmAssociateContext(hWnd, hIMC);
                    break;

                case WM_INPUTLANGCHANGE:
                    ImmAssociateContext(hWnd, hIMC);
                    returnCode = (IntPtr)1;
                    break;
            }

            return returnCode;
        }
    }
}

在您的Game1.cs (或您决定命名的任何内容)中,将using EventInput;置于最顶层。

在Initialize方法中,添加以下内容:

EventInput.EventInput.Initialize(this.Window);
EventInput.EventInput.CharEntered += new CharEnteredHandler(EventInput_CharEntered);

在你的Game1.cs中(再次,你的名字命名),添加相关的处理程序:

    void EventInput_CharEntered(object sender, CharacterEventArgs e)
    {

        if (char.IsControl(e.Character))
        {
            switch (e.Character)
            {
                case '\b':
                    if (hiScoreName.Length > 0)
                    {
                        hiScoreName = hiScoreName.Substring(0, hiScoreName.Length - 1);
                    }
                    break;
                case '\r':
                    //enter
                    break;
            }
        }
        else
        {
            hiScoreName += e.Character;
        }
    }

最后,在Draw()方法中,使用类似的代码绘制字符串:

        spriteBatch.Begin();
        spriteBatch.DrawString(spriteFont, hiScoreName, new Vector2(20f, 20f), Color.Black);
        spriteBatch.End();

要点:

这几乎使本主题中的其他内容无效(包括您的代码,以及我在其他答案中发布的所有内容),因为它使用不同的方式从用户那里获取输入。

将代码粘贴到CharEventArgs.cs中后,您可以安全地关闭它并且永远不会再打开它,它会做它应该做的事情。

您处理输入的要点是您的事件处理程序EventInput_CharEntered,您可以在其中检查按下的字符,然后对其进行操作。您应该使用此方法,添加您自己的代码,以便您可以在一种情况下将其用于hiScoreName输入,并将其用于其他位置的其他文本输入。

你可以在事件处理程序中看到,我对//输入进行了评论,所以你可以说放一个函数调用来处理在那里保存hiScoreName!

另请注意,CharacterEventArgs e 包含有关其他修饰符的信息,例如Alt,重复计数等。您可以使用这些来为程序添加其他逻辑。关于windows如何处理击键on this msdn link(非常易于理解)的有用读物。

如果需要,您还可以在EventInput类上注册KeyPressed和KeyReleased事件。

我测试了我在这里发布的代码,它对我有用!

玩得开心!

修改: 如果你想使用Unicode字符(与你的语言相关,即我使用ščćžđ),你需要像这样调用DLLImports:

[DllImport("user32.dll", CharSet = CharSet.Unicode)]

因此,您必须更改所有DllImport调用以包含CharSet = CharSet.Unicode

此外,不要忘记扩展spritefont的范围以包含您可能想要显示的其他字符。