2009-12-22 8 views
5

Ho un gioco a cui sto lavorando in un'applicazione console C#, puramente come pratica prima di passare a metodi migliori. Al contrario di usare qualcosa come un'app Windows Form, che ha funzionalità di pulsante integrate, sto cercando di afferrare la posizione del cursore (che so come fare) e confrontarla con un numero di aree all'interno di un'applicazione console come definito da forse la posizione dei pixel, ma non so nemmeno se ci sia una sorta di unità di spazio incorporata diversa dai pixel (quest'ultimo è la parte che non riesco a capire).Console App Clic con il mouse X Y Coordinate Detection/Comparison

P.S. So che questo è in termini generali, senza codice già fornito, ma non ritengo che sia necessario poiché tutto ciò che chiedo è una breve spiegazione su come acquisire le coordinate XY all'interno di un'applicazione console e incollarle in variabili int .

Molte grazie in anticipo! : D

+3

** Le applicazioni della Console ** normalmente non interagiscono con il mouse né hanno molte preoccupazioni sui sistemi di coordinate pixel. Mentre sono sicuro che stai per ottenere un sacco di risposte che descrivono come catturare il mouse e quant'altro, penso che sia meglio fare un passo indietro e riconsiderare quello che stai facendo. –

+2

Molte grazie per il commento, ma sto imparando il più possibile sulle app per console C#. Tra circa 3 mesi sarò soddisfatto e passerò ai moduli e infine alle nuove lingue. Ho letto parte del tuo blog e apprezzo le tue opinioni sull'apprendimento delle lingue. Ottenere una lingua per fare qualcosa che normalmente non fa in alcuni casi è solo una parte della sfida. – Bloodyaugust

risposta

4

Cosa ha detto Frank Krueger. Vuoi davvero farlo? Windows Forms è progettato per rendere più semplice questo .

In tal caso, sarà necessario utilizzare PInvoke nell'API Windows di basso livello. Prova a this come punto di partenza, ma tieni presente che questo è molto più complesso di quello che sarebbe l'applicazione Windows Form.

+0

Wow, è estremamente complicato. Ho avuto l'impressione che i giochi siano scritti come app per console o qualcosa di simile rispetto a Windows Form e simili, in quanto basati su eventi. Non c'è un modo più semplice? – Bloodyaugust

+0

Ti abbiamo avvertito! Le applicazioni di console sono in realtà utilizzate solo per le utilità che elaborano testo o file. Su PC, i giochi con grafica intensiva usano in genere DirectX, WPF o Windows Form - Windows Form è probabilmente il modo più semplice per iniziare. I framework dell'interfaccia utente come Windows Form riguardano tutti gli eventi e come gestirli, come scoprirai. –

+0

Allora perché ho sempre sentito che i giochi non sono basati su eventi, ma piuttosto basati su loop? – Bloodyaugust

3

Quando si scrive un gioco senza l'utilizzo di eventi ... tutto ciò che si sta realmente facendo è implementare eventi personalmente. Questo è vantaggioso, perché puoi renderlo molto più efficiente rispetto all'utilizzo degli eventi incorporati nella tua lingua. I giochi scritti in questo modo sono meno soggetti a errori se sai cosa stai facendo.

Ad esempio, quando stavo cercando di insegnare a mio fratello come sono scritti i giochi, ho scritto per lui un semplice gioco di serpenti. Ho avuto il ciclo principale in una discussione, spostare il serpente e disegnarlo nella sua nuova posizione in un ciclo. Avrei un thread in esecuzione allo stesso tempo che controlla continuamente 4 cose:

1) Se il serpente si è schiantato su se stesso (game over); se si verifica un game over, interrompi il thread principale che aggiorna la posizione principale del serpente, stampa il gioco sullo schermo, attendi l'input da tastiera, quindi riavvia il gioco.

2) Se il serpente avesse mangiato una mela; incrementare la variabile contatore che indica quante mele sono state mangiate e stampare questo nuovo valore sullo schermo, sovrascrivendo ciò che era precedentemente presente.

3) Se il serpente aveva mangiato una quantità di mele divisibile per 10 (serpente cresce da 1 cella, sottrarre da una variabile di attesa che dice quanto tempo deve passare tra ogni movimento del serpente rende)

4) Se è stato premuto un tasto freccia. Se lasciato, imposta move to 0, se right set muove su 1, se down set muove su 2, se su set muovi verso 3. L'int in cui è memorizzato è un puntatore a un array di 4 delegati che fanno muovere il serpente nella giusta direzione.

