Repository navigation
MapView PhotoCamera GarageCamera
Three cameras outside of driving. MapView is the map while it's open: its cursor, its camera and its zoom. PhotoCamera lets you add to what photo mode's camera reads from the sticks. GarageCamera moves the garage's orbiting camera closer or further. A mouse mod uses them to drag the map, fly the photo camera with keys, and zoom the garage camera with the wheel.
Thread: MapView: the UI thread, inside Menus.Frame. PhotoCamera.InputRead: its handlers run on the game's thread. GarageCamera: any thread.
The left mouse button drags the map, W and S fly the photo camera forward and back, and the mouse wheel zooms the garage camera.
using System.Numerics;
using FH2;
using FH2ModPlatform;
public sealed class MouseCameras : Mod
{
private Vector2 _last;
public override ModInfo Info => new("MouseCameras", "1.0.0");
protected override void OnInit()
{
Menus.Frame += dt =>
{
var at = Menus.ToUi(GameWindow.Cursor);
if (MapView.Current is { } map && GameWindow.IsMouseDown(MouseButton.Left))
map.Pan(at - _last);
_last = at;
};
PhotoCamera.InputRead += input =>
input.Forward += (GameWindow.IsKeyDown(0x57) ? 1 : 0) - (GameWindow.IsKeyDown(0x53) ? 1 : 0); // W, S
GameWindow.MouseWheel += delta =>
{
if (GarageCamera.IsActive) GarageCamera.Zoom(delta / 120f * 0.5f); // half a metre a notch
};
}
}Imports System.Numerics
Imports FH2
Imports FH2ModPlatform
Public Class MouseCameras
Inherits [Mod]
Private _last As Vector2
Public Overrides ReadOnly Property Info As ModInfo
Get
Return New ModInfo("MouseCameras", "1.0.0")
End Get
End Property
Protected Overrides Sub OnInit()
AddHandler Menus.Frame,
Sub(dt)
Dim at = Menus.ToUi(GameWindow.Cursor)
Dim map = MapView.Current
If map IsNot Nothing AndAlso GameWindow.IsMouseDown(MouseButton.Left) Then map.Pan(at - _last)
_last = at
End Sub
AddHandler PhotoCamera.InputRead,
Sub(input)
input.Forward += If(GameWindow.IsKeyDown(&H57), 1.0F, 0.0F) - If(GameWindow.IsKeyDown(&H53), 1.0F, 0.0F) ' W, S
End Sub
AddHandler GameWindow.MouseWheel,
Sub(delta)
If GarageCamera.IsActive Then GarageCamera.Zoom(delta / 120.0F * 0.5F) ' half a metre a notch
End Sub
End Sub
End ClassThe map, while it's open. It's seen from straight above: X and Z are the world's, and in a Vector2 the world's Z is .Y. The map's cursor is what the stick moves; the camera above follows the cursor once it strays from the middle.
| Member | What it does |
|---|---|
Current (static property, MapView?) |
The map on screen (it has the focus), or null. |
Cursor (property, Vector2, get/set) |
The map's cursor in the world (X, Z): what A selects or puts a waypoint on. |
Camera (property, Vector2, get/set) |
Where the camera is (X, Z). It eases towards Target. |
Target (property, Vector2, get/set) |
Where the camera is going (X, Z). |
Zoom (property, float, get/set) |
The zoom, 0..1 as the game keeps it. Setting it keeps it between ZoomMin and ZoomMax. |
ZoomMin (property, float) |
One end of the zoom's range. |
ZoomMax (property, float) |
The other end of the zoom's range. |
Leash (property, float, get/set) |
How far the cursor goes from the middle before the camera follows it, in world units. |
UnitsPerPixel (property, float) |
World units per UI pixel at the current zoom. |
WorldAt(Vector2 ui) (method, returns Vector2) |
The spot under a point in the UI's pixels, in the world (X, Z). Use it to put the map's cursor under the mouse. |
Pan(Vector2 ui) (method) |
Moves what's on the map by ui UI pixels, like a drag: moves both the camera and where it's going. |
Handle (property, nint) |
The map's address. Two MapViews of the same map have the same one. |
Photo mode's camera, as it reads its controller each frame.
| Member | What it does |
|---|---|
InputRead (static event, Action<PhotoCameraInput>) |
The photo camera has just read its controller (every frame in photo mode, on the game's thread). Add your own values to what it read. |
What the photo camera read from its controller this frame: stick values, -1..1, that you can add to.
| Member | What it does |
|---|---|
Forward (property, float, get/set) |
Forward (+) and back: the left stick up and down. |
Right (property, float, get/set) |
Right (+) and left, as the camera has it: the left stick sideways. |
Rise (property, float, get/set) |
Up (+) and down. |
TurnX (property, float, get/set) |
Turning sideways (the right stick). |
TurnY (property, float, get/set) |
Turning up and down (the right stick). |
Zoom (property, float, get/set) |
Zooming in (+) and out. |
The garage's camera, and the other menus' car cameras that orbit the car.
| Member | What it does |
|---|---|
IsActive (static property, bool) |
True while the garage camera is updating (within the last 200 ms). |
Zoom(float metres) (static method) |
Moves the orbiting camera closer (+) or further (-) by metres at its next update. The distance is kept between 1.5 and 8 m. The game's own steps are 2, 3.5 and 5 m. |
-
They start with Menus.
PhotoCameraandGarageCameraswitch on whenMenusdoes: the first time a mod readsMenus.IsReadyor subscribes toMenus.Frame. (Subscribing toMenus.FrameforMapViewalready does it.) -
MapView.CurrentusesMenus.Focused, so it throwsInvalidOperationExceptionoff the UI thread. Get it again each frame; don't keep it. -
WorldAtandPantake the UI's pixels. Turn the mouse position into them withMenus.ToUi(GameWindow.Cursor). -
PhotoCameraInputvalues are what the controller gave. Add to them (+=) to keep the controller working alongside your keys. Use the object only inside your handler. - In the Autoshow, two cameras read the controller, so
InputReadcan come twice a frame there. - An exception in an
InputReadhandler is written to platform.log. If your mod is a hosted mod and it's reloaded, itsInputReadhandlers are removed. -
GarageCamera.Zoomadds up: calls before the camera's next update are added together. If the camera isn't updating, the amount waits until it next does, so checkIsActivefirst. The step only applies while the camera is orbiting the car. -
Unsupported game build: if the game's code isn't as expected,
PhotoCameraorGarageCameraswitches itself off and platform.log says "PhotoCamera off: ..." or "GarageCamera off: ...".InputReadthen never comes, andGarageCamera.IsActivestays false.MapView.Currentreturns null if it doesn't recognise the map.
- Camera: the driving camera.
- Menus and UiElement
- GameWindow: the cursor, the mouse buttons and the wheel.
-
GameInput: the garage camera's stick (
MenuControl.CameraRight,CameraUp) and the driving cameras' look controls. - World: the world's X and Z.
- Threads
Playing
Getting started
Writing mods
The FH2 classes
- Game
- Player
- Vehicle
- World
- Garage, CarCatalog, Profile
- UI and Hud
- Camera
- Audio
- ANNA
- Skills
- DebugOverlay
- Controller
- GameInput
- GameWindow
- Menus and UiElement
- SettingsTile
- ButtonIcons and Picture
- MapView, PhotoCamera, GarageCamera
More