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 Classic 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
Gold Codemaster
6
Years of Service
Recently Online
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
Recently Online
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
Recently Online
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
Recently Online
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
Recently Online
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
Ortu
DBPro Master
11
Years of Service
User Offline
Joined: 21st Nov 2007
Location: Austin, TX
Posted: 17th Jan 2019 03:29 Edited at: 17th Jan 2019 03:31
meh. Did anyone catch my mistake above? yep... readonly is not the droids I'm looking for. Fixed:



Next I've added a uint overload to the get/set bit functions and now I'm going to start on style property setter:



And I want to test that this is working as I expect it to before I go any further.



Good deal. Now, I'll just finish building out the prop list in the setter following this same pattern. Going to call it a short night tonight. Tomorrow I will work on resolving values based on style class, inheritance, and self "inline" styling.

Coming up after that, I will still need to load a view / ui document from a file, render the resolved elements, handle user interaction events, apply transitions (like fade in/out, slide in/out etc), and a few other odds and ends. I'll also be setting up some generic style classes and event handlers for things like buttons and menu options.
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: 19th Jan 2019 23:35
Need a couple of tweaks to the StylePropertyData class. I need to reference the “parent” element object so that I can mark it as dirty and probably some other things. This class can also belong to both a UI.Element or a UI.StyleClass, so we will need to make this dynamic:



Also need to be able to handle color values in a variety of hex forms: #fff #ffffff #ffffff. I also like the option of specifying as “rgb(255, 255, 255). The built in uint32 convert/parse doesn’t like these, so I will just bring over the function I’m using in agk:



Data.cs



UserInterface.cs



Next, I’ll set up methods that will be used to resolve elements during the UI update process. Most elements don’t change much once created and don’t need to be continuously resolved to the same values every loop. The IsDirty flag is used to only reset and resolve elements that have changed. This considerably improves the performance of the update process.
As for the resolving process, the idea is generally: reset anything that is dirty, apply inherited styles from parent element, apply inherited styles from style class, then finally apply self “in line” styles. This process also converts any % values to actual final pixels that will be used for rendering.

The reset:



Apply inherited styles from parent element:



Next, I can simplify applying styleclass properties and inline properties by using a single method, since they both derive from the same class. As an added bonus, I can run through a list of multiple styleClasses attached to the element. The agk version of this system only supported a single styleClass per element as it currently stands.



Going to call it here for today. Next session will implement the final application of style properties to resolved values and hopefully the actual update that hooks all this together and starts rendering the elements.


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: 20th Jan 2019 03:06 Edited at: 20th Jan 2019 03:14
Saturday night bonus!
Double feature, on to taking all the applied style properties and resolving them into final screen pixel values. An element does not get resolved until after it’s parent is resolved, so parent values will already be in final pixel form. first get the parent’s values as these will be needed for calculating relational % values on the current/child element:



Next I need to move over the utility function to parse size data from the property strings. I’m going to move this to Data.cs though I think.



Now get resulting pixel values for the element’s properties

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: 21st Jan 2019 06:32
Did a little cleanup of the StaticInvoke and UpdateHandler: StaticInvoke has been renamed to Dispatcher, and I’ve removed the Run method of UpdateHandler. I’ll now just call the Dispatcher directly and save a function call.



I’ve also changed UserInterface from a static class to a regular class, which gives me a constructor to manage initialization.

UserInterface.cs:



I’ve also got to move UpdateHandler.SortUpdateList(); from Core.cs to Program.cs so that it gets called after all the module inits are done.
Program.cs:



One thing to note, when registering Dispatcher callbacks and UpdateHandlers, it is important to qualify the function name. This won’t throw any compiler errors, but you will hit a runtime null object reference:



It needs to be:



The constructor also needs to create the root element as element 0. This is effectively the screen and will always match the application window dimensions.



Now I can build out the update method, first resolve style properties to final values:



Then update sprites and text:



At this point I should be able to run a simple test and get an element set up and rendered out, so in Program.cs:



