Sorry your browser is not supported!

You are using an outdated browser that does not support modern web technologies, in order to use this site please update to a new browser.

Browsers supported include Chrome, FireFox, Safari, Opera, Internet Explorer 10+ or Microsoft Edge.

AppGameKit Chat / Framework in AgkSharp

Author
Message
Ortu
DBPro Master
11
Years of Service
User Offline
Joined: 21st Nov 2007
Location: Austin, TX
Posted: 4th Jan 2019 01:26 Edited at: 4th Jan 2019 04:30
Hello people, happy new year! It seems like a good time to kick off a project I've had in mind for a while.

I’d like to convert my framework https://github.com/Ortu-/DBProject/tree/master/templates/default/src from DBPro to AGKSharp. It is a good foundation for game projects to be quickly built upon and there are plenty of improvements that both AppGameKit and C# can make available.

Now this isn't a tutorial, it is as much a learning process for myself as anything, and it won't be a 'getting started guide' because I'll probably be making mistakes, and things will change as we go but come along and make mistakes with me

First up, I'll be using the following:

AGKSharp - Thanks MadBit!
http://madbit.bplaced.com/?wpdmpro=agksharp-project-templates-2018-11-16

http://madbit.bplaced.com/?wpdmpro=agksharp-binaries-2018-12-16

ShaderPack – Thanks Janbo!
https://forum.thegamecreators.com/thread/220091

Visual Studio 2017 Community
https://visualstudio.microsoft.com/thank-you-downloading-visual-studio/?sku=Community&rel=15

Github -
https://github.com/Ortu-/AgkSharp-Framework

I'll start off by getting Madbit's latest template to use as a starting point. Personally, I’m mainly interested in 64bit Windows and C#, so I’m only going to work with that template for now. VS2017 doesn’t seem to like detecting these as new templates, so I will just load as a regular project then export to a template later on.



And I’ll just quickly do a compile and run to verify the basics are working. Yay.



Next I’ll do some cleanup of Program.cs I don’t want to have to maintain data like company and title in multiple places, so I am going to clear out the AppInfo block and pull this from the assembly instead:



Good deal:



And I do want to be able to handle command line arguments to easily change things like screen size, fullscreen, logging levels, and so on quickly and easily between executions without having to constantly change the actual code.



Before we can do anything with the arguments, we need to set up some data structures to apply the arguments against. I am going to go over to Core.cs for a moment: I am going to rename the Core class to App, and I’m going to add a couple of public structs:



Then, I’ll add a new function to handle our initializations, including the use of the command line arguments



You will see that I have moved the CreateWindow and AgkInit into this function from Main. Jumping back over to Program.cs, Main now looks like this:



We can now control the window size, fullscreen, and whatnot from runtime arguments and maintain positioning across any screen.





Well, that’s a good start for today I think. Check out the full source here https://github.com/Ortu-/AgkSharp-Framework and next up, I will be continuing to clean up and expand the App class and set up some of the utility modules from the dbpro framework, following that will come media handling and my UI system.
http://games.joshkirklin.com/sulium

A single player RPG featuring a branching, player driven storyline of meaningful choices and multiple endings alongside challenging active combat and intelligent AI.
blink0k
AGK Developer
5
Years of Service
User Offline
Joined: 22nd Feb 2013
Location: the land of oz
Posted: 4th Jan 2019 02:49
This looks awesome!
Does it have debug and Broadcasting?
Ortu
DBPro Master
11
Years of Service
User Offline
Joined: 21st Nov 2007
Location: Austin, TX
Posted: 4th Jan 2019 04:23
Visual studio has pretty extensive debugging capabilities, and I tend to build in robust logging into my systems.

As for broadcasting, I don't think so, as it doesn't produce the bytecode file which I think the broadcast players use. Can tier2 do broadcasting? This can basically be considered a "tier1.5" project.

The dll isn't really compatible with mobile to begin with, though you might could work it out with something like Xamarin I suppose. I dunno. That's an interesting thought though I don't have much familiarity with it.
http://games.joshkirklin.com/sulium

