Best way to reference multiple (unknown number of) sprites in libgdx (Android)? - java

Im working on a game in which multiple "notes" (sprites) are generated.
The notes are created at random. Each of them has a random velocity and are created in a different thread. The Notes class is a child of the sprite class. It has 2 properties and 1 method:
vel - a Velocity2 object holding the x and y component on the
velocity of the note object
pos - a Vector2 object holding the x and y coordinates of the note object.
changepos() - a method that changes the position based on the velocity of the object
(I cannot post the code of that class due to privacy reasons)
I currently have a static class "NoteStack", which can hold up to 64 references to Notes objects.
public class NoteStack {
public Notes[] note_array;
public int stack_len;
public NoteStack(){
note_array = new Notes[64];
stack_len = 0;
}
public void push(Notes n){
if(stack_len<64){
note_array[stack_len] = n;
stack_len++;
Gdx.app.log("push", "pushed");
}
}
public void delete_note(int pos){
if(note_array[pos] != null){
note_array[pos] = null;
for(int i = pos; i<stack_len; i++){
note_array[pos] = note_array[pos+1];
}
note_array[stack_len] = null;
stack_len = stack_len - 1;
}
}
}
Here's the code for my "update" function
public void update(float d, SpriteBatch b){
core.draw(b);
for(int i = 0; i< noteStack.stack_len; i++){
Gdx.app.log("update", "Update function running" + i);
noteStack.note_array[i].changePos(d);
noteStack.note_array[i].draw(b);
// scr_w - screen width , scr_h - screen height
if(noteStack.note_array[i].pos.x > scr_w || noteStack.note_array[i].pos.x < 0 || noteStack.note_array[i].pos.y > scr_h || noteStack.note_array[i].pos.y < 0){
noteStack.delete_note(i);
}
}
}
The issue (as you may see) is that whenever a note object from NoteStack gets removed (i.e. the delete_note method is called), other Notes objects in the array are affected.
Hence my question: What is the best way to reference multiple sprite (note) objects in LibGDX?

Generally speaking in programming, you should never implement own "classic" datastructures, only if it's really necessary and you can't use or extend a collection type, because the standard implementations are well programmed and tested, so those are safer to use.
In your case, I would use libGDX Array. That class has add, get, size methods, and if you really want to, you can extend the Array class to have an update function.
But in short, if you replace public Notes[] note_array; with public Array<Notes> note_array = new Array<>(true, 64); and use get and remove and size for iterating and managing the collection that should work.

Related

How do I optimize 3D xyz nested for loops in Java and stream Vectors?

I write plugins for Minecraft and often hit a situation where I have to loop through every block in a 3-dimensional space to process the block in some way. For example:
for (int x = minX; x <= maxX; x++) {
for (int y = minY; y <= maxY; y++) {
for (int z = minZ; z <= maxZ; z++) {
// Do something
}
}
}
These kind of loops happen a lot and it struck me that perhaps there was a better way to do it that would make the code more concise but still be as fast to execute. All I really want to do is loop through every coordinate in a 3 dimensional type.
The Bukkit API has such a type - a Vector class that can hold x,y,z. The Vector class has methods such as getBlockX, getBlockY and getBlockZ that return integers. So, I was thinking of creating a utility method like this:
Stream<Vector> getXYZStream(minVector, maxVector)
This would return a stream of all the vectors (x,y,z) values between the min and max vectors given. So then I could do:
getXYZStream(minVector, maxVector).forEach(v -> doSomething);
How can I make a stream of vectors in this regard? If I can code this approach, then I'll measure the difference in performance and see if it makes sense.
Or is there a better way to do this?
Or should I accept these loops are necessary and the best approach?
I found an approach where one can make a stream using a custom iterator, so one could do something like this:
public Stream<Vector> get3dStream(Vector minVector, Vector maxVector) {
Iterator<Vector> it = new CustomIterator(minVector, maxVector);
return StreamSupport.stream(Spliterators.spliteratorUnknownSize(it, Spliterator.DISTINCT), false);
}
class CustomIterator implements Iterator<Vector> {
private Vector minVector;
private Vector maxVector;
BoundingBox b = new BoundingBox();
// constructor
CustomIterator(Vector minVector, Vector maxVector) {
// initialize cursor
this.minVector = minVector;
this.maxVector = maxVector.add(new Vector(1,1,1));
}
// Checks if the next element exists
#Override
public boolean hasNext() {
return !minVector.equals(maxVector);
}
// moves the cursor/iterator to next element
#Override
public Vector next() {
Vector r = minVector;
increment();
return r;
}
private void increment() {
// Increment the vector - this takes a lot of code to do
}
}
However, as you can see, tracking the next() in the custom iterator will require quite a lot of code and comparing to the three for loops is much more complex and likely to be slower. I agree with the comment from kaya3, it could hide some of the looping, but it's not going to be faster, which is 100% a requirement.