Now here I would like to stress that while setting up elements directly in code is fine, this isn’t how most elements are intended to be created or configured. Mostly, the UI content is loaded from .xml files which allows for quick and easy editing without having to recompile the application. These also make it easy to load alternate views based on screen size, user theme or preference and so on. I’ll be working on the UI document loader very soon, right after UI event handling, but for a quick test of the core system I’m just hard coding an element to get something on the screen during this session.

At this point, when I hit compile I ran into a few typo related bugs static this, capitalized that, straight up typed out the wrong variable in one place… it took a while and a good bit of debug logging to clean all this up and to be honest I didn’t take the time to cover each of them in this post sorry, but definitely look at the latest revision of the repo as there are minor but important variable changes throughout UserInterface.cs

Anyway, all is good and we now have a single UI element, centered horizontally on any size window, positioned 20% up from the bottom on any size window, with text centered in the element on any size of element, you get the idea, and just like the web, this system is very handy for using parent “container” elements to manage the positioning of children as a group.


window 640x480


window 1280x720




I’ll wrap this up tonight with a few utility helper functions that we will mainly be using later.





And we should just about be able to redo the basic sky/balloon scene using the UI module at this point. The only bit missing is the sprite animation, which is something my UI module has never tried to handle before. This should be a pretty easy add though:



Program.cs:





Yep, looks just like ti did before. Surprise!

But! Balloon is now being positioned relative to sky-panel instead of relative to the window. Feel free to play around with it’s positioning. You can even go crazy with it and hook up a Timer interval to move the balloon around the sky.

These simple few elements and screenshots may not look like much, and it may seem like this is a lot of effort for results that could be achieved much easier, but what we have now is the foundation for a truly powerful, flexible, and responsive system.

Next up, I’ll work on the event handlers. This is what makes a UI actually functional and able to do all the things.

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: 25th Jan 2019 06:46
A quick little fix for the mouse input handling:

Hardware.cs



Another quick fix to ResolveFlowValues

UserInterface.cs



Ensure that a draging element is always resolved:



Now, we can hook up the balloon to bind to the mouse position and follow it around the screen



fun.

Ok, On to events.

Going to register a new update process, this will require that the page flow is updated first because we need current element positions and dimensions for the event detections:



Start off the update by updating some status flags



Then before I continue on with event detection, I'm going to need one of the math utilities, so I'll start a new class

MathF.cs



On we go... actually,scratch that. on second thought, now that the UI is using sprites, I should be able to leverage the built in sprite hit tests will give the added benefit of handling rotations. We will still need the Math utilities later though for other things.



Alright, wrapping it up, we can set up a simple test in Program.cs



With a callback:

you can now see the balloon detecting mouse in/out and click events. when clicked, it will invoke the App.DoStuff callback which will first set up an interval timer which will begin rotating the balloon element.



next up will be loading UI documents from .xml files and I think it will be in pretty good shape to move on and leave the UI behind us.
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: 29th Jan 2019 06:26 Edited at: 29th Jan 2019 06:27


Alright, getting started on document loading and the controllers.

I'll start with adding a new folder to the project UIControllers and I'll add a new class to handle the GameMenu:



This is a pretty basic controller, but shows a number of important things: MenuOnPress will be bind the menu container element to the escape key (defined by directive in the view) and when the event is called, we get a reference to the element that triggered the event allowing us to act on that element directly.
The menu element itself is not visible, but it is still "present" and enabled for keyboard events.
In the case of ResumeOnPress, we don't want to act on the calling element, which will be a graphical UI button. instead we want to get a reference to the menu container element by its ID then act on that element instead.

and of course because these are ui controllers designed to handle ui event callbacks, we'll definitely be registering these functions with the dispatcher and setting thier arguments up to handle dispathcer calls.

I'll also need to add a UI.Controller base class to UserInterface.cs this wont do much other than provide a common type that all the controllers can link to.



then instantiate the gamemenu controller in Program.cs



At this point it's time to write out a ui document, essentially a view which defines our elements, style classes, layout, and a directive based approach to linking events to the controller.