Il ciclo principale che aggiorna la posizione del serpente indica al thread il controllo di queste 4 cose su cosa sta facendo il serpente. Il modo in cui lo faccio è che ho ogni cella sullo schermo che la testa del serpente si muove per riferirsi a una matrice bidimensionale di delegati. Informazioni su questa serie di delegati:

Il gioco è scritto in modalità console e utilizza i colori della console. La console è impostata su 80x50 caratteri. Un delegato come segue: "delegate void ptr()"; quindi creo l'array con: "ptr [,] pos = new ptr [80,50]". Dite che la testa del serpente è in posizione (4,5) sullo schermo, dopo che si è spostata lì il ciclo principale eseguirà "pos [4,5] .Invoke();".

Uno di essi: Quando il serpente si sposta in una nuova posizione, il thread del ciclo principale otterrà ogni cella che il serpente copre sullo schermo e imposta il delegato in quella posizione in modo che punti a una funzione chiamata "gameover vuoto() "che imposta la variabile gameover_ su true. Quindi, quando il thread del loop che controlla lo stato del gioco controlla il gameover, blocca il gioco e stampa il gioco sullo schermo.

Altro: Quando una mela viene disegnata sullo schermo, la posizione del delegato su cui viene disegnata (che è casuale) è impostata su "void increment_apple()" che incrementa il contatore della mela, rimuove la mela corrente da visualizza e disegna una nuova mela sullo schermo, impostando la vecchia posizione della mela in modo che punti a "void nop()" che non fa nulla e la nuova posizione della mela indicherà "void increment_apple()".

Questo è fondamentalmente come funziona il gioco. Come puoi vedere, il serpente si sposta su queste posizioni sullo schermo, e senza eseguire controlli espliciti come "if (snake_position == some_position)", il gioco fa automaticamente tutto ciò che è necessario per tutto ciò che accade nel gioco, molto simile a come quando fai clic su un pulsante su un modulo, un'azione viene assegnata automaticamente a quell'evento, senza che tu debba controllare tu stesso l'evento.

Quindi, vedete, avrei potuto usare un modulo e gli eventi predefiniti forniti da C#, ma non l'ho fatto. Ho usato l'interfaccia della console e implementato il mio sistema di eventi.

Ecco come funziona dietro le quinte: il ciclo principale per la tua app form verrà eseguito in un thread che controlla l'input da tutti i pulsanti, ecc sullo schermo. Ognuno di questi elementi imposterà una variabile booleana che usano per vero. Quando si fa clic su questo pulsante, un altro thread che esegue un ciclo controlla ciò che si è premuto, e dice di aver premuto un pulsante chiamato "button1", a quel pulsante sarebbe stato assegnato un delegato; quel delegato viene quindi eseguito con qualunque cosa punti.

Tipo di difficile da spiegare, ma ha senso per te?

5

Inoltre, la console non è solo per l'elaborazione del testo. Puoi scrivere gestori di finestre abbastanza decenti per questo. Puoi fare qualsiasi cosa con esso. È solo più difficile.

È più lento, però. Ho implementato una macchina virtuale in C# utilizzando la console per l'interfaccia utente. Non stampa righe di testo una dopo l'altra; [l'interfaccia] agisce piuttosto come una GUI.

Se si desidera l'input del mouse sulla console, provate questo gancio: http://blogs.msdn.com/b/toub/archive/2006/05/03/589468.aspx?PageIndex=2#comments

5

Dopo aver cercato per molto tempo ho finalmente trovato this example. Scarica il example program nella pagina. Ti dà, tra le altre cose, la posizione del mouse nella finestra della console (basata sui caratteri).

EDIT: Questo è il mio ConsoleListener classe (con una parte della mia classe NativeMethods).
È possibile collegare un gestore allo MouseEvent (dopo aver chiamato il metodo Start()).

using System; 
using System.Runtime.InteropServices; 
using System.Threading; 
using static ConsoleLib.NativeMethods; 

namespace ConsoleLib 
{ 
    public static class ConsoleListener 
    { 
     public static event ConsoleMouseEvent MouseEvent; 

     public static event ConsoleKeyEvent KeyEvent; 

     public static event ConsoleWindowBufferSizeEvent WindowBufferSizeEvent; 

     private static bool Run = false; 