Copying Array from ArrayList Element

I'm building a Java based game in Swing, which is essentially a grid of Jbuttons
I have an Object called Cell, which is a custom JButton with additional parameters for storing objects. The game grid is represented by Cell[][]
I have an arraylist of type Cell[][] to allow me to store the state of the gamegrid after each move. If I want to undo the move, I need to copy the last element of the ArrayList to the game grid to allow it to be displayed on the UI.
My gamegrid is panelHolder and my arraylist is moveHolder.
So far I've tried Collections.copy(panelHolder, moveHolder.get(moveHolder.size())); which will not compile due to the "arguments not being applicable for the type Cell[][]"
I've also tried System.arraycopy(moveHolder.get(moveHolder.size()-1), 0, panelHolder, 0, panelHolder.length);, which throws and out of bounds exception. Initially I thought this was due to the moveHolder.size()-1, but even just as moveHolder.size() it has the same problem.
I've found numerous questions on StackOverflow and others that both show these two ways of doing it, but I can't seem to get it to work. Is there something more obvious I'm missing? Full class method below:
public class UndoClass implements MoveCommand{
public ArrayList<Cell[][]> moveHolder = new ArrayList<Cell[][]>();
public Cell[][] execute(Cell[][] panelHolder) {
if (moveHolder.size() > 0){
Collections.copy(panelHolder, moveHolder.get(moveHolder.size()));
if (moveHolder.size() > 0){
moveHolder.remove(moveHolder.size());
}
}
System.out.println("Move Undone. Undos available:" + moveHolder.size());
return panelHolder;
}
public void addMove(Cell[][] panelHolder){
moveHolder.add(panelHolder);
}
public ArrayList<Cell[][]> getMoves(){
return moveHolder;
}
}
Cell Class
public class Cell extends JButton {
int co_x = 0;
int co_y = 0;
ArrayList<Players> current = new ArrayList <Players>();
}
Just wanted to point our your execute(...) method accepts the Cell[][] both as a parameter and the return argument. That approach is going to force all of your commands to keep copying your input param arrays to the return statement array. Notice if you don't need to keep the two in sync and you just use the return arg, you don't have to worry about copying at all:
Cell[][] lastState = moveHolder.get(moveHolder.size()-1);
moveHolder.remove(moveHolder.size()-1);
return lastState; // Not updating the panelHolder array, just returning
But of course now the input parm and return are out of sync. Instead you might want to encapsulate that state into a single object to make your life easier. Something like this (note that the execute now returns a void):
public ArrayList<GameState> previousStates = new ArrayList<GameState>();
public void execute(GameState currentState) {
if (previousStates .size() > 0) {
GameState lastState = previousStates.get(previousStates.size()-1);
currentState.restoreFrom(lastState);
previousStates .remove(moveHolder.size()-1);
}
}
Good luck on the game!
if (moveHolder.size() > 0) {
for (int i = 0; i < panelHolder.length; i++) {
panelHolder[i] = moveHolder.get(moveHolder.size()-1)[i].clone();
}
moveHolder.remove(moveHolder.size()-1);
}
Try this. You need to make copies of each internal array when copying 2D arrays.
Try a Linked List
LinkedList<Cell[][]> ll = new LinkedList();
ll.removeLast();
panelHolder = ll.clone();

How do I check if I have just clicked on a certain object from an array?

