[Experiment] Custom Room Viewport and Camera

Started by Crimson Wizard, Mon 22/10/2018 02:11:02

Previous topic - Next topic

Crimson Wizard

UPDATE from 13th December 2018:
This is the practically working WIP version of a custom room viewport, built on top of AGS 3.5.0 (alpha 7):
Build: https://www.dropbox.com/s/lk7b25a8kwj9jfz/ags-3.5.0--roomviewport-wip5.zip?dl=0
The feature is discussed here on github: https://github.com/adventuregamestudio/ags/issues/473
Pull request: https://github.com/adventuregamestudio/ags/pull/535
Source branch: https://github.com/ivan-mogilko/ags-refactoring/tree/ags3--roomviewport

In short, this feature lets you to change the way ROOM (and all inside it) is shown on game screen. This is done by setting up Room Viewport and Camera.
Camera is an "eye" inside the room, that has certain size and position in room coordinates. The existing "viewport" commands in current AGS serve for the same purpose.
Viewport is a surface on game screen where what camera "sees" gets drawn to. It also has a size and position, but in screen coordinates.
You may imagine a piece of room (the size of camera) is cut out and pasted in the viewport, resized if necessary.

If the Camera is smaller than the viewport, then the room will appear scaled up on screen. If the Camera is larger than the viewport, the room will appear scaled down.

Note, that with the smaller Camera size you may get a scrolling room even with rooms that are equal to game's size.


Extra consequence is that from now on you may have room backgrounds (and essentially - rooms) of any size. For example, 320x120 room in a 320x200 game.
This may be useful if part of your game screen is constantly covered by GUI, or if you want to make one room which is smaller than others.


=======================================================================================


Script API:
Code: ags

builtin managed struct Camera
{
  /// Gets/sets the X position of this camera in the room.
  import attribute int X;
  /// Gets/sets the Y position of this camera in the room.
  import attribute int Y;
  /// Gets/sets the camera's capture width in room coordinates.
  import attribute int Width;
  /// Gets/sets the camera's capture height in room coordinates.
  import attribute int Height;
  /// Gets/sets this camera's room horizontal scaling relative to the viewport it is displayed in.
  import attribute float ScaleX;
  /// Gets/sets this camera's room vertical scaling relative to the viewport it is displayed in.
  import attribute float ScaleY;

  /// Gets/sets whether this camera will follow the player character automatically.
  import attribute bool AutoTracking;
};

builtin managed struct Viewport
{
  /// Gets/sets the X position on the screen where this viewport is located.
  import attribute int X;
  /// Gets/sets the Y position on the screen where this viewport is located.
  import attribute int Y;
  /// Gets/sets the viewport's width in screen coordinates.
  import attribute int Width;
  /// Gets/sets the viewport's height in screen coordinates.
  import attribute int Height;
  /// Gets the room camera displayed in this viewport.
  import readonly attribute Camera *Camera;
};


Viewport is a place on screen where room is drawn, defined in game coordinates (e.g. 0,0 - 320x200).
Camera is a place in room which is being drawn. Camera size may be determined in two ways:
a) explicitly setting Width and Height, which tells actual rectangle in the room;
b) setting ScaleX and ScaleY properties to automatically adjust displayed room relative to the viewport's size.
For example, if you set ScaleX/Y to 2.0, then room will appear zoomed in twice as large as the viewport's size. If you set ScaleX/Y to 0.5, it will appear twice as small (zoomed out).
NOTE: you cannot set camera size larger than the room's background, and it generally does not support "seeing" beyond room's background, same as before.

You access viewport and camera using commands:
Code: ags

Viewport *Game.RoomViewport;
Camera *Room.Camera;



Also:
Code: ags

/// Gets/sets whether the viewport should automatically adjust itself and camera to the new room's background size
Game.AutoSizeViewportOnLoad;


Old viewport functions are now deprecated, they may still be enabled if you set "Script compatibility level" to v3.4.1 or lower.
Here's correspondence between old and new functions:

