Deleting Body in Box2d - C++ - c++

I've tried looking for a solution for this issue everywhere to no avail. I've also tried many different approaches to try and resolve this problem myself but, nothing worked.
Everytime I try to delete a body from the world, I get a read access violation at the IsLocked method in Box2d.
I have tried creating a vector list and then deleting all of the bodies from the world that are in that list. Before deleting I make sure to check that I'm not stepping the world and that there are no duplicates in my list and that the world isn't locked.
I add them to the list like so:
for (size_t i = 0; i < m_PlankObjects.size(); i++)
{
m_Game->m_DestroyObjectList.push_back(m_PlankObjects[i].GetBody());
}
This is the GetBody() method:
b2Body * GameObject::GetBody()
{
return m_Body;
}
m_Body is defined like so:
b2Body* m_Body;
And destroy like so:
if (m_UpdateWorld)
{
World.Step(1 / 60.f, 8, 3);
}
else
{
if (!World.IsLocked())
{
if (m_DestroyObjectList.size() != 0)
{
for (size_t i = 0; i < m_DestroyObjectList.size(); i++)
{
World.DestroyBody(m_DestroyObjectList[i]);
m_DestroyObjectList.erase(m_DestroyObjectList.begin() + i);
}
}
}
}

After a night's sleep I went back to the issue and debugged it. I found out that I was not clearing the m_PlankObjects array and therefore in the next game loop update it was being accessed again, but since there were no bodies to access, Box2d was throwing an exception.

Related

How to create and use a QVector in other functions or is there an easier way to do this?

I am using multiple QSpinBoxes that all need to do the exact same thing when interacted with. I am able to get them to do what I want with no issue but I thought I could optimize them by just having them call on 1 function. The only issue was that I would have to have an if statement in order to set a new minimum every time they are interacted with. If I could put them into some sort of array or list then I would be able to easily just make the change based off of one number. Using various previous threads and websites I tried to make a QVector of QSpinboxes in order to use as I previously described. After a lot of trial and errors I've reached a point where I'm not sure why I am getting the error that I am.
Here is my code:
MainWindow::~MainWindow()
{
QVector <QSpinBox*> boxes(0);
QSpinBox* sqlBox=new QSpinBox(this);
QSpinBox* perlBox=new QSpinBox(this);
QSpinBox* rustBox=new QSpinBox(this);
QSpinBox* cSharpBox=new QSpinBox(this);
QSpinBox* htmlBox=new QSpinBox(this);
QSpinBox* cssBox=new QSpinBox(this);
QSpinBox* javaBox=new QSpinBox(this);
QSpinBox* pythonBox=new QSpinBox(this);
QSpinBox* cPlusBox=new QSpinBox(this);
boxes.append(sqlBox);
boxes.append(perlBox);
boxes.append(rustBox);
boxes.append(cSharpBox);
boxes.append(htmlBox);
boxes.append(cssBox);
boxes.append(javaBox);
boxes.append(pythonBox);
boxes.append(cPlusBox);
for (int i = 0; i <= 8; i++)
{
boxes[i]->setRange(0, 1000);
boxes[i]->accessibleName();
if (i < 3)
{
boxes[i]->setGeometry(100, 105+(90*i), 71, 21);
}
else if (i >= 3 && i < 6)
{
boxes[i]->setGeometry(290, 105+(90*(i%3)), 71, 21);
}
else
{
boxes[i]->setGeometry(460, 105+(90*(i%3)), 71, 21);
}
}
delete ui;
}
void MainWindow::buyPrgm(short unsigned int input,short unsigned int prgm)
{
int first = 0, second = 0;
first = revButton();
if (code >= prgmCostOG[prgm] + pow(prgmCost[prgm], 0.1*prgmAmount[prgm]))
{
prgmAmount[prgm] = input;
prgmCost[prgm] = prgmCostOG[prgm] + pow(prgmCost[prgm], 0.1*prgmAmount[prgm]);
ui->boxes[prgm]->setMinimum(prgmAmount);
}
else
{
ui->noBuy->setText("You don't have enough code!");
for(int i = 0; i > 1000; i++)
{
second = revButton();
if ((second - first) > 5)
{
ui->noBuy->setText("");
break;
}
}
}
}
The two errors I'm getting are:
"error: Member reference type 'QVector' (aka 'QList') is not a pointer; did you mean to use '.'? (fix available)"
// I can fix this error by changing
boxes[prgm]->setMinimum(prgmAmount[prgm]);
// into
boxes[prgm].setMinimum(prgmAmount[prgm]);
Even after making this fix I still have this error:
"error: No member named 'setMinimum' in 'QList'"
I assume this means that it only wants me to use the built in functions that work with QVectors for example "append' or "capacity" despite being able to use the built in functions for QSpinBoxes in the function that they were declared in.
Once again everything I've learned about QVectors has been through posts that people have already made so I may be making awful mistakes. Any help is welcome.

