I'm trying to make the game Tetris in java.
I've gotten it to the point where:
a new block is generated when it hits the floor or its y+1 is not null (meaning there's another block under it)
public void collisionCheck(int x, int y) {
if (activetile.getY() == this.height-2 || getTileAt(x, y+1) != null) {
activetile = new Tile(this, 0, 0);
}
}
A row clears when the bottom row is full of non-null values, or the Tetris pieces (for y = 4 (the floor), loop through x till x = 4 and check if all non-null)
public void checkBottomFull(int x, int y) {
while (getTileAt(x,y) != null) {
say("(" + x + ", " + y +")");
if (x == 3) {
say("row is full");
//replace full row with tiles from above
for (int i = 0; i < 4; i++) {
for (int j = 5; j > 0; j--) {
grid[j][i] = getTileAt(i,j-1);
grid[j-1][i] = null;
}
}
break;
}
x++;
}
}
Right now, I'm using keys to move the block:
public void keyPressed(KeyEvent e) {
int keyCode = e.getKeyCode();
if(keyCode == KeyEvent.VK_DOWN) {
activetile.setLocation(activetile.getX(), activetile.getY()+1);
System.out.println("coordinates: " + activetile.getX() + ", " + activetile.getY());
collisionCheck(activetile.getX(),activetile.getY());
checkBottomFull(0,4);
repaint();
}
}
There's two issues I'm having:
1) In the picture you'll notice I've dropped the block all the way to the floor... and the row cleared. After it's cleared, it will generate a block to the top left (x=0, y=1) which I have no control over.
2) On the floor there seems to be a red line... which I'm assuming is a row of blocks hidden by the JFrame... I'm not sure why that's there.
FYI: If you're wondering why grid[j][i] has the rows and columns flipped (aka, why it's not grid[i][j]) is because I instantiated it as grid = new Tile[height][width];
Any thoughts?
Thanks!
It is hard to say what is wrong without actually debugging your app.
But maybe try this one:
public void checkBottomFull(int x, int y) {
while (getTileAt(x,y) != null) {
say("(" + x + ", " + y +")");
if (x == 3) {
say("row is full");
//replace full row with tiles from above
for (int i = 0; i < 4; i++) {
for (int j = 4; j >= 0; j--) {
grid[j][i] = getTileAt(i,j-1);
grid[j-1][i] = null;
}
}
break;
}
x++;
}
}
You have 5 rows (indexed from 0 to 4) and 4 columns (indexed from 0 to 3).
What values of height and width do you pass to:
grid = new Tile[height][width];
Because from what I see you should do something like that:
grid = new Tile[5][4];
Bah,
Turns out in the key event, I needed to check if the bottom was full before checking if there is a collision.
I guess what was happening is, when I was checking collisionCheck(activetile.getX(),activetile.getY()); before checkBottomFull(0,4);, when the bottom was full, it would clear the row and set the current row equal to the row above it: grid[j][i] = getTileAt(i,j-1);, the problem was that collisionCheck was generating a new piece and the that newly generated piece was getting cleared and replaced by checkBottomFull.
Putting the collision check after the checkBottomFull ensures that the newly generated piece won't be replaced if bottom is full.
Related
javascript code in processing 3.5.3 not working, not sure why. It's supposed to create circles and have them bounce around the screen, instead it makes the right amount of circles but they don't move. It seems like intlist.set() isn't working, but I'm not sure why. Help would be appreciated.
import javax.swing.JOptionPane;
int x = 200;
int y = 150;
int b = 50;
float slope = -1;
int numOfCircles = 10;
IntList initPosX = new IntList();
IntList initPosY = new IntList();
IntList exes = new IntList();
IntList whys = new IntList();
IntList xSpeeds = new IntList();
IntList ySpeeds = new IntList();
void setup()
{
numOfCircles = int(JOptionPane.showInputDialog(frame, "How many circles ya want?"));
size(800,400);
for(int i = 0; i < numOfCircles; i++)
{
int toAddX = int(random(0,400));
initPosX.append(toAddX);
int toAddY = int(random(0,300));
initPosY.append(toAddY);
exes.append(0);//(int(random(-30,30)));
whys.append(0);//(int(random(-30,30)));
xSpeeds.append(1);
ySpeeds.append(1);
}
}
void draw()
{
background(100,100,100,255);
for(int i = 0; i < numOfCircles; i++)
{
ellipse(exes.get(i) + initPosX.get(i), whys.get(i) + initPosY.get(i), 20, 20);
exes.set(i, i + xSpeeds.get(i));
whys.set(i, i + ySpeeds.get(i));
if(exes.get(i) > width || exes.get(i) <= 0)
{
print("side wall hit");
xSpeeds.set(i, i*= slope);
}
if(whys.get(i) > height || whys.get(i) <= 0)
{
print("roof hit");
ySpeeds.set(i, i*= slope);
}
}
}
The problem is at those lines:
exes.set(i, i + xSpeeds.get(i));
whys.set(i, i + ySpeeds.get(i));
What you want to do there, is add the speed to the current value of exes/whys at the index i.
But what you are actually doing is set them to the index + the speed.
Since the index is never going to change, neither are the positions.
To fix this, replace it with this:
exes.set(i, exes.get(i) + xSpeeds.get(i));
whys.set(i, whys.get(i) + ySpeeds.get(i));
Update
When changing just this, your code still won't work properly, because the collision detection:
if(exes.get(i) > width || exes.get(i) <= 0)
{
print("side wall hit");
xSpeeds.set(i, i*= slope);
}
if(whys.get(i) > height || whys.get(i) <= 0)
{
print("roof hit");
ySpeeds.set(i, i*= slope);
}
does not detect collision for the actual position, because that would be the position (exes, whys) + the initPos's, so it should be
if (exes.get(i) + initPosX.get(i) > width || exes.get(i) + initPosX.get(i) <= 0)
{
//the code
}
if (whys.get(i) + initPosY.get(i) > height || whys.get(i) + initPosY.get(i) <= 0)
{
//the code
}
If you were to start it now however, you would get an error. That is because you changing to something negative. instead of i*= slope just use int(i * slope) (because int * float returns a float, you have to convert the result to an int by using int()).
Furthermore, you again don't actually want the index, but the current value at the index:
xSpeeds.set(i, int(xSpeeds.get(i) * slope); //the same for ySpeeds
To preface, I'll elaborate a bit more about what the current project is. I'm working on a game that puts the player in a room-based dungeon. Rooms are entered/exited through doors.
Here is the generator() method I have for my floor creator.
The turn(int, int, int) method takes in a direction (randomly created number 0 - 3), and two starting locations, and x and y coordinate, and returns an array that contains the previous numbers, but depending on the direction, one was modified by increment/decrements of one. For example, if the direction was 0, that would mean up, and the yCoordinate would be subtracted by 1. In the array, returned, the first value is he X, the second is the Y.
int roomTag = 1; //tags the room as created
int currentX = startLocation; //the column that navigates the map
int currentY = startLocation;
map[currentY][currentX] = roomTag;
int rooms = 0;
while(rooms < maxRooms) {
int direction = randomRange(0, 4);
currentX = turn(direction, currentX, currentY)[0];
currentY = turn(direction, currentX, currentY)[1];
if(currentX > 0 && currentY > 0 && currentX < maxSize && currentY < maxSize) {
if(map[currentY][currentX] != roomTag) {
map[currentY][currentX] = roomTag;
roomList[currentY][currentX] = new NormalRoom(this, currentX, currentY, rooms);
rooms++;
}
} else {
currentX = startLocation;
currentY = startLocation;
}
}
roomList[startLocation][startLocation] = new StartRoom(this, startLocation, startLocation, 0);
As of now, the procedural generation works great. It generates the dungeon differently each time, and all rooms are connected horizontally or vertically to at least one other room.
Rooms simply hold the information about the doors, location, and enemies, if applicable. Rooms have a method for searching for which doors to create. Here is the method that is called upon instantiation of the room. It is called first to get the amount of doors that are to be created, then sets the boolean created to true, then runs the method again to create/add the doors to the array.
int doorIndex = doorCount - 1;
int[][] tempMap = floor.getMap();
if(yLocation > 0) {
for(int i = 0; i < tempMap.length; i++) {
for(int j = 0; j < tempMap.length; j++) {
if(i == tempMap.length/2 && j == tempMap.length/2) System.out.print("() ");
else if(tempMap[j][i] == 1) System.out.print("[] ");
else System.out.print(" ");
}
System.out.println(" ");
}
if(tempMap[yLocation - 1][xLocation] == 1) {
System.out.println("UP DOOR IN: (" + yLocation + ", " + xLocation + ")");
if(created) {
door[doorIndex] = new Door(0);
doorIndex--;
} else {
doorCount++;
}
}
}
if(yLocation < floor.getMap().length - 1) {
if(tempMap[yLocation + 1][xLocation] == 1) {
System.out.println("DOWN DOOR IN: (" + yLocation + ", " + xLocation + ")");
if(created) {
door[doorIndex] = new Door(2);
doorIndex--;
} else {
doorCount++;
}
}
}
if(xLocation < floor.getMap().length - 1) {
if(tempMap[yLocation][xLocation + 1] == 1) {
System.out.println("RIGHT DOOR IN: (" + yLocation + ", " + xLocation + ")");
if(created) {
door[doorIndex] = new Door(1);
doorIndex--;
} else {
doorCount++;
}
}
}
if(xLocation > 0) {
if(tempMap[yLocation][xLocation - 1] == 1) {
System.out.println("LEFT DOOR IN: (" + yLocation + ", " + xLocation + ")");
if(created) {
door[doorIndex] = new Door(3);
} else {
doorCount++;
}
}
}
if(created) {
for(int i = 0; i < door.length; i++) {
door[i].instantiate();
}
}
The problem seems to be that it never creates left doors, and rarely creates up doors. I cannot seem to find out why it is doing this. I have been stuck on this for awhile now and cannot seem to find what is causing it, as I cannot find any consistencies in the issue.
I leaned on the side of posting less code, but if more is needed, let me know.
Thanks in advance.
Edit: I may have found the solution. Given the fact the method is called during instantiation of the rooms, it cannot find the other rooms as they don't exist yet. The rooms can only create doors for rooms that were created before it. If they were created after, the map would't have them listed as existing yet.
I will try to amend the problem with this in mind.
EDIT 2: This was the problem. Creating the rooms after creating the map fixed it. I simply made it so the room creator was separate from the map creator, then I created the rooms based on the map.
The problem was that I was instantiating the rooms as I was creating them, and part of the rooms' instantiation was finding the amount of and the location of the doors. The problem with this is that I was asking the algorithm to find rooms that weren't created yet.
To fix this, I used the map I created earlier, that was a 2DArray, with the rooms marked with 1s, and everything else marked with a 0. After the map was fully created, I iterated through the map, and put a Room in the coordinates marked with a 1 in a separate 2DArray that contained Room objects.
The doors now accurately lead to new rooms as they are supposed to.
I am trying to save the method outOfBounds which is called inside the lengthOfColor method more than once to a local variable, so that less processing power is used. I provided the lengthOfColor method in which I want to store the variable, and I also provided the outOfBounds method. As you can see the outOfBounds method is a boolean and I am not sure how to store it with integer parameters.
private Integer[] lengthOfColor(int col, boolean color, int pattern, int row) {
int x = 0;
int y = 0;
if (pattern == 1) {
// vertical pattern
y = 1;
} else if (pattern == 2) {
// horizontal pattern
x = 1;
} else if (pattern == 3) {
// diagonal slope left pattern
x = 1;
y = 1;
} else {
// diagonal slope right pattern
x = 1;
y = -1;
}
// length = how many neighbor slots are of same color
// possible equals number of slots, that you can play off of.
// whichSide = left or right if horizontal and top or bottom if vertical.
int length = 0;
int possible = 0;
Integer[] whichSide = new Integer[]{1, -1};
for (int side : whichSide) {
int i = 1;
boolean complete = false;
//while complete is false continue the loop
while (!complete) {
//mainX == horizontal pattern distance
//mainY == vertical pattern distance
int mainX = x * i * side;
int mainY = y * i * side;
//if still inbounds and if the same slot is filled and it matches the color, increment length
if (!outOfBounds(col, mainX, mainY, row) && getIsFilled(col, mainX, mainY, row) &&
checkColor(col, mainX, mainY, row) == color)
{
length++;
}
//if still inbounds and if the same slot is empty, increment possible number of spots and change complete to true
else if (!outOfBounds(col, mainX, mainY, row) && !getIsFilled(col, mainX, mainY, row) &&
getLowestEmptyIndex(myGame.getColumn(col + mainX)) == getLowestEmptyIndex(myGame.getColumn(col)) + mainY - row)
{
possible++;
complete = true;
}
//finish the statement to avoid a infinite loop if neither conditions are met.
else
{
complete = true;
}
// If not complete, then check one slot further.
i = i + 1;
}
}
return new Integer[] {length, possible};
}
private boolean outOfBounds(int col, int x , int y, int row)
{
int currentX = col;
int currentY = getLowestEmptyIndex(myGame.getColumn(col)) - row;
return currentX + x >= myGame.getColumnCount() || currentY + y >= myGame.getRowCount() || currentX + x < 0 || currentY + y < 0;
}
I see that mainX and mainY change values so there isn't any real optimization that can be done outside of the for and while loop besides creating a boolean value that holds the result of outOfBounds before the if check is called which would reduce the number of operations you need to do. To be honest, the optimization is so insignificant that it wouldn't really matter but would be good coding practice I suppose (JIT might optimize for you as well depending on your code). More importantly the method reduces the extra lines of code you need to type and does not necessarily mean that there is less computing.
So something like this before any outOfBounds call but inside the while loop,
boolean outOfBounds = outOfBounds(col, mainX, mainY, row);
and change your current if(!outOfBounds(col, mainX, mainY, row) && ....) into if (!outOfBounds && ...)
Also the #1 rule to optimization is to not optimize until you are done with your project and notice a significant performance dip. In which case you would start with the biggest bottleneck until the optimal performance is gained. Of course this does not mean coding in an incorrect way which would of course create unnecessary performance losses. In those cases it would also be wise to consider whether or not you are looking at the problem the right way rather than micro-optimizing.
Here's a snippet of what I would do to micro-optimize the code shown.
private Integer[] lengthOfColor(int col, boolean color, int pattern, int row) { // consider changing Integer[] into
// int[] if you don't need a boxed integer. It will increase performance
int x = 0;
int y = 0;
// length = how many neighbor slots are of same color
// possible equals number of slots, that you can play off of.
// whichSide = left or right if horizontal and top or bottom if vertical.
int length = 0;
int possible = 0;
switch (pattern) { // switch may be a tad faster but insignificant. More importantly it provides clarity.
case 1:
y = 1;
break;
case 2:
x = 1;
break;
case 3:
x = 1;
y = 1;
break;
default:
x = 1;
y = -1;
break;
}
//int[] whichSide = new int[]{1, -1}; // changed to int[] because you don't need a boxed primitive from what is
// shown
// nevermind, this line isn't needed and you will be able to avoid an instantiation.
for (int i = 1; i != -3; i-=2) {
int count = 1;
int mainX; // bring this to a higher scope. (honestly this is micro optimization but a habit of mine if this is
// can be considered in scope)
int mainY;
boolean outOfBounds = false;
//boolean complete = false; // removed as its unnecessary to break out of the while loop.
//while complete is false continue the loop
while (true) {
//mainX == horizontal pattern distance
//mainY == vertical pattern distance
mainX = x * count * i;
mainY = y * count * i;
outOfBounds = outOfBounds(col, mainX, mainY, row);
//if still inbounds and if the same slot is filled and it matches the color, increment length
if (!outOfBounds && getIsFilled(col, mainX, mainY, row) &&
checkColor(col, mainX, mainY, row) == color) {
length++;
}
//if still inbounds and if the same slot is empty, increment possible number of spots and change complete to
// true
else if (!outOfBounds && !getIsFilled(col, mainX, mainY, row) &&
getLowestEmptyIndex(myGame.getColumn(col + mainX)) == getLowestEmptyIndex(myGame.getColumn(col)) + mainY -
row) {
possible++;
break;
}
//finish the statement to avoid a infinite loop if neither conditions are met.
else {
break;
}
// If not complete, then check one slot further.
count++;
}
}
return new Integer[]{length, possible}; // once again consider whether or not you need a boxed integer
}
private boolean outOfBounds(int col, int x, int y, int row) {
//int currentX = col; this is an unnecessary line
int currentY = getLowestEmptyIndex(myGame.getColumn(col)) - row;
return col + x >= myGame.getColumnCount() || currentY + y >= myGame.getRowCount() || col + x < 0 ||
currentY + y < 0;
}
I'm working on creating an inventory system for a game. The inventory system will use cells and 2D images to represent items, like Minecraft, Diablo, or WoW. I've hit a bit of a snag when trying to let the player place items in the bag (click and drag style).
So far, I've successfully created the inventory grid, which is really smoke and mirrors:
public class InventoryMenu {
boolean objectSelected = false;
Item selectedItem;
public final int COLUMNS = 5;
public final int ROWS = 7;
ArrayList<Item> inv = new ArrayList<Item>((ROWS + 1) * COLUMNS);
Sprite slot;
public void render(Graphics g) {
for (int i = 0; i < 40; i++) {
int col = i % COLUMNS;
int row = ROWS - i / COLUMNS;
slot.draw(g, (Camera.width - slot.getWidth() * COLUMNS) + slot.getWidth()* col, row * slot.getHeight());
if (inv.get(i) != null) {
inv.get(i).render(g);
}
}
if (selectedItem != null) {
selectedItem.render(g);
}
}
Basically, I have an arraylist that can hold items, and it just draws the "slot" image 40 times and if there's an item in that "slot" it draws the item image too. Cool? Cool. The problem comes if I want to allow the player to select an item in their inventory and move it to a different slot. I have no problem letting them pick it up (I use a pretty primitive brute force, but with any reasonably sized inventory, it works):
private Item grabItem(Point2D mouse) {
for (Item i : inv) {
if (i != null) {
if (i.getPhysicsShape().contains(mouse)) {
Item pick = i;
selectedItem = pick;
objectSelected = true;
i = null;
return pick;
}
}
}
return null;
}
That bit of code lets a player pick up an item, but placing it fails - it rarely gets the right slot, except if its the bottom row (0-4):
setDown(){
int slotLoc = calcSlot(InputHandler.mouseCoords);
placeItem(slotLoc);
}
private void placeItem(int loc) {
if(loc < 0 || loc > ROWS * (COLUMNS + 1))
return;
int col = loc % COLUMNS;
int row = ROWS - loc / COLUMNS;
selectedItem.pickUp((Camera.width - slot.getWidth() * COLUMNS) + slot.getWidth() * col, row * slot.getHeight());
inv.set(loc, selectedItem);
selectedItem = null;
objectSelected = false;
}
private int calcSlot(Point2D mouse){
int colPos = (int) (COLUMNS - (Camera.width - mouse.getX()) / slot.getWidth());
int rowPos = (int) (ROWS+1 - (mouse.getY()) / slot.getHeight());
if (colPos > COLUMNS || colPos < 0 || rowPos < 0 || rowPos > ROWS + 1) {
dropItem();
return -1;
} else {
return colPos + 4*rowPos;
}
}
I'm fairly confident that the problem is in calcSlot, but I can't seem to find where. Any help would be greatly appreciated, I'm sure it's something silly.
Images!!
So, I can pick up an item with no problem, and it automatically places it in the last slot. So far, everything is gold.
I can then click that image and lift it out of the grid, and it follows my mouse (mouse is hidden by PrntScrn, but its at the top left corner of the image:
When I try to place the item by clicking in the middle of slot 33, however, it derps and places it, inexplicably, in slot 27.
private int calcSlot(Point2D mouse){
int colPos = (int) (COLUMNS - (Camera.width - mouse.getX()) / slot.getWidth());
int rowPos = (int) (ROWS - ((mouse.getY())- slot.getHeight()) / slot.getHeight());
if (colPos > COLUMNS || colPos < 0 || rowPos < 0 || rowPos > ROWS + 1) {
dropItem();
return -1;
} else {
return COLUMNS*rowPos + colPos;
}
}
The difference % between 33 and 27 is "6"
So i suggest you look very carefully at the fact the numbers and rows run from bottom to top meaning its landed in the "sixth row up" so your linear calculation in the process for which is a row and which is the coord result is getting in the way somewhere in the calculation process (to hazard a guess).
Try placing it in 32 and see what happens for the cell it places.
but also you show the code for calcSlot twice here and in one version it has "ROWS+1"
Im working on a falling blocks game in java where you have to move the player around the screen to dodge the blocks. Whenever you get hit by a block, depending on the block type, it will either decrease or increase a int in the player class. Im having the problem that when the player is hit by the block the int keeps going down until the now invisible block is offscreen. Basically I just need a way to check through a object array and when a object meets a specified condition such as (delete == true) it will set the current position of that block in the array to null.
Method for updating the block position:
public void dropFoods(int speed) {
for (int x = 699 - speed; x >= 0; x--) {
for (int y = 0; y < 7; y++) {
if(x > (699 - GUI.HEIGHT) - 10) {
food[y][x] = null;
continue;
}
food[y][x + speed] = food[y][x];
food[y][x] = null;
}
}
}
Method for drawing the blocks (the food are the different types of blocks):
for(int x = 0; x < food.length; x++) {
for(int y = 0; y < food[x].length; y ++) {
Object o = food[x][y];
if(o instanceof Apple) {
new Apple(x * 100, y - Apple.HEIGHT, g);
}
if(o instanceof Burger) {
new Burger(x * 100, y - Burger.HEIGHT, g);
}
}
}
Method for detecting collision with player:
if(Food.getHitBox().intersects(Player.hitBox())) {
willDraw = false;
Player.weight -= 1;
}
You should provide more code and debugging result first.
if(Food.getHitBox().intersects(Player.hitBox())) {
willDraw = false;
Player.weight -= 1;
}
So when something gets hit by player, do you remove the Food object off the map? Because according to this code, you are not.