* SetViewport(x, y); ===> Room.Camera.X = x; Room.Camera.Y = y; Room.Camera.Auto = false;
* ReleaseViewport(); ===> Room.Camera.Auto = true;
* GetViewportX(); ===> Room.Camera.X;
* GetViewportY(); ===> Room.Camera.Y;


Little demonstration:
https://www.dropbox.com/s/l9783w8t1i8qk3t/roomcameramadness.mp4?dl=0

At this point nothing of above is set in stone, and I'd very much appreciate any functionality tests and thoughts on script commands.


KNOWN ISSUES:
* because of a bug the AGS script does not let you write "Room.Camera.X = 10"; instead you have to do something like:
Code: ags

Camera *cam = Room.Camera;
cam.X = 10;

   this is very inconvenient indeed, and I think we need to fixing this issue, or change something in the above script API to make it simplier.
* some possible optimization issues with software renderer when you change camera size too often.



=======================================================================================




The purpose of these features are:
1) Display room in a custom rectangle on screen;
2) Add zoom in/out effects in your game.
3) Potentially - also add rotation to the room view (if I am able to make it work correctly).

This also have a potential to have multiple room cameras in the future, although that may not be easy to add so I don't really plan to do that for AGS 3.5.0. The problem here is not so much in drawing them on screen, but rather in resolving interactions. Right now all the logic in AGS is based on the single viewport into the room: clicks, finding objects on screen, etc. Multiple viewports would demand a good thought and possibly script redesign.
Spoiler
But like I said, drawing them alone is not a problem:
[imgzoom]https://i.imgur.com/Kum2M2b.gif[/imgzoom]
[close]

Danvzare

Ooh, sounds useful.
With something like that, it should be possible to make some really cinematic looking games.

eri0o

Multiple cameras could be used for minimaps. Other than this I don't see much use for multiple cameras.

I am adding below an error that occurs when I leave this AGS and an error that occurs when I open this AGS after closing it.
Spoiler


[close]

Crimson Wizard

#3
Quote from: eri0o on Mon 22/10/2018 23:29:20
Multiple cameras could be used for minimaps. Other than this I don't see much use for multiple cameras.

Basically any case of looking into something while still seeing your current position: zooming into an item/object or peeing into a keyhole, simulating an event in "another room" without actually changing rooms - all this could be simulated by displaying a separate and otherwise unaccessible part of the room in a second viewport. Showing two or more locations at once during cutscene. Split-screen multiplayer. These are cases I may think of.

It may be non optimal for a minimap though, since engine will have to process every sprite it sees, including its tints, lighting, scaling, walkbehinds etc.


Quote from: eri0o on Mon 22/10/2018 23:29:20
I am adding below an error that occurs when I leave this AGS and an error that occurs when I open this AGS after closing it.

This might be a previously reported error that happens when you close the 3.5.0 Editor. But this time there is some interesting information about SpriteCache destructor.

Danvzare

Quote from: eri0o on Mon 22/10/2018 23:29:20
Multiple cameras could be used for minimaps. Other than this I don't see much use for multiple cameras.
I can. Finally we can make a splitscreen adventure game! :-D

And yes, I have always wanted to do that.

Crimson Wizard

#5
Updated the test build, now supports Software renderer too (this means 8-bit games):
https://www.dropbox.com/s/au7nw0zjrr13eaq/ags-3.5.0--roomviewport-wip2.zip?dl=0

Built on top of the latest 3.5.0 Alpha 7, so to test this you may install a copy of 3.5.0 somewhere and unpack the archive over it.

Still TODO:
- Better script API to manage the new viewport/camera functionality;
- Some optimisation for software rendering.
- more testing...
- Actually allowing room backgrounds of any size (less than game screen).


Crimson Wizard

#7
Alright, here's practically working version with a new script API:
https://www.dropbox.com/s/ecc5ldzi1dqvc5k/ags-3.5.0--roomviewport-wip4.zip?dl=0

Script API:
Code: ags

