Domanda

Sto cercando di capire come gestire manualmente l'intero ciclo di gioco in un gioco di Windows, senza utilizzare il gioco normale Microsoft.xna.framework.game.

Il motivo è che l'utilizzo della normale classe di gioco provoca una balbuzie nel mio gioco. Non molto, ma a causa della natura specifica del gioco è ancora abbastanza visibile.

Dopo aver provato un sacco di diverse impostazioni (Vsync, FixeTimestep, vari framerati ecc.) Ho deciso di provare a scrivere la mia classe di gioco per avere il pieno controllo dei tempi. Non sono sicuro che lo risolverà, ma almeno in questo modo ho il pieno controllo.

Fondamentalmente ho bisogno di:

  1. Imposta la finestra del gioco
  2. In un ciclo: fare tutto il rendering come al solito, quindi scaricare il risultato sullo schermo, gestire i backbuffer ecc.

Qualcuno sa come farlo? Sembra abbastanza facile in effetti, ma non è riuscito a trovare alcuna documentazione su come farlo.


Non sono sicuro di cosa sto facendo di sbagliato, ma ho il seguente codice (solo per i test, i tempi verranno gestiti in modo diverso) e il ciclo funzionerà per un po 'di tempo, poi si fermerà. Una volta che passerò il mio mousepointer sopra la finestra, il ciclo funzionerà per un po 'di nuovo.

    private void Application_Idle(object pSender, EventArgs pEventArgs)
    {
        Thread.Sleep(500);
        //Message message;
        //while (!PeekMessage(out message, IntPtr.Zero, 0, 0, 0))
        {
            gametime.update();
            Update(gametime);
            Draw(gametime);
            GraphicsDevice.Present();
        }
    }

Se abilita il "While PeekMessage", il ciclo funzionerà continuamente, ma ignorando il sonno e si ferma anche quando il mouse si muove sopra la finestra. Non sono sicuro di cosa stia succedendo qui ...

Penso che in modo ottimale vorrei solo fare qualcosa di semplice come questo nel loop di rendering principale:

    while (alive)
    {
      Thread.Sleep(100);
      gametime.update();
      Update(gametime);
      Draw(gametime);
      GraphicsDevice.Present();
    }

Ma in questo caso la finestra rimane vuota, poiché sembra che la finestra non venga effettivamente ridisegnata con il nuovo contenuto. Ho provato una forma.refresh (), ma ancora niente vai ... nessuna idee?

È stato utile?

Soluzione

(Aggiunte informazioni Xbox)

Per Windows è fondamentalmente bisogno di creare un modulo e mostrarlo, quindi conservare il suo manico e il modulo stesso. Utilizzando questa maniglia è possibile creare un graphicsdevice. Quindi agganci Application.Idle alla tua funzione che chiama l'aggiornamento e il rendering. Per esempio

public class MyGame
{
public Form form;
public GraphicsDevice GraphicsDevice;

public MyGame()
{
    form = new Form();
    form.ClientSize = new Size(1280, 1024);
    form.MainMenuStrip = null;

    form.Show();
}

public void Run()
{      
    PresentationParameters pp = new PresentationParameters();
    pp.DeviceWindowHandle = form.Handle;

    pp.BackBufferFormat = SurfaceFormat.Color;
    pp.BackBufferWidth = 1280;
    pp.BackBufferHeight = 1024;
    pp.RenderTargetUsage = RenderTargetUsage.DiscardContents; 
    pp.IsFullScreen = false; 

    pp.MultiSampleCount = 16;

    pp.DepthStencilFormat = DepthFormat.Depth24Stencil8;

    GraphicsDevice = new GraphicsDevice(GraphicsAdapter.DefaultAdapter,
                                              GraphicsProfile.HiDef,
                                              pp);
    Application.Idle += new EventHandler(Application_Idle);
    Application.Run(form);
}

 private void Application_Idle(object pSender, EventArgs pEventArgs)
 {
    Message message;
    while (!PeekMessage(out message, IntPtr.Zero, 0, 0, 0))
    {
        /* Your logic goes here
         Custom timing and so on
        Update();
        Render();
        */
    }

 }

 void Render()
 {
      GraphicsDevice.Clear(ClearOptions.DepthBuffer | ClearOptions.Target, Color.Black, 1, 0);
      //Your logic here.
     GraphicsDevice.Present();
 }
    [StructLayout(LayoutKind.Sequential)]
    private struct Message
    {
        public IntPtr hWnd;
        public int msg;
        public IntPtr wParam;
        public IntPtr lParam;
        public uint time;
        public Point p;
    }