Always before I used xml for these documents, but this time I think I'm going to switch to json



So, next I need to add a couple of loader functions to the UserInterface class. Json by nature is very dynamic and extensible, I'll also want a little helper function in Data to help determine if a given property exists or not:

Data.cs



UserInterface.cs



I ran in to a few more bugs in the UI that needed to be fixed: when instantiating a StyleClass object I also needed to instantiate the Style member object. When rendering text content, I need to use content XY/WH instead of final XY/WH, a few others, you can check the diff on the latest merge.

Ahh... but I've just hit another problem. If an element is set to opacity 0, it produces no sprite, but then it cant use sprite hit test. That's ok, just a minor adjustment to GetInterfaceInput:



And it's in pretty good shape! We've now got a functional pause menu, and hopefully it is apparent how easily the layout and function of the UI and it's components can be changed, just by editing the .json 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.

Attachments

Login to view attachments
Ortu
DBPro Master
11
Years of Service
User Offline
Joined: 21st Nov 2007
Location: Austin, TX
Posted: 30th Jan 2019 04:10 Edited at: 30th Jan 2019 04:20
Tonight I'm just going to quickly build out some common event handlers to handle things like button hover/press feedback. Other types of common events will get added as we go.

UIControllers/Common.cs:




media/ui/partials/common.json:




media/ui/partials/gameMenu.json:




I'm also going to flesh out managing the styleClasses applied to an element a bit better:

UserInterface.cs



So now I've got my basic button style classes moved out of gameMenu. common should be pretty generic across any project, but gameMenu will be much more project specific.

The text buttons now have additional directives calling back to the common controller to be handled based on it's tag: "button".

Any button type element using these controller events will gain a "-pop" styleClass on mouse in and lose it on mouse out, the details of what the pop styleClass do can vary from text size or color, to background or whatever. The main thing is that every button can have a similar behavior with a few simple classes and a common event controller.

Almost done with the UI for a while, I promise. About all that is left for the underlying generic framework will be transition handling. It could use some clean up and refactoring but it is in a pretty serviceable state for now and I'm ready to move on.
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
Ortu
DBPro Master
11
Years of Service
User Offline
Joined: 21st Nov 2007
Location: Austin, TX
Posted: 2nd Feb 2019 22:56
After all of the UI documents have been loaded, I find it handy to preload images during the initialization when some loading delay is expected. If you wait until the element is first rendered you can get noticable lags as the images get loaded from disk during gameplay. better to load them up front

UserInterface.cs



A general purpose screen fader is also handy. This will fade to or from the specified color over the specified time
Just a linear change atm, but easing could be added pretty easily.

UserInterface.cs:



for a fun little screen fade test:

Program.cs



AGK is not thread safe, so anything which calls Agk.Sync() has to be run on th main thread or you will get exceptions.
This means that I can't register this with the Dispatcher as it currently stands. I'm going to think on this a bit.

Wrapping up the UI for a bit we need to support transitions, that is changing property values over time. any numeric value based property can by transitioned.

UserInterface.cs





GameMenu.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
Recently Online
Joined: 25th Jun 2009
Location: Germany
Posted: 3rd Feb 2019 21:43 Edited at: 3rd Feb 2019 21:45
Sorry if I'm a little behind. I have a few suggestions for your logging system.

At the beginning of your function you could insert this line.
[System.Diagnostics.Conditional("DEBUG")]

Then you wouldn't have to check every time with the preprocessor if you are in debug mode or not to hide this line from the compiler. The code would then become a little more readable.

For determining the current source line and method there are also some functions.
StackTrace.GetFrame(offset).GetMethod().Name;
StackTrace.GetFrame(offset).GetFileName();
StackTrace.GetFrame(offset).GetFileLineNumber();


Then your method could look something like this.


Then the call from this ...



... would become this,

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 Feb 2019 00:47
Quote: "Then you wouldn't have to check every time with the preprocessor if you are in debug mode or not to hide this line from the compiler. The code would then become a little more readable."