builtin managed struct Camera
{
  /// Gets/sets the X position of this camera in the room.
  import attribute int X;
  /// Gets/sets the Y position of this camera in the room.
  import attribute int Y;
  /// Gets/sets the camera's capture width in room coordinates.
  import attribute int Width;
  /// Gets/sets the camera's capture height in room coordinates.
  import attribute int Height;
  /// Gets/sets this camera's room horizontal scaling relative to the viewport it is displayed in.
  import attribute float ScaleX;
  /// Gets/sets this camera's room vertical scaling relative to the viewport it is displayed in.
  import attribute float ScaleY;

  /// Gets/sets whether this camera will follow the player character automatically.
  import attribute bool Auto;
};

builtin managed struct Viewport
{
  /// Gets/sets the X position on the screen where this viewport is located.
  import attribute int X;
  /// Gets/sets the Y position on the screen where this viewport is located.
  import attribute int Y;
  /// Gets/sets the viewport's width in screen coordinates.
  import attribute int Width;
  /// Gets/sets the viewport's height in screen coordinates.
  import attribute int Height;
  /// Gets the room camera displayed in this viewport.
  import readonly attribute Camera *Camera;
};


Viewport is a place on screen where room is drawn, defined in game coordinates (e.g. 0,0 - 320x200).
Camera is a place in room which is being drawn. Camera size may be determined in two ways:
a) explicitly setting Width and Height, which tells actual rectangle in the room;
b) setting ScaleX and ScaleY properties to automatically adjust displayed room relative to the viewport's size.
For example, if you set ScaleX/Y to 2.0, then room will appear zoomed in twice as large as the viewport's size. If you set ScaleX/Y to 0.5, it will appear twice as small (zoomed out).
NOTE: you cannot set camera size larger than the room's background, and it generally does not support "seeing" beyond room's background, same as before.

You access viewport and camera using commands:
Code: ags

Viewport *Game.RoomViewport;
Camera *Room.Camera;



Old viewport functions are now deprecated, they may still be enabled if you set "Script compatibility level" to v3.4.1 or lower.
Here's correspondence between old and new functions:

* SetViewport(x, y); ===> Room.Camera.X = x; Room.Camera.Y = y; Room.Camera.Auto = false;
* ReleaseViewport(); ===> Room.Camera.Auto = true;
* GetViewportX(); ===> Room.Camera.X;
* GetViewportY(); ===> Room.Camera.Y;



KNOWN ISSUES:
* because of a bug the AGS script does not let you write "Room.Camera.X = 10"; instead you have to do something like:
Code: ags

Camera *cam = Room.Camera;
cam.X = 10;

   this is very inconvenient indeed, and I think we need to fixing this issue, or change something in the above script API to make it simplier.
* some possible optimization issues with software renderer when you change camera size too often.



Little demonstration:
https://www.dropbox.com/s/l9783w8t1i8qk3t/roomcameramadness.mp4?dl=0

At this point nothing of above is set in stone, and I'd very much appreciate any functionality tests and thoughts on script commands.

Crimson Wizard

