Screen coordinates


How can I set coordinates so that x,y = 0,0 bottom left screen of the background image for this scene sprite?
0,0 is now centre of the viewport; that is not usable.
So I would like to align it to bottom left corner of the image.

Viewport.Camera.Orthographic.Width := 1920;
Viewport.Camera.Orthographic.Height := 1080;
Viewport.Camera.Orthographic.Origin := Vector2(0.5, 0.5);

PlayerScene.Translation := Vector3(0, 0, 1);

About the first question (coordinates): Just shifting the scene by (SceneWidth / 2, SceneHeight / 2) works without any issues:

I’ve tried to create something like a point-and-click example, see here : GitHub - eugeneloza/point-and-click: A small example of point-and-click movement

Many thanks for this.

I am trying to move a custom mouse (scene) to the same coordinates as the Windows mouse. When I move it to 0,0 (bottom left screen) they match but moving it up and right the distance between them becomes larger. I don’t know what I am doing wrong here.

Mouse.Translation := Vector3(Window.MousePosition.X - Background.BoundingBox.Size.X/2 , Window.MousePosition.Y - Background.BoundingBox.Size.Y/2, 1);

Note that TCastleWindowBase.MousePosition (or TCastleUserInterface.Container.MousePosition) are in “screen coordinates” - i.e. in real pixels of the Player’s monitor. And all UI and transforms are scaled in “effective coordinates” - i.e. original (unscaled) pixels before they are scaled to Player’s monitor. There are two values proportional to each other with TCastleUserInterface.UIScale (or TCastleWindowBase.Container.UiScale) coefficient. You can see this conversion done in the example I’ve posted above. For your specific case it will be:

Mouse.Translation := Vector3(Window.MousePosition.X / Window.Container.UiScale - Background.BoundingBox.Size.X/2 , Window.MousePosition.Y / Window.Container.UiScale - Background.BoundingBox.Size.Y/2, 1);

Thanks, but now I get a message that there is no UIScale member?

Ah indeed, it’s TWindowContainer, not TUiContainer. And TUiContainer in turn hides FCalculatedUIScale as a private field allowing TCastleUserInterface to access it through “almost-cheating” mechanism :slight_smile:

So, overall, that means you cannot get access to UiScale except from a TCastleUserInterface derivative. I see you have designed your game screen in Castle Editor - that means you should load it as a TUiState, right?

This way if you are updating the Mouse.Translation inside some of the TUiState events (e.g. Update) then you can simply:

Mouse.Translation := Vector3(Window.MousePosition.X / UiScale - Background.BoundingBox.Size.X/2 , Window.MousePosition.Y / UiScale - Background.BoundingBox.Size.Y/2, 1);

I am not using Castle Editor and TUistate (yet), the game screen was just for trying things out.
So how can I access UiScale within a TCastleUserInterface derivative?

