Class TCastleAbstractPointerLock

Unit

Declaration

type TCastleAbstractPointerLock = class(TObject)

Description

Logic of "pointer lock".

When Active is True:

  1. Mouse cursor is hidden.

  2. Mouse movement is unbounded by the window/canvas borders. At least it seems to be unbounded, from the perspective of user dragging something with mouse or rotating 3D view in FPS game.

  3. Use ready TInputMotion.Delta for any logic you want:

    function TViewMain.Motion(const Event: TInputMotion): Boolean;
    begin
      Result := inherited;
      if Result then Exit; // allow the ancestor to handle event
      MoveSomething := MoveSomething + Event.Delta;
    end;

  4. The position of the mouse, as reported by TInputMotion.Position and TInputMotion.OldPosition, are undefined.

    Some implementations (desktop) may expose there "chaotic movement" (as we forcefully reposition mouse to the middle of the screen). Some implementations (web) may expose there "no movement at all" (as the "pointer lock API" on WWW browsers pretends that mouse didn't move at all).

    Do not assume that TInputMotion.Delta is equal to TInputMotion.Position - TInputMotion.OldPosition, it is not (on neither platform, now).

This is automatically used by TCastleWalkNavigation.MouseLook.

You can use it yourself explicitly for custom dragging. Follow the template below. See the engine example examples/user_interface/dragging_test/ for a working code demonstrating this to drag 2D objects with mouse.


function TViewPlayGame.Press(const Event: TInputPressRelease): Boolean;
begin
  Result := inherited;
  if Result then Exit;

  if Event.IsMouseButton(buttonLeft) then
    Container.PointerLock.Active := true;
end;

function TViewPlayGame.Release(const Event: TInputPressRelease): Boolean;
begin
  Result := inherited;
  if Result then Exit;

  if Event.IsMouseButton(buttonLeft) then
    Container.PointerLock.Active := false;
end;

function TViewPlayGame.Motion(const Event: TInputMotion): Boolean;
begin
  Result := inherited;
  if Result then Exit;

  if Container.PointerLock.Active then
  begin
    // Use Event.Delta to perform any logic you want.
  end;
end;

This is an abstract class, as the implementation of pointer lock is platform-specific.

  • On some backends (non-web right now, using TCastleDesktopPointerLock), we actually hide the mouse cursor and keep repositioning mouse to the middle of the screen.

  • On web, we use the pointer lock API of browsers, which does all the work for us, but comes with some quirks:

    • Pointer lock is activated with some delay after setting Active to True, and potentially user may reject it altogether.

    • It has to be activated in response (or shortly after) to a user interaction with a page (e.g. mouse click).

    • It may be cancelled by the user at any time (typically by pressing Escape key). When this happens, Active is set to False and callbacks registered with AddUserCancelledListener are called.

    • See known limitations of web backend for more details about pointer lock on web.

Never create instances of this class directly. Use only the instance provided by TCastleContainer.PointerLock, which provides the correct implementation for the current platform, tied with the current container and window.

Source: ui/castleuicontrols_pointer_lock_abstract.inc (line 139).

Hierarchy

  • TObject
  • TCastleAbstractPointerLock
Show Additional Members:

Overview

Methods

Protected procedure ActiveChanged(const NewValue: Boolean); virtual; abstract;
Protected procedure CancelActive;
Protected function Delta(const Event: TInputMotion): TVector2; virtual; abstract;
Public constructor Create(const AContainer: TCastleContainer);
Public destructor Destroy; override;
Public procedure AddUserCancelledListener(const Event: TNotifyEvent);
Public procedure RemoveUserCancelledListener(const Event: TNotifyEvent);

Properties

Protected property Container: TCastleContainer read FContainer;
Public property Active: Boolean read FActive write SetActive;
Public property Controller: TCastleUserInterface read FController write SetController;

Description

Methods

Protected procedure ActiveChanged(const NewValue: Boolean); virtual; abstract;

Start or stop mouse look.

Source: ui/castleuicontrols_pointer_lock_abstract.inc (line 151).

Protected procedure CancelActive;

Set Active to False without calling virtual ActiveChanged. Calls callbacks registered with AddUserCancelledListener. To be used by descendants when we lost pointer lock because of user action, e.g. on web when user cancels pointer lock by pressing Escape key.

Descendants are responsible to reset their own state before/after calling this, since this does not call ActiveChanged, so descendants are not notified about the cancellation in any other way. E.g. web implementation sets "FEffective := false" before calling this.

Source: ui/castleuicontrols_pointer_lock_abstract.inc (line 165).

Protected function Delta(const Event: TInputMotion): TVector2; virtual; abstract;

Read current delta of mouse movement, assuming that mouse look is active.

This is internally used by container to set TInputMotion.Delta before TInputMotion is passed to TCastleUserInterface.Motion events.

This is called exactly once per motion event (so it doesn't need to be deterministic based on Event; on desktop it will indeed do some side-effects like repositioning mouse).

Always returns zero if Active is False. Remember that on some platforms (web) user may cancel the "pointer lock" mode at any moment, so be sure to tolerate it by doing nothing when this is zero.

It also returns zero if Active is True but we have not yet prepared for this mode. On desktops, "not prepared" means that mouse was not positioned correctly yet. On web, "not prepared" means that browser/user didn't yet confirm pointer lock mode. Again, the simple solution is to do nothing when this is zero.

The returned value is in final device pixels, so it is not affected by UI scale. If you want to adjust to UI scale (so user will have to move mouse by more pixels, when on a larger screen, when UI is larger) then just use Delta / UiScale.

Source: ui/castleuicontrols_pointer_lock_abstract.inc (line 193).

Public constructor Create(const AContainer: TCastleContainer);

This item has no description.

Source: ui/castleuicontrols_pointer_lock_abstract.inc (line 195).

Public destructor Destroy; override;

This item has no description.

Source: ui/castleuicontrols_pointer_lock_abstract.inc (line 196).

Public procedure AddUserCancelledListener(const Event: TNotifyEvent);

Called when user cancels pointer lock, e.g. by pressing Escape key on web. When this is triggered, the Active property is already set to False.

Source: ui/castleuicontrols_pointer_lock_abstract.inc (line 213).

Public procedure RemoveUserCancelledListener(const Event: TNotifyEvent);

This item has no description.

Source: ui/castleuicontrols_pointer_lock_abstract.inc (line 214).

Properties

Protected property Container: TCastleContainer read FContainer;

Container performing this pointer lock logic.

Source: ui/castleuicontrols_pointer_lock_abstract.inc (line 154).

Public property Active: Boolean read FActive write SetActive;

Enable or disable pointer lock. Note that on some platforms (web) user can cancel pointer lock at any time, in which case this property turns to False. On other platforms (desktop) this stays completely under control of the application.

Source: ui/castleuicontrols_pointer_lock_abstract.inc (line 203).

Public property Controller: TCastleUserInterface read FController write SetController;

Optionally, when requesting pointer lock (by setting Active to True), you can set this property to indicate what component is responsible for the pointer lock.

This controller, and not others, should process deltas during pointer lock. This controller should also set Active to False when no longer needed. This is just a convention: since in practice it doesn't make sense for mutliple components to process pointer lock deltas at the same time, this property allows them to coordinate "which one processes deltas now" and avoid conflicts. E.g. when we have multiple viewports and multiple TCastleWalkNavigation instances, only one of them can effectively process pointer lock deltas.

Guaranteed effects of setting this:

  1. We will make sure to pass TCastleUserInterface.Motion events to this controller, first, even if it is not focused. This allows to capture motion events even by TCastleWalkNavigation that doesn't fill the middle of the screen. This makes sense, since mouse position in general should not matter when pointer lock is active.

  2. When we detect this no longer exists (or parent of it) we will call TCastleUserInterface.ReleasePointerLock on it. Once the control (and its parents) exist again, it will again receive events like TCastleUserInterface.Update, so it can again then grab mouse look (that's what TCastleWalkNavigation does, for example).

Source: ui/castleuicontrols_pointer_lock_abstract.inc (line 250).


Generated by PasDoc 1.0.4.