Image upload and gradual rendering

Hello!
For orientation in game world i use minimap. In Lazarus i create it in 2 steps: download all .bmp/.png files into TBitmap variables, and use Canvas to draw map cells around player location. I can make really big map, but draw only then cells, what player can see. I want to carry this in CGE.
I saw isometric_game example code, but don’t understand, how images download to game before drawing. And i little confused by many types in 2D manual :pensive:
Can i download all files in Pascal code and draw it into Viewport item Plane like texture? I think its easyest and faster method at first development steps.

How it looked in Lazarus:
minmap

Loading image is as simple as Castle Game Engine: CastleGLImages: Class TDrawableImage then you draw them by Castle Game Engine: CastleGLImages: Class TDrawableImage :

procedure TMyState.Create(AOwner: TComponent);
begin
  inherited;
  SomeImage := TDrawableImage.Create('castle-data:/image.png');
end;
...
procedure TMyState.Render;
begin
  inherited;
  SomeImage.Draw(XOnScreen, YOnScreen, WidthOnScreen, HeightOnScreen);
end;

There’s a way to significantly optimize this (using batched rendering - up to 100x drawing speed) and make much more “beautiful” (by creating a specific derivative of TCastleUserInterface for rendering the map or minimap), but let’s do one step at a time :slight_smile:

To add to what @eugeneloza writes, the manual page about using images is Images | Manual | Castle Game Engine (I have just improved that page, to incorporate some advises from this and Eugene’s post).

Indeed we have too many types related to images (with time I hope to hide/unify some of the existing practically-internal types).

But the recommendation on that page hopefully helps :slight_smile: The 3 images types (and drawing methods) that we recommend are:

  1. TCastleImageControl . It’s an image that acts as a UI control and you can set it up using code or using CGE editor. It’s a close analogy to using TImage component to put image on form in LCL / VCL. It’s demonstrated in chapter Designing user interface and handling events (press, update) within the state | Manual | Castle Game Engine , you can put them using code or CGE editor.

  2. TCastleImageTransform. It’s an image within a viewport, where you can shift the camera. See Viewport with scenes, camera, navigation | Manual | Castle Game Engine . You can also place such image in a viewport.

    Then you have the full power to combine it with physics, other TCastleScene instances (that can also use animations in any format, like sprite sheets), even with 3D objects. I recently made a video about viewport on Viewports, cameras and navigation in Castle Game Engine - YouTube .

    You can also place such images in CGE editor. So you even don’t need to write code, you can design map in CGE editor. (Though we don’t yet have “snapping” which would be useful for maps.)

  3. Drawing image yourself, using TDrawableImage.

In your case, using TDrawableImage (AD 3) is indeed the most straightforward path to convert your code to CGE.

Depending on what you will need later, I would recommend trying out option AD 2 too (TCastleImageTransform). If you think of later adding there animations, physics, also designing it using CGE editor – then TCastleImageTransform is a more powerful option for the future.

Note: For 2D map, you can also design it using Tiled, https://www.mapeditor.org/ . You can then load ready map in TCastleTiledMapControl. See 2 examples in examples/tiled/. Since 2 days, you can also load Tiled map into TCastleScene, but for now TCastleTiledMapControl support a few more Tiled options.

Thanks, i try both variants. Really, i want to get smooth scrolling and rotation of minimap.

Seems interesting. Right now I’m using a text file with a matrix of cell information to build the game world. This is convenient, because in percpective it will allow to edit a set of cell parameters - nested objects, images, names. I want to assign to each cell many events, like in strategy game Eador, and personal screen like town view in HoMM… But it’s another story, i thunk.
Thanks for help :slightly_smiling_face:

Good day!
How can i get access to ViewPort from code? I can do it for analogy with TCastleLabel with FPS?
I try to add my ViewPort in private of main game state window, but it doesen’t work.
My target is to add and delete child images in TCastleImageTransform to next work with a camera.
I am in the wrong way?
And can i use downlodable from code image in Viewport, if it doesen’t pre-loaded in *.castle-user-interface?


unit GameStateMain;

interface

uses Classes, CastleFilesUtils, CastleGLImages,
  CastleVectors, CastleUIState, CastleComponentSerialize,
  CastleUIControls, CastleControls, CastleKeysMouse;

type
  { Main state, where most of the application logic takes place. }
  TStateMain = class(TUIState)
  private
    { Components designed using CGE editor, loaded from gamestatemain.castle-user-interface. }
    FPS: TCastleLabel;
    Map: TCastleViewport;
    ExampleImage: TDrawableImage;
  public
    constructor Create(AOwner: TComponent); override;
    procedure Start; override;
    procedure Update(const SecondsPassed: Single; var HandleInput: Boolean); override;
    procedure Render; override;
    function Press(const Event: TInputPressRelease): Boolean; override;
  end;

Screenshot_20220813_211938

First of all, while writing the answer to this, I realized I want to have a CGE example that shows this properly :slight_smile: And I actually already planned to make such example. Recently I moved the old isometric_game (using TDrawableImage drawing) to examples/deprecated_to_upgrade/isometric_game/. Today I have implemented a new examples/isometric_game project that shows how to trivially generate a random terrain by adding TCastleTransformImage instances to TCastleViewport.

I have just committed this, it is in examples/isometric_game/ . You can get it by downloading CGE from GitHub, GitHub - castle-engine/castle-engine: Cross-platform (desktop, mobile, console) 3D and 2D game engine supporting many asset formats (glTF, X3D, Spine...) and using modern Object Pascal .