Just like UiScale (as in `Self.UiScale).

Hm, I’m not using TUiState either, do I need to use that really?
My code is in topic “Collision Course”.

TUiState is a derivative of TCastleUserInterface. As far as I understand:

You are using a TCastleUserInterface, right - who updates it? I’ll check your code in that topic a later.

@Carring Indeed the UI scaling is making this non-trivial.

But we have a dedicated methods in CGE viewport to make it easy: TCastleViewport.PositionTo2DWorld documented on Castle Game Engine: CastleViewport: Class TCastleViewport . If you pass ScreenCoordinates = true parameter, then it will automatically work with mouse position, and handle some things:

  • that UI scaling may be used (mouse positions are provided unscaled)

  • that viewport may not fill the entire screen.

So you can use the resulting value as MyTransform.Translation. E.g. code like this will make sense:

MyTransform.Translation := MyViewport.PositionTo2DWorld(Event.MousePosition, true);

The ScreenCoordinates = false is also useful, it means that you provide values in viewport size (so e.g. X between 0 and MyViewport.EffectiveWidth). This allows to reliably ask “what is the position inside world at any corner of the TCastleViewport”, e.g. MyViewport.PositionTo2DWorld(Vector2(0, 0), false) (left-bottom corner), MyViewport.PositionTo2DWorld(Vector2(MyViewport.EffectiveWidth, MyViewport.EffectiveHeight), false) (right-top corner) etc.

See also some related utils like PositionToRay, PositionToCameraPlane, PositionToWorldPlane. They all define ScreenCoordinates parameter with the same meaning.

Now I stumble upon that Scene translation is Vector3 and PositionTo2DWorld “wants” Vector2.
I have this (which gives error):

procedure SetMouse;
Mouse := TCastleScene.Create(Application);
Mouse.Load(‘castle-data:/mouse-icons.starling-xml#fps:8, anim-naming:strict-underscore’);
Mouse.Translation := vector3(Window.MousePosition.X, Window.MousePosition.Y, 5);
Mouse.PlayAnimation(‘mouse-icons-2’, true);

procedure WindowUpdate(Container: TUIContainer);
Mouse.Translation := Viewport.PositionTo2DWorld(Window.MousePosition, true);


That’s because mouse position doesn’t have Z coordinate - it’s on the screen. But scenes indeed have Z coordinate - it goes from the Player (negative values) to depth (positive value). Here Z means Z-sorting, i.e. the scenes with higher Z are further away from the camera. So, if you want to have mouse cursor over all other scenes - be sure to give it the minimal Z coordinate value, e.g. like this:

Mouse.Translation := Vector3(Viewport.PositionTo2DWorld(Window.MousePosition, true), -1);

P.S. Note, that it’s much more readable if you enclose the code pieces in “`” symbols (the back-quote one, which is on the same key where “~” symbol is.

If you use a matching pair those in one line you get blocks like this.

Or you can put three back-quote symbols on a separate line - and another three in the end to have a big block of code inside

This way your code will be much more readable and won’t lose indentation. (Yes, I didn’t yet look at your code in that topic you’ve mentioned above just for this reason - I can’t such a big piece without any indentation and markup, and indenting it manually would take a lot of time.

For very large pieces of code you might also want to use or even a public git repository (at GitHub, GitLab or any other convenient git hosting with visual code viewer).

Thanks, finally got this working.

And I will experiment with your suggestion on the enclosure of the code.

About the Z coordinate: Is it not the other way around that the higher the Z coordinate is, the scene is more placed in the front? When I give Mouse Z - 1 it is not visible on the screen. I think it is placed behind the background scene as it has Z 0 and other scenes have Z 1 and 2. I have to give Mouse Z a higher Z value than the other scenes to place it in front of all the others. so I gave it number 9.

1 Like

Yes, the Z coordinate (in standard 2D projection in CGE) grows as you get closer to the viewer.

In standard 2D projection in CGE:

  • X grows to the right
  • Y grows up
  • Z grows toward the camera

It’s easy to visually using “right-hand rule”, as CGE has “right-handed coordinate system”, Right-Handed Coordinate System -- from Wolfram MathWorld , Right-hand rule - Wikipedia .

As for placing the mouse cursor in front: remember to also call Viewport.Items.SortBackToFront2D after adding new mouse scene to Viewport.Items, to make proper blending. See Blending · castle-engine/castle-engine Wiki · GitHub for gory details.

@eugeneloza I looked at your GitHub - eugeneloza/point-and-click: A small example of point-and-click movement again because I remembered an interesting thing you put in code:
MoveMap := LoadImage(‘castle-data:/…zmap.png’, [TGrayscaleImage]) as TGrayscaleImage;

I would like to use it in some way to make a certain picture area (sea) not accesible for a walking character (Transform). So I guess it is a transparant color overlapping an area that cannot be used for movement. It is however not quite clear how I can use this. Do you have a small example of this?

I believe you can do it a similar way. However, as you’ve noticed that the example has an image underneath, because in that case it’s trivial - just check the appropriate pixel with scaling. As you’re using TCastleTransforms, it becomes significantly less straightforward - you either need to hack into the TCastleScene underneath to get access to the texture’s pixels or (worse at the first glance, but in a general case may be more reliable) render it into a texture and access pixels of this texture without displaying it on screen.

A hacky, but maybe a viable solution for this specific usecase is use separate alpha channel, but not to render alpha, but to store “depth map” or “movement map” (as in TGrayscaleImage from the example) and have it rendered with a special shader to keep the information and have easy access to it when the user clicks the screen, but do not actually render it as an alpha channel. TGA format allows to do something like that, but I believe you’ll need a custom shader for that if done correctly, and this is a complex task.

A much more valid (but incomparably more complex) approach is to create a mesh (almost a navmesh) underneath, that would receive clicks and generate corresponding callbacks or using MouseRayHit. This should work out of the box and delegate the problem to the graphic artist (as in just click in blender to create the outline of the walkable area for every location properly aligned with the image).

Thanks, but this all seems abacadabra to me. Is there not a simple way that when a Scene collides with a specific color it stops moving?