I was wondering if someone could help figure this out. I've been trying to display a pyramid using nested for loops and I've only been able to get the first row (base row) working. The pyramid is suppose to have 10 rectangles at the bottom and as it increments up, the rectangle count decreases to 9, 8, 7, 6, etc. I've been looking at this for days and have had no luck.
Thank you!
public class Legos2 extends JFrame {
private int startX;
private int startY;
private int legoWidth;
private int legoHeight;
private int baseLength;
private int arcWidth;
private int arcHeight;
// Constructor
public Legos2() {
super("Jimmy's LEGOs");
startX = 20;
startY = 300;
legoWidth = 50;
legoHeight = 20;
baseLength = 10;
arcWidth = 2;
arcHeight = 2;
}
// The drawings in the graphics context
public void paint(Graphics g)
{
// Call the paint method of the JFrame
super.paint(g);
int currentX = startX;
int currentY = startY;
//row = 0 is the bottom row
for (int row = 1; row <= baseLength; row++)
{
currentX = currentX + legoWidth;
if (row % 2 == 0)
g.setColor(Color.blue);
else
g.setColor(Color.red);
System.out.println(row);
for (int col = 0; col <= baseLength; col++)
{
System.out.println(col);
g.fillRoundRect(currentX, currentY, legoWidth, legoHeight, arcWidth, arcHeight);
}
//currentY = currentY - legoHeight;
}
}
// The main method
public static void main(String[] args) {
Legos2 app = new Legos2();
// Set the size and the visibility
app.setSize(700, 500);
app.setVisible(true);
// Exit on close is clicked
app.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
The currentY value should be decremented at each iteration of the outer loop: for each row, you want to restart from a lower Y. So you should uncomment the line
//currentY = currentY - legoHeight;
The currentX must be incremented after each column, so at the end of the inner loop, and not at the beginning of the outer loop. And it must be reset to the start X position of the current row before you enter the inner loop.
If you just reset currentX to startX, you'll get a wall of bricks. But you need a pyramid. So there should be one less iteration of the inner loop at each iteration of the outer loop, and the startX should also be incremented after each iteration of the outer loop:
for (int row = 1; row <= baseLength; row++) {
currentX = startX;
if (row % 2 == 0) {
g.setColor(Color.blue);
}
else {
g.setColor(Color.red);
}
System.out.println("row = " + row);
for (int col = 0; col <= baseLength - row; col++) {
System.out.println("col = " + col);
g.fillRoundRect(currentX, currentY, legoWidth, legoHeight, arcWidth, arcHeight);
currentX = currentX + legoWidth;
}
currentY -= legoHeight;
startX += legoWidth / 2;
}
Related
So I created an "UpdateShopPosition" method, that should place Sprites in correct spots on the screen. However when I launch it in the render method, it seems that every Sprite shares the exact same position, not only that but the position keeps moving right and down.
I want MyX and MyY to have a different value for each shopList.get(i) . Is there a problem with the loop or is it because I placed this method in the render() section?
public class ObjectCreator {
public static int rowsX = 4;
public static int rowsY = 5;
public static void UpdateShopPosition(List<Sprite> spriteList){
for (int i = 0; i < spriteList.size(); i++) {
float myX = playerSprite.getX() + ((i + 4) * 100);
if (i > 0){
myX+= i*3;
}
float myY = playerSprite.getY() + 410;
if (i >= rowsX && i < rowsX*2 ) {
myX = myX - 400;
myY = myY - 101;
}
else if (i >= rowsX*2 && i < rowsX*3){
myX = myX - 800;
myY = myY - 202;
}
else if (i >= rowsX*3 && i < rowsX*4){
myX = myX - 1200;
myY = myY - 303;
}
else if (i >= rowsX*4 && i < rowsX*5){
myX = myX - 1600;
myY = myY - 404;
}
else if (i >= rowsX*5 && i < rowsX*6){
myX = myX - 2000;
myY = myY - 505;
}
spriteList.get(i).setPosition(myX, myY);
}
}
public static void CreateUsable(Usables name, Sprite sprite){
GameApp.shopList.add(sprite);
name.setImage(GameApp.shopList.get(0));
}
}
for (int i = 0; i < usableList.size(); i++) {
usableList.get(i).draw(batch);
usableList.get(i).setPosition(playerSprite.getX() - 1200, playerSprite.getY() + 300);
usableList.get(i).setSize(60,60);
// the next (i) should be placed on the next grid, so a little bit to the right
}
I tried to create a method that keeps placing items sprites respectively to my player sprite, I want these sprites to have the same position no matter where I move on the map
I'm trying to make a minesweeper clone using Processing's java. I have managed to create a grid and each tile has a randomized value assigned to it, but for some reason, only the top left tile can be clicked, the rest of the tiles do not return a value. If anyone knows whats wrong with my code or what I could try that would be great, thanks
Here's my Java code:
int realWidth = 500;
int realHeight = 500;
int tilemount = 0;
boolean mousePress = false;
//calls class
Tile[] tile = new Tile[100];
float tileSize = realWidth/10;
//sets size of window
void settings() {
size(realWidth, realHeight);
}
//Draws 100 tiles on the grid and assignemts each with a value from -1 - 9
void setup() {
int x = 0;
int y = 0;
for (int i = 0; i < tile.length; i++) {
tile[i] = new Tile(x, y, int(random(-1,9)), tilemount);
x += tileSize;
if (x > realWidth-tileSize) {
x = 0;
y += tileSize;
}
tilemount ++;
}
print("done");
}
//updates each tile in the list
void draw() {
background(50);
for (int i = 0; i < tile.length; i++) {
tile[i].display();
tile[i].tileClicked();
}
//checks if tiles are clicked
clickCheck();
}
//sets up tile class
class Tile {
int x;
int y;
int value;
int tilemount;
Tile(int x, int y, int value, int tilemount) {
this.x = x;
this.y = y;
this.value = value;
this.tilemount = tilemount;
}
//positions the tile based off of display values
void display() {
rect(x, y, tileSize, tileSize);
}
//the problem:
/*if tile is clicked, it should run through all 100 tiles in the list,
detect if the mouse is inside the x value assinged to each tile, and produce the
value of the tile the mouse is currently inside */
void tileClicked() {
if (mousePressed && mousePress == false) {
mousePress = true;
for (int i = 0; i < tile.length; i++) {
//println(tile[i].x, tile[i].y);
//println(tile[2].x, tile[2].y, mouseX, mouseY);
if (mouseX > tile[i].x && mouseX < tileSize && mouseY > tile[i].y && mouseY < tileSize) {
println(i, tile[i].value);
break;
}
}
}
}
}
//Checks if mouse is clicked
void clickCheck() {
if (!mousePressed) {
mousePress = false;
}
}
There is a bit of confusion over classes/objects and global variables in your code.
I say that for a few reasons:
boolean mousePress = false; is a global variable, accessible throughout the sketch, however you are accessing it and changing it from each Tile instance, resetting it's value: you probably meant to use a mousePress property for Tile. This way, each Tile has it's mousePress without interfering with one another.
in tileClicked() you're itereratting through all the tiles, from Tile which means unecessarily doing the loop for each time: each tile can check it's own coordinates and position to know if it's being clicked or not (no need for the loop)
speaking of checking bounds, checking if mouseX < tileSize and mouseY < tileSize will only check for the top left tile: you probably meant mouseX < x + tileSize and mouseY < y + tileSize
Here's a version of your sketch with the above notes applied
(and optional debug text rendering to double check the printed value against the tile):
int realWidth = 500;
int realHeight = 500;
int tilemount = 0;
//calls class
Tile[] tile = new Tile[100];
float tileSize = realWidth/10;
//sets size of window
void settings() {
size(realWidth, realHeight);
}
//Draws 100 tiles on the grid and assignemts each with a value from -1 - 9
void setup() {
int x = 0;
int y = 0;
for (int i = 0; i < tile.length; i++) {
tile[i] = new Tile(x, y, int(random(-1,9)), tilemount);
x += tileSize;
if (x > realWidth-tileSize) {
x = 0;
y += tileSize;
}
tilemount ++;
}
println("done");
}
//updates each tile in the list
void draw() {
background(50);
for (int i = 0; i < tile.length; i++) {
tile[i].display();
tile[i].tileClicked();
//checks if tiles are clicked
tile[i].clickCheck();
}
}
//sets up tile class
class Tile {
int x;
int y;
int value;
int tilemount;
boolean mousePress = false;
Tile(int x, int y, int value, int tilemount) {
this.x = x;
this.y = y;
this.value = value;
this.tilemount = tilemount;
}
//positions the tile based off of display values
void display() {
fill(255);
rect(x, y, tileSize, tileSize);
fill(0);
text(value,x + tileSize * 0.5, y + tileSize * 0.5);
}
//the problem:
/*if tile is clicked, it should run through all 100 tiles in the list,
detect if the mouse is inside the x value assinged to each tile, and produce the
value of the tile the mouse is currently inside */
void tileClicked() {
if (mousePressed && mousePress == false) {
mousePress = true;
//println(tile[2].x, tile[2].y, mouseX, mouseY);
if (mouseX > x && mouseX < x + tileSize && mouseY > y && mouseY < y + tileSize) {
println(value);
}
}
}
//Checks if mouse is clicked
void clickCheck() {
if (!mousePressed) {
mousePress = false;
}
}
}
I understand the intent of the mousePress variable, but I'd like to mousePressed() function which you can use to avoid de-bouncing/resetting this value and simplify code a bit.
Here's a version of the above sketch using mousePressed() (and renaming variables a touch to be in line with Java naming conventions):
int realWidth = 500;
int realHeight = 500;
int tileCount = 0;
//calls class
Tile[] tiles = new Tile[100];
float tileSize = realWidth/10;
//sets size of window
void settings() {
size(realWidth, realHeight);
}
//Draws 100 tiles on the grid and assignemts each with a value from -1 - 9
void setup() {
int x = 0;
int y = 0;
for (int i = 0; i < tiles.length; i++) {
tiles[i] = new Tile(x, y, int(random(-1,9)), tileCount);
x += tileSize;
if (x > realWidth-tileSize) {
x = 0;
y += tileSize;
}
tileCount ++;
}
println("done");
}
//updates each tile in the list
void draw() {
background(50);
for (int i = 0; i < tiles.length; i++) {
tiles[i].display();
}
}
void mousePressed(){
for (int i = 0; i < tiles.length; i++) {
tiles[i].click();
}
}
//sets up tile class
class Tile {
int x;
int y;
int value;
int index;
Tile(int x, int y, int value, int tileCount) {
this.x = x;
this.y = y;
this.value = value;
this.index = tileCount;
}
//positions the tile based off of display values
void display() {
fill(255);
rect(x, y, tileSize, tileSize);
// optional: display the tile value for debugging purposes
fill(0);
text(value, x + tileSize * 0.5, y + tileSize * 0.5);
}
//the problem:
/*if tile is clicked, it should detect if the mouse is inside the
value assinged to each tile, and produce the
value of the tile the mouse is currently inside */
void click() {
if (mouseX > x && mouseX < x + tileSize && mouseY > y && mouseY < y + tileSize) {
println("index", index, "value", value);
}
}
}
Good start though: keep going!
The rest of the code is good, congrats on using a 1D array for a 2D grid layout (and the logic for resetting the x position on each row). Have fun learning !
I think you did a good job naming functions and classes and what not. From this point, I would recommend breaking your code down so that you can make sure that at least 3 tiles function fully before allowing the tilelength to be something like 100 (for 100 tiles).
so from what I can see:
+ You have 3 apostrophes at the end of your code that need to be removed.
+ you use tile.length but never defined a length variable in your class, so when your for loop runs it has no number to use to determine it's number of cycles.
+ Please double check your logic in the tileClicked function with the help of a truth/logic table: https://en.wikipedia.org/wiki/Truth_table . I didn't look too deeply into this but this looks like an area where one misunderstanding could throw ;
Try adding this.length = 3; in your tile class and then re-run your code to see if more tiles pop up.
And it might be good to use some system.out.println calls to print conditional values out so that you can see the intermeediate results and compare them to what you expect to happen. Just some basic debugging.
Good luck.
How would I go about modifying my current code for a checker board so that the checker pieces recursively alternate in color? Just to be clear, I don't want each piece to be a solid color - I want them to have levels that alternate in color in on itself. So for example, the currently yellow pieces would change to being yellow and blue pieces, having an outer level of yellow, followed by a level of blue, then yellow, etc. I hope that makes sense? I don't believe I can highlight code, but the checker pieces start after the first nested for statement in the checkerBoard method. There are 2 cases, the first being the top 2 rows, and the second being the bottom two.
import java.awt.*;
import java.applet.*;
public class Checkerboard extends Applet
{
private final int DIST = 100;
private final int SIZE = 1000;
public void checkerBoard(int row, int col, int x, int y, boolean b, Graphics g)
{
for ( row = 0; row < 8; row++ )
{
for ( col = 0; col < 8; col++)
{
x = col * 100;
y = row * 100;
if ( (row % 2) == (col % 2) )
g.setColor(Color.black);
else
g.setColor(Color.red);
g.fillRect(x, y, 100, 100);
}
}
for ( row = 0; row < 2; row++ )
{
for ( col = 0; col < 8; col++)
{
x = col * 100;
y = row * 100;
g.setColor(Color.yellow);
g.fillOval(x, y, 100, 100);
}
}
for ( row = 7; row > 5; row-- )
{
for ( col = 0; col < 8; col++)
{
x = col * 100;
y = row * 100;
g.setColor(Color.green);
g.fillOval(x, y, 100, 100);
}
}
}
public void paint(Graphics g)
{
checkerBoard(0, 0, 0, 0, true, g);
}
}
is this what you want
for ( row = 0; row < 2; row++ )
{
for ( col = 0; col < 8; col++)
{
for ( int ring = 0; ring < 5; ring++) {
x = col * 100 + (ring * 10);
y = row * 100 + (ring * 10);
if((ring & 1) == 0){
g.setColor(Color.yellow);
}else{
g.setColor(Color.blue);
}
g.fillOval(x, y, 100-(ring*20), 100-(ring*20));
}
}
}
recursive method would be like,
private void drawCircle(int x, int y, int circleSize, int ringSize, Color primary, Color alternate, Graphics g){
if(circleSize > 0){
g.setColor(primary);
g.fillOval(x, y, circleSize,circleSize);
drawCircle(x+ringSize/2,y+ringSize/2,circleSize-ringSize,ringSize,alternate,primary, g);
}
}
for ( row = 0; row < 2; row++ )
{
for ( col = 0; col < 4; col++)
{
int y = row * 100;
int x = ((col * 2) + (col & 1)) * 100; // want to alternate squares
drawCircle(x, y, 100, 20, Color.Yellow, Color.Blue,g);
}
}
I am having a bit of trouble making this grid be drawn 10 pixels from the top and 10 pixels from the left of the Frame.
I can make it do it by increasing this.getY() + 10 to a higher number, just wondering why if is remove the + 10 it getting drawn off screen.
Ignore the variable names and any formatting I just threw this together
package griddrawing;
import java.awt.*;
import javax.swing.*;
public class Grid extends JFrame
{
private int TILEWIDTH;
private int TILEHEIGHT;
private int COLS;
private int ROWS;
private int defaultX;
private int defaultY;
private int currentX;
private int currentY;
public Grid()
{
setSize(800,400);
TILEWIDTH = 30;
TILEHEIGHT = 30;
COLS = 10;
ROWS = 10;
defaultX = this.getX() + 10;
defaultY = this.getY() + 10;
currentX = 0;
currentY = 0;
}
#Override
public void paint(Graphics g)
{
super.paint(g);
currentX = defaultX;
currentY = defaultY;
g.setColor(Color.black);
for(int i = 0; i < COLS; i++)
{
for(int k = 0 ; k < ROWS; k++)
{
g.drawRect(currentX - (TILEWIDTH / 2), currentY - (TILEHEIGHT / 2), TILEWIDTH, TILEHEIGHT);
g.drawString("" + k, currentX, currentY);
currentY += TILEWIDTH;
System.out.println("COL: " + i + " ROW: " + k + " Current X: " + currentX + " Current Y: " + currentY);
}
currentY = defaultY;
currentX += TILEHEIGHT;
}
}
}
Don't set the size of the frame.
Don't paint directly to the frame either.
Instead of both:
Override the paintComponent(Graphics) method of a JComponent or JPanel.
Either call theComponent.setPreferredSize(Dimension) or override that same method.
Add the custom component to the frame and call pack().
That lot should mean you no longer need to account for any offset (which might change by platform or PLAF).
I have a sprite sheet which has each image centered in a 32x32 cell. The actual images are not 32x32, but slightly smaller. What I'd like to do is take a cell and crop the transparent pixels so the image is as small as it can be.
How would I do that in Java (JDK 6)?
Here is an example of how I'm currently breaking up the tile sheet into cells:
BufferedImage tilesheet = ImageIO.read(getClass().getResourceAsStream("/sheet.png");
for (int i = 0; i < 15; i++) {
Image img = tilesheet.getSubimage(i * 32, 0, 32, 32);
// crop here..
}
My current idea was to test each pixel from the center working my way out to see if it is transparent, but I was wondering if there would be a faster/cleaner way of doing this.
There's a trivial solution – to scan every pixel. The algorithm bellow has a constant performance of O(w•h).
private static BufferedImage trimImage(BufferedImage image) {
int width = image.getWidth();
int height = image.getHeight();
int top = height / 2;
int bottom = top;
int left = width / 2 ;
int right = left;
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
if (image.getRGB(x, y) != 0){
top = Math.min(top, y);
bottom = Math.max(bottom, y);
left = Math.min(left, x);
right = Math.max(right, x);
}
}
}
return image.getSubimage(left, top, right - left + 1, bottom - top + 1);
}
But this is much more effective:
private static BufferedImage trimImage(BufferedImage image) {
WritableRaster raster = image.getAlphaRaster();
int width = raster.getWidth();
int height = raster.getHeight();
int left = 0;
int top = 0;
int right = width - 1;
int bottom = height - 1;
int minRight = width - 1;
int minBottom = height - 1;
top:
for (;top <= bottom; top++){
for (int x = 0; x < width; x++){
if (raster.getSample(x, top, 0) != 0){
minRight = x;
minBottom = top;
break top;
}
}
}
left:
for (;left < minRight; left++){
for (int y = height - 1; y > top; y--){
if (raster.getSample(left, y, 0) != 0){
minBottom = y;
break left;
}
}
}
bottom:
for (;bottom > minBottom; bottom--){
for (int x = width - 1; x >= left; x--){
if (raster.getSample(x, bottom, 0) != 0){
minRight = x;
break bottom;
}
}
}
right:
for (;right > minRight; right--){
for (int y = bottom; y >= top; y--){
if (raster.getSample(right, y, 0) != 0){
break right;
}
}
}
return image.getSubimage(left, top, right - left + 1, bottom - top + 1);
}
This algorithm follows the idea from pepan's answer (see above) and is 2 to 4 times more effective. The difference is: it never scans any pixel twice and tries to contract search range on each stage.
The worst case of the method's performance is O(w•h–a•b)
This code works for me. The algorithm is simple, it iterates from left/top/right/bottom of the picture and finds the very first pixel in the column/row which is not transparent. It then remembers the new corner of the trimmed picture and finally it returns the sub image of the original image.
There are things which could be improved.
The algorithm expects, there is the alpha byte in the data. It will fail on an index out of array exception if there is not.
The algorithm expects, there is at least one non-transparent pixel in the picture. It will fail if the picture is completely transparent.
private static BufferedImage trimImage(BufferedImage img) {
final byte[] pixels = ((DataBufferByte) img.getRaster().getDataBuffer()).getData();
int width = img.getWidth();
int height = img.getHeight();
int x0, y0, x1, y1; // the new corners of the trimmed image
int i, j; // i - horizontal iterator; j - vertical iterator
leftLoop:
for (i = 0; i < width; i++) {
for (j = 0; j < height; j++) {
if (pixels[(j*width+i)*4] != 0) { // alpha is the very first byte and then every fourth one
break leftLoop;
}
}
}
x0 = i;
topLoop:
for (j = 0; j < height; j++) {
for (i = 0; i < width; i++) {
if (pixels[(j*width+i)*4] != 0) {
break topLoop;
}
}
}
y0 = j;
rightLoop:
for (i = width-1; i >= 0; i--) {
for (j = 0; j < height; j++) {
if (pixels[(j*width+i)*4] != 0) {
break rightLoop;
}
}
}
x1 = i+1;
bottomLoop:
for (j = height-1; j >= 0; j--) {
for (i = 0; i < width; i++) {
if (pixels[(j*width+i)*4] != 0) {
break bottomLoop;
}
}
}
y1 = j+1;
return img.getSubimage(x0, y0, x1-x0, y1-y0);
}
I think this is exactly what you should do, loop through the array of pixels, check for alpha and then discard. Although when you for example would have a star shape it will not resize the image to be smaller be aware of this.
A simple fix for code above. I used the median for RGB and fixed the min() function of x and y:
private static BufferedImage trim(BufferedImage img) {
int width = img.getWidth();
int height = img.getHeight();
int top = height / 2;
int bottom = top;
int left = width / 2 ;
int right = left;
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
if (isFg(img.getRGB(x, y))){
top = Math.min(top, y);
bottom = Math.max(bottom, y);
left = Math.min(left, x);
right = Math.max(right, x);
}
}
}
return img.getSubimage(left, top, right - left, bottom - top);
}
private static boolean isFg(int v) {
Color c = new Color(v);
return(isColor((c.getRed() + c.getGreen() + c.getBlue())/2));
}
private static boolean isColor(int c) {
return c > 0 && c < 255;
}
[Hi I tried the following. In the images file idle1.png is the image with a big transparent box while testing.png is the same image with minimum bounding box
'BufferedImage tempImg = (ImageIO.read(new File(fileNPath)));
WritableRaster tempRaster = tempImg.getAlphaRaster();
int x1 = getX1(tempRaster);
int y1 = getY1(tempRaster);
int x2 = getX2(tempRaster);
int y2 = getY2(tempRaster);
System.out.println("x1:"+x1+" y1:"+y1+" x2:"+x2+" y2:"+y2);
BufferedImage temp = tempImg.getSubimage(x1, y1, x2 - x1, y2 - y1);
//for idle1.png
String filePath = fileChooser.getCurrentDirectory() + "\\"+"testing.png";
System.out.println("filePath:"+filePath);
ImageIO.write(temp,"png",new File(filePath));
where the get functions are
public int getY1(WritableRaster raster) {
//top of character
for (int y = 0; y < raster.getHeight(); y++) {
for (int x = 0; x < raster.getWidth(); x++) {
if (raster.getSample(x, y,0) != 0) {
if(y>0) {
return y - 1;
}else{
return y;
}
}
}
}
return 0;
}
public int getY2(WritableRaster raster) {
//ground plane of character
for (int y = raster.getHeight()-1; y > 0; y--) {
for (int x = 0; x < raster.getWidth(); x++) {
if (raster.getSample(x, y,0) != 0) {
return y + 1;
}
}
}
return 0;
}
public int getX1(WritableRaster raster) {
//left side of character
for (int x = 0; x < raster.getWidth(); x++) {
for (int y = 0; y < raster.getHeight(); y++) {
if (raster.getSample(x, y,0) != 0) {
if(x > 0){
return x - 1;
}else{
return x;
}
}
}
}
return 0;
}
public int getX2(WritableRaster raster) {
//right side of character
for (int x = raster.getWidth()-1; x > 0; x--) {
for (int y = 0; y < raster.getHeight(); y++) {
if (raster.getSample(x, y,0) != 0) {
return x + 1;
}
}
}
return 0;
}'[Look at Idle1.png and the minimum bounding box idle = testing.png][1]
Thank you for your help regards Michael.Look at Idle1.png and the minimum bounding box idle = testing.png]images here
If your sheet already has transparent pixels, the BufferedImage returned by getSubimage() will, too. The default Graphics2D composite rule is AlphaComposite.SRC_OVER, which should suffice for drawImage().
If the sub-images have a distinct background color, use a LookupOp with a four-component LookupTable that sets the alpha component to zero for colors that match the background.
I'd traverse the pixel raster only as a last resort.
Addendum: Extra transparent pixels may interfere with collision detection, etc. Cropping them will require working with a WritableRaster directly. Rather than working from the center out, I'd start with the borders, using a pair of getPixels()/setPixels() methods that can modify a row or column at a time. If a whole row or column has zero alpha, mark it for elimination when you later get a sub-image.