Porting game to MonoAGS

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

Previous topic - Next topic

tzachs

Quote from: Crimson Wizard on Mon 24/09/2018 21:23:22
Since I was referencing examples of my work I decided to update my games to the latest version of MonoAGS too. Some issues were easy to fix, but I am currently trying to figure out what happened to the IImageRenderer interface which I implemented as SpriteFontRenderer in my game.
The IImageRenderer interface was removed and replaced with IRenderer and IRenderInstruction, see this PR: https://github.com/tzachshabtay/MonoAGS/pull/279
This was a follow-up to our discussion on refactoring the rendering logic: https://github.com/tzachshabtay/MonoAGS/issues/207

For the most simple example of this in action, see the pivot renderer component (basically just draws a cross where the pivot point is): https://github.com/tzachshabtay/MonoAGS/blob/master/Source/Engine/AGS.Engine/Graphics/Drawing/PivotRendererComponent.cs

Quote from: Crimson Wizard on Mon 24/09/2018 11:54:42
You may find different use cases for the Origin (I will use this term, which is equivalent to Pivot at the moment) within same game or even within same room.
Sure, I'm not disputing that, I'm just not sure we understood each other's point. What I argued against is your quote about "not relying on engine defaults and creating utility functions". I think that if you build a standard point & click game with this engine, then most chances are that the defaults match what you need (and if that's not the case, we should change that). If you're creating a different genre game, or have fancy non standard puzzles/sequences in your game, then yes, you might want to create utility functions for those (or, perhaps, we might have in the future, a feature similar to Unity's prefabs, that you can use instead of utility factories), but I don't think that applies to the vast majority of the games that will be created with this engine, and I don't think it applies to cat's game which is a standard point&click (mechanics wise, ignoring the wonderful puppets ;-D) with standard puzzles.

cat

Quote from: tzachs on Sun 23/09/2018 22:25:35
Quote from: cat on Thu 20/09/2018 21:36:31
When I use this and click somewhere, nothing happens in the game but I see an exception in the output window:
Exception thrown: 'System.IndexOutOfRangeException' in AGS.Engine.dll
I can't reproduce this, so I need more information.
Is this the entire error message, no additional stack trace? Also, where did you put this code? And can you also write the code you used to add the walkable area? Or alternatively, can you send me your project again (with the error reproducing)?

I put the init code in the loadUi function in the GameStarter.

I turned on "break on exception" and get this:
Spoiler

>   AGS.Engine.dll!AGS.Engine.StaticGrid.GetNodeAt(int iX, int iY) Line 128   C#
   AGS.Engine.dll!AGS.Engine.JumpPointParam.JumpPointParam(AGS.Engine.BaseGrid iGrid, AGS.Engine.GridPos iStartPos, AGS.Engine.GridPos iEndPos, bool iAllowEndNodeUnWalkable, bool iCrossCorner, bool iCrossAdjacentPoint, AGS.Engine.HeuristicMode iMode) Line 56   C#
   AGS.Engine.dll!AGS.Engine.EPPathFinder.getWalkPoints(AGS.Engine.BaseGrid grid, AGS.API.Position from, AGS.API.Position to) Line 45   C#
   AGS.Engine.dll!AGS.Engine.EPPathFinder.GetWalkPoints(AGS.API.Position from, AGS.API.Position to) Line 40   C#
   AGS.Engine.dll!AGS.Engine.AGSWalkComponent.getWalkPoints(AGS.API.Position destination) Line 337   C#
   AGS.Engine.dll!AGS.Engine.AGSWalkComponent.walkAsync(AGS.Engine.AGSWalkComponent.WalkInstruction currentWalk, AGS.API.Position location, bool straightLine, System.Collections.Generic.List<AGS.API.IObject> debugRenderers) Line 275   C#
   AGS.Engine.dll!AGS.Engine.AGSWalkComponent.processWalkInstruction(AGS.Engine.AGSWalkComponent.WalkInstruction previousWalk, AGS.Engine.AGSWalkComponent.WalkInstruction currentWalk) Line 262   C#
   AGS.Engine.dll!AGS.Engine.AGSWalkComponent.getWalkInstruction() Line 211   C#
   AGS.Engine.dll!AGS.Engine.AGSWalkComponent.onRepeatedlyExecute() Line 126   C#
   AGS.Engine.dll!AGS.Engine.AGSEvent<AGS.API.IRepeatedlyExecuteEventArgs>.InvokeAsync(AGS.API.IRepeatedlyExecuteEventArgs args) Line 64   C#
   AGS.Engine.dll!AGS.Engine.AGSGame.onUpdateFrame(object sender, AGS.Engine.FrameEventArgs e) Line 189   C#
   AGS.Engine.dll!AGS.Engine.AGSUpdateThread.RaiseUpdateFrame(double elapsed, ref double timestamp) Line 219   C#
   AGS.Engine.dll!AGS.Engine.AGSUpdateThread.DispatchUpdateFrame(object sender, System.EventArgs e) Line 185   C#
   AGS.Engine.dll!AGS.Engine.AGSUpdateThread.threadLoop() Line 167   C#