A single player RPG featuring a branching, player driven storyline of meaningful choices and multiple endings alongside challenging active combat and intelligent AI.
MadBit
Valued Member
9
Years of Service
User Offline
Joined: 25th Jun 2009
Location: Germany
Posted: 4th Jan 2019 06:31
That's cool, I like it,
I think it's great what you're doing. I am curious how this template will grow.

It's just a suggestion. You might be able to load an optional config file. In which the same parameters can be used.
If it exists then it should be loaded. (File.Exists(file))
Read all lines as strings. (File.ReadAllLines(file))
The lines can then be interpreted with the same routine as the command line parameters.
If in your foreach loop every line starting with '#' is skipped. Then you could also write comments in this config file.

As I said only a suggestion.
Share your knowledge. It\'s a way to achieve immortality. (Tenzin Gyatso)
Ortu
DBPro Master
11
Years of Service
User Offline
Joined: 21st Nov 2007
Location: Austin, TX
Posted: 4th Jan 2019 13:01
Quote: "It's just a suggestion. You might be able to load an optional config file. In which the same parameters can be used. "


Thanks man, and yeah, that is exactly what I do in regular tier 1 since it doesn't support command line arguments.

I will be adding a user preferences config file down the road for configuration which is more "long term"

In my view, the command line is meant for short temporary testing and debugging changes like how does the UI look at this resolution, now how does it look at this resolution, now let's crank up the log output for one run then let it drop back to normal.

Whether these are passed in at run time or loaded from a config file, it works out about the same either way so it's mostly just personal preference.

In dbpro, I was typically testing the game from my project manager application, which managed multiple versions in multiple locations, it was easier to just pass in arguments from the launcher than to manage multiple config files in multiple locations, so I guess the preference really grew out of that.
http://games.joshkirklin.com/sulium

A single player RPG featuring a branching, player driven storyline of meaningful choices and multiple endings alongside challenging active combat and intelligent AI.
Ortu
DBPro Master
11
Years of Service
User Offline
Joined: 21st Nov 2007
Location: Austin, TX
Posted: 5th Jan 2019 05:27 Edited at: 5th Jan 2019 05:52
Erp. Realized we will have a problem if the AgkInit fails since this was moved into another function. Going to need to pass a bool back up to Main. I also forgot to set the screen layout flag. This will be used by the UI system later to load a different view for small screens.

Next I’m going to expand App to include some status data in order to manage loading and main loop state and I’ll implement the log file here as well.

Core.cs now looks like this:



And a quick update to Program.cs:



So now we have some decent log output, and we can control what gets written out based on volume, channel, and whether it is running on a debug or release build:

Release, default level3, all channels:



Debug, default level 3, all channels:



Debug, level 1, all channels:



Anytime the level is dropped to 1, it is a literal spew of data. This level needs to be used carefully, and usually isolated down to just a couple of necessary channels. In a decent sized project with logging cranked up all the way, you can produce several 100k lines in just a few minutes.

I generally follow a convention of 5 = error, 4 = warning, 3 = notice, 2 = info, 1 = every last bit of detail you might need to see.

5s will pretty much always get written as these usually accompany the termination of the program and include info as to why. 4s and lower will usually be wrapped into #if Debug and only be written on non-release builds.

Anyway, I didn’t get quite as much done as I wanted to tonight, but C# provides a lot of things built in that I’ve previously had to work around or build out myself and I’m finding that a good bit of the original framework simply doesn’t need to be converted over so that’s a plus.

Tonight's progress has been pushed to the repo.
http://games.joshkirklin.com/sulium

A single player RPG featuring a branching, player driven storyline of meaningful choices and multiple endings alongside challenging active combat and intelligent AI.
MadBit
Valued Member
9
Years of Service
User Offline
Joined: 25th Jun 2009
Location: Germany
Posted: 6th Jan 2019 14:21
Do you know this C# class (System.Diagnostics.Debug)? It's not the same as yours. But maybe you find it useful.
Share your knowledge. It\'s a way to achieve immortality. (Tenzin Gyatso)
Ortu
DBPro Master
11
Years of Service
User Offline
Joined: 21st Nov 2007
Location: Austin, TX
Posted: 6th Jan 2019 18:58
Quote: "Do you know this C# class (System.Diagnostics.Debug)? It's not the same as yours. But maybe you find it useful."


