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
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.
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
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 The 3 images types (and drawing methods) that we recommend are:
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.)
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
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;
First of all, while writing the answer to this, I realized I want to have a CGE example that shows this properly 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.
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.
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:
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:
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.
@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.
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.