I'm new to using C++ Builder so apologies if I'm making any rudimentary mistakes.
I have drawn out a TLayout named 'Collection' with a 5x5 grid of TRectangles within it. The cells are named like so "CellXY".
I presumed it might be easier to draw these out on the Form rather than instantiating them with code, now I'm thinking otherwise, but I would still like to solve the problem this way to better my understanding.
I'm trying to write a method which will return a pointer to a TRectangle whose name contains the coordinates passed to the method.
Currently, I'm trying to do this by iterating through the children of the TLayout Collection:
TRectangle* __fastcall TForm2::getCellRectangleFromCoordinate(int X, int Y){
TRectangle* targetCell = NULL;
char targetCellName[6];
sprintf(targetCellName, "Cell%i%i", X, Y);
for (int cIndex = 0; cIndex < Collection->ChildrenCount; ++cIndex)
{
TRectangle* cellRect = (TRectangle*) Collection->Children[cIndex]; // Error Here
string cellName = cellRect->Name;
if (targetCellName == cellName) {
targetCell = cellRect;
break;
}
}
return targetCell;
}
But I am given an error reading:
E2031 Cannot cast from 'TFmxChildrenList' to 'TRectangle *'
If anyone could help, I'd be very grateful!
The Children property is a pointer to a class type (TFmxChildrenList) that internally holds an array of objects. Children is not the actual array itself, like you are trying to treat it as.
Children[cIndex] is using pointer arithmetic, which is not what you want in this situation. You need to use the Children->Items[] sub-property instead, by changing this statement:
Collection->Children[cIndex]
To either this:
Collection->Children->Items[cIndex]
Or this:
(*(Collection->Children))[cIndex]
Related
I have a 2048-type game where the gameboard grid is made up of a list of 4 other lists (each of which contain 4 Tiles).
Each time a move is made, the newGameboardGrid is saved in yet another list (so that removeLast can be called when a player wants to undo a move).
When a player swipes, I need to compare the newGameboardGrid grid with the previous one to see if any actual movement took place or if the tile values are still the same (as would happen if a player swiped in a direction where no movement was possible).
I tried this code:
if (newGameboardGrid == listOfGameboardGrids.last) {
// do something
}
It almost works in that it is comparing the <List<List< Tile>> from the new move with the <List<List< Tile>> of the last move, but the problem is that it never results in the two <List<List< Tile>> as being equal, even when all the tile values are identical. I believe it's because it is comparing hashcodes and/or other things.
The Tile class has lots of stuff in it, but the thing I would like to compare is that int named "value". Is it possible to compare only the "value" variable for these two <List<List< Tile>>?
Thanks in advance for any help! (And apologies if any of my terms are imprecise.)
Dart list implementations do not override the operator== of Object, so they are only ever equal to themselves. The elements are not checked.
IF you want to compare the elements (and here, the elements of the list elements), you can use the Equality classes from package:collection:
const boardEquality = ListEquality(ListEquality(DefaultEquality()));
This creates a ListEquality object which compares two lists of lists of elements by checking that they contain the same number of lists, which again contains the same number of equal elements. (I assume that Tile implements operator==).
You can the use it as: if (boardEquality.equals(newGameboardGrid, listOfGameboardGrids.last)) { ... }.
You could do an extension method on your particular list type and make an isEqual method for that compares each Tile:
extension TileListComp on List<List<Tile>> {
bool isEqual(List<List<Tile>> other) {
if(this.length != other.length || this[0]?.length != other[0]?.length) {
return false;
}
for(int x = 0; x < this.length; x++) {
for(int y = 0; y < this[0].length; y++) {
if(this[x][y] != other[x][y]) {
return false;
}
}
}
return true;
}
}
If you have not implemented any kind of comparison for your Tile class, you will need to do so. I can advise if necessary.
I have this function in my code to load and setup sizes for sprites.
function aux.Sprite:setTexture(renderer,imgPath)
... -- Not important for this question
img = loadImage(renderer,imgPath)
self.texture = img.texture
self.rect.w = img.w
self.rect.h = img.h
end
(loadImage here is the function implemented in C, and is returning the correct values)
Using it should be easy enough
bg = aux.Sprite:new()
bg:setTexture(R, "testfiles/bg.png")
ship = aux.Sprite:new()
ship:setTexture(R, "testfiles/testship.png")
The problem is that after the second call for setTexture the values for the FIRST sprite is changed!
for example
bg = aux.Sprite:new()
bg:setTexture(R, "testfiles/bg.png")
print(bg.rect.w)
ship = aux.Sprite:new()
ship:setTexture(R, "testfiles/testship.png")
print(bg.rect.w)
should return
1920 1920
because I'm printing the width for bg twice
but I'm getting
1920
300
That is, the second setTexture changes the value for "bg" and not only for "ship".
My guess is that self.rect.w = img.w is setting a "pointer", or whatever is called in lua, to img.w and when I use the function later this pointer is updated in all references?
What I'm doing wrong here? Is this the correct lua behavior?
PS: The definition of the Sprite:new function as asked
function aux.Sprite:new(o)
o = o or {}
setmetatable(o, self)
self.__index = self
return o
end
There is nothing in the provided code that actually creates the rect (or aux.Sprite for that matter). I guess this means that is done via something like
aux.Sprite = { rect = {} }
This is a problem because it means all your sprites share the same rect.
aux.Sprite:new() returns a new empty table that has the metatable and its __index set to Sprite. Thus when in setTexture self.rect is searched in this empty table the one from Sprite is returned via __index.
You need to make sure every sprite has its own unique rect.
I don't really know what is the typical Lua object pattern here, but you can eg. have self.rect = { w = img.w, h = img.h } in setTexture or maybe o.rect = {} in new - something that actually sets rect to a new table for this particular sprite.
I have two rectangles, one that is occasionally reset to some other rectangle. In C++ I'd just do:
_rect = _resetRect;
But in Dart that actually means that _rect now refers to the same object as _resetRect which is not what I want.
My current solution is this:
_rect.left = _resetRect.left;
_rect.width = _resetRect.width;
_rect.top = _resetRect.top;
_rect.height = _resetRect.height;
This is idiotic. Other questions suggest that there is no built-in way to copy objects (like there is in C++), and you have to rely on the object providing a clone() method. But Rectangle doesn't have one so what do I do?
Also even if it did have a clone() method, wouldn't that allocate an entirely new Rectangle rather than just setting the fields of the existing one (like C++'s operator=), and therefore be less efficient?
C++ also does not have a way to deep-copy an object which contains pointers/references to other objects. In Dart, all values are references, so that restriction applies to all objects.
I assume this is a MutableRectangle since the Rectange in dart:math is unmodifiable.
That class indeed does not have a way to clone the values of another rectangle, so you have to copy each of them. I would use a cascade for that:
_rect
..left = _resetRect.left
..top = _resetRect.top
..width = _resetRect.width
..height = _resetRect.height;
Alternatively, if it happens often enough, you can create a helper function:
void copyRectangle(MutableRectangle target, Rectangle source) {
target
..left = source.left
..top = source.top
..width = source.width
..height = source.height;
}
Using Tiled I generated a Lua file which contains a table. So I figured that I'd write a for loop which cycles through the table gets the tile id and checks if collision is true and add collision if it was. But, I've been unable to get the tile id's or check they're properties. But it returned a error saying that I tried to index nil value tileData.
Here is the Map file
return {
version = "1.1",
luaversion = "5.1",
-- more misc. data
tilesets = {
{
name = "Tileset1",
firstgid = 1,
tilewidth = 16,
tileheight = 16,
tiles = {
{
id = 0,
properties = {
["Collision"] = false
}
},
}
}
layers = {
{
type = "tilelayer",
name = "Tile Layer 1"
data = {
-- array of tile id's
}
}
}
}
And here is the for loop I wrote to cycle through the table
require("Protyping")
local map = love.filesystem.load("Protyping.lua")()
local tileset1 = map.tilesets
local tileData = tileset1.tiles
local colision_layer = map.layers[1].data
for y=1,16 do
for x=1,16 do
if tileData[colision_layer[x*y]].properties["Colision"] == true then
world:add("collider "..x*y,x*map.tilewidth, y*tileheight,tilewidth,tileheight)
end
end
end
Try this:
tileset1 = map.tilesets[1]
instead of
tileset1 = map.tilesets
lhf's answer (map.tilesets[1] instead of map.tilesets) fixes the error you were getting, but there are at least two other things you'll need to fix for your code to work.
The first is consistent spelling: you have a Collision property in your map data and a Colision check in your code.
The second thing you'll need to fix is the way that the individual tiles are being referenced. Tiled's layer data is made of 2-dimensional tile data laid out in a 1-dimensional array from left-to-right, starting at the top, so the index numbers look like this:
You would think you could just do x * y to get the index, but if you look closely, you'll see that this doesn't work. Instead, you have to do x + (y - 1) * width.
Or if you use zero-based x and y, it looks like this:
Personally, I prefer 0-based x and y (but as I get more comfortable with Lua, that may change, as Lua has 1-based arrays). If you do go with 0-based x and y, then the formula is x + 1 + y * width.
I happen to have just written a tutorial this morning that goes over the Tiled format and has some helper functions that do exactly this (using the 0-based formula). You may find it helpful: https://github.com/prust/sti-pg-example.
The tutorial uses Simple Tiled Implementation, which is a very nice library for working with Tiled lua files. Since you're trying to do collision, I should mention that STI has a plugins for both the bump collision library and the box2d (physics) collision library.
Specific question
How to create an array of buttons on Borland C++ Builder and work with it?
I'm using Borland C++ Builder 6 and Borland Developer Studio 2006 (Turbo C++ 2006).
Purpose
To work with a lot of buttons on a form just using a for loop with an index, for example, changing their caption, size and position.
I know if I have a button called Button1 and inside a click event of this button if I create another button (through TButton *Button2 = new TButton(Form1)), I can assign Button1 to Button2 (Button2 = Button1) and them I can simply modify caption of Button1 with Button2->Caption. So I would like to extend it assigning pointers of real components to elements of an array to them work with all of them with a for loop.
Well, if someone found an way to add all buttons as an array on a form, it's better :)
Tries
Following tests were made putting respective code on TForm1::Button1Click(), an event of a button on a form:
Test 1
Description: Creating an array directly
Code:
TButton Buttons[3];
Result: Compile error:
> [C++ Error] Unit1.cpp(23): E2248 Cannot find default constructor
> to initialize array element of type 'TButton'
Comments:
I tested some variants of this test (e.g. TButton Buttons = new TButton[3], working with calloc function and others), but all of them points to the issue that TButton does not have a constructor without arguments, i.e., TButton(), but only TButton (TComponent *AOwner), TButton(void *ParentWindow) and TButton(const TButton &);
Any way to use operator new with arguments for TButton constructor prototypes, for an array?
Test 2
Description: Creating a vector
Code: Also add #include "vector.h" on unit header...
vector<TButton> Buttons;
Buttons[0].Caption="it is ok";
Buttons[1].Caption="mayday, mayday";
Result: Debugger exception on 3rd line:
> Project Project1.exe raised exception class EAccessViolation
> with message 'Acceess violation at address 401075B9 in module
> 'vcl60.bpl'. Read of address 00000254'. Proccess stopped. Use
> Step or Run to continue.
Comments:
Yeah, I expected that it would be raised, but I put it here to someone say how to allocate memory for more elements on that vector after created, since vector<TButton> Buttons(3); does not work for the same reason test1 failed :(
General question
How to do it for any visual component?
All of your attempts failed for the same reason - you are trying to create an array/vector of actual TButton object instances instead of an array/vector of pointers to TButton instances.
To create a fixed-length array of button pointers:
TButton* Buttons[3];
...
Buttons[0] = Button1;
Buttons[1] = Button2;
Buttons[2] = Button3;
...
for(index = 0; index < 3; ++index)
{
TButton *Btn = Buttons[index];
// use Btn as needed...
}
To create a dynamic-length array of button pointers:
TButton** Buttons;
...
Buttons = new TButton*[3];
Buttons[0] = Button1;
Buttons[1] = Button2;
Buttons[2] = Button3;
...
for(index = 0; index < 3; ++index)
{
TButton *Btn = Buttons[index];
// use Btn as needed...
}
...
delete[] Buttons;
To create a vector of button pointers:
std::vector<TButton*> Buttons;
...
Buttons.push_back(Button1);
Buttons.push_back(Button2);
Buttons.push_back(Button3);
...
for(index = 0; index < 3; ++index)
{
TButton *Btn = Buttons[index];
// use Btn as needed...
}
/*
Or:
for(std::vector<TButton*>::iterator iter = Buttons.begin(); iter != Buttons.end(); ++iter)
{
TButton *Btn = *iter;
// use Btn as needed...
}
*/
All this is very nice and correctly true. But I mean the user’s question was some other intention. If for all the buttons we get each of them index does no special benefit - this is only a true method: the aim is to control all components with a click (button, panels, shapes and so on…) and don’t write for each index a new code,
That why I changed a few program’s code:
void __fastcall TForm1::Button1Click(TObject *Sender)
{
typedef TPanel* TPanels;
TPanels Panels[3] = {Panel1, Panel2, Panel3};
int count;
for(count=0;count<3;count++)
Panels[count]->Left=random(100);
}
As one can see instead of index here is count. Certainly don’t forget insert randomize() to TForm1
Miraculous Typedef + Pseudo Array = Solution
Miraculous Typedef:
After hours searching a way, I saw a typedef on that Stack Overflow and Google search journey and thought why not to:
typedef TButton* TButtons;
Well, it changes all the things, because I could perform:
TButtons Buttons[3];
Pseudo Array:
The issue remained on how to allocate memory for data stored on that Buttons[3] array, but with knowledge of 2nd paragraph of Purpose section of my question, I thought: forget new data, data is there, point to there (so I call that to build a pseudo array, because I create only an array of pointers to existing data):
TButtons Buttons[3] = {Button1, Button2, Button3};
Where Button1, Button2 and Button3 were already created when I put them on the form normally (through my mouse).
Working example
Create a new vcl/forms application project;
Put 3 buttons as those on the left on figure bellow (Button1, Button2, Button3) to demonstrate that solution, and 1 great button (Button4) also as figure bellow to do the actions;
Insert following code on click event of the fourth button, the great one (Button4);
typedef TButton* TButtons;
TButtons Buttons[3] = {Button1, Button2, Button3};
int index;
for(index=0;index<3;index++)
{
Buttons[index]->Caption=(AnsiString)"teste "+index+" - "+(1+random(100));
Buttons[index]->Left=25+4*random(100);
Buttons[index]->Top=25+4*random(100);
}
Perform a "shazam!" run and play with that...