Delete all box 2D bodies from world in libGDX - java

Recently I have been trying to delete all my bodies from Box 2D world and I ran into a little bit of a trouble.
Here is my code for deleting all the bodies:
#Override
public boolean keyDown(int keycode) {
if(keycode==Keys.R){
LevelHolder.clearLevel();
}
}
...
public static void clearLevel(){
System.out.println("deleting bodies");
Array<Body> bodies = new Array<Body>();
world.getBodies(bodies);
for(Body bod: bodies){
world.destroyBody(bod);
}
System.out.println("deleted bodies");
}
And it seems like a reasonable peace of code to me however this crashes sometimes (the message "deleted bodies" never gets printed) with error message from native code:
Assertion failed: (m_bodyCount > 0), function DestroyBody, file /Users/badlogic/jenkins/workspace/libgdx-mac/extensions/gdx-box2d/gdx-box2d/jni/Box2D/Dynamics/b2World.cpp, line 133.
When deleting all bodies that I just got from the world Im somehow deleting more bodies than there are in the world. Also it doesn't happen all the time.
The piece of code is completely isolated and my game has no multi threading. So nothing could be deleting bodies while I'm stepping through all the bodies in the world.
What could be happening here? Maybe this is not the right way of deleting all the bodies and someone could give me an insight of how to do it better? Thank you.
Using libGDX 1.5.2 here.

I was using the same code as you to destroy all bodies and ran into a similar problem. I believe the problem is that you are trying to destroy bodies while your box2d.World is locked(you can check with world.isLocked()). During your world.step() your box2d.World is locked. This was my problem, as I was trying to destroy bodies as a result of a beginContact() event and couldn't because this was being handled within world.step().
My solution was to assign a boolean flag to true and just before doing world.step() i would check to see if it was true and if it was, then I destroy all bodies using same code as you did above.
Here is an example i tested:
private boolean destroyAllBodies;
public void clearBodies()
{
destroyAllBodies = true;
}
public void update()
{
if(destroyAllBodies)
{
Array<Body> bodies = new Array<Body>();
world.getBodies(bodies);
for(int i = 0; i < bodies.size; i++)
{
if(!world.isLocked())
world.destroyBody(bodies.get(i));
}
destroyAllBodies = false;
}
...
world.step(1/60f, 6, 2);
...
}
I hope that helped. Cheers!

Try to get the count of bodies first, and then write a for loop that goes through the array using that count, rather than an enumerator. Editing a collection while it's being enumerated can cause issues.

The problem is that you are trying to delete the bodies in the keydown event so the world may be locked.
What you should do is delete them in the render method.
boolean clearLevel=false;
#Override
public boolean keyDown(int keycode) {
if(keycode==Keys.R){
clearLevel=true;
}
}
...
public boolean render(float delta){
world.step(delta, 8, 4);
if(clearLevel){
System.out.println("deleting bodies");
Array<Body> bodies = new Array<Body>();
world.getBodies(bodies);
for(Body bod: bodies){
world.destroyBody(bod);
}
System.out.println("deleted bodies");
clearLevel=false;
}
}

Related

Inconsistent behavior/performance of Gradle/Android Studio especially with bool variables