Hmm, would that make the function only present/callable when running in debug?

I do like getting the stack trace, but I don't want to constantly be calling the function in release
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
Recently Online
Joined: 25th Jun 2009
Location: Germany
Posted: 4th Feb 2019 06:27
Yes, it is automatically hidden when compiled in release mode.



You can read about it here. I also saw some examples somewhere on the StackOverflow page.
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: 5th Feb 2019 02:55
Thanks man, tested this out and it is much cleaner!

I couldn't get the stacktrace info though, name always returns NullOrEmpty and line returns 0. I'll keep playing with it a bit.

Also, I'm happy to review and merge pull requests anytime someone has a contribution
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 Feb 2019 04:58
Alright time to move on. from here I've converted nearly everything that had previously been converted from dbpro to agk tier 1. There is a good bit more still in the original dbpro framework version, but we'll be getting into sections that have less direct translations.

Next up I'll tackle the animation handler. Now I'm not a 2d guy, like at all. Using sprites for the UI elements when moving to AppGameKit is the only experience I've had with using sprites. I never messed with them in dbpro at all. My interst is mostly 3d and I am itching to get back to it.

So, my animation handler has always been focused on object animation, but really there is no reason it couldn't also handle sprite animation as well, so I'll build it out to handle both this time.

Animation in AppGameKit is a bit wacky.

SetSpriteFrame works just how you would expect: pass a sprite id and a frame number and it sets the animaton to that frame. works pretty much the same as animation in dbpro, well and good then.

ditto GetSpriteCurrentFrame, gives you the current frame. straightforward and equivalent to similar in dbpro.

but the 3d object equivalents...

SetObjectAnimationFrame(UINT objID, animName, time, tweentime ) -> yeah, set frame doesn't actually take a frame. it takes a time. what?

I don't want to work in time. Time is arbitrary. Frame is absolute. Maybe I'm just stubborn on this but I want to work in frame not time.

There is also no GetObjectAnimationFrame, only GetObjectAnimationTime. That's ok I guess, considering the set command should really be named SetObjectAnimationTime

Anyway, it's ok. I can convert from frame to time and after some playing around I've got things working well enough as I want them.





Here is the dbpro animation module that I'll be working from. It uses some of my custom pre-processor directives, but the gist of it should be apparent.




For AGKSharp I'm going to want to refactor much of this into a class that can be attached to both character and world entities. I'll be setting up an entity base class pretty soon that pretty much all objects (and sprites?) will derive from as all objects will have similar base data such as filepath, world position/rotation, animation, collision, textures/shaders, and so on.

So, refactoring this as an animation handler that gets attached to an individual entity should simply things like animation queuing quite a bit, as it will be much easier to run through and work with the animations that have been applied to a given object instead of having to run through a list of all objects and animations to filter out just those that are applied to the given object.

Animation queuing is hugely helpful both for things like scripting and for handling complex actions such as combat and jumping. We'll get more into this when working on a basic character controller.

Next, just as timer based movement is essential to maintaining a steady rate of change across all systems and performance levels, timer based animation is equally important for the same reasons. If an animation needs to have advanced 20 frames of animation for each second it needs to always advance 20 frames of animation each second regardless of what screen/rendering framerate the application is hitting.

And yes, I am aware of the irony in stating this just after complaining about working with animation in terms of time []

Lastly, you should also notice in the dbpro code that I once again make use of the dispatcher and callback functions during the animation update process. This lets me do a number of things from executing something when an animation completes, to checking if a looping animation should continue looping. It allows me to apply project specific conditions and actions to animation events while keeping the animation handler itself very generic and flexible across all projects. That's really the overall design principal of the framework as a whole.

I think the general structure of this module will be similar to that of the UI: a general singleton for loading and retrieving animation data, and a class that we will attach to entities. I'll do away with the registered update and instead provide a GetNextFrame method that the entities will call during thier own updates.

Here is a start of the blockout for this module, its getting late so I'll call it a night here.

