I'm an undergrad and in one of my classes, we have this assignment to make the Asteroids game (you know, the retro one!) on Processing (which is basically a simplified Javascript program). I have the code for a button:
void setup()
{
size(1280, 720);
}
void draw()
{
background(0,0,0);
drawButton();
}
Boolean pointIsInRectangle(int left, int top, int right, int bottom, int pointX, int pointY)
{
if (pointX >= left
&& pointX <= right
&& pointY <= bottom
&& pointY >= top
)
return true;
else
return false;
}
void drawButton()
{
int left = 350;
int top = 145;
int right = 860;
int bottom = 220;
rectMode(CORNERS);
color background = color(0,0,0);
if (pointIsInRectangle(left,top,right,bottom,mouseX,mouseY))
{
background = color(255);
}
// draw outer rectangle
stroke(255);
fill(background);
rect(left,top,right,bottom);
// draw caption
fill(255);
textSize(100);
text(" ASTEROIDS", left,bottom);
}
and I have the preliminary code for the ship for the game, but I need the button to get to an "in between" page so that when the button is clicked, it leads to a new screen that says "click anywhere to play game" and when any point in the screen is clicked, the ship appears and asteroids begin appearing and the game begins. HOW DO I GET THE BUTTON TO LEAD TO A NEW PAGE, AND HOW DO I CREATE THAT PAGE? I really cannot figure it out. Crossing my fingers that someone will be able to give me some guidance!!!!!
The actual result I'm seeing is that nothing is happening when the button is clicked. This makes sense because I don't know how to add the next page that says Click to Play Game, so this is the issue I'm facing. The code I have so far can be found above.
This is not question about a button, but about a game engine with multiple scenes.
Your game is a collection of screens (scenes). Every scene have definition of visuals, definition of logic, and definition of switch for another scene.
Here is the minimal solution of Your problem.
1 - Define visuals of Your screens in .display() method,
2 - Define logic condition for next screen switch in stateCondition().
boolean mouseReleased;
GameScreen screen;
GameScreen splashScreen;
GameScreen inBetweenPage;
void setup() {
size(1280, 720);
GameScreen splashScreen = new SplashScreen();
GameScreen inBetweenPage = new InBetweenPage();
splashScreen.setNextScreen(inBetweenPage);
inBetweenPage.setNextScreen(splashScreen);
screen = splashScreen;
mouseReleased = false;
}
void draw() {
screen.display();
if (screen.stateCondition()) {
screen = screen.getNextScreen();
}
}
interface GameScreen {
/**
** screen visual definition
**/
void display();
/**
** screen change state condition
**/
boolean stateCondition();
void setNextScreen(GameScreen scr);
GameScreen getNextScreen();
}
class SplashScreen implements GameScreen {
GameScreen nextScreen;
int left = 350;
int top = 145;
int right = 860;
int bottom = 220;
void display() {
background(0, 0, 0);
rectMode(CORNERS);
color background = color(0, 0, 0);
if (pointIsInRectangle(left, top, right, bottom, mouseX, mouseY)) {
background = color(255, 255, 255, 160);
}
// draw outer rectangle
stroke(255);
fill(background);
rect(left, top, right, bottom);
// draw caption
fill(255);
textSize(100);
textAlign(CENTER, CENTER);
text("ASTEROIDS", (left+right)/2, (top+bottom)/2-16);
}
boolean stateCondition() {
if (mouseReleased && (mouseButton == LEFT)) {
mouseReleased = false;
return pointIsInRectangle(left, top, right, bottom, mouseX, mouseY);
}
return false;
}
GameScreen getNextScreen() {
return nextScreen;
}
void setNextScreen(GameScreen target) {
this.nextScreen = target;
}
}
class InBetweenPage implements GameScreen {
GameScreen nextScreen;
int left = 0;
int top = 0;
int right = width;
int bottom = height;
void display() {
background(0, 0, 0);
// draw caption
fill(255);
textSize(24);
textAlign(CENTER, CENTER);
text("< click anywhere to play game >", width/2, height/2);
}
boolean stateCondition() {
if (mouseReleased && (mouseButton == LEFT)) {
mouseReleased = false;
return pointIsInRectangle(left, top, right, bottom, mouseX, mouseY);
}
return false;
}
GameScreen getNextScreen() {
return nextScreen;
}
void setNextScreen(GameScreen target) {
this.nextScreen = target;
}
}
Boolean pointIsInRectangle(int left, int top, int right, int bottom, int pointX, int pointY) {
if (pointX >= left && pointX <= right && pointY <= bottom && pointY >= top) {
return true;
} else
return false;
}
void mouseReleased() {
mouseReleased = true;
}
There is a relatively simple solution to this problem, which would be to make some space for a special "splashscreen" state in your code. It would almost be ninja coding to implement it, although in this case as you are learning it's not taking shortcuts but more like climbing a new step in the learning stairwell. Here's a proof of concept which shows what I'm speaking about:
// this boolean keeps track of the current game state: splashscreen or not
boolean splashScreen = true;
void setup() {
size(600, 400);
}
void draw() {
background(0);
// if the game has yet to start, show the splashscreen
if (splashScreen) {
drawSplashScreen();
} else {
playGame();
}
}
// this method draws the splashScreen
// it could be coded in the 'draw()' method, but it's easier to read this way
void drawSplashScreen() {
textAlign(CENTER);
textSize(30);
fill(255);
text("THIS IS THE SPLASHSCREEN \n click anywhere to play the game", width/2, height/2);
}
// this method contains everything your game loop needs to work
void playGame() {
textAlign(CENTER);
textSize(40);
fill(200, 0, 200);
text("YOU ARE CURRENTLY \n PLAYING THE GAME", width/2, height/2);
}
void mouseClicked() {
if (splashScreen) {
splashScreen = !splashScreen;
}
}
Here you have 2 game states: the splash screen and the game itself, but you could implement more than just these two. There's a design pattern called Finite State Machine that would be just perfect for your needs. Although you already have everything you need to code your assignment, let me explain a little further:
A FSM let you determine the context which can lead to another context and limit some actions to it's own context. A good example of this is Mario in the original Super Mario Bros game: when he's small, getting a magic mushroom will transform him into Super Mario. When he's Super, getting a flower will transform him into Fire Mario. But while small, getting a flower will only make him into Super Mario, not Fire Mario (in the ooold first game at least). That's because each one of these states have rules, and he cannot just jump from one to the other without regard for these.
Your game's logic has it's own rules too: you have the first screen with the "start" button. When this button is clicked, there's a second state where it says "click anywhere to play the game". If the user clicks, then the game itself starts. That makes for 3 states (3 screens if you like) where every state has it's own set of rules - which we often call 'business rules'.
We could schematize this assignment like this:
And here's the skeleton code that would implement such a game, including a bonus rectangle collision detection method:
// the game states are as follow:
// 0 is welcome screen with button
// 1 is click anywhere screen
// 2 is the game itself
int gameState = 0;
void setup() {
size(600, 400);
}
void draw() {
background(0);
// let's use the right game state here
switch(gameState) {
case 0:
drawWelcomeScreen();
break;
case 1:
drawClickAnywhereScreen();
break;
case 2:
playGame();
break;
}
}
void drawWelcomeScreen() {
fill(0, 0, 100);
rect(100, 100, 400, 100);
textAlign(CENTER);
textSize(30);
fill(255);
text("Click here to play", 300, 150);
text("THIS IS THE WELCOME SCREEN", width/2, 50);
}
void drawClickAnywhereScreen() {
textAlign(CENTER);
textSize(30);
fill(255);
text("Click anywhere to play the game", width/2, height/2);
}
void playGame() {
textAlign(CENTER);
textSize(40);
fill(200, 0, 200);
text("YOU ARE CURRENTLY \n PLAYING THE GAME \n click anywhere to go \n back to the welcome screen", width/2, height/2);
}
void mouseClicked() {
// now this will be more complicated, because you'll want to deal with clicks differently depending on the game state
// which kinda answers the question as how we'll deal with this issue: same as in the 'draw()' method
switch(gameState) {
case 0:
// if the click's coordinates are in the rectangle's coordinates (use math here, or a collision method)
// (in fact, use math pretty much everywhere)
// (I hope you like math)
// anyway here's an old collision method I paste everywhere on SO, feel free to steal it and improve on it!
// I wrote it as a student to deal with pretty much the same stuff that you're going through
if (intersect(100, 100, 400, 100, mouseX, mouseY, 1, 1)) {
gameState = 1;
}
break;
case 1:
gameState = 2;
break;
case 2:
gameState = 0;
break;
}
}
// enter the xy coordinates, the width and the heigh of 2 rectangle shapes and it'll return true if they intersect
boolean intersect(float x1, float y1, float w1, float h1, float x2, float y2, float w2, float h2) {
boolean checkX = false;
boolean checkY = false;
if ( (x1<x2 && (x1+w1)>x2) || (x1<(x2+w2) && (x1+w1)>x2+w2) || (x1>x2 && (x1+w1)<(x2+w2)) ) {
checkX = true;
}
if ( (y1<y2 && (y1+h1)>y2) || (y1<(y2+h2) && (y1+h1)>y2+h2) || (y1>y2 && (y1+h1)<(y2+h2)) ) {
checkY = true;
}
return checkX && checkY;
}
I hope I'm not confusing you with all this material. I'll try and keep an eye out for any question you may have about these things.
Good luck and have fun!
I want to prevent zooming of a QChartView into negative x or y axis values. So I'm trying to intercept the mouseReleaseEvent in my subclass of QChartView and then modify the coordinates of the rubberBandRect before performing the zoom. But it seems the rubberBandRect coordinates are always zero after mouse release, so I think I'm not working with the correct rubberBandRect.
I'm trying to adapt based on the documentation for QChartView (emphasis added):
void QChartView::mouseReleaseEvent(QMouseEvent *event)
If the left mouse button is released and the rubber band is enabled, the event event is accepted and the view is zoomed into the rectangle specified by the rubber band.
When I tried looked at the actual values in the rubberBandRect in the code snippet below, the x/y and height/width values of the rectangle are always zero no matter how I zoomed with the cursor.
I also looked at the answer to this question: Qt How to connect the rubberBandChanged signal but in that case, they wanted the behavior in the main window, which is not what I want. Thanks!
class MyQChartView : public QtCharts::QChartView
{
Q_OBJECT
public:
MyQChartView(QWidget *parent = 0);
//...
protected:
void mouseReleaseEvent(QMouseEvent *event);
void keyPressEvent(QKeyEvent *event);
};
void MyQChartView::mouseReleaseEvent(QMouseEvent *event)
{
//always shows zeroes for the x and y position
if(event->button() == Qt::LeftButton){
std::cout << "x: " << this->rubberBandRect().x() << " y: " << this->rubberBandRect().y() << std::endl;
QChartView::mouseReleaseEvent(event);
}
//any other event
QChartView::mouseReleaseEvent(event);
}
After extensive experiments, I think I found the best workaround. It was necessary to make a rubberBand pointer and then findChild to get to the actual rubberband. Then after mouse release, I get the plot area coordinates as well as the rubberband coordinates. Then I made a bounding box (bounded_geometry) and limited its values if the rubberband went out of bounds. Then I zoomIn to the bounded geometry box. It won't work to just modify the rubberband because those coordinates are not floating point and rounding errors cause wrong zooming.
class MyQChartView : public QtCharts::QChartView
{
Q_OBJECT
public:
MyQChartView(QWidget *parent = 0):
QChartView(parent)
{
myChart = new QtCharts::QChart();
setRubberBand(QtCharts::QChartView::RectangleRubberBand);
rubberBandPtr = this->findChild<QRubberBand *>();
this->setChart(myChart);
//...other things
}
//...
protected:
void mouseReleaseEvent(QMouseEvent *event);
void keyPressEvent(QKeyEvent *event);
private:
QRubberBand * rubberBandPtr;
QtCharts::QChart * myChart;
};
void MyQChartView::mouseReleaseEvent(QMouseEvent *event)
{
if(event->button() == Qt::LeftButton){
//first get the plot area and save the needed values (could change each time)
QRectF plotArea = myChart->plotArea();
qreal xbound = plotArea.x(); //values < xbound are out of bounds on x axis
qreal ybound = plotArea.y() + plotArea.height(); // !!! note that values > ybound are out of bounds (i.e. negative y values)
QPointF rb_tl = rubberBandPtr->geometry().topLeft();
QPointF rb_br = rubberBandPtr->geometry().bottomRight();
QRectF bounded_geometry = rubberBandPtr->geometry();
if(rb_tl.x() < xbound){
bounded_geometry.setX(xbound);
}
if(rb_br.y() > ybound){ //note that values > ybound are out of bounds
bounded_geometry.setBottom(ybound);
}
myChart->zoomIn(bounded_geometry);
rubberBandPtr->close();
return;
}
//any other event
QChartView::mouseReleaseEvent(event);
}
I have subclassed a qgraphicsscene and trying to get the mouse coords inside a "normal" function. I only get it working on "mouse involved" function. Sorry I'm amateur programmer.
For exmample here scenePos() works:
void mousePressEvent(QGraphicsSceneMouseEvent *event)
{
// qDebug() << "Custom scene clicked.";
if(event->modifiers() == Qt::ControlModifier) {
if(event->button() == Qt::LeftButton) {
QPointF pos = {event->scenePos().x(), 70};
addChordnueve(pos); // crea 1 item at mouse x e y = 70
// } if(event->modifiers() == Qt::ControlModifier & event->modifiers() == Qt::ShiftModifier) {
qDebug() << "Control!!!";}}
Here it doesn't works at all, but got QCursor::pos() giving "weird" positions:
void preaddExtChord()
{
auto *hellos = scenePos(); //<- It doesn't works
int xplace = QCursor::pos().x()-620;
int yplace = QCursor::pos().y()-380;
QGraphicsSimpleTextItem *item = new QGraphicsSimpleTextItem("n");
item->setFont(QFont ("omheads", 20));
item->setPos(xplace, yplace);
addItem(item);
}
I searched a lot during months but couldn't find a solution,...
maybe I'm doing a wrong approach, or either there is some easier possibilitie to get the mouse coords inside this type of functions?
Thanks! :-)
If you want to obtain the position with respect to the cursor scene you must first obtain that QGraphicsView is below the cursor (a QGraphicsScene can be part of QGraphicsView), for this we must iterate and verify if it is inside the viewport, then calculate the position with respect to the scene using the mapToScene method of QGraphicsView:
QPoint p = QCursor::pos();
for(QGraphicsView *view: views()){
QWidget *viewport = view->viewport();
QRect vr = viewport->rect();
QPoint vp = viewport->mapFromGlobal(p);
if(vr.contains(vp)){
QPointF sp = view->mapToScene(vp);
QGraphicsSimpleTextItem *item = new QGraphicsSimpleTextItem("n");
item->setFont(QFont("omheads", 20));
item->setPos(sp);
addItem(item);
}
}
The idea is that the user clicks on a shape and the information of the shape is shown on a table. This works well if the user selects the shape (drag the mouse over the shape). I'm trying to modify this code to do that action, but not lucky. This is what I'm doing for the select mode:
I have a a call in the:
void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);
When the mouse is released I will update the data:
//enter the mode for selecting
if(theMode == SelectObject){
if (this->items().isDetached()){
//we check if the object is selected
if (this->selectedItems().isEmpty()){
qDebug() << "not selected";
isSelected = false;
return;
}
//we get a list of the shapes
QList<QGraphicsItem*> stackOfShapes = this->items();
//we get index of selected shape
QGraphicsItem *selected = this->selectedItems().first();
int indexOfShape = stackOfShapes.indexOf(selected);
//we see which shape is (For a Rectangle)
switch (selected->type()) {
case 346:
{
updateDataOfRect();
}
break;
}
}
The problem is that:
//we get index of selected shape
QGraphicsItem *selected = this->selectedItems().first();
How to do this when the shape is clicked not selected?
I tried to modify the subclass of the shape in the mousePressEvent :
if (event->button() == Qt::MouseButton::LeftButton) {
this->setSelected(true);
}
Can any one help to find a solution?
Thanks.
QList<QGraphicsItem *> QGraphicsView::items(const QPoint &pos) const
Returns a list of all the items at the position pos in the view. The
items are listed in descending stacking order (i.e., the first item in
the list is the uppermost item, and the last item is the lowermost
item). pos is in viewport coordinates.
Example use by overloading QGraphicsView::mousePressEvent():
void CustomView::mousePressEvent(QMouseEvent *event) {
qDebug() << "There are" << items(event->pos()).size()
<< "items at position" << mapToScene(event->pos());
}
I am new to java. i created a program to find the area of polygons.
I wanted it to ask the type of polygon an then find the area. i used if, else if and else statements
but whenever i type the name of the polygon nothing happens.
Here is the script
import java.util.Scanner ;
public class Area
{
static Scanner sc = new Scanner(System. in );
public static void main(String[] args) {
System.out.print("Enter the type of polygon: ");
String polygon = new String("polygon");
polygon = sc.next();
String square = new String("square");
String rectangle = new String("rectangle;");
String triangle = new String("triangel");
polygon = sc.next();
if (polygon == square) {
double side;
System.out.print("Enter the side: ");
side = sc.nextDouble();
double area;
area = side * side;
System.out.print("The Area is: " + area);
} else if (polygon == rectangle) {
double length;
double breadth;
System.out.print("Enter the length: ");
length = sc.nextDouble();
System.out.print("Enter the breadth: ");
breadth = sc.nextDouble();
double area;
area = length * breadth;
System.out.print("The Area is : " + area);
} else if (polygon == triangle) {
double base;
double height;
System.out.print("Enter the base: ");
base = sc.nextDouble();
System.out.print("Enter the height: ");
height = sc.nextDouble();
Double area;
area = base * height / 2;
System.out.print("The Area is: " + area);
} else {
System.out.print("ERROR it is not a polygon");
}
}
}
Please help me out
thank you
Okay, I see a couple of better ways to do things in your code.
You do not need to create a String object just to compare your input to the string. You could just use:
if(polygon="square")
Or even better you could use the switch statement. I think the reason you are running into an issue is because you are using .next() instead of .nextLine(). I would use a switch statement and do the following:
Scanner sc = new Scanner(System. in );
myInput = sc.nextLine() // Get the next line from the keyboard
switch (myInput.toLowerCase()) // Make the inputted text to lower case, do switch
{
case "square":
// DO SQUARE LOGIC HERE
break;
case "rectangle":
// DO RECT LOGIC HERE
break;
case "triangle":
// DO TRIANGLE LOGIC HERE
break;
default:
// Did not recognize the type of polygon entered.
}
it seems you are comparing the variable polygon(which always holds the string "polygon") to the next line.
try this:
import java.util.Scanner;
public class Area
{
static Scanner sc = new Scanner(System.in);
public static void main (String [] args)
{
System.out.println("To find the area of a polygon type,");
System.out.println("\"1\" for a Square");
System.out.println("\"2\" for a Rectangle");
System.out.println("\"3\" for a Triangle");
System.out.println("Enter the number to select the type of polygon: ");
int p = sc.nextInt();
if (p == 1 ) {
double side ;
System.out.print("Enter the squaer side: ");
side =sc.nextDouble() ;
double area;
area =side * side;
System.out.print("The Area is: " + area);}
else if (p == 2)
{
double length;
double breadth;
System.out.print("Enter the rectangle length: ");
length =sc.nextDouble();
System.out.print("Enter the breadth: ");
breadth =sc.nextDouble();
double area;
area =length * breadth;
System.out.print("The Area is : " + area); }
else if (p == 3)
{
double base;
double height;
System.out.print("Enter the triangle's base: ");
base =sc.nextDouble();
System.out.print("Enter the height: ");
height =sc.nextDouble();
Double area ;
area = base * height / 2 ;
System.out.print("The Area is: " + area) ; }
else
{
System.out.println("ERROR it is not a polygon");
System.out.print("Please enter a number: 1, 2 or 3.");
}
}
}
I've tried it a few times and it works fine. hope this looks better for your coding as well as it is "user-friendlier" in my opinion because the programmer would ask a user for numbers instead of strings (which may cause miss-spelling). good luck.