[close]

Quote
Not necessarily better, but you can get an empty bitmap as I said before, and then add a translate component which gives you the ability to "move" an area on the screen.
I guess I'll just stick to my implementation for now.

Quote
Ah, right, I think I changed it to all images after realizing the same logic applies, you can have multiple animations with different widths and it makes it easier to align by default (and also it's more consistent). I don't necessarily have too strong opinions on this, willing to discuss if you have a better suggestion.
Quote
I think cat's struggle here stems for the fact that she's porting an existing game, I don't think she'd encounter this issue when creating a new game (we'll see when people actually create new games, though).
I think the main issue with the pivot is (apart from being a bit obscure in the first place), that you don't set it to a specific pixel position but to a relative point within the sprite. This does not matter when positioning an object manually in an editor, but it is confusing when doing it in code. Especially with low-res games, where every pixel counts, this can be problematic, because you don't know the exact pixel position you have to use when pivot is 0.5.

Quote
The engine should be geared with defaults that apply to the most common use-cases for point&click (or in the future, like we discussed, we might have templates with different defaults, geared to that specific template use case).
This sounds like a good idea. I thought about suggesting something already.

Quote from: tzachs on Mon 24/09/2018 22:05:54
I don't think it applies to cat's game which is a standard point&click (mechanics wise, ignoring the wonderful puppets ;-D) with standard puzzles.
:-*

tzachs

From the stacktrace it seems that the issue is related to the way you created the walkable area, can you write the code you used for that (or send me the current project)? Thanks.

cat

#63
A quick update for everyone following here:
tzach fixed the issue and a few others I've found. They are already merged to the master branch.

And again I have a question:
The default position of text shown for character.SayAsync is a bit strange: it is a bit above (OK) but on the right side next to the character. Why?
I know I could write a custom ISayLocationProvider, but ideally, this basic thing should work right out of the box.



Edit: One more question: What does the ? do in
Code: ags
_bottle?.GetComponent<IHotspotComponent>().Interactions.OnInteract(AGSInteractions.INTERACT).SubscribeToAsync(onBottleInteract);

tzachs

Quote from: cat on Fri 05/10/2018 19:49:51
The default position of text shown for character.SayAsync is a bit strange: it is a bit above (OK) but on the right side next to the character. Why?
Because for some reason I thought the text should go to the right of the character (I was imagining a speech bubble).
Sorry, will fix soon.

In the meantime, you can set a TextOffset for your characters with a negative x to move the text to the left (you can also set a bigger label size so the text in the screenshot won't wrap, the default label width is 250 but your game is high resolution so makes sense to have a bigger label -> we should probably make the default label size resolution dependent).

For example:
Code: csharp

cornelius.SpeechConfig.TextOffset = (-100,0);
cornelius.SpeechConfig.LabelSize = (500,500);

cat

Yes, I think having the size resolution dependent makes sense.

About the other question I sneaked in (the ? in _bottle?.GetComponent...) - it's hard to google a question mark :P

tzachs

Quote from: cat on Fri 05/10/2018 19:49:51
What does the ? do in
Code: ags
_bottle?.GetComponent<IHotspotComponent>().Interactions.OnInteract(AGSInteractions.INTERACT).SubscribeToAsync(onBottleInteract);

The "?." is called the null conditional operator (sometimes nicknamed "the Elvis operator"). If "_bottle" is null it will skip the subscription afterwards.
Here's some more details on this operator: http://www.informit.com/articles/article.aspx?p=2421572

cat


Monsieur OUXX

usually when I'm searching weird operators I just type the name of the character in full letters into Google, followed by the name of the language, and there's always some stackoverflow question that deals with it. All I have to do then it to spot the official name of that operator somewhere in the discussion (here : "conditional operator") and then feed it again in google.

Example :
Code: ags

what is "C#" "question mark" operator
 

cat

Good idea, I'll keep that in mind 👍

cat

I found some time to work on the game again today and already found another bug :P

I click somewhere to walk there (walk-to-point). While the character is still walking, I right-click somewhere and the character says something async. The following happens:

  • The character switches to the speak animation but still moves towards the walk-to-point. Since there is no 'speakandwalk' outfit, I guess the character should stop walking?
  • When the say async is done, the character will quickly slide to the walk-to-point without any animation. This happens no matter if I click to stop the sayasync or not.

tzachs

Ah, you're right, the character should stop walking. Even if there was a 'speak and walk' animation, you'd still expect that when you right click, it will cancel the existing walk. A 'speak and walk' animation might be interesting to do, though, when you programmatically say something while walking.

As for the second bug, I think I understand why it's happening, after the say async is completed it doesn't switch back to the walk animation, and because you have "MovementLinkedToAnimation" with no moving animation, it defaults to the animation speed which is really fast (because it's configured to work in tandem with the animation, not as a "regular" walking speed).

Thanks, I'll look into fixing both bugs (and the talk position issue, I haven't forgotten it) soon.

cat

Quote from: tzachs on Thu 01/11/2018 15:41:02
Thanks, I'll look into fixing both bugs (and the talk position issue, I haven't forgotten it) soon.

Thanks! No need to hurry, there is still a lot to do on my side anyway.

cat

A question about including resources:

The default value for build action when I add a new resource is Content. I noticed that you use Embedded resource in the demo game. What is the difference between those actions and why did you pick embedded resource?

ChamberOfFear

Quote from: cat on Fri 09/11/2018 20:03:20
A question about including resources:

The default value for build action when I add a new resource is Content. I noticed that you use Embedded resource in the demo game. What is the difference between those actions and why did you pick embedded resource?

Embedded resource means that the file will quite literally will be embedded into the .exe that is built by the .NET compiler. If you go into the build directory of your game you will notice that the file can't be found, but the .exe is a little bigger in size.

Content will add the file into the build directory unaltered.

Choosing one over the other is a preference, doesn't really matter. I guess if you want to hide your assets from the end consumer then embedded resource makes sense, however anyone who is really interested in seeing assets will be able to if they know how.

tzachs

Right, though besides the preference choice (do I want to easily allow people to view/edit individual assets in the game?), I would say that embedding make some stuff easier:
1. If you don't embed then you need to worry about shipping your asset files with the game (and making sure that they're in the same folder hierarchy).
2. If you want to debug your game on mobile devices and you don't embed your assets, then it won't copy them to the device and you will need to copy those yourself.

And not embedding, otoh, makes it easier to swap asset files when developing (and we might add support for hot reloading those assets directly in the file explorer while your game is running -> though it might be possible for embedded assets as well, not sure).

For the reasons mentioned above, the default behavior that I'm currently thinking of for the editor is to have it as contents while developing, and embedded when deploying.
Though once we have a complete editor, we might be able to eliminate those problems with some programming on the editor side, so we'll see.

cat

Good to know, I'll stick to embedded for now. Thanks, guys!

cat

In my game there is no inventory, but when you pick up things, the mouse cursor will change to it - it will basically be your active inventory item.

So, when initializing the game, I create the inventory items and assign them to the player character. Later, OnInteract with a hotspot, I set the active item:
Code: ags

_character.Interactions.OnInteract(AGSInteractions.INTERACT).Subscribe(_ =>
{
    _game.State.Player.Inventory.ActiveItem = InventoryItems.Mouse;
});


However, this does not work with the TwoClickInputScheme. I will rewrite it, but the question is: What is the best approach? I could

  • Add a method for setting the active inventory to the TwoClickInputSchemeclass (public void SetActiveInventoryItem(IInventoryItem inventoryItem)). This is pretty simply but has two disadvantages: I have to remember to call it and I will somehow have to have a reference to the instance.
  • The next idea is to subscribe to repeatedly_execute in the TwoClickInputScheme and continuously check for the active item. This would be the AGS-style approach, but it feels unnecessary to do the check every cycle when the active item only changes ever so often. Which brings me to my third idea:
  • Is it somehow possible to attach an observer to ActiveItem? Then the InputScheme could just subscribe to it and do the cursor handling accordingly. The AGSInventory.Items property is already a BindingList, is there something similar for basic properties?

Any ideas/opinions?

tzachs

Hi, there's a new branch with a bunch of fixes for all the latest issues you mentioned: https://github.com/tzachshabtay/MonoAGS/tree/CatFixes2.
If you can test that everything works as expected I'll merge it to master.

Quote
However, this does not work with the TwoClickInputScheme. I will rewrite it, but the question is: What is the best approach? I could

  • Add a method for setting the active inventory to the TwoClickInputSchemeclass (public void SetActiveInventoryItem(IInventoryItem inventoryItem)). This is pretty simply but has two disadvantages: I have to remember to call it and I will somehow have to have a reference to the instance.
  • The next idea is to subscribe to repeatedly_execute in the TwoClickInputScheme and continuously check for the active item. This would be the AGS-style approach, but it feels unnecessary to do the check every cycle when the active item only changes ever so often. Which brings me to my third idea:
  • Is it somehow possible to attach an observer to ActiveItem? Then the InputScheme could just subscribe to it and do the cursor handling accordingly. The AGSInventory.Items property is already a BindingList, is there something similar for basic properties?
Great feedback!
What I did is a combination of 1 and 3.
The two buttons input scheme now has a SetInventoryCursor method, which you can either pass an inventory item or pass nothing which will make it take the current active item for the cursor.
In addition, I added the inventory item change event which I missed somehow.
So, you can now do:
Code: csharp
cornelius.Inventory.OnPropertyChange(nameof(IInventory.ActiveItem), () => scheme.SetInventoryCursor());


Quote
The character switches to the speak animation but still moves towards the walk-to-point. Since there is no 'speakandwalk' outfit, I guess the character should stop walking?
When the say async is done, the character will quickly slide to the walk-to-point without any animation. This happens no matter if I click to stop the sayasync or not.
So 2 fixes here:
1. Any user interaction will now trigger stop walking before applying the interaction.
2. I "added" a 'speak and walk' optional animation to the outfit.  If there's a programmatic SayAsync while the character is walking and you have a 'speak and walk' animation it will be used. If you don't have a 'speak and walk' animation then it will stop walking before speaking.
If you're using portraits, for example, and the walking animation itself can be used as 'speak and walk', then you can just write:
Code: csharp
cornelius.Outfit[AGSOutfit.SpeakAndWalk] = cornelius.Outfit[AGSOutfit.Walk];


Quote
the default label width is 250 but your game is high resolution so makes sense to have a bigger label -> we should probably make the default label size resolution dependent).
The default label size for speech is now resolution dependent (by default it's 80% of your virtual resolution).

Quote
The default position of text shown for character.SayAsync is a bit strange: it is a bit above (OK) but on the right side next to the character. Why?
Fixed, it's now centered above the character.

cat

Wow, that was quick 8-0

Quote from: tzachs on Sun 11/11/2018 03:27:19
The two buttons input scheme now has a SetInventoryCursor method, which you can either pass an inventory item or pass nothing which will make it take the current active item for the cursor.
In addition, I added the inventory item change event which I missed somehow.
Works, but only kind of:
When the player performs a right click when an inventory is active, there is a null reference exception at AGS.Engine.TwoButtonsInputScheme.SetInventoryCursor(IInventoryItem inventoryItem) in line 37
onRightMouseDown sets the ActiveItem to null, which then triggers the OnPropertyChanged handler.
I'd change SetInventoryCursor to reset the default cursor when null is passed and the active inventory is null. This would help regarding the exception, but also allow the programmer to change back the active inventory programmatically. This is needed when I use the active inventory item on a hotspot and this results in me losing the inventory item.

Another issue: You might want to check if the previous cursor should really be changed. Otherwise, there will be weird issues when setting two active items after each other without returning to the default cursor in between.
Consider renaming _previousCursor to _defaultCursor, then it will be more clear. You could also provide a method SetDefaultCursor. This way you can safely change the default cursor, no matter if there is currently an inventory active or not.

Quote
1. Any user interaction will now trigger stop walking before applying the interaction.
Confirmed (nod) I didn't test the SpeakAndWalk animation, though.

Quote
The default label size for speech is now resolution dependent (by default it's 80% of your virtual resolution).
Works, but 80% seems a bit too much. Maybe 70% is enough? (But this might be because of my setup with the stage frame around it.)
Btw, is it on purpose that the text wraps left-aligned instead of centered? It doesn't look that nice, especially when there is only a word or two in the second line.

Quote
Fixed, it's now centered above the character.
Confirmed (nod)

SMF spam blocked by CleanTalk