I'm attempting to create a entity manager for a game I'm making so I can easily manage all the entities within the game (Player, Enemies, Objects) and be able to call their Update and Render functions without having to do it manually.
I'm recording the entities using a map that takes in a int and a Entity class
std::map<int, Entity*> entities;
Whenever the Entities are added to the map, they'll also be given a random ID with that ID being added to a vector just so I can record what ID's have been used so I don't end up using the exact same entity
IDS = std::vector<int>();
void EntityManager::AddEntity(Entity* entity)
{
int ID = rand();
for(int i = 0; i < IDS.size(); i++)
{
if(IDS[i] == ID)
{
ID = rand();
i = 0;
}
}
entities.insert(pair<int, Entity*>(ID, entity));
}
I then finally call the Update and Render functions of each entity
void EntityManager::Update(float deltaTime)
{
if(!entities.empty())
{
for(int i = 0; i < entities.size(); i++)
{
if(entities[i] != nullptr)
{
entities[i]->Update(deltaTime);
}
}
}
}
void EntityManager::Render()
{
if(!entities.empty())
{
for(int i = 0; i < entities.size(); i++)
{
if(entities[i] != nullptr)
{
entities[i]->Render();
}
}
}
}
The problem I'm having is the frame rate will instantly drop to something like 2FPS as it seems when I'm checking the size of the map within the Update and Render functions, the size is something like 16587 instead of what it should be which is just 2 as at this point I'm only inputting 2 entities into the map.
I've tried to figure out what the cause is but I just can't find it, the size is 2 after the "entities.insert" in the AddEntity function but once the Update and Render functions are called, the size instantly goes to 16587 or some number like that. I'm not calling AddEntity anywhere else except for in a constructor within another class:
entityManager->AddEntity(player);
entityManager->AddEntity(diamond);
entities is a map, and you're using random integers as the key. You cannot use an index loop to access the members of the map. Every iteration of that loop will add another entry in the map (with a nullptr Entity), increasing the size causing your loop to continue on.
To iterate thru the map, you can use
for (const auto &ent: entities)
ent.second->Render();
then you'd only see the entries in the map and not add any new ones.
Related
This is a continuation of my previous question: Nested vector<float> and reference manipulation.
I got the loops and all working, but I'm trying to add new instances of arrays to a total vector.
Here's one example of what I mean:
array<float, 3> monster1 = { 10.5, 8.5, 1.0 };
// ...
vector<array<float, 3>*> pinkys = { &monster1};
// ...
void duplicateGhosts() {
int count = 0;
int i = pinkys.size(); // this line and previous avoid overflow
array<float, 3>& temp = monster1; // this gets the same data, but right now it's just a reference
for (auto monster : pinkys) { // for each array of floats in the pinkys vector,
if (count >= i) // if in this instance of duplicateGhosts they've all been pushed back,
break;
pinkys.push_back(&temp); // this is where I want to push_back a new instance of an array
count++;
}
}
With the current code, instead of creating a new monster, it is adding a reference to the original monster1 and therefore affecting its behavior.
As mentioned in a comment you cannot insert elements to a container you are iterating with a range based for loop. That is because the range based for loop stops when it reaches pinkys.end() but that iterator gets invalidated once you call pinkys.push_back(). It is not clear why you are iterating pinkys in the first place. You aren't using monster (a copy of the elements in the vector) in the loop body.
The whole purpose of the loop seems to be to have as many iterations as there are already elements in the container. For that you need not iterate elements of pinkys but you can do:
auto old_size = pinkys.size();
for (size_t i=0; i < old_size; ++i) {
// add elements
}
Further, it is not clear why you are using a vector of pointers. Somebody has to own the monsters in the vector. If it isnt anybody else, it is the vector. And in that case you should use a std::vector<monster>. For shared ownership you should use std::shared_ptr. Never use owning raw pointers!
Don't use a plain array for something that you can give a better name:
struct monster {
float hitpoints; // or whatever it actually is.
float attack; // notice how this is much clearer
float defense; // than using an array?
};
With those modifications the method could look like this:
void duplicateGhosts() {
auto old_size = pinkys.size();
for (size_t i=0; i < old_size; ++i) {
pinkys.push_back( pinkys[i] );
}
}
From the name of the method I assumed you want to duplciate the vectors elements. If you want to just add the same monster as many times as there were elements before, that is
void duplicateGhosts() {
auto old_size = pinkys.size();
for (size_t i=0; i < old_size; ++i) {
pinkys.push_back( monster{} );
}
}
I'm using the std::map data structure in C++ and I'm trying to increment the value at a certain position each time.
If I understand the map has a key and a value associated with that specific key.
So I'm iterating over an array that has unique integers stored inside him.
What I was trying to do is that as I was iterating over the array, pass the value stored in that specific index of the array as a key to my map.
For example:
std::map<int, int> my_map;
for(int i = 0; i < array.size(); ++i)
{
my_map.insert(array[i], ...); // the ... part is supposed to be the increment
}
I was thinking to pass ++my_map[array[i]]. I haven't tried that cause I currently don't have access to my Laptop. It's just an idea I just came up with while I'm not home and I wanted to ask to be sure.
Also if my_map[array[i]] is valid I would like to use it as an if statement inside my loop:
std::map<int, int> my_map;
for(int i = 0; i < array.size(); ++i)
{
// set the initial value to 0 if the element doesn't exist in the map
// else increment the previous value by one
if(!my_map.find(array[i]))
{
my_map.insert(array[i], 0);
}
else
{
my_map.insert(array[i], ++my_map[array[i]]);
}
}
If I have things wrong in my head please correct me. I hope that I translated my problem good enough for you to understand it. Thank you all!
Edit:
As I said in the comments the correct code is:
for(int i = 0; i < array.size(); ++i)
{
// set the initial value to 0 if the element doesn't exist in the map
// else increment the previous value by one
if(!(my_map.count(array[i])))
{
my_map.insert(std::pair<int,int>(array[i], 0));
}
else
{
my_map.insert(std::pair<int,int>(array[i], ++my_map[array[i]]));
}
}
Again thank you all!!!
if(my_map.find(array[i])==my_map.end()){//YOU CAN ALSO USE stl function count
my_map.insert(make_pair(array[i], 0));
}else{
my_map.insert(make_pair(array[i], ++my_map[array[i]]));
}
OR:
if(my_map.find(array[i])==my_map.end()){
my_map[array[i]]=0;
}else{
my_map[arr[i]]++;
}
I am making a system in Unreal Engine to spawn items my character can pick up. I have a single Item C++ class with this in the header file :
UPROPERTY(EditAnywhere)
bool Inventoriable;
UPROPERTY(EditAnywhere)
float Power;
UPROPERTY(EditAnywhere)
int Spawn_Weight;
In my Game Mode header, I have an array meant to hold all possible items
UPROPERTY(EditAnywhere)
TArray<TSubclassOf<AItem>> PlayerRecharge;
And in the cpp file, i have a SpawnPlayerRecharge() function that looks like this:
for (int i = 0; i < NUM_ITEMS; i++) {
Item_Spawned = Cast<AItem>(PlayerRecharge[i]);
if (Item_Spawned) {
Total_Spawn_Weights += Item_Spawned->Spawn_Weight;
}
}
// Choose random number for spawn selection
int Rand = FMath::RandRange(0, Total_Spawn_Weights);
// Iterates through spawnable items and tries to cast as Item object
// Weight variable stores the summed weights to compare to the random number
//if Weight > the random number, store the index in Weight and break from loop
int Weight = 0;
for (int i = 0; i < NUM_ITEMS; i++) {
Item_Spawned = Cast<AItem>(PlayerRecharge[Weight]);
if (Item_Spawned) {
Weight += Item_Spawned->Spawn_Weight;
if (Weight > Rand) {
Weight = i;
break;
}
}
}
FActorSpawnParameters Params;
Params.Owner = this;
// Spawn AItem actor from the array based on index found above
GetWorld()->SpawnActor<AItem>(PlayerRecharge[Weight], SpawnPosition, SpawnRotation, Params);
Basically, all the types of items I have are in Blueprint classes that inherit from Item with different meshes, Power properties, etc. In SpawnPlayerRecharge(), I'm trying to randomly spawn one of the different types based on weights set in each child's Blueprint editor. In the function, the cast always fails (I checked with UE_LOG), and if I try to access the weights directly with PlayerRecharge[i], it gives me an error saying that UOBJECT does not have property Spawn_Weight. How do I get access to those Blueprint children, or is there a better way to do this?
If you need to see code I didn't post, it's on my GitHub: https://github.com/Chriq/BatteryMan
I am currently working through code which has a Road class. This has a vector of lanes and includes a lane class. The lane class has a vector of vehicles and each vehicle has its derived classes (Car, Motorcycle, Lorry).
I would like a vehicle to be able to assess whether it can move into another lane, i.e. be inserted into another lane's vector of vehicles, safely (a car requires some safe distance, so I have already implemented how a vehicle knows if it is clear to switch lanes).
void Road::Simulate(double time)
{
for (unsigned int iLane = 0; iLane < getNLanes()-1; iLane++)
{
for (unsigned int iV = 0; iV < getCurrentLane(iLane)->getNVehiclesinLane(); iV++)
{
std::cout<< " AllowedOvertake TEST "<< getCurrentLane(iLane+1)->allowedOvertake(getCurrentLane(iLane)->getCurrentVehicle(iV)->getPosition(), getCurrentLane(iLane)->getCurrentVehicle(iV)->getMinDistance())<<std::endl;
getCurrentLane(iLane+1)->setCanOvertake(allowedOvertake(getCurrentLane(iLane)->getCurrentVehicle(iV)->getPosition(), getCurrentLane(iLane)->getCurrentVehicle(iV)->getMinDistance()));
if (getCurrentLane(iLane+1)->getCanOvertake() == true)
{
getCurrentLane(iLane+1)->insertVehicle(getCurrentLane(iLane)->getCurrentVehicle(iV), 0);
delete getCurrentLane(iLane)->getCurrentVehicle(iV);
}
}
}
for (unsigned int iLane = 0; iLane < getNLanes(); iLane++)
{
getCurrentLane(iLane)->Simulate(time);
}
}
I loop over all the present lanes, except the last one as any vehicle in this lane cannot overtake. After looping over the vehicles contained in each lane, I have a function which returns a Boolean which confirms whether an overtake scenario can be executed. This is done in allowedOvertake(). If this returns true, I implement an insert function.
My issue/question: How can I make this ideology work, and whether it is sensible to have these setCanOvertake() and getCanOvertake() functions.
One possible solution could be to just pushback a new vehicle into the intended lane, but with the appropriate positions, velocities etc. However, I am not sure how to ensure that the vehicle being entered has the same type (Car, Lorry...) too.
Currently, I do not get any build errors if I exclude the insertVehicle() function, and I have vehicle motion being drawn using QPainter. However, with the insertVehicle() function, I do not get any build errors but I do get a crash once I run the project.
Any help would be appreciated, and apologies for any coding errors (I'm a keen, but very inexperienced C++ user).
For reference, I have the above functions' definitions as follows
bool Lane::allowedOvertake(double pos, double mindist)
{
for (unsigned int iV = 0; iV < getNVehiclesinLane() - 1; iV++)
{
if ((fVehicles[iV]->getPosition() > pos - mindist)// If inside rear safety distance.
|| fVehicles[iV]->getPosition() < pos + mindist)// If inside front safety distance.
{}//continue
else {return false;}
}
return true;
}
//IN Lane.h
bool getCanOvertake() const{return FREE_LANE_OVERTAKE;}
//IN Lane.h
void setCanOvertake(bool overtake = true){FREE_LANE_OVERTAKE = overtake;}
Apologies, I was under the impression I had referenced my insertVehicle() function definition.
void Lane::insertVehicle(Vehicle*v, int ielement) {
Vehicle* vins = new Vehicle(v->getPosition(), v->getVelocity(), v->getAcceleration());
for (unsigned int iDist = 0; iDist < fVehicles.size()+1; iDist++){fVehicles.insert(fVehicles.begin() + (ielement+1), vins);}
}
I have a collection of polygons that I retrieve from the database and which I wish to store in a binary tree for fast accessing. As a binary tree I use std::map.
I created this solution, which is outlined below, but I think that it is not correct because I do not call free() to release the memory allocated by malloc().
My questions (problems):
Is it correct to use std::map if I only need to insert and access elements of this map? I just want to find geometries fast by their ID's.
In the std::map I store pointers to the geometries instead of storing geometries themselves. Is this a good idea? Before I tried to store the geometries themselves, but then I realized that the std::map makes a copy of the object, which created problems.
In the method ConvertSpatial2GPC(..) I create gpc_geometry objects, which create references, which I release at gpc_free_polygon(..). But I can't release the gpc_geometry object itself, because I do not have a reference to it at that point.
I use the following structures:
typedef struct /* Polygon vertex structure */
{
double x; /* Vertex x component */
double y; /* vertex y component */
} gpc_vertex;
typedef struct /* Vertex list structure */
{
int num_vertices; /* Number of vertices in list */
gpc_vertex *vertex; /* Vertex array pointer */
} gpc_vertex_list;
typedef struct /* Polygon set structure */
{
int num_contours; /* Number of contours in polygon */
int *hole; /* Hole / external contour flags */
gpc_vertex_list *contour; /* Contour array pointer */
} gpc_polygon;
typedef std::map<long, gpc_polygon*> layer;
My workflow is as follows:
Load items from database
Call method initializeLayer() which returns a layer (see previous typedef)
... Work with the layer ...
Call method freeLayer() to free the memory used by the layer
Code for initializing geometry objects:
layer initializeLayer() {
//... database connection code
//find the count of objects in database
int count = ...
//helper object for loading from database
spatial_obj* sp_obj = NULL;
//initialize a array to hold the objects
gpc_polygon* gpc_objects;
gpc_objects = (gpc_polygon*)malloc(sizeof(gpc_polygon) * count);
layer myLayer;
int i = 0;
//... query database
while(db.Fetch()) {
id = db.GetLongData(0);
db.GetSDO_Object(&sp_obj); //load from database
db.ConvertSpatial2GPC(sp_obj, &gpc_mullad[i]); //convert polygon to GPC format
//insert a pair (ID->pointer to the geometry)
myLayer.insert(layer::value_type(id, &gpc_objects[i]);
i++;
}
return layer;
}
Code for freeing layer:
void freeLayer(layer myLayer) {
for (layer::iterator it = myLayer.begin(); it != myLayer.end(); ++it) {
gpc_free_polygon(it->second); //frees the memory from this geometry object
}
}
Code for freeing geometry object:
void gpc_free_polygon(gpc_polygon *p)
{
int c;
for (c= 0; c < p->num_contours; c++) {
FREE(p->contour[c].vertex);
FREE(p->hole);
FREE(p->contour);
p->num_contours= 0;
}
I think that I am making things more complicated that they should be.
I don't really need a std::map to store the pointers. I can instead ask the polygons from the database so that they they are already ordered by their ID's. And then I can store the polygons in a static structure (array or vector). When I need to find a element by its ID, I will just use a binary search algorithm to find it (which is logarithmic time like the search algorithm used by the binary tree, anyway).
So, my method initializeLayer() will return an array or vector instead, which I will free at the end of the program.
EDIT: I found that I don't have to implement the binary search myself. There is a class for this: std::binary_search. Link: Binary search algorithm
EDIT2: So, that is what I ended up with:
Object structure
typedef struct {
long id;
gpc_polygon gpc_obj;
} object;
Layer structure
typedef std::vector<muld*> layer;
Code for initializing geometry objects:
layer initializeLayer() {
//... database connection code
//find the count of objects in database
int count = ...
//helper object for loading from database
spatial_obj* sp_obj = NULL;
object* object_ptr = NULL;
layer myLayer;
myLayer.reserve(count);
int i = 0;
//... query database
while(db.Fetch()) {
id = db.GetLongData(0);
db.GetSDO_Object(&sp_obj); //load from database
object_ptr = new object;
object_ptr->id = id;
db.ConvertSpatial2GPC(sp_obj, &object_ptr->gpc_obj);
myLayer.push_back(object_ptr);
i++;
}
return layer;
}
Code for freeing layer:
void freeLayer(layer myLayer) {
for(std::vector<int>::size_type i = 0; i != myLayer.size(); i++) {
gpc_free_polygon(&myLayer[i]->gpc_obj);
delete myLayer[i];
}
}
Code for doing binary search:
I found out that the std::binary_search only returns whether it found or did not find the object. std::lower_bound() to the rescue!
//Create empty object for searching
object* searched_obj = new obj;
object* found_obj = NULL;
searched_obj->id = id;
layer::iterator it;
it = std::lower_bound(myLayer.begin(), myLayer.end(), searched_obj, obj_comparer);
if(it != kiht.end()) {
found_obj = *it;
if(found_obj->id != id) {
//Error!
}
} else {
//Error!
}
//Release memory
delete searched_obj;
Function for comparing objects
bool obj_comparer(object *a, object *b) {
return a->id < b->id;
}