     public static void Start() 
     { 
      if (!Run) 
      { 
       Run = true; 
       IntPtr handleIn = GetStdHandle(STD_INPUT_HANDLE); 
       new Thread(() => 
       { 
        while (true) 
        { 
         uint numRead = 0; 
         INPUT_RECORD[] record = new INPUT_RECORD[1]; 
         record[0] = new INPUT_RECORD(); 
         ReadConsoleInput(handleIn, record, 1, ref numRead); 
         if (Run) 
          switch (record[0].EventType) 
          { 
           case INPUT_RECORD.MOUSE_EVENT: 
            MouseEvent?.Invoke(record[0].MouseEvent); 
            break; 
           case INPUT_RECORD.KEY_EVENT: 
            KeyEvent?.Invoke(record[0].KeyEvent); 
            break; 
           case INPUT_RECORD.WINDOW_BUFFER_SIZE_EVENT: 
            WindowBufferSizeEvent?.Invoke(record[0].WindowBufferSizeEvent); 
            break; 
          } 
         else 
         { 
          uint numWritten = 0; 
          WriteConsoleInput(handleIn, record, 1, ref numWritten); 
          return; 
         } 
        } 
       }).Start(); 
      } 
     } 

     public static void Stop() => Run = false; 


     public delegate void ConsoleMouseEvent(MOUSE_EVENT_RECORD r); 

     public delegate void ConsoleKeyEvent(KEY_EVENT_RECORD r); 

     public delegate void ConsoleWindowBufferSizeEvent(WINDOW_BUFFER_SIZE_RECORD r); 

    } 


    public static class NativeMethods 
    { 
     public struct COORD 
     { 
      public short X; 
      public short Y; 

      public COORD(short x, short y) 
      { 
       X = x; 
       Y = y; 
      } 
     } 

     [StructLayout(LayoutKind.Explicit)] 
     public struct INPUT_RECORD 
     { 
      public const ushort KEY_EVENT = 0x0001, 
       MOUSE_EVENT = 0x0002, 
       WINDOW_BUFFER_SIZE_EVENT = 0x0004; //more 

      [FieldOffset(0)] 
      public ushort EventType; 
      [FieldOffset(4)] 
      public KEY_EVENT_RECORD KeyEvent; 
      [FieldOffset(4)] 
      public MOUSE_EVENT_RECORD MouseEvent; 
      [FieldOffset(4)] 
      public WINDOW_BUFFER_SIZE_RECORD WindowBufferSizeEvent; 
      /* 
      and: 
      MENU_EVENT_RECORD MenuEvent; 
      FOCUS_EVENT_RECORD FocusEvent; 
      */ 
     } 

     public struct MOUSE_EVENT_RECORD 
     { 
      public COORD dwMousePosition; 

      public const uint FROM_LEFT_1ST_BUTTON_PRESSED = 0x0001, 
       FROM_LEFT_2ND_BUTTON_PRESSED = 0x0004, 
       FROM_LEFT_3RD_BUTTON_PRESSED = 0x0008, 
       FROM_LEFT_4TH_BUTTON_PRESSED = 0x0010, 
       RIGHTMOST_BUTTON_PRESSED = 0x0002; 
      public uint dwButtonState; 

      public const int CAPSLOCK_ON = 0x0080, 
       ENHANCED_KEY = 0x0100, 
       LEFT_ALT_PRESSED = 0x0002, 
       LEFT_CTRL_PRESSED = 0x0008, 
       NUMLOCK_ON = 0x0020, 
       RIGHT_ALT_PRESSED = 0x0001, 
       RIGHT_CTRL_PRESSED = 0x0004, 
       SCROLLLOCK_ON = 0x0040, 
       SHIFT_PRESSED = 0x0010; 
      public uint dwControlKeyState; 

      public const int DOUBLE_CLICK = 0x0002, 
       MOUSE_HWHEELED = 0x0008, 
       MOUSE_MOVED = 0x0001, 
       MOUSE_WHEELED = 0x0004; 
      public uint dwEventFlags; 
     } 

     [StructLayout(LayoutKind.Explicit, CharSet = CharSet.Unicode)] 
     public struct KEY_EVENT_RECORD 
     { 
      [FieldOffset(0)] 
      public bool bKeyDown; 
      [FieldOffset(4)] 
      public ushort wRepeatCount; 
      [FieldOffset(6)] 
      public ushort wVirtualKeyCode; 
      [FieldOffset(8)] 
      public ushort wVirtualScanCode; 
      [FieldOffset(10)] 
      public char UnicodeChar; 
      [FieldOffset(10)] 
      public byte AsciiChar; 

