Porting game to MonoAGS

Started by cat, Sun 13/05/2018 21:12:08

Previous topic - Next topic

cat

Good news first: The Z-order works and also setting the color. However, suddenly the idle dialog options were shown right aligned:

[imgzoom]https://www.clowdergames.com/stuff/MonoAGS/dialog_alignment.jpg[/imgzoom]

Highlighted and chosen lines are shown just fine. I only set the default for the idle dialog options:

Code: ags
                var defaultFont = game.Factory.Fonts.LoadFontFromPath("Fonts/Acme-Regular.ttf", 20f, FontStyle.Regular);
                game.Settings.Defaults.Fonts.Speech = defaultFont;
                game.Settings.Defaults.Fonts.Text = defaultFont; 
                game.Settings.Defaults.Dialog.Idle = new AGSTextConfig
                {
                    Brush = game.Factory.Graphics.Brushes.LoadSolidBrush(Colors.OrangeRed),
                    Font = defaultFont
                };


I then realized that I probably don't even have to set a new Object, just setting the brush does the trick and everything is left-aligned again:
Code: ags
                var defaultFont = game.Factory.Fonts.LoadFontFromPath("Fonts/Acme-Regular.ttf", 20f, FontStyle.Regular);
                game.Settings.Defaults.Fonts.Speech = defaultFont;
                game.Settings.Defaults.Fonts.Text = defaultFont;
                game.Settings.Defaults.Dialog.Idle.Brush = game.Factory.Graphics.Brushes.LoadSolidBrush(Colors.OrangeRed);


So, doing it this way it works alright. I just wonder why a new config object would yield right-aligned text...


I hope all my bug reports are not too frustrating  (roll)

tzachs

Quote from: cat on Sat 04/05/2019 14:39:14
I just wonder why a new config object would yield right-aligned text...
That's because I've set the defaults for the text config creation in the factory method (factory.Fonts.GetTextConfig) and not in the object itself. I'm leaning into eventually providing factory methods for generating everything instead of using constructors for the object directly, for several reasons:
1. Discoverability- you'll be able to browse the factories, and just with the help of code completion and nothing else you'll be able to see everything you can create.
2. Consistency- every time you need to create something, you'll know that you need to find a factory for it.
3. Context- The object might need information that will not be "easy" for the user to pass if using the constructor. For example, we might add a general default text config object like I have just done for the dialogs, in that case the AGSTextConfig constructor will need access to that default object -> the factory method will have that context and do it for you so you won't have to figure it out for yourself...
4. Injection- by using the factory you'll be able to inject your own factory (or a plugin author's factory) with different behaviors than those built in in the engine.

Anyway, for now I pushed a fix to have defaults for AGSTextConfig to match the defaults for the factory method, but not sure if in the future we'll want to continue exposing those constructors.

Quote from: cat on Sat 04/05/2019 14:39:14
I hope all my bug reports are not too frustrating  (roll)
Not at all, I live for this! (just realized that sounds sad).

cat

Thank you for the detailed explanation. I will make sure to use the factory whenever possible.

Quote from: tzachs on Sat 04/05/2019 22:24:59
Anyway, for now I pushed a fix to have defaults for AGSTextConfig to match the defaults for the factory method, but not sure if in the future we'll want to continue exposing those constructors.
How would hide the constructors? Make them internal?
How does the factory pattern work anyway in connection with autofac dependency injection? When do you use what?

Quote
Quote from: cat on Sat 04/05/2019 14:39:14
I hope all my bug reports are not too frustrating  (roll)
Not at all, I live for this! (just realized that sounds sad).
Then I will continue posting them  :)

cat

#123
Good news - I was able to code the dialog tree for the puzzle :)

The next step will be adding actions. The expected type is System.Action. But I see that you just pass a function there - how is this supposed to work and what can I do? Is a function the same as an Action?
Can I just add one function and code all the stuff I want to write there? Or should I add one action per statement?