That looks handy, I'll have to play around with it a bit, thanks!
http://games.joshkirklin.com/sulium

A single player RPG featuring a branching, player driven storyline of meaningful choices and multiple endings alongside challenging active combat and intelligent AI.
Ortu
DBPro Master
11
Years of Service
User Offline
Joined: 21st Nov 2007
Location: Austin, TX
Posted: 6th Jan 2019 19:04 Edited at: 6th Jan 2019 19:09
Continuing to look at converting the old “system” module, next I will add some hardware input handling.

The first thing I need to do here is some cleanup of the input handling in Core.cs. Madbit’s template is already providing event handlers to capture hardware input and pass it through to agk, I’m going to make use of these handlers, but I’m not going to bother with the passthrough. I will be handling all of the input and logic in the C# layer so there is no need to be able to access raw input values from agk methods.

For games, I’m a strong believer in disconnecting input from action and prefer to interject a state layer between them. Instead of something like:



I do something like:



This let’s me easily modify state by many different things such as abilities, the environment, the game state and so on, in addition to raw input before actually resolving the character’s movement. It also lets me handle all character movement for the player, network players, and AI with the same generic character controller.

I’ve previously converted this module from dbpro to T1 agk, so this is what I’ve been working from:



Now this touches on another of the old modules “common” which is largely no longer needed. In particular, the keymap definition can replaced with the key enum in System.Windows



I do need some things like get/set bit though, so I’ll start new classes for Data and for Hardware.

Common.cs:



Bit operators a slightly different in C# than in agk or dbpro, so I will drop a quick test into Program.cs to verify that they are working correctly:



Looks good. I’ll skip the rest of Common for now and move on to hardware inputs. I’m going to move the input handlers from Core.cs to Hardware.cs

This will leave Core.cs to handle the underlying window/form events while Hardware.cs will handle input devices.

Now in agk/dbpro we have to roll our own “event handlers” we can detect a button’s current state, but we have to build handling to determine a new press from a hold, a new release from a not being pressed. And to do this we have to poll key states every loop.

Now in C# with the form event handlers, we don’t have to constantly poll the state of every key and can easily detect a new press/new release when the event fires… for the mouse at least. Unfortunately holding a keyboard key is firing multiple onKeyDown events, so I still need to handle detection of a new press vs a held press.

Hardware.cs



And leaves us with Core.cs:



And Program.cs can give us some quick testing.



I can tell this is going to need some further work, but it's at a decent spot to move forward with for now.

Repo is updated.
http://games.joshkirklin.com/sulium

A single player RPG featuring a branching, player driven storyline of meaningful choices and multiple endings alongside challenging active combat and intelligent AI.
Ortu
DBPro Master
11
Years of Service
User Offline
Joined: 21st Nov 2007
Location: Austin, TX
Posted: 8th Jan 2019 05:40 Edited at: 8th Jan 2019 05:46
Alright, tonight I’m going to move on to intervals and time handling. I’d like to make use of the callback functions provided by the Timer class, this touches on threading though and might cause issues down the road. I’ll go with it for now and we can refactor later if needed.
First I’m going to clean up file/folder naming a bit



I’ll start off about as simple as it gets, test, then build out from there. That’s a common theme throughout my development process really.

Scheduler.cs


Core.cs



Program.cs



We should now get a message box every 3 seconds:



Cool. Time to expand this into something a bit more useful:

Scheduler.cs


Core.cs



Program.cs



This is still a very simple test case usage, but the functionality is there, and you will notice that it still follows the same basic idea as in the agk/dbpro interval handler.

Now we are specifying the timer’s configuration as arguments to Scheduler.SetInterval, we are chaining the specified callback function with an internal TickInterval callback to keep tracking and cleanup centralized to the scheduler itself and not have to handle cleanup in each individual callback.

In this test, we will delay the start of the timer for 5 seconds, then tick the timer every 3 seconds for a total of 3 ticks. The callback function will additionally make use of arguments passed in from the initial call to SetInterval. After 3 ticks, the scheduler will dispose of the timer and clean itself up.