      public const int CAPSLOCK_ON = 0x0080, 
       ENHANCED_KEY = 0x0100, 
       LEFT_ALT_PRESSED = 0x0002, 
       LEFT_CTRL_PRESSED = 0x0008, 
       NUMLOCK_ON = 0x0020, 
       RIGHT_ALT_PRESSED = 0x0001, 
       RIGHT_CTRL_PRESSED = 0x0004, 
       SCROLLLOCK_ON = 0x0040, 
       SHIFT_PRESSED = 0x0010; 
      [FieldOffset(12)] 
      public uint dwControlKeyState; 
     } 

     public struct WINDOW_BUFFER_SIZE_RECORD 
     { 
      public COORD dwSize; 
     } 

     public const uint STD_INPUT_HANDLE = unchecked((uint)-10), 
      STD_OUTPUT_HANDLE = unchecked((uint)-11), 
      STD_ERROR_HANDLE = unchecked((uint)-12); 

     [DllImport("kernel32.dll")] 
     public static extern IntPtr GetStdHandle(uint nStdHandle); 


     public const uint ENABLE_MOUSE_INPUT = 0x0010, 
      ENABLE_QUICK_EDIT_MODE = 0x0040, 
      ENABLE_EXTENDED_FLAGS = 0x0080, 
      ENABLE_ECHO_INPUT = 0x0004, 
      ENABLE_WINDOW_INPUT = 0x0008; //more 

     [DllImportAttribute("kernel32.dll")] 
     public static extern bool GetConsoleMode(IntPtr hConsoleInput, ref uint lpMode); 

     [DllImportAttribute("kernel32.dll")] 
     public static extern bool SetConsoleMode(IntPtr hConsoleInput, uint dwMode); 


     [DllImportAttribute("kernel32.dll", CharSet = CharSet.Unicode)] 
     public static extern bool ReadConsoleInput(IntPtr hConsoleInput, [Out] INPUT_RECORD[] lpBuffer, uint nLength, ref uint lpNumberOfEventsRead); 

     [DllImportAttribute("kernel32.dll", CharSet = CharSet.Unicode)] 
     public static extern bool WriteConsoleInput(IntPtr hConsoleInput, INPUT_RECORD[] lpBuffer, uint nLength, ref uint lpNumberOfEventsWritten); 

    } 
} 


per farlo funzionare correttamente, probabilmente si vuole eseguire il codice prima:

IntPtr inHandle = GetStdHandle(STD_INPUT_HANDLE); 
uint mode = 0; 
GetConsoleMode(inHandle, ref mode); 
mode &= ~ENABLE_QUICK_EDIT_MODE; //disable 
mode |= ENABLE_WINDOW_INPUT; //enable (if you want) 
mode |= ENABLE_MOUSE_INPUT; //enable 
SetConsoleMode(inHandle, mode); 

Con questo file di intestazione:

using System; 
using static ConsoleLib.NativeMethods; 
+1

Questa è una scoperta incredibile! Mi ha aiutato molto, grazie! – WolfyD

1

Dopo molto studio, ho trovato una soluzione. Con la classe Button e la GUI che ho creato, è possibile creare un pulsante, e si fa clic con il mouse o con il mouse (non funziona perfettamente). E devi importare System.Windows.Forms e System.Drawing.

-1
using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Threading.Tasks; 
using Traingames.NetElements; 
//using System.Windows.Forms; 
using System.Drawing; 

namespace ConsoleTools.NET 
{ 
class Program 
{ 
    static ConsoleFramework c = new ConsoleFramework(); 
    static public Point MousePos; 
    static Button One = new Button(); 
    static Pixel Mouse = new Pixel(); 

    static void Main(string[] args) 
    { 
     Console.ForegroundColor = ConsoleColor.White; 
     // t.Draw(10, 40, ConsoleColor.Gray); 
     One.Set(0, 10, "░░1░░", ConsoleColor.Gray); 

     GUI.Add(One); 
     GUI.CalculateOnStart(); 
     for (;;) 
     { 
      MousePos = new Point(System.Windows.Forms.Control.MousePosition.X/(Console.LargestWindowWidth/24), System.Windows.Forms.Control.MousePosition.Y/(Console.LargestWindowHeight/7)); 
      if (One.Pressed(MousePos)) 
      { 
       Console.Write("1"); 
      } 
      // Console.Clear(); 
     } 
    } 
} 
} 

namespace Traingames.NetElements 
{ 
    public class ConsoleFramework 
    { 
    public char[] chars = { '█', '▓', '▒', '░' }; 