Another update. I have fixed number of bugs since previous versions. (The fact that no one reported them probably means that no one have tried it...  :( )
https://www.dropbox.com/s/lk7b25a8kwj9jfz/ags-3.5.0--roomviewport-wip5.zip?dl=0

Also added Game.AutoSizeViewportOnRoomLoad which lets you control whether viewport is reset to default size when the new room is loaded.

I don't remember if this was in previous version already, but from now on you may have rooms of literally any size, for example have a 100x100 room in a 320x200 game, and so forth. This allows you to stop adding those empty black borders to the room backgrounds if part of your game screen is supposed to be covered by fixed GUI.


I haven't found any serious bugs in this latest build, so unless someone else does I am going to embrace and put this into the 3.5.0 release.

eri0o

#9
using version from here -> 3.5.0.8 here

Yo, I am using Camera *cam = Room.Camera ! I want to do a GetWalkableAreaAt! When I use a camera that doesn't use System.ViewportHeight and System.ViewportWidth, as cam.Height and cam.Width, I can't seem to figure out how to get it correctly!  ???

Also , think it broke this (it's ignoring cam.Height and cam.Width) ->
Code: ags

  if (keycode == eKeyCtrlA) Debug(2,0); // Ctrl-A, show walkable areas


Nevermind -> Figured out -->>

Code: ags

function GetWalkableAreaAtRoomXY(int x, int y){
  Camera * cam;
  cam = Room.Camera;
  Viewport * vport = Game.RoomViewport;  
  int walkableat;
  
  walkableat = GetWalkableAreaAt(vport.Width*(x-cam.X)/cam.Width+vport.X, 
                      vport.Height*(y-cam.Y)/cam.Height+vport.Y); 
    
  return walkableat;
}



Did something

Download AGS PROJECT HERE



Crimson Wizard

#10
@eri0o I guess you did the math right, but I wanted to save people from doing that by hand, so my next ammendment to the cameras are these functions:
https://github.com/adventuregamestudio/ags/pull/549

In short there will be commands like Viewport.GetRoomLocation and Viewport.GetScreenLocation that convert screen coordinates to room and back through the given viewport, as well as Screen.GetRoomLocation which first finds if there is any viewport at a given point and if there is then does Viewport.GetRoomLocation. The latter is assuming that there may be multiple viewports (at some point in future).


Quote from: eri0o on Tue 25/12/2018 16:06:44
Also , think it broke this (it's ignoring cam.Height and cam.Width) ->
Code: ags

  if (keycode == eKeyCtrlA) Debug(2,0); // Ctrl-A, show walkable areas


It's quite possible that I missed some command. Since this code is already in repository could you open a bug report?

eri0o

here is the new issue on github! Just freshly opened. :) I will try to add more details later.

Also thanks for the new handy functions, they will be handy :D

One thing about the walkable/region/hotspot areas, imagining a multiple camera/viewport situation, the functions would require both camera and viewport information to be able to get the correct X and Y, wouldn't?

Crimson Wizard

#12
Quote from: eri0o on Tue 25/12/2018 19:16:41
One thing about the walkable/region/hotspot areas, imagining a multiple camera/viewport situation, the functions would require both camera and viewport information to be able to get the correct X and Y, wouldn't?

Viewport has a reference to Camera it is rendering. Right now its a readonly property, later it may become settable to change cameras in a viewport.
When converting coordinates from screen to room and back viewport is using its camera reference already.

eri0o

Hey CW, I think I have two additional questions on this feature:

why Game.RoomViewport, instead of Room.Viewport ? Is Game.RoomViewport the same for all Rooms and if it would be in Room.Viewport it would get destroyed on each Room load?

Also Camera.ScaleX and Camera.ScaleY, I think they should not existe, since the Viewport has a Camera assigned to it, but the Camera "doesn't know" about the viewport - so if there would exist a ScaleX and ScaleY, they should belong to the Viewport and not the Camera, right? Example, a Camera could theoretically be assigned to two Viewports.


Crimson Wizard

#14
Quote from: eri0o on Wed 26/12/2018 01:33:02
why Game.RoomViewport, instead of Room.Viewport ? Is Game.RoomViewport the same for all Rooms and if it would be in Room.Viewport it would get destroyed on each Room load?

Viewport does not belong to the room but to the screen. Room class has things that exist in the room and room's coordinate system. Viewport is a rectangle on the screen, which draws contents of room camera.
If we theoretise about multiple rooms loaded at the same time viewport will still be same and not owned by any room in particular.
BTW I like to move this property from Game to Screen class as next update, since Game is a hoard of various functions and properties already.


Quote from: eri0o on Wed 26/12/2018 01:33:02
Also Camera.ScaleX and Camera.ScaleY, I think they should not existe, since the Viewport has a Camera assigned to it, but the Camera "doesn't know" about the viewport - so if there would exist a ScaleX and ScaleY, they should belong to the Viewport and not the Camera, right? Example, a Camera could theoretically be assigned to two Viewports.

This is something that is bothering me because there is more problems in this than just question of who knows who.

Initially I wanted this simply as means of automating camera size. I had a long thought before putting it to camera. I think in the end I did this because it is Camera that currently sets the boundaries of the visible part of the room with Camera.Width and Camera.Height. If I'd move Scale to Viewport then there would be inconsistency between camera's Width/Height and viewport's demands to scale camera to what it wants. Then viewport would have to actually modify camera's width and height, which could make sense but in turn bring question about multiple viewports using same camera (and generally would mean that viewports control cameras while now they only connected to them).
Currently it's like the camera has two modes: fixed size that's always the same and stretched to viewport and dynamic size that adjusts itself to viewport to match the required scaling. Since its camera that has both properties it may switch between them.

But thinking this over again, the biggest issue right now is not even that scaling is set by camera but that with non-fixed size you cannot tell how to position camera around player unless you put both camera and viewport into calculation, because right now the camera's position is defined as a top-left of a rectangle, and for example when tracking the player character camera is placed at character's location minus half-size of camera.
So this is truly not good at all.

What are the alternatives?

1) Completely moving camera's field of view configuration to the viewport. This will leave only position in camera (and probably rotation when its supported).
In such case we also must change how camera's position is determined and speak of a point in room camera is centered at instead of a rectangle in room. This is not necessarily bad, but will change the positioning logic agsers were used to.

2) Or completely remove automatic scaling property and leave camera with fixed Width and Height which is probably logical variant too, but users will have to calculate camera size themselves if they want to achieve certain level of zoom.