I’ve quickly noticed a problem though. If the callback is blocking such as a messagebox, then the chained TickInterval does not get called until the blocking item is complete, however the underlying timer continues to tick, meaning if you leave a messagebox open through the next tick, the count will not have advanced and the previous tick will repeat.
Now for one thing, we don’t really want to use blocking operations to begin with, but sometimes it is necessary, a pick file / save file dialog for instance. Also, even if the callback isn’t actually blocking, the same issue could occur if it is simply long running.
So, instead of chaining the callbacks with += I’m going to need to call a delegate in order to check the end condition within TickInterval before invoking the actual callback. This works better:

Scheduler.cs




Tomorrow I will loop in some game pause handling. I'll also want to add the ability to manually cancel a timer early.
http://games.joshkirklin.com/sulium

A single player RPG featuring a branching, player driven storyline of meaningful choices and multiple endings alongside challenging active combat and intelligent AI.
MadBit
Valued Member
9
Years of Service
User Offline
Joined: 25th Jun 2009
Location: Germany
Posted: 8th Jan 2019 07:26
Wow, the code is very advanced.
For me one of the most interesting posts here in the forum. I get some inspiration when I read the code.
I'm looking forward to the next posts.
Share your knowledge. It\'s a way to achieve immortality. (Tenzin Gyatso)
Ortu
DBPro Master
11
Years of Service
User Offline
Joined: 21st Nov 2007
Location: Austin, TX
Posted: 8th Jan 2019 14:40
Glad you like it man, and thanks for the libraries.

On a side note since I forgot to mention it before: I had to remove the log file output from this process as trying to write to a single file from multiple threads is of course a quick way to hit a threading exception! I think i'l have to switch this up to write to a buffer which only the main thread can actually output to file.
http://games.joshkirklin.com/sulium

A single player RPG featuring a branching, player driven storyline of meaningful choices and multiple endings alongside challenging active combat and intelligent AI.
Ortu
DBPro Master
11
Years of Service
User Offline
Joined: 21st Nov 2007
Location: Austin, TX
Posted: 9th Jan 2019 05:21
It occurs to me that I don’t really need to build out anything for locating or cancelling timers early, other than to track the callback function name. A little bit of linq can take care of this:

Program.cs



Scheduler.cs



And as I was building this out and messing about with getting the name as a string from the delegate, I started thinking… hey, I can use this to build out the dispatcher. Now there are probably better ways of going about this, Roslyn comes to mind, but it seemed like a neat idea so I ran with it.

Here is the old dispatcher from dbpro, the agk T1 version is almost identical but with fewer functions so far:



I’ve always hated having to list out function lookups in hard code like that, and I really wanted to avoid it on this project, but I need to be able to call functions by nothing more than a name as a string, this is essential to my UI system and to my database driven scripting and event systems.

I’ll probably take a look at implementing Roslyn at some point regardless, because scripting support is always a plus, but for now I’ve set up this fairly simple StaticInvoke class. I’m calling it “Static” because it really only works for calling static methods, although with some more work it could probably be made to work with objects as well.

Anyway, we need to install the always awesome Newtonsoft.Json NuGet package for converting JSON strings to objects and back again. We also need the Microsoft.CSharp package to enable the <dynamic> type.







With both of those installed, and using the Scheduler as a template, I’ve built out a somewhat similar StaticInvoke class. I’m abusing the TimerCallback in a way it was almost certainly never meant to be used, but this is working:

StaticInvoke.cs



Any function registered as an invokable needs to accept or have an overload that accepts an <object> argument:

Core.cs



And we can then call the invokable like so:

Program.cs




I'm using the JsonConvert dynamic because i dont really want to clutter things up with struct definitions for the arguments of every function i may want to add as an invokable. I know the types where I'm invoking, and the end function being invoked knows its types, the passthru handlers in between dont need to know or care what they are handling. they just need to route it.

Well, I got distracted tonight and didn’t get to the pause timing, but tonight’s session was interesting.
I’m happy to hear any better ideas for this type of dynamic functionality
I’m also happy to merge pull requests if anyone gets an urge to join in.
http://games.joshkirklin.com/sulium

