SUMMARY: Last time, I got as far as creating the beginning of a Screen architecture and supporting concepts. The short-term goal is to get enough of this hooked in so I can just write debug messages to a console. That’s really all I want to shoot for tonight.
ROUGH NOTE CAPTURE MODE
Ok, I have a ScreenDisplayList and that maintains a list of things for it to draw. Subclassers of the base Screen class can use that to handle the Load(), Unload(), and Paint() virtual methods to handle a display list. However, for our console screen, I think it will work a bit different and hold lines of text.
I just realized that the DisplayList class I made is really just a List. DUH.
DELEGATES?
Anyway, let’s grab the old DebugManager implementation for the console. It has a HandleKey to manage the text structures. We need to register DebugManager for Key events. Do I need to use a delegate? What’s the way to do this in C#? A delegate is a TYPE that can hold a reference to any function that matches its signature.
public **delegate** *function signature*
where an example would look like this:
public delegate int MyDelegateFunction(int x, int y);
In this case, MyDelegateFunction
is the delegate’s name. I’d declare it like this:
MyDelegateFunction myFunction;
and I can assign it to any other function declaration that matches the original declaration’s function signature. So if I had a pair of functions like this…
public static int PlotPoint (int x, int y) { blah blah blah } public static int PlotPoint2 (intx, int y) { more blah blah }
…I could do something like this, if I’m understanding this right.
MyDelegateFunction myFunction;
myFunction = PlotPoint;
myFunction(1,2);
myFunction = PlotPoint2;
myFunction(1,2);
You can pass a delegate as an object! So you can use it for asynchronous callbacks to handle events. A delegate object can also hold MULTIPLE methods to invoke. You can also assign a Delegate object a method within an object instance, and that works too. Pretty swanky. It’s described here.
So what do I know now that helps me write my thing? I need to add some delegates support to the InputManager class. However, if there are multiple listeners, I need to check the return value of EACH listener, and abort if the key event is handled. After doing some digging, I used the Delegate.GetInvocationList() and just called them one after the other. This seems to be what you’re supposed to do.
So now, the DebugManager needs to hook itself up to the InputManager and do some wiring in BaseGameINIT and BaseGameRun. Now using Console.WriteLine() to see if I’m capturing anything…cool! It works!
- There’s some interesting stuff here with Asynchronous callbacks…hmm. Any delegate type has a method called BeginInvoke added, which takes your parameters PLUS an AsyncCallback delegate and an object that “passes information into the callback method. It’s of type IASyncResult, and has some data in it (and probably other interesting things). More of this is described on the Calling Synchronous Methods Asynchronously page.
On a side note, you can also create a delegate by calling new() on the type, initializing it with an existing function or method. delegate void MyDelegateType ( int index ); MyDelagateType delegate = new MyDelegateType( someobject.somefunction ); delegate ( 10 );
Mysterious and cool! I’m capturing text. Now I need to DRAW it. PRINTING OUTPUT TO OUR OWN CONSOLE Another source of text in our DebugConsole window will be a regular DebugManager. I just looked at the built-in System.Diagnostics.Debug and Trace classes, which seem to do a lot of the same thing. However, I’m not seeing a way to actually redirect the output back into our system so I can write it to the screen as an overlay. I’ll sleep on this a bit and tackle it again tomorrow. I might as well just port the old code and use that, as it does work. AND WE’RE BACK Each type of Screen (ConsoleScreen, for example) is designed to integrate with a certain kind of visual data. They are what keep track of all the visual-related classes that draw things. I’m thinking that a GameScreen probably maintains the list of Visuals as associated with Pieces, and Pieces no longer maintain them. Each Screen-derived subclass would be initialized with a list of pieces it was interested in, and it would then create the visuals that go with each of those pieces. The pieces may have hints as to its appearance, name, and other unique qualities. A slight detour: The GameXML file that defines all the elements of a working game will be split into multiple files. Pieces can be defined within various context like Player, etc. They have at minimum a unique Name. If a piece is declared for the first time, it is added to the global piece list, and also added to whatever context (e.g. Player)’s list. Screens also declare what pieces it is interested in, though it can refer to any containing context (e.g. Player) also to read in all the pieces as needed. Handling RenderPasses: A Screen may have several renderpasses. I’m not very clear on the architecture of HLSL shaders and renderpasses, so for now I’m going to just assume every screen has one renderpass, during which all visuals are drawn. Ok, let’s get back to consolescreen… BaseGame.Draw() is where the magic would be happening. It needs to tell the ScreenManager to draw. Let’s let’s add that. … hours pass … Ok, I have implemented the following:
- Diagrammed the correct startup sequence for the XNA Game Class. Initialize() is followed by LoadContent() which is then followed by Update() and Draw() in a loop, until the game quits.
-
In Initialize(), called the constructors of all “main game objects”, which create local data structures only.
- After all main game objects are constructed, each of them gets their Initialize() function called.
- After all main game objects are Initialized(), they are allowed to MakeConnections() with other game objects
Then, XNA Game called LoadContent(), which is when other major game objects get their LoadContent() called as well. This is a bit messy and confusing, so let me think about this a bit:
- What is a Main Game Object (MGO)? Generally, they’re singletons that perform some function, and there’s only one of them. They’re usually a manager class of some kind.
-
There are different categories of MGO. Some are System-level, like GraphicsDeviceManager, ContentManager, which are responsible for implementing the XNA subsystem and loading content that it can use. Related are MGOs like a SoundManager and InputManager. Let’s call these the HARDWARE LEVEL MGOs that encapsulate what the hardware and operating system can provide in a game-friendly package.
-
There are some high level MGOs that aren’t related to hardware/OS, such as DebugManager and SettingsManager. They are foundational and are used by everyone else.
-
The other MGOs implement systems that are used for the game portion. ScreenManager manages Screens, which uses the A/V MGOs to draw graphics. PhysicsManager imposes physics on pieces in the game. Players also manage pieces. Referee monitors the state of the game.
-
Game Loaders, Persistent Storage, Level Loading and State Preservation are high-level MGOs that manipulate the state of the game-related MGOs above.
<
p>There’s some fishiness with the way I’m creating the ConsoleScreen. Currently, DebugManager is the data holder, and it maintains a “DebugBuffer” class with all the string tables and offsets that it needs to keep track of. It’s a class because they can be passed by reference to ConsoleScreen, which keeps its own reference to that datastructure so it can draw from it.
ConsoleScreen is created by a call to ScreenManager by DebugManager sometimes after DebugManager is fully initialized. This means:
- all MGOs constructed!
- All MGOs initialized!
- All MGOs have loaded content
At this point, any MGO can request information from any other MGO and be confident that it’s ready. Right now, that is handled in the Start() method, which is called at the very end of BaseGame.LoadContent()
And that closes out this phase of development. We have a clean, ready-to-use base that can print text to the screen.
0 Comments