Recent

Author Topic: Persisten and accessible TRect tiles  (Read 7105 times)

TomTom

  • Full Member
  • ***
  • Posts: 170
Persisten and accessible TRect tiles
« on: January 22, 2019, 01:35:27 pm »
I don't know if I'm using good description of my problem but...
I've created program which is generating random tile map using TImage as a tile. In two for loops Im filling canvas of TPanel with TImage. It looks like on attached image. It's ok. But takes quite some time to finish generating, but I have access to each TImage object (I want to create custom TImage with few extra properties). This is what I want.

But then I tried to find a way to speed up map generation. So I wrote another program which draws same thing but I used TRect (other attached image). It's indeed much faster but there this map is not persistent and canvas of TPanel is cleared if I for example send Form to task bar. And also I don't have access to each tile (I need it for example to determin if im able to place other object there). Maybe I'm getting to it from wrong side.
I think I need to draw random map and to store information where each TRect was created (for example X and Y coord) . Save it to image file and then display it. And then I need some kind of control which will check those stored coords if there was TRect and if so disable placing other control there. Am I thinking right? Or is there easier sollution ? 

:)

Handoko

  • Hero Member
  • *****
  • Posts: 5131
  • My goal: build my own game engine using Lazarus
Re: Persisten and accessible TRect tiles
« Reply #1 on: January 22, 2019, 02:58:03 pm »
Are you going to create a game or a map for game?

If yes, you should know there is something called game world. You manipulate your game object in the game world. You save and load data based on the data on the game world.

Here has a detailed tutorial for snake game, it is very long but newbies will learn many basic things for game programming:

- Game loop
- User input detection
- Using array to represent the the game world
- Collision detection
- How to use TList (to replace array)
- Using buffer for keyboard input
- Using buffer to reduce flickering

https://forum.lazarus.freepascal.org/index.php/topic,38136.msg258381.html#msg258381

TomTom

  • Full Member
  • ***
  • Posts: 170
Re: Persisten and accessible TRect tiles
« Reply #2 on: January 22, 2019, 03:13:12 pm »
Thanks Handoko :)
Well I've made simple Idle Clicker game. You have 20$$$ for start, you can buy factory for 10$$$, You can place it on canvas (grid 24x24 or whatever you set). Each factory generates income blah blah blah :) But in my game You can place factory anywhere on canvas. So I thought I could use some map (land and sea) to narrow possibilities of placing factories ;) (I even created little program that checks if a tile has neighbour and from which side to determine texture of that tile). Thats whole story. So yes, in overall Im writing a game (see attached image).  I used TTimer which calculates income and does other stuff so I guess this is my Game Loop :)

I will definitely look in to Your Snake game :)
Thanks again :)

Handoko

  • Hero Member
  • *****
  • Posts: 5131
  • My goal: build my own game engine using Lazarus
Re: Persisten and accessible TRect tiles
« Reply #3 on: January 22, 2019, 05:03:06 pm »
Wow, your game sound interesting.
Once you finish it, please send a demo here, so we can enjoy it.

Some suggestions for you:
  • Don't use TImage, but draw the objects directly on the canvas of the TPanel or TForm. It will improve the performance.
  • You may need 2 worlds: game world and terrain world.
  • Don't save the world data as images, but save the data of the world's arrays to the file as a binary or text files. Then you write some procedures to do the loading and painting: LoadTerrainFile, LoadGameWorldFile, DrawGameWorld, etc.

TomTom

  • Full Member
  • ***
  • Posts: 170
Re: Persisten and accessible TRect tiles
« Reply #4 on: January 22, 2019, 08:51:20 pm »
Thank You for kind words :)
I need to learn this stuff and then sort things out to find best approach. I used array (as array of xxx) for the first time 2 days ago :). I'm also going to read Your tutorial.
I think I know what to do know (thanks to Your suggestions) but I need to acctually make project to test it (oh. man I'm loving this :P)

* declare array for world (World)
* generate random terrain (Land/Water)
* store information about generated terrain - thats coords of blocks (for now it will be only land as soil/grass whatever, maybe with random variant of image, and empty=sea)
* declare array for WorldObjects //like factories etc. Same dimensions as World array
* get info where player had clicked on map and transfer/translate this info to hm.. this will be complicated  :)
    - If player is in building mode (i.e. placing factory/budynek) then World array will be checked but WorldObjects also needs to be checked if place is free (no building is there)
    - If player is in destruction mode (i.e. destroying factory) then WorldObjects array will be checked
    - If player is in normal mode (i.e. upgrading factory or sth) then  WorldObjects array will be checked
 