A single player RPG featuring a branching, player driven storyline of meaningful choices and multiple endings alongside challenging active combat and intelligent AI.
Ortu
DBPro Master
11
Years of Service
User Offline
Joined: 21st Nov 2007
Location: Austin, TX
Posted: 13th Jan 2019 00:29 Edited at: 13th Jan 2019 00:31
Alright the weekend is here, so time for more progress. First I’ll add some timing data and a basic pause toggle:

Core.cs



Scheduler.cs



Program.cs



Now I’m just quickly hooking in the AddKeyDownHandler temporarily to give a quick means of toggling pause in order to test. Once the control module gets built out this will be replaced more gracefully.

So with what we’ve got here, between the timer popups, you can press A to pause and the timer’s will stop popping until you unpause. The timer is still ticking off intervals, but the intervals simply exit without doing anything. This works in a basic manner, but it could be used to cheat in a real game by pausing during the wait time between ticks and unpausing just before an interval hits, you effectively drop the interval tme to nothing as far as gameplay is concerned. What really needs to happen here is to delay the processing of the tick by the length of the pause.

This should do what we need to ensure that a full tick elapsed during active gameplay without adding a bunch of complication in actually changing the timer interval:

Scheduler.cs
http://games.joshkirklin.com/sulium

A single player RPG featuring a branching, player driven storyline of meaningful choices and multiple endings alongside challenging active combat and intelligent AI.
Ortu
DBPro Master
11
Years of Service
User Offline
Joined: 21st Nov 2007
Location: Austin, TX
Posted: 14th Jan 2019 04:49 Edited at: 14th Jan 2019 04:55
Today I’m looking to build out the main loop. All the pre-requisite functionality is in so on we go. When originally building out this section in dbpro I wanted to be able to require modules and register updates in a way where the main loop didn’t need to be hard coded or changed from project to project. This would let any given project include modules it needed, and not include those it didn’t. Modules would take care of registering any updates they needed themselves and the main loop would be a very small, simple, generic iterator over the update queue.
This went through several iterations, mainly working to resolve issues in the order that updates would get called, and with registering updates that depend on other updates to run first. I finally ended up with the dbpro framework process that has been very reliable and flexible. I’ve previously converted the dbpro version to AppGameKit T1, and that is what I will be working off of today.

main.agc



Well, there are a number of TODO still in there, the conversion from DBPro to AppGameKit wasn’t quite completed, but the section I’m looking at today is the Standard Gameplay loop block, specifically this bit here, and I’ll handle loading transitions later.



That’s it. That’s essentially what the main loop of any one of my projects consists of. During initialization, each module will register any updates it provides, each update can specify a list of other updates it needs to have completed before it can run. After all modules are loaded, the queue is sorted based on the prerequisites and the main loop just runs through the list of registered updates and calls them. Updates can be registered as ignoring pause, meaning they will still be called even if gameplay is paused (to keep the UI running is a prime example) otherwise while paused, the update is skipped.

Next let’s take a look at the building and sorting of the update queue in AppGameKit T1:

main.agc



AddRequiredToUpdate is typically used when a module needs to add itself as a prerequisite to an existing update. An example of this would be that the Characters module has registered the basic update UpdateCharacters. Not all projects will load the AI module, but those that do will then need to add UpdateAI as a prerequisite to UpdateCharacters. The characters module is largely ignorant of the AI module, so the AI module takes care of updating UpdateCharacters to add UpdateAI as a Required update.

Anyway, setting up a pretty straight forward UpdateHandler class in Core.cs:



And a basic test, still in Core.cs:



Here we’ve registered 4 updates, some of which require other updates to complete before running, these then get sorted at the end of the initialization phase from 1, 2, 3, 4 to 4, 1, 3, 2

Lastly, a quick update to the main loop in Program.cs to run through the queue:



Well, the framework core is really starting to take shape. Next up I think I need to look at media handling and then it will be ready to tackle the UI. UI will be a big one, but I am hoping that with proper class support I should be able to simplify things a bit.

The repo has been updated as per usual.
http://games.joshkirklin.com/sulium

