I'm creating a game in which the user has to match a randomly generated target image made up of a grid of squares. The user can select an entire row (circle selectors on left) an entire column( circle selectors on top) and one individual cell and that counts as one move. Currently my program allows me to select the row, column and cells (and deselected them) but i haven't been able to figure out a way to fill the selected cells from my color selector at the top of the canvas. I was wondering if anyone could give me any tips on how to achieve this?
final color RED = #D12020;
final color BLUE = #515DD8;
final color GREEN = #21AF20;
final color YELLOW = #F5EF74;
final color ORANGE = #F59219;
final color PURPLE = #B219F5;
final color WHITE = #FFFFFF;
final int ROWS = 12;
final int COLUMNS = 8;
final int BLOCK_SIZE = 40;
int blockSize;
int spacing;
int cornerX;
int cornerY;
int size;
int space;
int cell = -1;
int col = -1;
int row = -1;
color[] colour = {RED, BLUE, GREEN, YELLOW, ORANGE, PURPLE, WHITE};
void setup() {
size(1000, 1000);
background(0);
cornerX = width/8;
cornerY = height/8;
size = height/33;
space = width/100;
targetImage();
}
void draw() {
//background(0);
colourCells();
grid();
selectCell();
columnSelectors();
rowSelectors();
counter();
}
//draws the colour selectors
void colourCells() {
int blockSize = width/20;
int spacing = width/200;
int colourBlocks = 7;
int squareX = blockSize;
for (int i=0; i < colourBlocks; i++) {
stroke(167);
fill(colour[i]);
rect(squareX, 0, blockSize, blockSize);
squareX += spacing+blockSize;
}
}
//draws the grid
void grid() {
cornerX = width/8;
cornerY = height/5;
noFill();
stroke(167);
strokeWeight(1);
for (int i = 0; i<ROWS; i++) {
cornerX = width/8;
for (int j = 0; j<COLUMNS; j++) {
rect(cornerX, cornerY, BLOCK_SIZE, BLOCK_SIZE);
cornerX += BLOCK_SIZE;
}
cornerY += BLOCK_SIZE;
}
}
//changes the selected cell stroke to red (or deselected cell outline back to grey)
void selectCell() {
cornerX = width/8;
cornerY = height/5;
if (cell != -1) {
stroke(RED);
rect(cornerX + (cell % COLUMNS) * BLOCK_SIZE, cornerY + (cell / COLUMNS) * BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE);
stroke(167);
}
}
//creates the circular column selectors
void columnSelectors() {
int columnX = (cornerX) + (size/2) + (space/2);
int columnY = (cornerY) - (size/2) - space;
noFill();
for ( int i = 0; i<COLUMNS; i++) {
if (col != -1 && col == i)
stroke(RED);
else
stroke(167);
ellipse(columnX, columnY, size, size);
columnX += size + space;
}
}
//creates the circular row selectors
void rowSelectors() {
int rowX = (cornerX) - (size/2) - space;
int rowY = (cornerY) + (size/2) + (space/2);
noFill();
for ( int j = 0; j<ROWS; j ++) {
if (row != -1 && row == j)
stroke(RED);
else
stroke(167);
ellipse(rowX, rowY, size, size);
rowY += size + space;
}
}
//creates the score counter at the bottom of canvas
void counter() {
float x = width/3;
float y = 4*height/5;
float boxLength = width/3;
float boxHeight = height/20;
int counter = 0;
fill(255);
rect(x, y, boxLength, boxHeight);
String counterText = "Num Moves: " + counter;
fill(0);
textSize(40);
text(counterText, x + (boxLength*1/14.0), y + (boxHeight*3/4.0));
}
//loads in a random target image
void targetImage() {
float y = height/5;
float x = width/2;
String [] file = {"target0.png", "target1.png", "target2.png", "target3.png", "target4.png"};
PImage target;// create a variable that can point to an off-screen buffer
String filename = file[int(random(0, 4))]; //"target" + int(random(0,4)) + ".png";// create filename
target = loadImage(filename);// load image file into off-screen buffer
image(target, x, y, width/3.125, height/2.08);// display the buffer on canvas at location x, y
}
//allows the user to select/deselect individual cells
int cellSelected() {
int x;
int y;
int xPos= mouseX - cornerX;
int yPos= mouseY - cornerY;
x = xPos/BLOCK_SIZE;
y = yPos/BLOCK_SIZE;
int num = x + (y*COLUMNS);
if (num == cell)
return -1;
else
return num;
}
//allows the user to select/deselect a column
int selectColumn() {
int x;
int xPos= mouseX - cornerX;
x = xPos/(size+space);
if (x == col)
return -1;
else
return x;
}
//allows the user to select/deselect a row
int selectRow() {
int y;
int yPos= mouseY - cornerY;
y = yPos/(size+space);
if (y == row)
return -1;
else
return y;
}
//mouse click functions for selecting cells/rows/columns/colour
void mouseClicked() {
spacing = width/200;
blockSize = width/20;
if (mouseX > cornerX && mouseY > cornerY && mouseX < cornerX + COLUMNS*BLOCK_SIZE
&& mouseY < cornerY + ROWS*BLOCK_SIZE) {
cell = cellSelected();
}
if (mouseX > cornerX && mouseY > (cornerY - (space + size)) && mouseX < cornerX + COLUMNS*BLOCK_SIZE
&& mouseY < cornerY) {
col = selectColumn();
}
if (mouseX > (cornerX - (space + size)) && mouseY > cornerY && mouseX < cornerX
&& mouseY < cornerY + ROWS*BLOCK_SIZE) {
row = selectRow();
}
if (mouseX < blockSize && mouseX > ((blockSize*colour.length) + (spacing*(colour.length - 1)))) {
for (int c = 0; c < colour.length; c++) {
if (get(mouseX, mouseY) == colour[c]) {
fill(colour[c]);
break;
}
}
}
}
/*
to do
fill colour
increse score counter
*/```
[1]: https://i.stack.imgur.com/eyX4g.png
I like your project. And I especially like that you commented it. Good job so far. I should be sleeping, but I'll give you a hand instead. And, let's face it, I'm bad at sleeping anyway.
First, as you can guess, to color your solution you'll have to stock these informations somewhere.
Then, to verify if the player has found the right answer, you'll have to be able to compare his work to the solution.
I see you used an image to show the targeted state. I suggest you code it instead, so then you can compare it more easily (and you can also generate many different "levels" easily).
Java is object-oriented. You didn't wrote any class, so I suppose that you're still learning. Let me tell you something: class are awesome. They are their own object, with their own methods and variables. You can instantiate them in many ways. Like, let's say... an array of your very own object. An object which could be... let's say... a square? A colored square?
Yes. Instead of drawing about 100 squares, you could define one class and let them draw themselves. And remember their own color. And know if they have been clicked on.
And, basically, whatever you want.
Here's an example of a Square class:
class Square{
//modal variables
private float xPos = 0;
private float yPos = 0;
private float myWidth = 0;
private float myHeight = 0;
public color myColor = color(0); //myColor and isSelected are public because I want to access them directly from outside the class
public boolean isSelected = false; //public modal variables are not a popular choice, with good reasons, but for now let's just roll with it
//this is a constructor. Every time you instantiate this class, you must call it's constructor. This one can be overloaded with the square's cordinates and size
public Square(int xx, int yy, float ww, float hh) {
xPos = xx;
yPos = yy;
myWidth = ww;
myHeight = hh;
}
//you can call this method to make the square draw itself
public void Render() {
if (isSelected) {
stroke(RED);
} else {
stroke(167);
}
fill(myColor);
rect(xPos, yPos, myWidth, myHeight);
}
//give coordinates to this function to know if this squared has been clicked on
public boolean ClickedOn(float xx, float yy) {
return ((xx > xPos && xx < xPos + myWidth) && (yy > yPos && yy < yPos + myHeight));
}
}
Now, I'm not going to re-write the whole thing, but here's a minimal, reproductible example of this class's power (you can copy and paste the following code into Processing and learn from it):
final color RED = #D12020;
final color BLUE = #515DD8;
final color GREEN = #21AF20;
final color YELLOW = #F5EF74;
final color ORANGE = #F59219;
final color PURPLE = #B219F5;
final color WHITE = #FFFFFF;
final int ROWS = 12;
final int COLUMNS = 8;
final color[] colour = {RED, BLUE, GREEN, YELLOW, ORANGE, PURPLE, WHITE};
ArrayList <Square> picker, grid, answer; //there are 3 groups of squared to manage
class Square{
//modal variables
private float xPos = 0;
private float yPos = 0;
private float myWidth = 0;
private float myHeight = 0;
public color myColor = color(0); //myColor and isSelected are public because I want to access them directly from outside the class
public boolean isSelected = false; //public modal variables are not a popular choice, with good reasons, but for now let's just roll with it
//this is a constructor. Every time you instantiate this class, you must call it's constructor. This one can be overloaded with the square's cordinates and size
public Square(float xx, float yy, float ww, float hh) {
xPos = xx;
yPos = yy;
myWidth = ww;
myHeight = hh;
}
//you can call this method to make the square draw itself
public void Render() {
if (isSelected) {
stroke(RED);
} else {
stroke(167);
}
fill(myColor);
rect(xPos, yPos, myWidth, myHeight);
}
//give coordinates to this function to know if this squared has been clicked on
public boolean ClickedOn(float xx, float yy) {
return ((xx > xPos && xx < xPos + myWidth) && (yy > yPos && yy < yPos + myHeight));
}
}
//setup() is meant for you to initialize stuff before the main loop, which is draw()
void setup() {
size(1000, 1000);
InitializeSquares();
}
//let's initialize EVERYTHING SQUARE-SHAPED!
public void InitializeSquares(){
float blockSize = width/20;
float cornerX = width/12;
float cornerY = height/8;
float answerCornerX = cornerX * 6.5;
float answerCornerY = cornerY;
float pickerSize = blockSize * 1.5;
float pickerCornerX = cornerX/2;
float pickerCornerY = cornerY - (pickerSize * 1.5);
//the grid:
grid = new ArrayList <Square>();
for (int i = 0; i<ROWS; i++) {
for (int j = 0; j<COLUMNS; j++) {
grid.add(new Square(cornerX + (j*blockSize), cornerY + (i*blockSize), blockSize, blockSize));
}
}
//the target answer:
answer = new ArrayList <Square>();
for (int i = 0; i<ROWS; i++) {
for (int j = 0; j<COLUMNS; j++) {
answer.add(new Square(answerCornerX + (j*blockSize), answerCornerY + (i*blockSize), blockSize, blockSize));
answer.get(answer.size()-1).myColor = colour[floor(random(colour.length))];
}
}
//the color pickers
picker = new ArrayList <Square>();
for (int i = 0; i < colour.length; i++) {
picker.add(new Square(pickerCornerX + (i*pickerSize), pickerCornerY, pickerSize, pickerSize));
picker.get(picker.size()-1).myColor = colour[picker.size()-1];
}
}
void draw() {
background(0); //it's ok, the squares will draw themselves
//let's draw the player's squares
for (Square s : grid) {
s.Render();
}
for (Square s : grid) {
//I'm drawing the selected squared over the unselected ones so we see them better
if (s.isSelected) {s.Render();}
}
//drawing the answer grid
for (Square s : answer) {
s.Render();
}
//drawing the color pickers
for (Square s : picker) {
s.Render();
}
}
void mouseClicked() {
//checking if is the player selecting squares
for (Square s : grid) {
if (s.ClickedOn(mouseX, mouseY)) {
s.isSelected = !s.isSelected;
}
}
//checking if the player is coloring squares. If so, coloring the squared and unselecting them
for (Square p : picker) {
if (p.ClickedOn(mouseX, mouseY)) {
for (Square s : grid) {
if (s.isSelected) {
s.isSelected = false;
s.myColor = p.myColor;
}
}
}
}
}
From what I read from your code, I'm confident that you will be able to use these lines to reach your goals. Your code showed that you had still a lot to learn, but still it showed a lot of promises. If you did it all by yourself, then I'm also confident that you'll get very good at this, and very soon. So I'm not going to list every little thing you did which was wrong or whatever. You'll learn in due time.
Don't forget to have fun while learning. It's more than half the work! I'll hang around in case you have questions.
Here's some code based on your original post. It's an implementation of "being able to select one column and one row and one cell" and the putting some color in there, but without any class.
I'll be honest here, this was way harder than what I did yesterday.
final color RED = #D12020;
final color BLUE = #515DD8;
final color GREEN = #21AF20;
final color YELLOW = #F5EF74;
final color ORANGE = #F59219;
final color PURPLE = #B219F5;
final color WHITE = #FFFFFF;
final int ROWS = 12;
final int COLUMNS = 8;
final int BLOCK_SIZE = 40;
int blockSize;
int spacing;
int cornerX;
int cornerY;
int size;
int space;
int cell = -1;
int col = -1;
int row = -1;
color[] colour = {RED, BLUE, GREEN, YELLOW, ORANGE, PURPLE, WHITE};
//I added these variables. The color array is 2 dimentional, so you can use it like if it had grid coordinates (square [0][2] is third on the first line)
PVector square_selected; //this will hold the grid coordinates of the currently selected square, or -1 for "nothing"
color[][] square_color;
int columnSelected = -1;
int rowSelected = -1;
void setup() {
size(1000, 1000);
background(0);
cornerX = width/8;
cornerY = height/8;
size = height/33;
space = width/100;
targetImage();
//initializing the new array
square_color = new color[COLUMNS][ROWS];
for (int i=0; i<COLUMNS; i++) {
for (int j=0; j<ROWS; j++) {
square_color[i][j] = color(0);
}
}
square_selected = new PVector(-1, -1);
//You might have heard about this or not, but as a general rule global variables are shunned upon. That's because when the program becomes more complex, they become exponentially harder to control,
//and can cause unexpected behavior. No joke, Toyota programmers actually caused death because of these (search for "Toyota spaghetti code" if you're curious)
//So I'm initializing these here and removing the places where the code would change them unexpectedly.
//You may have had a better idea of what you're doing that I do, though, so you can chalk up this decision to my coding preferences.
blockSize = width/20;
spacing = width/200;
}
void draw() {
background(0);
grid();
colourCells();
//selectCell();
columnSelectors();
rowSelectors();
counter();
}
//draws the colour selectors
void colourCells() {
int colourBlocks = 7;
int squareX = blockSize;
for (int i=0; i < colourBlocks; i++) {
stroke(167);
fill(colour[i]);
rect(squareX, 0, blockSize, blockSize);
squareX += spacing+blockSize;
}
}
//draws the grid
void grid() {
for (int i=0; i<ROWS; i++) {
for (int j=0; j<COLUMNS; j++) {
stroke(167);
fill(square_color[j][i]);
rect(cornerX + (BLOCK_SIZE * j), cornerY + (BLOCK_SIZE * i), BLOCK_SIZE, BLOCK_SIZE);
}
}
//drawing the selected square over the others
if (square_selected.x > -1) {
stroke(RED);
fill(square_color[(int)square_selected.x][(int)square_selected.y]);
rect(cornerX + (BLOCK_SIZE * (int)square_selected.x), cornerY + (BLOCK_SIZE * (int)square_selected.y), BLOCK_SIZE, BLOCK_SIZE);
}
}
//creates the circular column selectors
void columnSelectors() {
int columnX = (cornerX) + (size/2) + (space/2);
int columnY = (cornerY) - (size/2) - space;
noFill();
for ( int i = 0; i<COLUMNS; i++) {
if (i == columnSelected) {
stroke(RED);
} else {
stroke(167);
}
ellipse(columnX, columnY, size, size);
columnX += size + space;
}
}
//creates the circular row selectors
void rowSelectors() {
int rowX = (cornerX) - (size/2) - space;
int rowY = (cornerY) + (size/2) + (space/2);
noFill();
for ( int j = 0; j<ROWS; j ++) {
if (j == rowSelected) {
stroke(RED);
} else {
stroke(167);
}
ellipse(rowX, rowY, size, size);
rowY += size + space;
}
}
//creates the score counter at the bottom of canvas
void counter() {
float x = width/3;
float y = 4*height/5;
float boxLength = width/3;
float boxHeight = height/20;
int counter = 0;
fill(255);
rect(x, y, boxLength, boxHeight);
String counterText = "Num Moves: " + counter;
fill(0);
textSize(40);
text(counterText, x + (boxLength*1/14.0), y + (boxHeight*3/4.0));
}
//loads in a random target image
void targetImage() {
float y = height/5;
float x = width/2;
String [] file = {"target0.png", "target1.png", "target2.png", "target3.png", "target4.png"};
PImage target;// create a variable that can point to an off-screen buffer
String filename = file[int(random(0, 4))]; //"target" + int(random(0,4)) + ".png";// create filename
target = loadImage(filename);// load image file into off-screen buffer
//image(target, x, y, width/3.125, height/2.08);// display the buffer on canvas at location x, y
}
//allows the user to select/deselect a column
int selectColumn() {
int x;
int xPos= mouseX - cornerX;
x = xPos/(size+space);
if (x == col)
return -1;
else
return x;
}
//allows the user to select/deselect a row
int selectRow() {
int y;
int yPos= mouseY - cornerY;
y = yPos/(size+space);
if (y == row)
return -1;
else
return y;
}
//mouse click functions for selecting cells/rows/columns/colour
void mouseClicked() {
//user is clicking inside the grid
if (mouseX > cornerX && mouseY > cornerY && mouseX < cornerX + COLUMNS*BLOCK_SIZE
&& mouseY < cornerY + ROWS*BLOCK_SIZE) {
//cell = cellSelected();
square_selected.x = floor((mouseX - cornerX)/BLOCK_SIZE);
square_selected.y = floor((mouseY - cornerY)/BLOCK_SIZE);
}
//user is selectong a column
if (mouseX > cornerX && mouseY > (cornerY - (space + size)) && mouseX < cornerX + COLUMNS*BLOCK_SIZE
&& mouseY < cornerY) {
if (columnSelected == selectColumn()) {
columnSelected = -1;
} else {
columnSelected = selectColumn();
}
}
//user is selecting a row
if (mouseX > (cornerX - (space + size)) && mouseY > cornerY && mouseX < cornerX
&& mouseY < cornerY + ROWS*BLOCK_SIZE) {
if (rowSelected == selectRow()) {
rowSelected = -1;
} else {
rowSelected = selectRow();
}
}
//user is picking a color
if (mouseY < blockSize) {
//validating the color
for (int c = 0; c < colour.length; c++) {
if (get(mouseX, mouseY) == colour[c]) {
//if a square has been selected
if (square_selected.x > -1) {
square_color[(int)square_selected.x][(int)square_selected.y] = colour[c];
square_selected = new PVector(-1,-1);
}
//if a row has been selected
if (rowSelected > -1) {
for (int i = 0; i < COLUMNS; i++) {
square_color[i][rowSelected] = colour[c];
}
rowSelected = -1;
}
//if a column has been selected
if (columnSelected > -1) {
for (int i = 0; i < ROWS; i++) {
square_color[columnSelected][i] = colour[c];
}
columnSelected = -1;
}
break;
}
}
}
}
The trick is to keep everything tidy and to avoid unexpected changes. To realize this, some good working habits helps a lot. Habits like:
So, you'll see it in the code, but basically I'm just adding a couple variables which will track the squares' colors, and which cell or row/column is currently selected. I also tweaked the functions which draws your objects so the colors are depending on attributes (in the variables I just mentioned) and not just something the program draws once when it happens - so a new iteration of the loop isn't going to erase them. Oh, and I erased functions which weren't necessary anymore because of these changes.
As with last time, I'll stick around, so don't hesitate to ask questions about anything which don't stick or that you don't get. And have fun!
Collected from the Internet
Please contact [email protected] to delete if infringement.
Comments