PS. I am actually leaning towards removing scaling property at all... at least for now. There is no automatic re-centering anyway and it is not hard to calculate Width and Height for the required zoom by multiplying or dividing viewport by some float.

eri0o

I see no problem in removing Scale, I think the API should be as small as needed. Zoom could be provided by a module, and it would mostly likely have TweenZoom too. The advantage of the module is that it's API can evolve faster.

I understand the camera as a way to transform pixels from the game world to the pixel space of the Viewport. So for Zoom the Camera Width and Height would be make smaller, but mapped to a viewport that stays at the same size.

Creamy

#16
Hello fine people behind the curtains,

I'm attempting a splitscreen game with 2 characters playable at the same time â€" just like the demo with the bridge.
The only difference is that my screen is split horizontally. You control one guy when the mouse in the upper part of the screen. You control the other when the mouse is on the lower part of the screen.

So far it works great and I managed to have a scrollable room where each camera follows each character appropriately.

I just have an oddly specific bug with the hotspots.

On "mouse over hotspot" I call a function to change the sprite of the mouse:
Code: ags
mouse.Mode = eModeUsermode2;


in "repeatedly execute always" I put this line to revert the cursor back when the mouse is no longer on the hotspot:
Code: ags
if (GetLocationType(mouse.x,mouse.y) == eLocationNothing) mouse.Mode = eModeWalkto;  


My room is higher than the screen. These two lines work with both characters. With just one exception:

When I go to the upper part of the room with the character followed by the first camera, the hotspots still work - I can see their name and click on them.
However, the sprite of the mouse doesn't change.

Strangely, everything works perfectly with the second character followed by the second camera.

I tried to extend the room vertically with a big black space above the scenery to get around this problem but it doesn't work.

Do you have an idea of what may cause the problem?
 

Crimson Wizard

#17
Creamy, do you have (or can make) a simple test game that demonstrates the issue? It will be easier to test then.

There may be something missing in the new camera system, but could be simply a script bug, it's hard to tell without seeing this in action.

Creamy

Right. I'll send you the test game tonight. Thanks
 

Crimson Wizard

I sent detailed explanation in PM, but just to mention something, the problem was again that there's an active Smooth Scrolling module in the game that messes up the camera position in rep-exec-always, and some actions get different results depending on which relative order they have (before or after room script).

SMF spam blocked by CleanTalk