A single player RPG featuring a branching, player driven storyline of meaningful choices and multiple endings alongside challenging active combat and intelligent AI.
Ortu
DBPro Master
11
Years of Service
User Offline
Joined: 21st Nov 2007
Location: Austin, TX
Posted: 15th Jan 2019 04:41
Tonight will be short and simple: load an image from file, if it has previously been loaded, locate and return the existing image (caching), if it has been previously loaded but is no longer valid automatically reload it (this is mainly a hold over from dbpro in order to recover from a lost d3d device, I’m not sure if AppGameKit even has to worry about that sort of thing, but the check couldn’t hurt)

The idea is that you don’t call loadImage directly, or manually assign image numbers, etc. You don’t even need to know if the image has already been loaded or not. Anytime you need to work with an image (or other media type later on) you instead just always call Media.GetImageAsset() it will always either return a valid and loaded image (loading it if necessary), or it will error out and let you know the file isn’t found.

Additionally, it allows you to make an image by passing in size and color values to Media.MakeColorImage() handy for generating basic images without needing to include media files.

Later on I’ll add a SoundList, ObjectList, ShaderList, and so on with similar handlers.

So, in a new Media.cs file:


I’ve also noticed that I have not yet defined App.StopRunning() so let’s go ahead and do that in Core.cs:



And a quick adjustment to the mainloop in Program.cs:



http://games.joshkirklin.com/sulium

A single player RPG featuring a branching, player driven storyline of meaningful choices and multiple endings alongside challenging active combat and intelligent AI.
MadBit
Valued Member
9
Years of Service
User Offline
Joined: 25th Jun 2009
Location: Germany
Posted: 15th Jan 2019 06:47
Asset-Management - very usefull.

I have to admit that with the update system I didn't really understand at first. But after thinking and reading the code for a while I have to say - very useful.
Especially for larger projects, where several systems have to work together depending on each other.
Share your knowledge. It\'s a way to achieve immortality. (Tenzin Gyatso)
Ortu
DBPro Master
11
Years of Service
User Offline
Joined: 21st Nov 2007
Location: Austin, TX
Posted: 15th Jan 2019 12:50 Edited at: 15th Jan 2019 13:09
Yeah, this grew out of a very large and complex RPG project spanning 5 or 6 years of work. Its hard for small projects to capture my interest so i tend to go big. The thing is though, if the framework is designed to support the large and complex, it can very quickly and easily handle the small and simple as well.

I think things will be more clear when I start hooking some sort of gameplay together. Once the 2d basics are finished, I plan to use this for a couple of tiny little example games before moving on into the 3d sections.
http://games.joshkirklin.com/sulium

A single player RPG featuring a branching, player driven storyline of meaningful choices and multiple endings alongside challenging active combat and intelligent AI.
Ortu
DBPro Master
11
Years of Service
User Offline
Joined: 21st Nov 2007
Location: Austin, TX
Posted: 16th Jan 2019 04:48 Edited at: 16th Jan 2019 12:56
Alright here we go.

UserInterface.cs



Before I get started, I’ll preface this by saying I have a strong history in web development, and so I have based my UI system on similar concepts and design patterns to those that build out the web.

This post from 2014 is still a pretty good intro, and the system has continued to evolve and improve over the years through both dbpro and agk.

https://forum.thegamecreators.com/thread/211983?page=2#msg2536563

Here is the current version in AppGameKit T1 that I will be working to convert over to AgkSharp. Get a snack, its nearly 2400 lines. This will touch on all of the previous modules and I am going to take it bit by bit across the next several posts.

ui.agc (attached due to length)

First, let’s bring over the style properties. These are obviously based on CSS and should be very familiar for the most part. These will be readonly publicly, and are not intended to be set directly as we need to do a number of things internally when setting a property value.



Now, I’m going to compress a lot of data into those last 2 items with the Data.GetBit / Data.SetBit that we set up earlier, so I also need to set up a couple of bit enums to make them easier to manage:



A couple of little utility classes:



And I will finish out tonight by wrapping all this up into the main three top level classes:



Whew.
Ok, I think that’s all the class properties that we’ll need. Tomorrow I will be starting on the methods.
http://games.joshkirklin.com/sulium

A single player RPG featuring a branching, player driven storyline of meaningful choices and multiple endings alongside challenging active combat and intelligent AI.

Attachments

Login to view attachments

Login to post a reply

Server time is: 2019-01-16 13:20:30
Your offset time is: 2019-01-16 13:20:30