    [return: MarshalAs(UnmanagedType.Bool)]
    [SuppressUnmanagedCodeSecurity, DllImport("user32.dll", CharSet = CharSet.Auto)]
    private static extern bool PeekMessage(out Message msg, IntPtr hWnd, uint
        messageFilterMin, uint messageFilterMax, uint flags);
}

EDIT 1

Per Xbox potresti essere in grado di posizionare la tua funzione di corsa personalizzata con il tuo loop di gioco in un loop True. All'interno di quella corsa fuori dalla parte superiore del tempo vero, probabilmente dovrai fare l'inizializzazione e la verifica del dispositivo grafico con intptr.Zero come maniglia

EDIT 2Uso qualcosa del genere (ottenuto da http://www.koonsolo.com/news/dewitters-gameloop/ )

        private long nextGameTick;

        private Stopwatch stopwatch;

        const int ticksPerSecond = 60;
        const int skipTicks = 1000 / ticksPerSecond;
        private const int maxSkip = 10;
        `constructor 
         stopwatch = Stopwatch.StartNew();

        nextGameTick = stopwatch.ElapsedMilliseconds; 

        `loop 
        int loops = 0;
        long currentTick = stopwatch.ElapsedMilliseconds;
        while ( (ulong)(currentTick - nextGameTick) > skipTicks && loops < maxSkip)
        {
            Update(16.667f);
            nextGameTick += skipTicks;
            loops++;

        }

        PreRender();
        Render();
        PostRender();

EDIT 3

La creazione di un gestore di contenuti era un po 'più di lavoro, ma comunque gestibile. È necessario creare una classe che implementa IserviceProvider. Questa classe prende un graphicsdevice nel suo costruttore per creare la classe successiva, implementa IGRAPHICSDEVICEPROVIDER. Inoltre implego getService in questo modo

    //in implementer of IServiceProvider
    public object GetService ( Type serviceType )
    {
        if ( serviceType == typeof ( IGraphicsDeviceService ) )
        {
            return myGraphicsService;
        }

        return null;
    }

Per comodità aggiungo anche un metodo alla classe per creare e restituire manager

    //in implementer of IServiceProvider
    public ContentManager CreateContentManager( string sPath )
    {

        ContentManager content = new ContentManager(this);

        content.RootDirectory = sPath;

        return content;

    }

Inoltre, creo una classe che implementa iGraphicsDeviceservice e fa riferimento al mio graphicsDevice. poi creo una proprietà e un campo in esso così

    //in implementer of IGraphicsDeviceService 
    private GraphicsDevice graphicsDevice;
    public GraphicsDevice GraphicsDevice
    {
        get
        {
            return graphicsDevice;
        }
    }

Quindi la chiamata finisce per essere un po 'come

MyServiceProvider m = new MyServiceProvider(graphicsDevice);
ContentManager content = m.CreateContentManager("Content");

dove

MyServiceProvider(GraphicsDevice graphicsDevice)
{
      myGraphicsService = new MyGraphicsDeviceService(graphicsDevice);
}

MyGraphicsDeviceService(GraphicsDevice gfxDevice)
{
     graphicsDevice = gfxDevice;
}

-Soria per frammentazione del codice in giro ma non è qualcosa che ho scritto troppo di recente, quindi ho difficoltà a ricordare le parti.

EDIT 4

Ho avuto un caso strano con il mio gioco personalizzato che mi sono appena ricordato quando ne ho nuovo il modulo che dovevo legare

    private void IgnoreAlt(object pSender, KeyEventArgs pEventArgs)
    {
        if (pEventArgs.Alt && pEventArgs.KeyCode != Keys.F4)
            pEventArgs.Handled = true;

    }

a

    form.KeyUp += IgnoreAlt;
    form.KeyDown += IgnoreAlt;

Altrimenti ho ottenuto alcune orribili bancarelle.

Altri suggerimenti

Di recente ho creato una serie di post sul blog su come eliminare completamente la dipendenza da Microsoft.xna.framework.game. Non mi piacciono diverse cose sulla classe di gioco, in particolare che viola gravemente il principio di responsabilità singola. Il mio gioco e l'editor di avventura di testo non fanno nemmeno riferimento all'assembly Microsoft.xna.framework.game.

Comunque, ecco i link ai cinque post, in ordine:

http://blog.thecognizantcoder.com/2012/01/replacing-xnas-game-class-xnacontrol.html http://blog.thecognizantcoder.com/2012/01/replacing-xnas-game-class-xnagame.html http://blog.thecognizantcoder.com/2012/01/replacing-xnas-game-class-puting-it.html http://blog.thecognizantcoder.com/2012/01/replacing-xnas-game-class-renderers.html http://blog.thecognizantcoder.com/2012/01/replacing-xnas-game-class-updaters.html

E infine, il progetto di avventura di testo su GitHub:

https://github.com/nathaNalden/textAdventure

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top