Basicly I am creating a game that you click on falling objects, E.G cookies, and I need to know how to check and see if a certain cookie has been pressed so it can disappear but the problem is that its in an array.
Here is a bit of my code:
Input class...
public class Input implements MouseListener, MouseMotionListener{
#Override
public void mousePressed(MouseEvent e) {
if(e.getSource().equals(MainGame.CG)){
if(MainGame.MG.inGame){
//There is actually something else here but its classified (haha sorry about that)
if(e.getPoint().x > /*I NEED SOMETHING HERE*/){
//tells you if the object has been pressed
MainGame.CG.cookieClicked = true; //CG = ClickerGame
}
}
}
}
}
class with array...
public class ClickerGame extends JPanel{
public int amount;
public FallingObject[] fo = new FallingObject[120]; //THE ARRAY I'M HAVING TROUBLES WITH
/*THE REST IS A SECRET (SORRY ABOUT THAT)*/
}
If you don't understand here is a picture to demonstrate what I need...
In order to avoid having to check the coordinates of 120 different items on each click, make every element inside FallingObject[] aware of three things:
Its own area of influence (see sn00fy's answer)
The containing class (in this case probably ClickerGame
Its location in the array (an int)
To do this, you would need to change your FallingObject constructor to look something like this:
public void FallingObject(ClickerGame master, int index); //add whatever else is needed for Falling Object.
Then you could instantiate the array as follows.
for(int i = 0; i < 120; i++) {
fo[i] = new FallingObject(this, i ); //add anything else needed for the constructor
}
Then each FallingObject is responsible for its own state, and when clicked it is able to report back to the ClickerGame instance. All you need now is a method in ClickerGame which each FallingObject can call.
public void clickedObj(int index) {
FallingObject temp = null;
if(index >= 0 && index < 120) {
temp = fo[index];
//Do stuff with temp :)
}
}
To call this method from within FallingObject just reference the 'master' variable (which you should probably save as a global variable within the class.
You have to check every element in your FallingObject[] array if it intersects with the mouse pointer coordinates at the moment of the click.
You can implement a simple rectangle test or use a circle for each cookie as explained here:
Equation for testing if a point is inside a circle

Increment a static variable in object before returning that object

I have some code that creates Objects called Weights. Now there are subclasses of these Weights called - WeightSmall, WeightMedium, and WeightLarge - each with their own static variable called onScreen. This variable should increment when one of either WeightSmall, WeightMedium or WeightLarge is added, however these get return on the call of the create method rather than being added to an array. I have an array of Weight objects - is there a way to access what subclass type an element is in the array of the parent class?
Here is the code for creating weights:
public Weight createWeight() {
decider = Math.random() * 1;
// creates rocks randomly with the lowest chance for l, and the highest chance for m
if (decider <= 0.33) {
// small weight
return new WeightSmall(BitmapFactory.decodeResource(getResources(), R.drawable.weight_s), new Random().nextInt(screenWidth), -10);
} else if (decider <= 0.5 && decider > 0.33) {
// large weight
return new WeightLarge(BitmapFactory.decodeResource(getResources(), R.drawable.weight_l), new Random().nextInt(screenWidth), -10);
} else {
// medium weight
return new WeightMedium(BitmapFactory.decodeResource(getResources(), R.drawable.weight_m), new Random().nextInt(screenWidth), -10);
}
}
What needs to happen is for WeightSmall lets say, it needs to check WeightSmalls onScreen variable to see if it's smaller than, let's say 3. if it is return the weight. However I can't think of a way to access WeightSmall's onScreen variable as it needs to be created more than once and I tried implement them into an ArrayList but it causes complications in the update method. Here is the rest of the code (that matters) for the class:
public void render(Canvas canvas) {
if (canvas != null) {
canvas.drawColor(Color.WHITE);
player.draw(canvas);
Weight[] weightArray = weights.toArray(new Weight[0]);
for (Weight weight : weightArray) {
weight.draw(canvas);
}
}
}
// updates the weight's position on the screen and checks collision with the player
public void update() {
Weight[] weightArray = weights.toArray(new Weight[0]);
for (Weight weight : weightArray) {
weight.update();
if (weight.getBounds().intersect(player.getBounds())) {
player.setTouched(false);
Intent gameOverIntent = new Intent(this.getContext(), GameOverActivity.class);
this.getContext().startActivity(gameOverIntent);
((Activity) getContext()).finish();
}
}
}
// count down timer spawning weights in every tick
public void timer() {
if (start == true) {
if (weightSpawnTimer != null) {
weightSpawnTimer.cancel();
weightSpawnTimer = null;
}
weightSpawnTimer = new CountDownTimer(30000, 800) {
public void onTick(long millisUntilFinished) {
weights.add(createWeight());
}
public void onFinish() {
weightSpawnTimer.start();
}
}.start();
}
}
Edit for clarity: What I need to happen, is in the onTick method, check if the subclass of weight's onScreen variable is <= 3, if it is, create a new weight, if it isn't do nothing. Once the weight is then offscreen, decrement this variable so new weights of that subclass can then be created.
What about
class WeightSmall {
public WeightSmall(...) {
// increment static
}
}
Let each class be responsible to increment it's own number in case an instance is created.
Modifying a static variable from an instance is usually considered bad practice. The few legit use-cases are typically some sort of instance counting. At least when you don't count down.
Counting down is where the trouble starts because Objects do have a defined start but their end is not guaranteed. You can count down in finalize - i.e. when the garbage collector has found your instance - but that's not guaranteed to happen soon or at all. Using that to find out how many instances are on screen would correlate to the actual number but could be completely wrong.
So in a case when you want to know how many objects you show on screen you must actively count that number down once the place responsible for showing the objects let's go of one.
And since that is already a responsibility for the class that wants to know how many objects are on screen, it should as well keep track of the numbers in a local variable of it's own.
By using a static property you limit yourself to have just 1 screen. If you let the other place count the number you don't limit yourself.
Static variable initialized at 0, then on the constructor your make it +1

ArrayList of abstract subclasses (must have different hashcode)

I am creating a game for a school project, and I have a 2 classes called Pieces and Powers, with subclasses Piece_Yellow, Piece_Blue ... and Power_Explode, Power_ChangeColor etc...
I was doing it with enum and someone from this website (mikera to be more precise) help me changing that and creating a better interaction with the pieces and the powers.
But now I have to change the old code, and I have problems with that because I was doing like this :
int x = 10, y = 5;
for (int i=0; i<10; i++) {
if (pecas[x][y] == null)
pecas[x][y] = new Piece(arrayPecas.get(rand.nextInt(arrayPecas.size())));
}
Like this my array was partially filled with 10 new pieces Objects with different colors (but no powers, that was the problem), and every single one had a different hashcode (for finding , comparing and deleting the pieces later)...
But Since we can't initiliaze an abstract class, the only solution that I've found to this problem was to do this :
int x = 10, y = 5;
for (int i=0; i<10; i++) {
if (pecas[x][y] == null)
pecas[x][y] = arrayPecas.get(rand.nextInt(arrayPecas.size()));
}
Like this the object is still added, but now I have 1 problems :
1 - All the hashcode from the pieces with the same color are the same... I don't really know how to solve this. I've read that I could override the hashcode method but there is no information to make the difference between them (and I can't store the position because I had to change it every time the piece change position).
It doesn't seem that really the hashCodes that are your problem. It's that the objects are simply equal. Code that relies on two unequal objects having different hashCodes isn't strictly correct. return 1; is a perfectly legal implementation of Object#hashCode().
The simplest thing seems to be to put a clone() method on Piece that all the subclasses can implement in order to return copies in places where you want different, distinct, objects.
public abstract class Piece {
public abstract Piece clonePiece();
}
public class YellowPiece extends Piece {
#Override
public Piece clonePiece() {
return new YellowPiece(this.relevantThing1, this.relevantThing2 // etc etc)
}
}
int x = 10, y = 5;
for (int i=0; i<10; i++) {
if (pecas[x][y] == null)
pecas[x][y] = arrayPecas.get(rand.nextInt(arrayPecas.size())).clonePiece();
}

Categories