Or maybe I could use one array to store terrain and update it while game is in progress.
   - IF tile is land THEN  check
            IF its occupied (two states of World array element.. Good to be occupied and occupied by sth (this will need to be few other states e.g. 'Occupied by Shoe Factory', 'Occupied by Donut Factory' etc.) THEN show message that selected building can't be placed
             IF place is NOT occupied THEN place selected building and change state of World array
 Save World array and render it on canvas.
I could also add option for player mode here.. that would be another IF statement (Player mode=[pmBuilding,pmDestroying, pmUpgrading etc] Hm :) Not sure what would work but I'll try :)

And ofcourse game will be free as I'm doing it just for fun and to learn programming :)

furious programming

  • Hero Member
  • *****
  • Posts: 853
Re: Persisten and accessible TRect tiles
« Reply #5 on: January 22, 2019, 09:46:29 pm »
@TomTom — for image generation to be efficient, you should use additional buffering.

Declare an additional 24-bit bitmap (as back buffer) and paint everything you need on it. To visualize the results, use the TPaintBox control — in the OnPaint event, simply paint the entire contents of the bitmap (or its fragment, if required) on the component canvas.

To store the list of coordinates, use the generic list instead of the regular array. These are more convenient to use and efficient enough.
Lazarus 3.2 with FPC 3.2.2, Windows 10 — all 64-bit

Working solo on an acrade, action/adventure game in retro style (pixelart), programming the engine and shell from scratch, using Free Pascal and SDL. Release planned in 2026.

TomTom

  • Full Member
  • ***
  • Posts: 170
Re: Persisten and accessible TRect tiles
« Reply #6 on: January 23, 2019, 08:51:30 am »
I would like to add TileStatus variable/property to TRect. If this is possible, how can I do that? This could be used to store information if this tile is occupied by sth.

Code: [Select]

TTile = class(TRect)
    private
      FTileStatus: String;
    public
       property TileStatus: string read FTileStatus write FTileStatus;
end;

Pascal

  • Hero Member
  • *****
  • Posts: 932
Re: Persisten and accessible TRect tiles
« Reply #7 on: January 23, 2019, 09:35:23 am »
Nearly. As TRect is a record and not a class you could add it to your TTile class:

Code: Pascal  [Select][+][-]
  1. TTile = class
  2.     private
  3.       FRect: TRect;
  4.       FTileStatus: String;
  5.     public
  6.        property TileStatus: string read FTileStatus write FTileStatus;
  7.        property Rect: TRect read FRect;
  8. end;
laz trunk x64 - fpc trunk i386 (cross x64) - Windows 10 Pro x64 (21H2)

howardpc

  • Hero Member
  • *****
  • Posts: 4144
Re: Persisten and accessible TRect tiles
« Reply #8 on: January 23, 2019, 09:46:16 am »
Or you could declare a record helper.
Helpers are not permitted to have data fields, so you would have to use a work-around something like this
Code: Pascal  [Select][+][-]
  1. {$ModeSwitch advancedrecords}
  2.  
  3. interface
  4.  
  5. uses
  6.   Types;
  7.  
  8. type
  9.  
  10.   TRectTileHelper = record helper for TRect
  11.   private
  12.     function GetTileStatus: String;
  13.     procedure SetTileStatus(aValue: String);
  14.   public
  15.     property TileStatus: String read GetTileStatus write SetTileStatus;
  16.   end;
  17.  
  18. implementation
  19.  
  20. var
  21.   FTileStatus: String = '';
  22.  
  23. function TRectTileHelper.GetTileStatus: String;
  24. begin
  25.   Exit(FTileStatus);
  26. end;
  27.  
  28. procedure TRectTileHelper.SetTileStatus(aValue: String);
  29. begin
  30.   FTileStatus := aValue;
  31. end;


TomTom

  • Full Member
  • ***
  • Posts: 170
Re: Persisten and accessible TRect tiles
« Reply #9 on: January 23, 2019, 03:16:28 pm »
@Pascal
So I'm guessing that if I need to set the Trect for my Tile I need to add 
Code: [Select]
       property Rect: TRect read FRect write FRect; But Then I get error with this
Code: [Select]
...
var
  Form1: TForm1;
  World: array[1..10,1..10] of TTile;
  curRect: TRect;
implementation

{$R *.lfm}

{ TForm1 }

procedure TForm1.Button1Click(Sender: TObject);
begin
 curRect:=TRect.Create(0,0,10,10);

 World[1,1].Rect:=curRect;
 world[1,1].TileStatus:='Occupied';
 Form1.Canvas.Rectangle(world[1,1].Rect);
 Edit1.Text:=world[1,1].TileStatus;

Nearly. As TRect is a record and not a class you could add it to your TTile class:

Code: Pascal  [Select][+][-]
  1. TTile = class
  2.     private
  3.       FRect: TRect;
  4.       FTileStatus: String;
  5.     public
  6.        property TileStatus: string read FTileStatus write FTileStatus;
  7.        property Rect: TRect read FRect;
  8. end;

howardpc

  • Hero Member
  • *****
  • Posts: 4144
Re: Persisten and accessible TRect tiles
« Reply #10 on: January 23, 2019, 06:32:19 pm »
You have to instantiate your TTile class, and make sure it is destroyed after use.

Code: Pascal  [Select][+][-]
  1.   TTile = class
  2.   private
  3.     FRect: TRect;
  4.     FTileStatus: String;
  5.     procedure SetRect(aValue: TRect);
  6.   public
  7.     property Rect: TRect read FRect write SetRect;
  8.     property TileStatus: string read FTileStatus write FTileStatus;
  9.   end;
  10.  
  11. var
  12.   Form1: TForm1;
  13.   World: array[1..10,1..10] of TTile;
  14.   curRect: TRect;
  15.  
  16. implementation
  17.  
  18. procedure TTile.SetRect(aValue: TRect);
  19. begin
  20.    if FRect <> aValue then
  21.     FRect := aValue;
  22. end;
  23.  
  24. procedure TForm1.Button1Click(Sender: TObject);
  25. begin
  26.   curRect := TRect.Create(0,0,10,10);
  27.   World[1,1] := TTile.Create;
  28.   try
  29.    World[1,1].Rect := curRect;
  30.    World[1,1].TileStatus := 'Occupied';
  31.    Form1.Canvas.Rectangle(World[1,1].Rect);
  32.    Edit1.Text := World[1,1].TileStatus;
  33.  finally
  34.    World[1,1].Free;
  35.  end;
  36. end;
                   

Handoko

  • Hero Member
  • *****
  • Posts: 5131
  • My goal: build my own game engine using Lazarus
Re: Persisten and accessible TRect tiles
« Reply #11 on: January 23, 2019, 06:47:53 pm »
It's okay to create a new class for the tile but I personally prefer not to do it, for only that simple thing. OOP is great but overuse will only make the code harder to maintain, unless you have already planned it properly.

Here I wrote a simple demo showing how to load and draw map:

Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls;
  9.  
  10. const
  11.   MapWidth   = 6;
  12.   MapHeight  = 5;
  13.   MapSize    = 64;
  14.   MapX       = 150;
  15.   MapY       = 40;
  16.   ImageCount = 4;
  17. type
  18.  
  19.   { TForm1 }
  20.  
  21.   TForm1 = class(TForm)
  22.     btnLoad: TButton;
  23.     procedure btnLoadClick(Sender: TObject);
  24.     procedure FormCreate(Sender: TObject);
  25.     procedure FormDestroy(Sender: TObject);
  26.   private
  27.     FGameWorld : array[1..MapWidth, 1..MapHeight] of Byte; // <--- for later use
  28.     FMapWorld  : array[1..MapWidth, 1..MapHeight] of Byte;
  29.     FImageData : array of TBitmap;
  30.     procedure LoadImageData;
  31.     procedure LoadMap(const MapName: string);
  32.     procedure DrawMap;
  33.   end;
  34.  
  35. var
  36.   Form1: TForm1;
  37.  
  38. implementation
  39.  
  40. {$R *.lfm}
  41.  
  42. { TForm1 }
  43.  
  44. procedure TForm1.btnLoadClick(Sender: TObject);
  45. begin
  46.   LoadImageData;
  47.   LoadMap('Beach.map');
  48.   DrawMap;
  49. end;
  50.  
  51. procedure TForm1.FormCreate(Sender: TObject);
  52. begin
  53.   SetLength(FImageData, 0);
  54. end;
  55.  
  56. procedure TForm1.FormDestroy(Sender: TObject);
  57. var
  58.   i: Integer;
  59. begin
  60.   for i := 0 to Length(FImageData)-1 do
  61.     FImageData[i].Free;
  62.   SetLength(FImageData, 0);
  63. end;
  64.  
  65. procedure TForm1.LoadImageData;
  66. var
  67.   S: string;
  68.   i: Integer;
  69. begin
  70.   SetLength(FImageData, ImageCount);
  71.   for i := 0 to ImageCount-1 do
  72.   begin
  73.     S := 'terrain0'+(i+1).ToString+'.bmp';
  74.     if not(FileExists(S)) then
  75.     begin
  76.       ShowMessage('File data missing!');
  77.       FormDestroy(nil);
  78.       Halt;
  79.     end;
  80.     FImageData[i] := TBitmap.Create;
  81.     FImageData[i].LoadFromFile(S);
  82.   end;
  83. end;
  84.  
  85. procedure TForm1.LoadMap(const MapName: string);
  86. var
  87.   MapFile : TextFile;
  88.   S       : string;
  89.   X, Y    : Integer;
  90. begin
  91.   if not(FileExists(MapName)) then
  92.   begin
  93.     ShowMessage('Map file ' + MapName + ' not found.');
  94.     FormDestroy(nil);
  95.     Halt;
  96.   end;
  97.   AssignFile(MapFile, MapName);
  98.   Reset(MapFile);
  99.   for Y := 1 to MapHeight do
  100.   begin
  101.     ReadLn(MapFile, S);
  102.     for X := 1 to MapWidth do
  103.       FMapWorld[X, Y] := Ord(S[X])-Ord('0');
  104.   end;
  105.   CloseFile(MapFile);
  106. end;
  107.  
  108. procedure TForm1.DrawMap;
  109. var
  110.   SRect   : TRect;
  111.   DRect   : TRect;
  112.   Terrain : Byte;
  113.   X, Y    : Integer;
  114. begin
  115.   for Y := 1 to MapHeight do
  116.     for X := 1 to MapWidth do
  117.     begin
  118.       SRect := Rect(0, 0, MapSize, MapSize);
  119.       with DRect do
  120.       begin
  121.         Left   := MapX + (X-1)*MapSize;
  122.         Top    := MapY + (Y-1)*MapSize;
  123.         Width  := MapSize;
  124.         Height := MapSize;
  125.       end;
  126.       Terrain := FMapWorld[X, Y];
  127.       case Terrain of
  128.         0..3: Canvas.CopyRect(DRect, FImageData[Terrain].Canvas, SRect);
  129.         else begin
  130.           ShowMessage('Map data error!.');
  131.           FormDestroy(nil);
  132.           Halt;
  133.         end;
  134.       end;
  135.     end;
  136. end;
  137.  
  138. end.

The code is quickly written, many improvements can be added:
  • Line #11, #12 - The world size is hardcoded, it should be dependable on the map data.
  • Line #28 - The map uses byte data, it should be enumerated type.
  • Line #27, #28 - I used names: GameWorld, MapWorld. More meaningful: ObjectLayer and TerrainLayer.
  • Line #73 - The file name is hardcoded, don't do it.
  • Line #85..#106 - LoadMap does not have data validation.

The code does not draw objects (building, tree, car, animal, etc). Those things should be put in the GameWorld. It becomes more complicated if you want allow multiple objects to occupy the same location. For example: in front of a building, there has a duck. It's become extremely difficult to do selection (using mouse) if the location contains multiple objects.

You need to has a file that explains the rules of allowed objects on certain terrain. For example you can put a boat on the sea, you can put a tree on a land. If you do not want to use a file, you can write a function to check the rules.

There still many things needed to be solved if you want to finish the game ...
Have fun!

TomTom

  • Full Member
  • ***
  • Posts: 170
Re: Persisten and accessible TRect tiles
« Reply #12 on: January 23, 2019, 08:50:05 pm »
[EDIT]
I've prepared simple world generator using advices  that You guys gave me :) It works :D (attached picture)
I can add properties to Tiles as needed :)
Now I need to figure out how to translate mouse position and check if its over particular tile and if then show info of that tile :)