    Point MousePos() 
    { 
     return new Point((System.Windows.Forms.Control.MousePosition.X/(Console.LargestWindowWidth/24)) - 100, System.Windows.Forms.Control.MousePosition.Y/(Console.LargestWindowHeight/7)); 
    } 

    public void SetPixel(int x, int Y, ConsoleColor color) 
    { 
     int y = (int)Math.Floor(Y/1.5f); 

     for (int i = 0; i < y; i++) 
     { 
      Console.WriteLine(""); 
     } 

     for (int i = 0; i < x - 1; i++) 
     { 
      Console.Write(" "); 
     } 
     Console.BackgroundColor = color; 
     Console.Write(" "); 
     Console.BackgroundColor = ConsoleColor.Black; 
    } 
} 

public class Pixel : GUI 
{ 
    public void Set(int X, int Y, string text) 
    { 
     ConsoleColor backColor = ConsoleColor.Black; 
     BackColor = backColor; 
     int yyyyyy = (int)Math.Floor(Y/1.5f); 
     Text = text; 
     y = Y; 
     x = X; 
    } 
} 

public class GUI 
{ 
    public int x, y; 
    public static GUI[,] GraphicalUserInterfaces = new GUI[1000, 1000]; 
    public ConsoleColor BackColor; 
    public string Text; 

    public void Draw() 
    { 
     int X = x; 
     int Y = y; 
     ConsoleColor backColor = BackColor; 
     string text = Text; 


     for (int i = 0; i < y; i++) 
     { 
      Console.WriteLine(""); 
     } 

     for (int i = 0; i < x - 1; i++) 
     { 
      Console.Write(" "); 
     } 
     Console.BackgroundColor = BackColor; 
     Console.Write("[" + text + "]"); 
     Console.BackgroundColor = ConsoleColor.Black; 
     Point M = ConsoleTools.NET.Program.MousePos; 

     // return M.X >= xx && M.X <= (xx + Text.Length + 1) && M.Y >= yy && M.Y <= yy + 2 && Control.MouseButtons == MouseButtons.Left; 
    } 
    static GUI Last; 
    public static void Add(GUI gui) 
    { 
     GraphicalUserInterfaces[gui.x, gui.y] = gui; 
    } 

    public static void CalculateOnStart() 
    { 
     for (int x = 0; x < 1000; x++) 
     { 
      for (int y = 0; y < 1000; y++) 
      { 
       if (GraphicalUserInterfaces[x, y] != null) 
       { 

        if (Last != null && y < Last.y) 
        { 
         GraphicalUserInterfaces[x, y].x = Last.x - GraphicalUserInterfaces[x, y].x; 
         GraphicalUserInterfaces[x, y].y = Last.y - GraphicalUserInterfaces[x, y].y; 
        } 
        GraphicalUserInterfaces[x, y].Draw(); 
        GraphicalUserInterfaces[x, y].x = x; 
        GraphicalUserInterfaces[x, y].y = y; 
        Last = GraphicalUserInterfaces[x, y]; 
       } 

      } 
     } 
    } 

} 

public class Button : GUI 
{ 

    public bool Over(Point M) 
    { 
     int yy = ((y * 2) - y/3) + 2; 

     int xx = (x/(Console.LargestWindowWidth/24)) + Text.Length; 

     if (M.X >= xx && M.X <= (xx + Text.Length + 1) && M.Y >= yy && M.Y <= yy + 2) 
      Console.BackgroundColor = ConsoleColor.DarkBlue; 

     return M.X >= xx && M.X <= (xx + Text.Length + 1) && M.Y >= yy && M.Y <= yy + 2; 
    } 

    public bool Pressed(Point M) 
    { 
     int yy = ((y * 2) - y/3) + 1; 

     int xx = (x/(Console.LargestWindowWidth/24)); 

     return M.X >= xx && M.X <= (xx + Text.Length * 1.5f) && M.Y >= yy && M.Y <= yy + 2 && System.Windows.Forms.Control.MouseButtons == System.Windows.Forms.MouseButtons.Left; 
    } 

    public void CalculateClick(Point M) 
    { 
     if (Pressed(M)) 
     { 
      Console.Clear(); 
      Draw(); 
     } 
    } 

    public void Set(int X, int Y, string text, ConsoleColor backColor) 
    { 
     BackColor = backColor; 
     int yyyyyy = (int)Math.Floor(Y/1.5f); 
     Text = text; 
     y = Y; 
     x = X; 

     int xx = (x/(Console.LargestWindowWidth/24)) + Text.Length; 
    } 

} 
} 
Problemi correlati