To answer your questions:

The TCastleLabel, TCastleViewport, TCastleImageTransform and all other components you see in the editor are also regular Pascal classes. So yes, you can access them all from code in the same way:

  • you can get an instance that you can have designed, like MainViewport := DesignedComponent('MainViewport') as TCastleViewport;, and then use this instance to change there anything, to add there new children etc.

  • you can also create an instance of any component using code, and then again change anything of this instance.

The examples/isometric_game/ shows this:

  • In this case, I have placed the TCastleViewport using editor (and just access existing instance from code) and I create new TCastleImageTransform instances from code. Many other combinations are possible :), our important feature is that you can mix stuff created in editor with stuff created in code – because, in the end, they are really just the same things.

  • The design of the example is trivial. I started from “Empty” template, added “Viewport 2D” to the design (and renamed it MainViewport), and removed Plane1. I also added “Navigation 2D” to trivially move / zoom within the map in game.

  • The code does the work in TStateMain.Start. That’s really the only interesting piece of code in that example :slight_smile: See it here: https://github.com/castle-engine/castle-engine/blob/master/examples/isometric_game/code/gamestatemain.pas#L55 . It could be much shorter, but I wanted to show and document a few things.

  • You can browse the example code and read the README on castle-engine/examples/isometric_game at master · castle-engine/castle-engine · GitHub

Yes, just set TCastleImageTransform.Url. It can be any URL – to an image in data, like castle-data:/my_image.png (see Data directory | Manual | Castle Game Engine ) or to any file on disk (if you have a filename, you can convert it to URL using FilenameToURLSafe, see Network, downloading and using URLs | Manual | Castle Game Engine ).

1 Like

Thanks, this example look very useful for me, will study it.

Good day!
Thanks again, now minimap works nice, as i want.
But, when i tried to move map code to another Unit as procedure, i get problem with FreeAtStop:

MapTiles[TileNum] := TCastleImageTransform.Create(FreeAtStop);

(Lazarus says: you don’t declared this)
Is i need some TComponent to use it?

Yes, FreeAtStop is provided by TUiState class (where “Stop” makes sense), and therefore is available in its methods. FreeAtStop is a protected function, so to use it outside you’ll need to either pass it to some child entity, e.g.:

procedure TUiState.Start;
begin
  inherited;
  MyCustomUi := TMyCustomUi.Create(FreeAtStop);
end;

constructor TMyCustomUi.Create(AOwner: TComponent)
begin
  inherited;
  MapTiles[TileNum] := TCastleImageTransform.Create(AOwner);
  //AOwner here is equal to `TUiSTate.FreeAtStop`
end;

or you should assign a different owner, e.g.:

constructor TMyCustomUi.Create(AOwner: TComponent)
begin
  inherited;
  MapTiles[TileNum] := TCastleImageTransform.Create(Self);
  //here  MapTiles[TileNum] will get freed after the instance of TMyCustomUi is freed
end;

Or in the end you can “manually” manage freeing of MapTiles, this is comfortable sometimes, e.g. if you want to “load them once” and forget. You can do it either by:

MapTiles[TileNum] := TCastleImageTransform.Create(Application);

where the corresponding MapTiles[TileNum] will get freed when the application quits or completely manually:

MapTiles[TileNum] := TCastleImageTransform.Create(nil);
...
//And when it's no longer needed call manually:
FreeAndNil(MapTiles[TileNum]);

But in this situation you are “in danger” of forgetting to free the class and get a memory leak. It’s nothing too bad (modern systems will eventually free all the allocated memory anyway), but it’s a bad programming habit + memory leaks often point at breaches in program logic and such “additional” memory leaks will make debugging those meaningful ones much harder.

2 Likes

I think, i started understand, thanks :+1:

1 Like

Hello!
I have new question about TCastleImageTransform.
In this example

we move images with this code:

procedure TStateMain.Update(const SecondsPassed: Single; var HandleInput: Boolean);
var
  PlayerPosition: TVector2;
begin
inherited;
  LabelFps.Caption := 'FPS: ' + Container.Fps.ToString;
  
  { update player position to fall down }
  PlayerPosition := ImagePlayer.AnchorDelta;
  PlayerPosition.Y := Max(PlayerPosition.Y - SecondsPassed * 400, 0);
  ImagePlayer.AnchorDelta := PlayerPosition;
end;

But here TCastleImageControl class be used. Can you suggest a similar method for TCastleImageTransform, please?

TCastleImageTransform is a descendant of TCastleTransform and as such has a Translation property. You can set it to move the object.

See the next chapters, Viewport with scenes, camera, navigation | Manual | Castle Game Engine and Tutorial: Designing a 3D world | Manual | Castle Game Engine , for an overview of viewport and translations :slight_smile: Translations work in 2D the same as in 3D (X goes to right, Y goes up, in standard 2D view).

1 Like

@Noscow Please create new threads for new questions, avoid attaching them to the existing threads. New questions usually imply answers and discussion that are not related to the original thread, so it is better to keep them in separate threads. This helps people answering and people who may visit this forum in the future.

1 Like

Sure, thanks :relieved:

Note: In latest Castle Game Engine, this is more consistent: TCastleUserInterface also has a Translation property (it is a 2D vector). I never liked that weird name AnchorDelta :), it is now only a deprecated alias for Translation.

So TCastleUserInterface.Translation (2D) and TCastleTransform.Translation (3D) are easier to remember. The manual Designing user interface and handling events (press, update) within the state | Manual | Castle Game Engine has been updated.

1 Like