----------------------------



Thanks howardpc and Handoko :)
Handoko I'm planning my game to be simple.
Basics:

1. Generate random map (without fancy algorithm, just random tiles)
2. Player have starting budget (e.g. 20Pascals)
3. Player can choose from diffrent buildings (with different prices,generating different income) to place on generated tiles. Buildings are generating income over time. 
   If tile is occupied by previously placed building and player have money ask player if he wants to destroy old building to place new one.
   If tile is free and player have money place selected type of building
   If tile is free but player has no money tell him that he need to wait for more money
   If tile is occupied by building and player is in Demolish Mode destroy building
4. If player have enough money unlock appropriate level (this means bigger income multiplier, and new buildings he can buy)
5. Goal is to build ULTRA GIGANTIC MEGA Factory
   *optional feature : to build factory first build some Power Plant and then place this factory next to it.

 
« Last Edit: January 24, 2019, 09:10:49 am by TomTom »

Handoko

  • Hero Member
  • *****
  • Posts: 5131
  • My goal: build my own game engine using Lazarus
Re: Persisten and accessible TRect tiles
« Reply #13 on: January 24, 2019, 09:45:56 am »
Now I need to figure out how to translate mouse position and check if its over particular tile and if then show info of that tile :)

No need, because I already wrote an example demo for you. Almost finish, be patient.
 ;D

TomTom

  • Full Member
  • ***
  • Posts: 170
Re: Persisten and accessible TRect tiles
« Reply #14 on: January 24, 2019, 11:23:24 am »
Did it  :D
It may show wrong coords in memo on the right but I already fixed it :)
« Last Edit: January 24, 2019, 11:37:33 am by TomTom »

 

TinyPortal © 2005-2018