Edit: Right, I could actually read the documentation myself  (roll)
Action is just a function delegate. https://docs.microsoft.com/en-us/dotnet/api/system.action?view=netframework-4.8
However, the question above is still valid: what is best practise for dialogs?

tzachs

Quote from: cat on Sun 05/05/2019 14:32:03
How would hide the constructors? Make them internal?
Yes.

Quote from: cat on Sun 05/05/2019 14:32:03
How does the factory pattern work anyway in connection with autofac dependency injection? When do you use what?
The factory holds the resolver inside of it, and uses it to resolve dependencies (example).
As a user of the engine, you should be using factories to create stuff, and only use the resolver directly when you want to inject stuff (i.e if you find yourself in the need to use the resolver to resolve something, it might be a sign of a missing factory).

Quote from: cat on Sun 05/05/2019 17:41:34
Good news - I was able to code the dialog tree for the puzzle :)
;-D

Quote from: cat on Sun 05/05/2019 17:41:34
However, the question above is still valid: what is best practise for dialogs?
Hmm, one reason to break things up is for debugging: each dialog action can be disabled separately (dialogAction.Enabled = false), so, for example, you can do a single action with all of the talking and another action for giving an inventory item, and then you can disable the talking when you're testing stuff.
Other than that, I added single actions mostly planning for the editor, but no reason to use them if you're using the engine directly (and for the editor I'll probably change how it's written now anyway, I want to combine the current dialog actions with general visual scripting when I get around to adding it to the editor, as it's basically the same thing).

tzachs

Quote from: cat on Sun 28/04/2019 20:25:55
Now, when I click somewhere on the screen while the cat is talking, it will immediately slide to the clicked position without walk animation being played.
I pushed a fix here, can you test to see that it solved the issue for you?
Thanks.

cat

Before pulling the branch, I tried it with the old version again and had problems reproducing it because I changed the intro scene in the meantime.

I narrowed the initial problem down to where I place the character initially. When I do

Code: ags
splashScreen.Events.OnAfterFadeIn.SubscribeToAsync(async () =>
            {
               ...
                game.State.Player = Characters.Cornelius;

                await game.State.Player.ChangeRoomAsync(Rooms.Stage, 2300,[b]100[/b]);


and in the room script
Code: ags
private async void PlayIntroCutscene()
        {
            Characters.Cornelius.PlaceOnWalkableArea();
            
            await Characters.Cornelius.SayAsync("Hello, here I am.");
            await Characters.Mortimer.SayAsync("* squeak *");

            FollowCat();
        }

the issue happens.

When I place the cat directly on the walkable area
Code: ags
await game.State.Player.ChangeRoomAsync(Rooms.Stage, 2300,[b]5[/b]);

it didn't happen.

Anyway, I can confirm that with the changes from your pull request, it doesn't happen in any case - it seems to be fixed  :)


Please note that I might work less on MonoAGS related stuff for the next weeks because I'm joining MAGS.

tzachs

The bug happened whenever you started the walk after the character started talking AND it was still walking when done talking. So maybe the walk you do when you place the character yourself was shorter and ended before you were done talking, otherwise I have no explanation.
Anyway, good that it's solved, I merged it to master (and I think that covers all of your reported issues, let me know if I forgot something).
And good luck with MAGS  :)

cat

Tiny update: I managed to add an action with AddAsyncActions and it seems to work  :)

cat

There is this line I copied from the demo game for character initialization:
Code: ags
_character = game.Factory.Object.GetCharacter("Cornelius", outfit).Remember(game, c => _character = c);


What is the purpose of .Remember? Do I have to do this everytime I change the outfit? Or how would I go about changing the outfit?

tzachs

The purpose of "Remember" was for the save system, and I don't know if it will be kept in the same way after I'll rewrite it.
The main reason for this is that you're creating the character into your own class variable, i.e it's not part of the state, so we don't have a way to load it automatically ("Remember" is just you passing a function to the engine that's called after loading a game to allow you to assign it to your variable again).
Anyway, it's only intended for the initial object creation (i.e this code will only usually be in the editor generated code, you won't need to touch it at all in your own code), you don't need it for changing an outfit, you just need to do:

Code: csharp

_character.Outfit = myOutfit;


cat

Setting the outfit does not immediately update the idle view. Only when the character walks a bit or speaks the new frames are used.

tzachs

Hmm, you can start the idle animation yourself after setting the outfit with something like this:

Code: csharp

_character.StartAnimation(_character.Outfit[AGSOutfit.Idle].Down);


Or instead, I think just calling face direction should work as well.

I can make it automatic, but then what if somebody doesn't want to start the idle animation in the new outfit? What if they want to continue the same animation from the previous outfit? But if we go down that route, then what do we do if there is no equivalent animation in the new outfit to match the old outfit?
As a more powerful example, I can imagine a scenario of a "falling from the skies Clark Kent" who wants to switch to "flying Superman": now, I do have a "falling" animation for superman but I don't want to switch to it, I want to switch to "flying" in the new outfit -> the engine doesn't have a way to know that, so I think having it as a two step process (i.e first select the outfit, then select the animation) is cleaner.
But I'm open to suggestions on how to make this more friendly.

cat

It seems illogic that the idle animation does not start automatically after setting the outfit but it starts after any other animation was started. IIRC the current AGS implementation requires you to initialize the idle animation separately and this often confuses authors. If an outfit does have an idle animation, I think it should work out of the box. If I don't want to use an idle animation, I simply don't create one.

I didn't want to search the thread, but wasn't there a thing about the character not being visible initially until you started some animation manually? I think this is the same topic.

As a game author, I think this stuff should work right away. There are people with great coding skills making games, but don't forget about those authors that are mostly writers or painters and mostly noobs regarding code. Imagine all the help threads they would create "I changed the outfit but it doesn't show, what should I do?"

tzachs

I'm still not completely convinced (if you set an outfit while the character is walking, won't you expect the new outfit to also change to walking, and not idle, for example?), but as you represent 100% of the user-base you get what you want.  (nod)
I'll make the change soon.

cat

Quote from: tzachs on Sun 21/07/2019 02:57:56
if you set an outfit while the character is walking, won't you expect the new outfit to also change to walking, and not idle, for example?
Actually, that's exactly what I'd expect. I'd expect that the character uses the correct animation for the current state, i.e. idle->idle animation, walking->walking animation, climbing->climb animation (how would I go about climb animation anyway?) etc.
Not sure what about states that don't have an animation. AGS would probably pick the first frame of the walking animation and do not animate.

At the moment, when switching outfits, the last outfit remains visible unless an action triggers an animation change. I don't think this is how it should work.

Quoteas you represent 100% of the user-base you get what you want.  (nod)
(laugh)

tzachs

Quote from: cat on Sun 21/07/2019 19:41:50
(how would I go about climb animation anyway?)

An outfit is just a map from a string key to a directional animation, and you can add any animation you want, so you can do for example:
Code: csharp

var clarkKentClimb = _game.Factory.Graphics.GetDirectionalAnimation(_baseFolder, "CK/Climb/Left", "CK/Climb/Right", "CK/Climb/Down", "CK/Climb/Up");
clarkKentOutfit["Climb"] = clarkKentClimb;

var supermanClimb = _game.Factory.Graphics.GetDirectionalAnimation(_baseFolder, "Superman/Climb/Left"); //no budget to animate all dirs, the left animation will always be used
supermanOutfit["Climb"] = supermanClimb;

//Then, when you want to animate:
_character.Outfit = supermanOutfit;
_character.StartAnimation(_character.Outfit["Climb"].Down);

...
_character.Outfit = clarkKentOutfit; //With the new addition, if superman was still climbing when changing the outfit, clark kent will be climbing now as the climb animation exists for it as well (and I guess if the climb animation doesn't exist, I'll rollback to the idle animation, and if the idle animation doesn't exist, I'll do nothing)


(But an animation doesn't have to be associated with an outfit, you can also start an animation directly).

SMF spam blocked by CleanTalk