Процесс ввода XNA-плеера - PullRequest
       9

Процесс ввода XNA-плеера

0 голосов
/ 03 декабря 2011

У меня есть код, который получает ввод игрока для его имени, как это (да, это старомодный) объявить переменную:

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 для Drawstring).

  2. Я использую "_" для заполнения пробела " ". И когда данные имени игрока сохранены, "_" также сохраняется в базе данных. Как я могу изменить "_" на " ", если оно сохранено во внешнем источнике ...?

  3. Как отобразить сообщение с предупреждением для игрока, когда он попытался сохранить имя игрока с символом "_" во всех полях ...?

Может кто-нибудь помочь ...?

Извините, если это довольно долго. И спасибо

Ответы [ 2 ]

0 голосов
/ 15 декабря 2011

Вот решение для ввода, основанное на наборе , а не выборе символов .

Этот код использует собственный API-интерфейс win32 для обнаружения нажатий клавиш (включая управляющие символы)).

Код предоставлен Promit, пользователем gamedev.net, в этом сообщении .

Создать новый класс в своем проекте,назовите его 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();

Резюме:

Это в значительной степени лишает законной силы все остальное в этой теме (включая ваш код и все, что я опубликовал в другом ответе), bпотому что он использует другой способ получения ввода от пользователя.

Как только вы вставите код в CharEventArgs.cs, вы можете безопасно закрыть его и никогда не открывать снова , он будет делать то, чтодолжно.

Основная точка , где вы обрабатываете ввод , - это ваш обработчик событий EventInput_CharEntered, где вы проверяете, какой символ нажимается, и воздействуете на него.Вы должны поиграть с этим методом, добавить свой собственный код, чтобы вы могли, например, использовать его для ввода hiScoreName в одном случае, и использовать его для другого ввода текста в другом месте.

Вы можете увидеть в событииобработчик, я прокомментировал // ввод, так что вы могли бы, скажем, поместить вызов функции для обработки сохранения hiScoreName там!

Также обратите внимание, что CharacterEventArgs e содержит информацию о дополнительных модификаторах ,такие как Alt, количество повторов и т. д. Вы можете использовать их для добавления дополнительной логики в вашу программу.Полезно прочитать о том, как Windows обрабатывает нажатия клавиш по этой ссылке msdn (очень понятно).

Вы также можете зарегистрировать события KeyPressed и KeyReleased в классе EventInput, если они вам нужны.

Я протестировал код, который выложил здесь, и он работает для меня!

Веселитесь!

РЕДАКТИРОВАТЬ : если вы хотите использовать символы Юникода (которые имеют отношениена ваш язык, т. е. я использую ščćžđ), вам нужно вызывать DLLImports следующим образом:

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

Таким образом, вы должны изменить все вызовы DllImport для включения CharSet = CharSet.Unicode.

Такжене забудьте расширить диапазон шрифта spritefont, включив в него те символы, которые вы, возможно, захотите отобразить.

0 голосов
/ 03 декабря 2011

Хорошо ...

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) в вашей программе, который указывает, что игрок пытался сохранить как все _.Затем в вашем методе рисования вы можете отобразить ваше сообщение, если этот флаг установлен.Когда игрок затем меняет персонажа, флаг должен быть сброшен.

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; при смене символа!

Кроме этого, я могу сказать вам, что это действительно плохой способ получить ввод и предложить другие более простые / лучшие способы,но это на самом деле не отвечает на вопрос, и я также ненавижу, когда люди не отвечают на вопрос:)

Веселитесь.

...