A lot of the update and management functions are going to go into the Entity class I think. I'm getting a rough shape in mind, we'll see how it plays out.

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 Feb 2019 06:26
Previously, I've been using a basic .csv file to configure animation data, something like this:



Which for a number of reasons required a bit of a complicated loading and management process.

Once again I will be switching to .json for this data. It is a bit more verbose, but that isn't necessarily a bad thing.



And more importantly, it now makes the loading process trivial:



tomorrow I'll knock out the frame update and hopefully get some test media up and running
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 Feb 2019 19:35
Ok, this update function can handle sprites or objects. a return of true will flag the calling entity update to remove this key from its animation queue.

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: 10th Feb 2019 18:03
Alright I had to correct the delta value in Core.cs, not sure what I was thinking...



There may have been a few minor changes in Animation.cs, but the main part of today's work is the initial base Entity

Some of this will get moved and expanded on by higher level classes like CharacterEntity, WorldEntity, and so on that will inherit off of this.



And we can now convert the balloon from a UI element to a game entity. While it can be tempting to just handle all 2d through the UI system, it isn't really designed for that, and would complicate things when it comes to implementing character controllers and the like.

So back in Program.cs



next up, player gameplay input handling and a simple character controller. These integrate tightly with the animation handler by controlling the entities animation queue based on entity state.
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 Feb 2019 05:13
Continuing to build out the character and entity classes, I've now split out CharacteryEntity to a new namespace AGKProject.

The base class Entity is generic and should be fully reusable as is across projects, this remains in the generic AGKCore namespace.
The derived CharacterEntity class will have project specific details and implementations that will need to be changed between projects, this moves to the AGKProject namespace.

The speed values are complete guesswork at this point, I'm more used to values appropriate to large 3d environments and this will almost certainly change. The important thing in tonight's work is the state enums. The gameplay input & controls module (and the ai module later) will not update objects or produce actions directly, it will instead set states. the character update handler will then use these states to control virtually everything about how the character needs to be updated, from position and rotation, to animation, allowable action, and so on.

Tomorrow I'll work on basic gameplay input handling.








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 Feb 2019 04:44 Edited at: 16th Feb 2019 05:02
Ok, super basic gameplay controls handler, simple enough right? :

AGKProject.Controls2d.cs



and a basic 2d entity update handler, I'll break this up into 2 pieces. first handling position:

AGKProject.CharacterEntity2d.cs



Add the Controls2d to Program.cs and we can simplify the character declaration. putting it all together we can move the balloon about the sky with WASD



One thing I should note:

CharacterHandler2d.MyCharacter.OldStatus = JsonConvert.DeserializeObject<CharacterEntity2d.CharacterStatus>(JsonConvert.SerializeObject(CharacterHandler2d.MyCharacter.Status));

Getting a deep copy of an object can often be a bit of a challenge, there are a number of approaches, but this is one trick I picked up from javascript and I'm a fan of super simple one-liners. It will produce a deep copy by converting an object to a json string, then parsing the string back into a new object.

A deep copy is necessary here because we need to be able to update status without changing oldstatus and aint nobody got time for manually copying individual properties.

Next up, I'll knock out that 'get new animation state' todo item.
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: 17th Feb 2019 20:27 Edited at: 17th Feb 2019 20:28
It's that time again. Today I've added the animation state handling to the character update. Now in addition to being able to move the character around the screen, we apply a different animation for each direction. My animation skills aren't great and I've not made any transitional animation frames so it just snaps from one direction to the next. Ideally you would want to build out the In and Out sequences to provide some transition blending.

This is also still in a very basic state, and is missing some of the more complex states that a full game would include. Many of those are fairly project specific though and I don't know how much value there is in adding them to the base framework.

Controls2d.cs



CharacterEntity2d.cs



Next up, I'll work on the world module which handles world entities (trees, rocks, buildings, etc...) similarly to character entities as well as handles level/scene loading. This will also involve going back and expanding the main loop to handle various load states.

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-03-21 05:03:29
Your offset time is: 2019-03-21 05:03:29