I am working on a app that simulates a game of Cricket. My logic was correct but program kept giving wrong output as the changes were not reflected. I invalidated caches and restarted the android studio. But still the glitch of not reflecting the changes kept going on. The changes were specially related to bool variables. Suddenly after two days I run the app and it was behaving as expected.
I am puzzled is there any glitch in Gradle build system? All this mess ate a lot of time of mine. I am thinking of switching to some other android IDE. Which one is better and do not use Gradle? Coz I am frustrated with the Android Studio and Gradle. I am not trying to spread the hate against Android Studio and Gradle but I am deeply frustrated with the performance and inconsistency.
Edit:
I have a class named Team which has two bool variables that indicate if a team is batting or bowling.
Code looks like this:
public class Team {
private int numberOfRunsScored, numberOfWicketsTaken;
private int numberOfBallsThrown, numberOfOversCompleted;
private String inningTypeString;
private boolean batting,balling;
public boolean isBatting() {
return batting;
}
public void setBatting(boolean batting) {
this.batting = batting;
}
public boolean isBalling() {
return balling;
}
public void setBalling(boolean balling) {
this.balling = balling;
}
In MainActivity to simulate the game, I made two objects of class Team as "a" and "b".
At the start of the game a random outcome decides which team will bat first and which team will bowl fist.
So for first inning I set the behavior as follows:
`public void performToss() {
tossCallByTeamA = a.tossCall();
tossOutput.setTossOutcome(tossOutput.doToss());
boolean tossOutCome = tossOutput.getTossOutcome();
if (tossOutCome == tossCallByTeamA) {
a.setInningType(Team.InningType.BATTING);
b.setInningType(Team.InningType.BOWLING);
a.setBatting(true);
b.setBalling(true);
} else {
a.setInningType(Team.InningType.BOWLING);
b.setInningType(Team.InningType.BATTING);
a.setBalling(true);
b.setBatting(true);
}
}`
After first inning the batting and bowling switches between the teams. So I wrote following code to switch the roles.
public void switchViewStubs() {
//stub1.setVisibility(View.GONE);
//stub2.setVisibility(View.GONE);
alloversCompleted = true;
createViewStubs();
if (a.getInningType().equalsIgnoreCase("batting")) {
newStub1.setLayoutResource(R.layout.bowling_team_layout);
newStub2.setLayoutResource(R.layout.batting_team_layout);
a.setBatting(false);
b.setBatting(true);
}
if (a.getInningType().equalsIgnoreCase("bowling")) {
newStub1.setLayoutResource(R.layout.batting_team_layout);
newStub2.setLayoutResource(R.layout.bowling_team_layout);
b.setBatting(false);
a.setBatting(true);
}
newStub1.inflate();
newStub2.inflate();
instantiateViewsAndSettingListenersToButtons();
aRuns = 0;
bRuns = 0;
}
Code to set text to runs TextView was the code that made the trouble and ate my precious 2-3 hours even though I was innocent and correct at writing the logic. Here is the code.
if (view.equals(oneRun)) {
//incrementBalls();
totalRuns++;
if (a.isBatting()) {
//new AlertDialog.Builder(this).setTitle("batting staus").setMessage("a is batting").show();
aRuns++;
//a.setNumberOfRunsScored(aRuns);
if (!alloversCompleted) {
incrementBalls();
updateRuns(aRuns);
}
if (alloversCompleted) {
updateRuns(0);
alloversCompleted = false;
}
if ((totalRuns > targetRuns) && (secondInning)) {
setTextOnFooter("Team A wins");
}
}
if (b.isBatting()) {
//new AlertDialog.Builder(this).setTitle("batting staus").setMessage("b is batting").show();
bRuns++;
//b.setNumberOfRunsScored(bRuns);
if (!alloversCompleted) {
incrementBalls();
updateRuns(bRuns);
}
if (alloversCompleted) {
updateRuns(0);
alloversCompleted = false;
}
if ((totalRuns > targetRuns) && (secondInning)) {
setTextOnFooter("Team B wins");
}
}
}
The code block of (b.isBatting) in above code was not executing at all even the batting role was switched. But I had written the correct code and logic to change the role of batting to bowling and bowling to batting.
I invalidated cashes and restarted android studio but the code was behaving incorrectly.
I have heard that Gradle build system is fast because it considers and builds only changed code instead of building all the code again and again.
I suspect some glitch in Gradle did not consider the changes made by me to bool variables. After some two days I rebuild the code and ran it again and it ran perfectly fine.
This behavior is inconsistent and problematic.
Can anyone predict what must have gone wrong? The Gradle version is gradle:4.2.2
If a team can be either "balling"/"bowling" or "batting" but not both then your design of theTeam class is error prone (because you can set both values to true, or both values to false which would mean that the team is neither "balling" nor "batting").
Let's assume that at the start team a is batting (a.batting is true, a.balling is false) and team b correspondingly balling (b.batting is false, b.balling is true).
If now void switchViewStubs() is called it will change the "batting" status for both teams and leave the "balling" status as it is. Therefore we will have the following situation:
a.batting is false, a.balling is false
b.batting is true, b.balling is true
Does that mean that team b is playing against itself?
If this "balling"/"bowling" or "batting" is truly exclusive you should change your Team class as follows:
public class Team {
private boolean batting;
public boolean isBatting() {
return batting;
}
public void setBatting(boolean batting) {
this.batting = batting;
}
public boolean isBalling() {
return !batting;
}

Input handling completely breaks when more than a single key is held at once

I have some suspicions about the way I'm handling the heldKeys variable (adding and removing keycodes from it).
Everything works fine when only a single key is pressed at once (and this is why I believe the problem, but be in the this class, and not the ones that take care of drawing or updating). However, everything completely breaks when I press more than one key at the same time.
By "breaks", it no longer works properly: pressing up moves all subscribers up sometimes, other times moves the one that should move up up, and the others down, etc. Other times a certain action stops doing anything until I restart the program.
I've tried debugging and seeing the evolution of the heldKeys array, but I really have no clue where the problem is.
Code:
<code>
public class MainInputProcessor implements InputProcessor {
// REFERENCE:
// <Listener, <ACTION, KEYCODE>>
private Array<ObjectMap<RacketMovementListener, ObjectMap<Racket.ACTIONS, Integer>>> racketMovementListeners;
// Stores the keys that are held down
private IntArray heldKeys;
// Initialisation
public MainInputProcessor() {
heldKeys = new IntArray();
racketMovementListeners = new Array<ObjectMap<RacketMovementListener, ObjectMap<Racket.ACTIONS, Integer>>>();
}
// Adds a RacketMovementListener to the respective subscriber list
public void subscribeToRacketMovement(RacketMovementListener newSubscriber, ObjectMap<Racket.ACTIONS, Integer> movementKeys) {
ObjectMap<RacketMovementListener, ObjectMap<Racket.ACTIONS, Integer>> map = new ObjectMap<RacketMovementListener, ObjectMap<Racket.ACTIONS, Integer>>();
map.put(newSubscriber, movementKeys);
racketMovementListeners.add(map);
}
// Update and execute event action
public void updateEvents() {
// Fire events for racket movement subscribers if the keys match
if (!heldKeys.isEmpty()) {
for (ObjectMap<RacketMovementListener, ObjectMap<Racket.ACTIONS, Integer>> map : racketMovementListeners) {
for (RacketMovementListener listener : map.keys()) {
for (Integer keycode : heldKeys.items) {
if (map.get(listener).get(Racket.ACTIONS.MOVE_DOWN).equals(keycode)) {
listener.moveDownPressed(listener);
} if (map.get(listener).get(Racket.ACTIONS.MOVE_UP).equals(keycode)) {
listener.moveUpPressed(listener);
}
}
}
}
}
}
#Override
public boolean keyDown(int keycode) {
// Add keycode to heldKeys if it's not already there
if (!heldKeys.contains(keycode)) {
heldKeys.add(keycode);
return true;
}
return false;
}
#Override
public boolean keyUp(int keycode) {
// Remove keycode from heldKeys since the key is no longer held down
for (Integer code : heldKeys.items) {
if (code == keycode) {
heldKeys.removeValue(code);
}
}
return true;
}
</code>
I'm not really looking for solutions or fixed code, just some starting point or somewhere to look, because I don't even know where to start. I've thought about rewriting this but I think I would do the same mistakes.
For context, this is code for a Pong clone, in LibGDX.
UPDATE
I rewrote the whole input manager to do the exact same but using switch statements and overall in a simpler way, but the exact same problem remains.
I'm now sure the problem is in the way I'm handling the heldKeys array.
However, I still can't figure out the problem with it...

Spigot codding, inventory

I made a simple plugin on actions. I have one issue.
When player press shift and quickly close inventory, he can get item without pay. I need to fix this, what do I need to do?
Code Below:
#EventHandler public void inventoryHandler(InventoryClickEvent e) throws Exception {
for (AuctionInventory[] inventories : inventoryMap.values()){
for (AuctionInventory inventory : inventories){
if (inventory.getInventoryType().equals(AuctionInventoryType.MAIN)) {
if (e.getClickedInventory().equals(inventory.getSource())) {
e.setResult(Event.Result.DENY);
e.setCancelled(true);
if (e.getCurrentItem().getType() != Material.AIR) {
Player p = (Player) e.getWhoClicked();
for (Button button : buttonList){
if (button.getItem().equals(e.getCurrentItem())){
button.doLogic(inventory.getSource(), p);
e.setResult(Event.Result.DENY);
}
}
IProduct product = InventoryUtil.getProductByItem(getProducts().values(), e.getCurrentItem());
if ((product != null)) {
buyProduct(p, product);
}
}
}
}
}
}
}
#Override #Deprecated #SuppressWarnings("all")
public boolean buyProduct(Player p, IProduct product) throws Exception {
if (!product.getSeller().equals(p.getUniqueId())) {
if (Economy.getMoney(p.getName()) < product.getPrice()) {
return false;
}
p.getInventory().addItem(product.getItem());
removeProduct(product);
Economy.setMoney(p.getName(), Economy.getMoney(p.getName()) - product.getPrice()); Economy.setMoney(Bukkit.getOfflinePlayer(product.getSeller()).getName(), Economy.getMoney(Bukkit.getOfflinePlayer(product.getSeller()).getName()) + product.getPrice());
return true;
}
return false;
}
It is hard to say, if your logic related to checking inventory is fine I would say that you missed InventoryDragEvent. You need to implement that event too, and block any interactions related to your inventories.
Also in your ClickEvent you should block interaction if your inventory is open - including clicks inside player inventory, as there are interactions that can move items from opened inventory by clicking inside own inventory.
Also getClickedInventory() can return null.
So you should just check event.getView().getTopInventory() check if it is not a null, and if it is your inventory.
Also your for loop with buttons does not break/return, so even if player will hit a button you will still try to find item to sell, that might cause some issues too.
Also I noticed few others problems here, you should not use .setMoney function if you are using Vault API as this might break compatibility with other plugins, same with checking money by getMoney.
There is special public boolean has(OfflinePlayer player, double amount); method to check if player have enough money, as this function will work well with plugins that allows for negative amounts, or paying in different way.
Then you should take money from player by: public EconomyResponse withdrawPlayer(OfflinePlayer player, double amount); also you should then check response to ensure that it was successful.
p.getInventory().addItem(product.getItem()); what if player does not have enough space in inventory? Note that this method returns Map<Integer, ItemStack> where key is index of item from method argument (as it is varargs, in your case key can be only a 0, as you are only passing single argument) and item that didn't fit into inventory. (note that one part of stack might be added, like 12 from 43 items in stack)

Bukkit Error: event cannot be resolved

public void run() {
if(spleggEnabled == 3){
if(player.isBlocking()){
player.sendMessage(ChatColor.GREEN + "Projected Splegg!");
WitherSkull head = (WitherSkull) ((ProjectileSource) player).launchProjectile(WitherSkull.class);
BlockIterator iterator = new BlockIterator(event.getEntity().getWorld(), event.getEntity().getLocation().toVector(), event.getEntity().getVelocity().normalize(), 0.0D, 4);
}
if(player.isDead()){
spleggEnabled = 0;
}
The error is on each event.getEntity(), and the error is event cannot be resolved.
Any ideas?
What it looks like is that event is undefined.
If you are unsure of what event.getEntity() is, try replacing it with head or player in your code.
If you know what it is and what it does, make the event from your listener accessible (define it in the class as public static Event event and set it in your listener.)
You obviously stole some code from online without a single clue as to what it does. Next time, read the whole forum/blog/wiki post instead of blindly copying.

LWJGL Keyboard loop

I've created a static Input class, that basicly have a method that I can call, which is this:
public static boolean GetKeyDown(int keyCode) {
while(Keyboard.next()) {
Keyboard.enableRepeatEvents(false);
if (Keyboard.getEventKeyState()) {
if (Keyboard.getEventKey() == keyCode) {
return true;
} else {
return false;
}
}
}
return false;
}
And in my game update loop, I've wanted to use this, instead of having to make a single while-loop:
if(Input.GetKeyDown(KeyCode.S)) {
//Something happens
}
if(Input.GetKeyDown(KeyCode.R)) {
//Something happens
}
//etc..
But it seems that only the first one loaded, will work. In this case 'S'. Is there a way for me to do be able to use the others too?
That is because in your GetKeyDown() method, you call Keyboard.next(), when you call that method it removes the Event of the current key from Keyboard, the only gets refilled with Events, when you call Display.update();
NOTE: This method does not query the operating system for new events. To do that, Display.processMessages() (or Display.update()) must be called first.
Source: LWJGL Docs
You Could
Instead you can use the Keyboard.isKeyDown(int key) method, to achieve what you're trying to do.
Though it returns true/false depending on the following.
Returns: true if the key is down according to the last poll()
But that still doesn't quite fix the problem because it relies on the poll() method.
Fixing The Problem
You can fix the problem by creating some custom methods to use with the Keyboard class, as you already did, though as said the Keyboard Events only gets updated when you call the Display.update(); method.
You already got the right idea about which function to create, though you need to split them into, two different methods. You need a secondary method which you call once each time you want to update your keyboard.
public class MyKeyboard {
/*
* Remember that the index where we store the value,
* is the index of the key. Thereby one key might have
* an index of 400, then your array need to have at least
* the same size, to be able to store it.
*/
public static boolean[] keys = new boolean[100]; // 100 is the amount of keys to remember!
public static void update() {
while(Keyboard.next()) {
if (Keyboard.getEventKey() < keys.length) {
keys[Keyboard.getEventKey()] = Keyboard.getEventKeyState();
}
}
}
public static boolean isKeyDown(int key) {
if ((key > 0) && (key < keys.length)) {
return keys[key];
}
return false;
}
}
Remember to only call the MyKeyboard.update() method once per Display.update() I also renamed your GetKeyDown() method to isKeyDown(), because I think that sounds and describes it better, but you can rename it again in your project if you want to.
The above code was made within this answer, without the use of an IDE, etc. So if there's anything wrong with it I apologize, but just comment and I will fix it.
One problem that arises with this method is the lack of rechecking. Since Keyboard.next() only checks the inputs that have occurred in the current frame. A button which was once pressed will remain "pressed" until it is pressed again. I ran into this problem while trying to implement this solution. The answer to this new problem is here:
public static void update() {
for(int i = 0; i < keys.length; i++) {
keys[i] = false;
}
while(Keyboard.next()) {
keys[Keyboard.getEventKey()] = Keyboard.getEventKeyState();
}
}
You must clear the keypresses of the previous frame by setting everything to false.

Categories

Resources