Updating image UI with unity

I was trying to make a program that updated the amount of hearts the player has every turn (full, half and empty hearts). When I was doing this I instantiated a gameobject of that prefab as a variable and then assigned it to my UI panel in unity. However (I'm not sure but I think that) the variables used in the update just before are still being referenced after being destroyed in the next loop giving me the error:
MissingReferenceException: The object of type 'GameObject' has been destroyed but you are still trying to access it.
Here is the update loop:
void Update()
{
if (HP <= 0)
{
anim.SetBool("Death", true);
Destroy(GameObject.Find("Hearts"));
Destroy(GameObject.Find("Inventory"));
text.alignment = TextAnchor.LowerCenter;
text.text = "\n YOU DIED";
text.fontSize = 150;
text.color = new Color(255, 0, 0);
} else {
foreach (Transform child in GameObject.Find("Hearts").transform)
{
Destroy(child.gameObject);
}
for (var i = 0; i<(int)HP; i++)
{
GameObject Heart = Instantiate(heart, new Vector3(0, 0, 0), Quaternion.identity) as GameObject;
Heart.transform.SetParent(GameObject.Find("Hearts").transform);
}
if (HP - (float)((int)HP) == 0.5F) {
GameObject HalfHeart = Instantiate(halfheart, new Vector3(0, 0, 0), Quaternion.identity) as GameObject;
HalfHeart.transform.SetParent(GameObject.Find("Hearts").transform);
}
for (var i =0; i<Mathf.Floor(MaxHP-HP); i++)
{
GameObject EmptyHeart = Instantiate(emptyheart, new Vector3(0, 0, 0), Quaternion.identity) as GameObject;
EmptyHeart.transform.SetParent(GameObject.Find("Hearts").transform);
}
}
Is there a way to instantiate a prefab without making a variable?, or a way to make the variable reference temporary so it only lasts one update?
Thank you for your help in advance!
The problem is that once HP goes below zero, every subsequent update will enter the first if-statement and try to delete the "Hearts" and "Inventory" objects over and over. You can solve this by adding a bool called isDead and change the statement to if (HP <= 0 && !isDead) and then set isDead = true inside the block. This will prevent it from entering it twice.
Frankly though, your way of solving things is entirely backwards. As others have pointed out, deleting and instantiating objects every frame is very inefficient, and Transform.Find is also slow. You don't really need to destroy anything at all - you can rather just have a list of hearts and enable/disable an appropriate amount whenever the HP changes. You can have a single half-heart at the end of the list and enable/disable it when appropriate - if you are using a HorizontalLayoutGroup, it will still align correctly. You might want to make it so that you can only change the HP using a property or function (something like ModifyHealth(float amount)), and put the logic for updating the hearts display in there.

Create a new object from pointer reference C++

So, I've this code below:
foreach (QLineSeries* series, lineSeriesMap.values())
{
// ...
}
And I will modify series objects in this loop and I don't want to modify the original one, but create a new edited one. I'm extremely new to C++ and Qt so, I want something as the Java code below:
QLineSeries editedSeries = new QLineSeries(series);
I'm deleting elements, editing and re-ordering them from series by the way. But, as I said I need them both.
EDIT:
I've tried your answers but best way I believe is putting the code. This is a project made by some co-worker who changed jobs so its not my code, as i said I dont know C++.
chartwidget.h
void fillAreaSeries();
//...
QHash<QString,QLineSeries*> lineSeriesEntersMap;
QHash<QString,QLineSeries*> lineSeriesExitsMap;
chartwidget.cpp
void ChartWidget::fillAreaSeries() {
foreach (QLineSeries* seriesEnter, lineSeriesEntersMap.values())
{
if (lineSeriesExitsMap.contains(seriesEnter->name())) {
QLineSeries* seriesExit = lineSeriesExitsMap.value(seriesEnter->name());
if (!((seriesEnter->points().size() == 1) && (seriesExit->points().size() == 1))) {
for(int i = seriesEnter->points().size() - 1; i > 0; i--)
{
if (seriesEnter->points().at(i - 1).y() > seriesEnter->points().at(i).y())
{
seriesEnter->removePoints(i, 1);
}
}
for (int i = seriesExit->points().size() - 1; i > 0; i--)
{
if (seriesExit->points().at(i - 1).y() < seriesExit->points().at(i).y())
{
seriesExit->removePoints(i-1, 1);
}
}
QVector<QPointF> editPoints = seriesExit->pointsVector();
std::sort(editPoints.begin(),editPoints.end(), [] (const QPointF & p1, const QPointF & p2)
{
return p1.y() < p2.y();
});
seriesExit->replace(editPoints);
qDebug() << "__Swap:__";
qDebug() << seriesEnter->points().at(0).y();
qDebug() << seriesExit->points().at(0).y();
qDebug() << seriesEnter->points().at(1).y();
qDebug() << seriesExit->points().at(1).y();
QAreaSeries* series = new QAreaSeries(seriesEnter, seriesExit);
series->setName(seriesEnter->name());
series->setOpacity(0.50);
series->setPen(Qt::NoPen);
series->setPointLabelsFormat(seriesEnter->name().split("-").at(0));
areaSeriesMap.insert(series->name(), series);
}
}
}
}
Edit 3:
So, QLineSeries contains QPointF list. I've the code below:
foreach (QLineSeries* seriesEnter, lineSeriesEntersMap.values())
{
QLineSeries* entersToBeEdited = new QLineSeries(chart);
entersToBeEdited->setName(seriesEnter->name());
entersToBeEdited->points().append(seriesEnter->points());
//...
append doesnt work and returns 0 points. But I can set a name. I also tried appending by looping through items and adding it by
entersToBeEdited->points().push_back(seriesEnter->points().at(i));
and still nothing. I also tried << and += but no luck.
Looking at the class definition of QLineSeries, I don't see any simple way to copy your instance in order to duplicate it.
Thus you will have first to create a new instance :
QLineSeries editedSeries;
and manually copy the content of your original series in it.
editedSeries.append(originalSeries.points());
As you cannot modify the data once it is in the QLineSeries object, I would recommend to subclass QLineSeries or modify the QList<QPointF> obtained via originalSeries.points() before adding it to your new chart.
QLineSeries is not copyable, so you can't do what you want by modifying a copy. You will need to create a new QLineSeries from scratch.

Else Statement doesn't work in unity?

Alright so here is my code. This should be really simple but it doesn't want to work with me for some reason. Raycast sends a ray from the mouse, if it hits an object with a tag, it assigns a number to a variable. If it doesn't hit an object, then it sets the variable as -99. For some reason mine is hanging on:
A. I don't hit the objects, it outputs -99 the first time but after that it hangs on getting assigned 4.
B.I hit the objects and it will work just fine. After I click OFF the objects it hangs on the variable from the object I just hit previously.
em.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GameFunctions : MonoBehaviour {
int Hitnum = -99;
void Start () {
}
void Update () {
if (Input.GetMouseButtonDown (0)) {
objectCheck ();
}
}
//end of UPDATE function
public void objectCheck () {
RaycastHit hit;
Ray ray = camera.ScreenPointToRay (Input.mousePosition);
if (Physics.Raycast (ray, out hit, 10000.0f)) {
if (hit.collider.tag == "Box") {
Hitnum = 1;
} else if (hit.collider.tag == "Sphere") {
Hitnum = 2;
} else if (hit.collider.tag == "Pyramid") {
Hitnum = 3;
} else if (hit.collider.tag == "Trapezoid") {
Hitnum = 4;
} else {
Hitnum = -99;
}
}
Debug.Log (Hitnum);
}
//end of check
}
Thanks in advance. This is driving me nuts because it should be simple.
EDIT: posted full code this time, yes all of it. I should mention that there are no other objects with tags, it's just null space. This was supposed to be a very basic shape identifier for kids.
EDIT:EDIT: I expect it to put out -99 if you do not hit the object. Later I will probably do something with the numbers but right now I jest need to fix this.
Restarted unity, everything works fine now. Thanks. Looked through my settings to see what had changed. I earlier deleted the background that had a background tag on it. I guess unity decided that if there is not hit on raycast, it will get the object that it last hit?

signal SIGABRT when destroying array of bodies

I have populated an array of b2bodys. Using the following method:
-(void)populateBodiesToDestroy:(b2Body*)body {
NSValue *bodyValue = [NSValue valueWithPointer:body];
[bodiesArray addObject:bodyValue];
}
Each of the bodies are joint one to another to form a chain. Using the above method I have populated the array.
I update the following method within the Sprite class every 1/60 of a second in the HelloWorldLayer to destroy the bodies and remove their CCPhysicsSprites.
-(void)updateBodies {
if (bodiesArray) {
for (int i = 0; i < bodiesArray.count; i++) {
b2Body *removeLinkBody = (b2Body*) [[bodiesArray objectAtIndex:i] pointerValue];
bWorld->DestroyBody(removeLinkBody); //signal SIGABRT happens here
removeLinkBody = NULL;
[self removeChildByTag:10 + i];
}
}
}
I keep getting a signal SIGABRT on the line:
bWorld->DestroyBody(removeLinkBody); //signal SIGABRT happens here
The chain also freezes. I am not sure whether the all or some bodies have been destroyed and it's just the CCPhyscisSprites which is appearing. How can I solve this?
When you destroy a body, you must also remove it from the array otherwise the pointer previously pointing to the body will become garbage:
b2Body *removeLinkBody = (b2Body*) [[bodiesArray objectAtIndex:i] pointerValue];
bWorld->DestroyBody(removeLinkBody);
[bodiesArray removeObjectAtIndex:i];