So I have a problem with my mouse detection using a object called Ornament that inherits JComponent and I am getting the mouse clicks using java.awt.event.MouseEvent.
The solution me and my group though would be the best is to compare the distance of each ornament to the mouse and if its less than 50 (which is the radius of the ornament) it would do something.
private class ME implements MouseListener {
#Override
public void mouseClicked(MouseEvent e) {
for(int i = 0; i < ORNAMENT_AMOUNT; i++) {
Ornament current = oh.getOrnament(i);
int distance = (int)(Math.sqrt(((e.getX() - current.getX()) * (e.getX() - current.getX())) + ((e.getY() - current.getY()) * (e.getY() - current.getY()))));
if(distance <= 50) {
System.out.println("CIRCLE CLICKED!");
current.reset();
}
}
}
The problem that I am getting is that it just does not work how it should. I click in the same spot many times and eventually it will trigger the event on one of the ornaments. Its random and confusing.
Here is the Ornament class
//ornament class to define the game object ornament
public class Ornament extends JComponent{
private int xPos;
private int yPos;
private int velocity;
private int screenWidth;
private int screenHeight;
private Random rand;
public Ornament(int screenWidth, int screenHeight, int velocity) {....}
public int getY() { return yPos; }
public int getX() { return xPos; }
#Override
public void paintComponent(Graphics graphics) {
float r = rand.nextFloat();
float g = rand.nextFloat();
float b = rand.nextFloat();
super.paintComponent(graphics);
graphics.setColor(new Color(r, g, b));
graphics.fillOval(0, 0, 50, 50);
}
....
public void reset() {
this.yPos = -(int)(Math.random()*500);
this.xPos = (int)(Math.random()*screenWidth);
this.velocity += 1;
update();
}
}
My whole approach to this might be wrong but it seems right in my head so help would be appreciated! :)
*note oh is an objecthandler class I made to run the game loop in a different thread that has an array of ornaments
public class ObjectHandler implements Runnable {
private int size;
private Ornament[] list;
private GUI game;
public ObjectHandler(int size, GUI game) {
....
list = new Ornament[size];
//init object list
for(int i = 0; i < size; i++) {
list[i] = new Ornament(game.getFrameX(), game.getFrameY(), game.getStartingVel());
game.add(list[i]);
}
}
public Ornament getOrnament(int index) { return list[index]; }
public void run() {
while(true) {
try {
Thread.sleep(50);
} catch (InterruptedException e) { e.printStackTrace(); }
game.loadBackground();
for(int i = 0; i < size; i++) {
if(list[i].getY() >= game.getFrameY())
list[i].reset();
else
list[i].update();
}
}
}
}
Thank you all for your help!
I used Gilbert Le Blanc's method to get this to work.
I removed the JComponent off of the Ornament class and instead created a class that inherits JPanel
ublic class GamePanel extends JPanel{
private Ornament[] objectList;
......
public GamePanel(GUI game) {
....
MouseListener ml = new MouseListener();
addMouseListener(ml);
this.setBounds(0, 0, 500, 500);
}
I then added a paint component and rendered each Ornament in the objectList array using a for loop
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
.....
for(int i = 0; i < objectList.length; i++) {
Color c = new Color(objectList[i].r, objectList[i].g, objectList[i].b);
g2.setColor(c);
g2.fillOval(objectList[i].getX(), objectList[i].getY(), 50, 50);
}
}
}
Furthemore I added the mouselistener to the JPanel component. Using the same technique for mouse to object collision and it worked!
class MouseListener extends MouseAdapter
{
#Override
public void mousePressed(MouseEvent e)
{
if(!gameOver) {
for(int i = 0; i < objectList.length; i++) {
int distance = (int)(Math.sqrt(((e.getX() - objectList[i].getX()) * (e.getX() - objectList[i].getX())) + ((e.getY() - objectList[i].getY()) * (e.getY() - objectList[i].getY()))));
if(distance <= 50) {
game.incScore();
objectList[i].reset();
}
}
...
}
}
Shoutout to hfontanez and Gilbert Le Blanc for the help! :)
As part of my assignment, I'm supposed to make a shape that moves when the keyboard is pressed, and also changes color as it moves. I'm trying to add duplicate shapes that move when selected by clicking on them. However, for some reason, I can't add more than one shape. Here's my code. WARNING: MySimplePanel is very long, but the comments explain what my code does.
import java.awt.Graphics2D;
public interface ActionShape {
/**
* Calls the draw method inherited from the shape
* #param g
*/
public void draw(Graphics2D g);
public boolean contains(double x, double y);
public double getWidth();
public double getHeight();
public void setFrame(double x, double y, double w, double h);
}
Driver.java
public class Driver {
public static void main(String args[]) {
MySimplePanel panel = new MySimplePanel(800, 800, Color.gray);
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(panel);
frame.pack();
frame.setVisible(true);
}
public static float random(float lower, float upper) {
float rand = (float) Math.random();
float range = upper - lower;
rand = range * rand;
System.out.println((lower + rand));
return (lower + rand);
}
}
myShape.java
public class myShape extends Rectangle2D.Double implements ActionShape{
static final int size = 50;
Ellipse2D oval;
Ellipse2D oval2;
Ellipse2D oval3;
public int red=127;
public int green=127;
public int blue = 0;
public myShape(int x, int y) {
//sets position of square to center
super(x-size/2, y-size/2, size, size);
oval = new Ellipse2D.Double(x-size/2, y - size, size, size);
oval2 = new Ellipse2D.Double(x-size/2, y + size/25, size, size);
oval3 = new Ellipse2D.Double(x - size, y-size/2, size, size);
}
/**
* Move all the shapes comprising this one
* #return
*/
#Override
public void draw(Graphics2D g) {
// TODO Auto-generated method stub
g.setColor(Color.black);
g.draw(this); //draw outline
g.draw(oval);
g.draw(oval2);
g.draw(oval3);
g.setColor(new Color(red,green,blue));//set color of all shapes
g.fill(this); //draw inside
g.fill(oval);
g.fill(oval2);
g.fill(oval3);
}
public void setFrame(double x, double y, double w, double h) {
super.setFrame(x, y, w, h);
oval.setFrame(x, y-size/2, w, h);
oval2.setFrame(x, y+size/2, w, h);
oval3.setFrame(x-size/2, y, w, h);
}
public boolean contains(double x, double y) {
if(super.contains(x, y) || oval.contains(x, y)|| oval2.contains(x, y)|| oval3.contains(x, y)) {
return true;
}else {
return false;
}
}
}
MySimplepanel
public class MySimplePanel extends JPanel implements MouseInputListener, KeyListener{
public static final int MOVE_PER_ARROW_KEY = 5; //variable that determines how many spaces the shape will move.
private int width;
private int height;
private boolean isPressed = false;
private boolean rightPressed = false;
private boolean otherPressed = false;
public myShape shape;
public int maxRed = 255;
public int maxGreen = 255;
public int maxBlue = 255;
public myShape selShape=null;
public int nextShape=0;
//An array of different shapes that all share an interface. This will be used when the right mouse button is pressed
public myShape[] shapes;
/**
* Construct a panel with specified width, height, and background color
* #param width
* #param height
* #param bgColor
*/
public MySimplePanel(int width, int height, Color bgColor) {
this.setPreferredSize(new Dimension(width, height));
this.setBackground(bgColor);
//Start to listen to mouse and keyboard input on this panel
this.addMouseListener(this);
this.addMouseMotionListener(this);
this.addKeyListener(this); //detect keyboard input
this.setFocusable(true); //allows you to select this window (required to be able to type in it, just like Processing)
this.setFocusTraversalKeysEnabled(false); //disables shift and tab
setup();
}
private void setup() {
//sets up the starting coordinates for each shape the shape is going to be on the panel.
//I still need to align the center of the panel with the shape's center.
shape = new myShape(400,400);
shapes = new myShape[100];
//array of shapes
shapes[nextShape] = new myShape(200,200);
repaint();
}
/**
* This method is called whenever you call repaint();
*/
protected void paintComponent(Graphics graphicHelper) {
super.paintComponent(graphicHelper); //basically background() in Processing, erases everything
Graphics2D g = (Graphics2D) graphicHelper; //used for drawing in 2D mode
//width and height of the panel
width= getWidth();
height = getHeight();
//draws the shape.
shape.draw(g);
for(int i=0;i<nextShape;i++) {
shapes[i].draw(g);
g.setColor(Color.red);
g.draw(shapes[i]);
}
//for the very first shape that is drawn
if(isPressed==true) {
shape.draw(g);
g.setColor(Color.red);
g.draw(shape);
System.out.println("Shape pressed");
}
//for the very other shape that is drawn by right-clicking
if(otherPressed==true) {
for(int i=0;i<nextShape;i++) {
selShape.draw(g);
g.setColor(Color.red);
g.draw(selShape);
System.out.println("Other Shape pressed");
}
}
}
#Override
public void mouseClicked(MouseEvent arg0) {
}
#Override
public void mouseEntered(MouseEvent arg0) {
}
#Override
public void mouseExited(MouseEvent arg0) {
}
#Override
public void mousePressed(MouseEvent arg0) {
if(arg0.getButton() == MouseEvent.BUTTON1) {
if(shape.contains(arg0.getX(), arg0.getY())) {
selShape=shape;
isPressed=true;
repaint();
}
}
if(arg0.getButton() == MouseEvent.BUTTON1) {
for(int i=0; i<nextShape; i++) {
if(shapes[i]!=null) {
if(shapes[i].contains(arg0.getX(), arg0.getY())) {
selShape=shapes[i];
otherPressed=true;
repaint();
}
}
}
}
if(arg0.getButton() == MouseEvent.BUTTON3) {
rightPressed=true;
if(rightPressed==true) {
selShape=shapes[nextShape];
nextShape++;
repaint();
}
}
}
#Override
public void mouseReleased(MouseEvent arg0) {
}
#Override
public void mouseDragged(MouseEvent arg0) {
}
#Override
public void mouseMoved(MouseEvent arg0) {
}
#Override
public void keyPressed(KeyEvent arg0) {
/**
* Scales the values of red and green based on the width and height respectively.
* red = 0 when the shape is all the way on the left, red = 255 when the shape is all the way on the right
* green = 0 when the shape is all the way up, green = 255 when the shape is all the way down
*/
selShape.red=(int) ((maxRed)*selShape.x/width);
selShape.green=(int) ((maxGreen)*selShape.y/height);
/**
* These two if statements are meant to keep the shape from moving off screen.
*/
if(selShape.x>width||selShape.x<(width=0)) {
selShape.x=width;
}
if(selShape.y>height||selShape.y<(height=0)) {
selShape.y=height;
}
/**
* Moves the shape in different directions depending on the key pressed. It also changes the values of red and green
*/
if(arg0.getKeyCode() == KeyEvent.VK_DOWN) {
selShape.setFrame(selShape.x, selShape.y + MOVE_PER_ARROW_KEY,
selShape.width, selShape.height);
selShape.green--;
repaint();
}else if(arg0.getKeyCode() == KeyEvent.VK_UP) {
selShape.setFrame(selShape.x, selShape.y - MOVE_PER_ARROW_KEY,
selShape.width, selShape.height);
selShape.green++;
repaint();
}else if(arg0.getKeyCode() == KeyEvent.VK_LEFT) {
selShape.setFrame(selShape.x - MOVE_PER_ARROW_KEY, selShape.y,
selShape.width, selShape.height);
selShape.red--;
repaint();
}else if(arg0.getKeyCode() == KeyEvent.VK_RIGHT) {
selShape.setFrame(selShape.x + MOVE_PER_ARROW_KEY, selShape.y,
selShape.width, selShape.height);
selShape.red++;
repaint();
}
/**
* If the shape reaches a certain spot, it will keep changing color randomly until it leaves that area.
*
* Still trying to get the shape's center to hit that spot.
*
*/
if(selShape.getCenterX()>=50 && selShape.getCenterX()<=150 && selShape.getCenterY()>=50 && selShape.getCenterY()<=150) {
selShape.red=(int)(Math.random()*maxRed)+1;
selShape.green=(int)(Math.random()*maxGreen)+1;
selShape.blue=(int)(Math.random()*maxBlue)+1;
System.out.println("x: "+selShape.x);
System.out.println("y: "+selShape.y);
}
}
#Override
public void keyReleased(KeyEvent arg0) {
}
#Override
public void keyTyped(KeyEvent arg0) {
}
}
myShape.java
public class myShape extends Rectangle2D.Double implements ActionShape {
static final int size = 50;
Ellipse2D oval;
Ellipse2D oval2;
Ellipse2D oval3;
public int red = 127;
public int green = 127;
public int blue = 0;
public myShape(int x, int y) {
// sets position of square to center
super(x - size / 2, y - size / 2, size, size);
oval = new Ellipse2D.Double(x - size / 2, y - size, size, size);
oval2 = new Ellipse2D.Double(x - size / 2, y + size / 25, size, size);
oval3 = new Ellipse2D.Double(x - size, y - size / 2, size, size);
}
/**
* Move all the shapes comprising this one
*
* #return
*/
#Override
public void draw(Graphics2D g) {
// TODO Auto-generated method stub
g.setColor(Color.black);
g.draw(this); // draw outline
g.draw(oval);
g.draw(oval2);
g.draw(oval3);
g.setColor(new Color(red, green, blue));// set color of all shapes
g.fill(this); // draw inside
g.fill(oval);
g.fill(oval2);
g.fill(oval3);
}
public void setFrame(double x, double y, double w, double h) {
super.setFrame(x, y, w, h);
oval.setFrame(x, y - size / 2, w, h);
oval2.setFrame(x, y + size / 2, w, h);
oval3.setFrame(x - size / 2, y, w, h);
}
public boolean contains(double x, double y) {
if (super.contains(x, y) || oval.contains(x, y) || oval2.contains(x, y) || oval3.contains(x, y)) {
return true;
} else {
return false;
}
}
I have an application where you place tiles. You can place tiles over tiles and I don't want that. I know that I need something like if the tile rectangle contains the mouse then don't place a tile over it. But this doesn't work. Look at this code:
for (int i = 0; i < b.toArray().length; i++) {
b.get(i).tick();
if (b.get(i).r.contains(Comp.mx, Comp.my)) {
canPlaceATile = false;
// System.out.println("yes");
}
else {
canPlaceATile = true;
//System.out.println("no");
}
if (b.get(i).remove) {
b.remove(i);
i--;
}
}
This is how I check if the mouse is inside the area of one of the tiles.
Block class:
public abstract class block {
public int x,id;
public int y;
protected Image img;
public boolean remove;
public int rotate;
public Rectangle r;
protected int bx, by;
public block() {
}
public abstract void tick();
public abstract void render(Graphics g);
public void createCollisionRect() {
r.setBounds(x - (int) play.camx, y - (int) play.camy, 20, 20);
}
}
an example of a tile:
public class wall extends block {
public wall(int x, int y, int rot) {
this.x = x;
this.y = y;
this.rotate = rot;
r = new Rectangle(x - (int) play.camx, y - (int) play.camy, 20, 20);
id = 0;
}
public void tick() {
createCollisionRect();
if (Comp.mr && r.contains(new Point((Comp.mx), (Comp.my)))) {
remove = true;
}
}
public void render(Graphics g) {
ImageIcon i62 = new ImageIcon("res/tiles/wall.png");
img = i62.getImage();
g.drawImage(img, x - (int) play.camx, y - (int) play.camy, null);
g.setColor(Color.red);
// g.drawRect(x -(int)play.camx, y - play.camy, 20,20);
}
}
I want to check if any of the rectangles contains the mouse and set canPlaceATile to false if the mouse is inside one of the rectangles. The code above doesn't work because when I print the console and I have my mouse over one of the tiles it says:
yes
no
yes
no
yes
no
yes
no
yes
no
yes
no
yes
no
yes
no
yes
no
yes
no
yes
no
yes
and this is when my mouse is on the rectangle without moving it at all. How can I fix this so that I cannot place tiles on top of tiles. Her is where I place tiles:
public void mousePressed(MouseEvent e) {
if (e.getButton() == MouseEvent.BUTTON1) {
Comp.ml = true;
if (manager.isplay && play.dragging == false) {
if (play.canPlaceATile) {
if (play.selectedID == 0) {
play.b.add(new wall((Comp.mx / 20) * 20, (Comp.my / 20) * 20, play.selectedRot));
}
}
}
}
}
In short:
I create Polygon object with a help of this method:
public static float[][] getPolygonArrays(float cx, float cy, float R, int sides) {
float[] x = new float[sides];
float[] y = new float[sides];
double thetaInc = 2 * Math.PI / sides;
double theta = (sides % 2 == 0) ? thetaInc : -Math.PI / 2;
for (int j = 0; j < sides; j++) {
x[j] = (float) (cx + R * Math.cos(theta));
y[j] = (float) (cy + R * Math.sin(theta));
theta += thetaInc;
}
return new float[][]{x, y};
}
and merge it to one dimension array with:
public static float[] mergeCoordinates(float[][] vertices) throws Exception {
if (vertices.length != 2 || vertices[0].length != vertices[1].length) throw new Exception("No valid data");
ArrayList<Float> mergedArrayList = new ArrayList<Float>();
float[] mergedArray = new float[vertices[0].length * 2];
for(int i = 0; i < vertices[0].length; i++) {
mergedArrayList.add(vertices[0][i]);
mergedArrayList.add(vertices[1][i]);
}
int i = 0;
for (Float f : mergedArrayList) {
mergedArray[i++] = (f != null ? f : Float.NaN);
}
return mergedArray;
}
I use 0 as value for X and Y for all newly created Polygons (named in code as Platform). And result of method mergeCoordinates i pass to method setVertices of Polygon object.
After this step i do setPosition with x = Gdx.graphics.getWidth()/2 and y = Gdx.graphics.getHeight()/2. Polygons are positioned good, right on the game screen center.
Than i create a new one Polygon, which must use origin coordinates from first Polygon object, this new polygon i named Figure. To set origin coordinates i use method setOrigin of Polygon class, and use X and Y of Platform Polygon object.
When i run method rotate of Platform Polygon object i also rotate Figure Polygon object, and Figure must rotate around origin point, center of Platform. But it does not.
Figure do rotation around bottom right corner.
For example:
my screen size is 640 x 480. Center point will be 320 x 240. This is X and Y of Platform Polygon object, i checked it with getX and getY of Polygon. I create Figure at 0,0, do setPosition(320, 200) (this is preferred orbit distance for figure to platform). And Figure positioned also good.
I run setOrigin(320, 240) for Figure Polygon Object.
I run rotate for Figure object. And it somehow think that right bottom corner have coordinates x = 320 and y = 240 and do rotation around this point.
Any could help me to solve this problem?
More details on problem you can find below(details, images, gifs, schemes and also sources).
More detailed part starts here
i'm trying to understand how coordinate system in libgdx work, cause i have a problem with positioning objects in game world.
I created simple application, with one big Red Polygon object(Platform in code),
10 White Triangle Polygons(Sector in code) which are included into big polygon object and inherits it's behavior(such like rotate, moveTo and etc).
Than i added inside each Sector one Green Polyline(Direction in code) from first vertice of Sector Polygon to midle point of the opposite side to first point.
This is technical line and i will use it's vertices(coordinates of two points) to move small Red Polygon Object(Figure in code), from center to oposite side to center point of Sector.
When i click on Stage and click coordinates are inside Platform i rotate it to the left or to the right(depends on where click was made). On rotate all Sectors and technical lines are rotated correctly.
http://i.imgur.com/s5xaI8j.gif
(670Kb)
As you can see on gif, figures rotates around theire's center point. I found that Polygon class has method setOrigin(float x, float y) and in annotation to this method said next:
/** Sets the origin point to which all of the polygon's local vertices
are relative to. */
So i tried to use this method, set origin X of Figure as center X of Platform and origin Y as center Y of Platform, and tried to rotate Platform.
http://i.imgur.com/pXpTuQi.gif
(1.06Mb)
As you can see, Figure polygon think that his origin coordinates are at right bottom corner. And Figure do rotation around right bottom corner.
I changed origin to next values: x = 50 and y = 50, here is a result:
http://i.imgur.com/Iajb9sN.gif
(640Kb)
I cannot get why it behave like that. What should i change in my logic?
I have not much classes in my project. I removed all imports and getters/setter to reduce amount of lines.
If it is necessary i could provide entire project.
GameScreen code:
public class GameScreen extends DefaultScreen {
private final GameWorld world;
private final GameRenderer renderer;
public GameScreen() {
world = new GameWorld();
renderer = new GameRenderer(world);
}
#Override
public void render(float delta) {
world.update(delta);
renderer.render();
}
#Override
public void resize(int width, int height) {
world.resize(width, height);
}
}
GameWorld code:
public class GameWorld {
private ArrayList < Platform > platforms = new ArrayList < Platform > ();
private OrthographicCamera camera;
private Stage stage;
private Array < Figure > activeFigures;
private Pool < Figure > figuresPool;
private long lastFigureTime = TimeUtils.nanoTime();
public GameWorld() {
setCamera(new OrthographicCamera());
getCamera().setToOrtho(true, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
setStage(new Stage());
getStage().setViewport(new ScreenViewport(getCamera()));
initializePools();
createPlatforms();
getStage().addListener(new InputListener() {
public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) {
float degrees = Config.PLATFORM_ROTATE_DEGREES;
if (x <= Gdx.graphics.getWidth() / 2) {
degrees *= -1;
}
int i = getPlatforms().size();
while (i-- > 0) {
Platform platform = getPlatforms().get(i);
if (!platform.isLocked() && platform.isRotatable() && platform.getShape().getPolygon().contains(x, y)) {
platform.addAction(Actions.rotateBy(degrees, 1, Interpolation.bounceOut));
break;
}
}
return true;
}
});
Gdx.input.setInputProcessor(getStage());
}
private void initializePools() {
setActiveFigures(new Array < Figure > ());
setFiguresPool(new Pool < Figure > () {#Override
protected Figure newObject() {
return new Figure();
}
});
}
private void createPlatforms() {
float max = Gdx.graphics.getHeight() / (Gdx.graphics.getWidth() / (Gdx.graphics.getWidth() / 2));
float x = Gdx.graphics.getWidth() / 2;
float y = Gdx.graphics.getHeight() / 2;
float sides = 10f;
Color color = Color.RED;
createPlatform(x, y, max * Config.THIRD_PLATFORM_RADIUS_MULTIPLIER, sides, color, true, false, false, null);
}
private Platform createPlatform(float x, float y, float radius, float sides, Color color, boolean rotatable, boolean locked, boolean isEmpty, Platform relatedTo) {
Platform platform = new Platform(0, 0, radius, sides, color, isEmpty, this);
platform.moveTo(x, y);
platform.setRotatable(rotatable);
platform.setLocked(locked);
getPlatforms().add(platform);
getStage().addActor(platform);
if (relatedTo != null) {
relatedTo.addRelation(platform);
}
return platform;
}
private Figure createFigure(float x, float y) {
Figure figure = getFiguresPool().obtain();
figure.init(this, 0, 0, 10f, 4f, Color.DARK_GRAY);
figure.moveTo(x, y);
getActiveFigures().add(figure);
getStage().addActor(figure);
return figure;
}
public void spawnFigure() {
if (getActiveFigures().size >= 10) return;
if (TimeUtils.nanoTime() - getLastFigureTime() <= 2000000000) return;
Platform platform = null;
for (Platform p: getPlatforms()) {
if (!p.isEmpty()) {
platform = p;
break;
}
}
if (platform == null) {
setLastFigureTime(TimeUtils.nanoTime());
return;
}
Sector sector = platform.getSectors().get(MathUtils.random(platform.getSectors().size() - 1));
float x = platform.getX();
float y = platform.getY();
Figure figure = createFigure(x, y);
figure.origin(x, y);
x = sector.getDirection().getTransformedVertices()[2];
y = sector.getDirection().getTransformedVertices()[3];
figure.addAction(Actions.moveTo(x, y, 1));
setLastFigureTime(TimeUtils.nanoTime());
}
public void update(float delta) {
updatePlatforms(delta);
updateFigures(delta);
spawnFigure();
}
private void updatePlatforms(float delta) {
for (Platform platform: getPlatforms()) {
platform.update(delta);
}
}
private void updateFigures(float delta) {
Figure figure;
int figures = getActiveFigures().size;
for (int i = figures; --i >= 0;) {
figure = getActiveFigures().get(i);
if (figure.isAlive() == false) {
getActiveFigures().removeIndex(i);
getFiguresPool().free(figure);
} else {
figure.update(delta);
}
}
}
public void resize(int width, int height) {
getCamera().setToOrtho(true, width, height);
getStage().getViewport().update(width, height, true);
for (Platform platform: getPlatforms()) {
platform.resize(true, width, height);
}
for (Figure figure: getActiveFigures()) {
figure.resize(true, width, height);
}
}
}
GameRenderer code:
public class GameRenderer {
private ShapeRenderer shapeRenderer;
private GameWorld world;
private SpriteBatch spriteBatch;
public GameRenderer(GameWorld world) {
setWorld(world);
setShapeRenderer(new ShapeRenderer());
getShapeRenderer().setProjectionMatrix(getWorld().getCamera().combined);
setSpriteBatch(new SpriteBatch());
getSpriteBatch().setProjectionMatrix(getWorld().getCamera().combined);
}
public void render() {
Gdx.gl.glClearColor(0f, 0.2f, 0.4f, 1f);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
getWorld().getCamera().update();
getSpriteBatch().setProjectionMatrix(getWorld().getCamera().combined);
getShapeRenderer().setProjectionMatrix(getWorld().getCamera().combined);
getWorld().getStage().act(Gdx.graphics.getDeltaTime());
getWorld().getStage().draw();
renderGameObjects();
}
private void renderGameObjects() {
renderPlatforms();
renderFigures();
}
private void renderFigures() {
getShapeRenderer().begin(ShapeRenderer.ShapeType.Line);
for (Figure figure: getWorld().getActiveFigures()) {
figure.render(getSpriteBatch(), getShapeRenderer());
}
getShapeRenderer().end();
}
private void renderPlatforms() {
getShapeRenderer().begin(ShapeRenderer.ShapeType.Line);
for (Platform platform: world.getPlatforms()) {
platform.render(getSpriteBatch(), getShapeRenderer());
}
getShapeRenderer().end();
}
}
Platform code:
public class Platform extends GameObject {
private ArrayList < Sector > sectors = new ArrayList < Sector > ();
private ArrayList < Platform > relations = new ArrayList < Platform > ();
private boolean rotatable = true;
private boolean locked = false;
private void initialize(float cx, float cy, float radius, float sides, Color color) {
setPosition(cx, cy);
setRadius(radius);
setShape(ShapeType.POLYGON.getInstance(new float[] {
cx, cy, radius, sides
}, color));
}
public Platform(float cx, float cy, float radius, float sides, Color color, boolean isEmpty, GameWorld gameWorld) {
setGameWorld(gameWorld);
initialize(cx, cy, radius, sides, color);
setEmpty(isEmpty);
if (!isEmpty()) {
generateSectors();
}
}
private void generateSectors() {
float[] vertices = getShape().getVertices();
for (int i = 0; i < vertices.length; i += 2) {
try {
Color color = Color.WHITE;
if (i + 3 > vertices.length) {
getSectors().add(new Sector(new float[] {
getX(), getY(), vertices[i], vertices[i + 1], vertices[0], vertices[1]
}, color, this, i / 2));
} else {
getSectors().add(new Sector(new float[] {
getX(), getY(), vertices[i], vertices[i + 1], vertices[i + 2], vertices[i + 3]
}, color, this, i / 2));
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
public void rotateBy(float degrees) {
setRotation(degrees);
getShape().rotate(degrees);
for (Sector sector: getSectors()) {
sector.rotate(degrees);
}
for (Platform platform: getRelations()) {
platform.rotateBy(degrees);
}
for (Figure figure: getGameWorld().getActiveFigures()) {
figure.rotate(degrees);
}
}
#Override
public void moveTo(float x, float y) {
super.moveTo(x, y);
getShape().moveTo(x, y);
for (Sector sector: getSectors()) {
sector.moveTo(x, y);
}
for (Platform platform: getRelations()) {
platform.moveTo(x, y);
}
}
public void addRelation(Platform platform) {
if (platform.equals(this)) return;
getRelations().add(platform);
}
#Override
public void update(float delta) {
for (Sector sector: getSectors()) {
sector.update(delta);
}
}
#Override
public void dispose() {
for (Sector sector: getSectors()) {
sector.dispose();
}
}
#Override
public void render(SpriteBatch spriteBatch, ShapeRenderer shapeRenderer) {
render(spriteBatch);
if (Config.DEBUG_LAYOUTS) render(shapeRenderer);
for (Sector sector: getSectors()) {
sector.render(spriteBatch, shapeRenderer);
}
}
private void render(ShapeRenderer shapeRenderer) {
shapeRenderer.setColor(getShape().getColor());
shapeRenderer.polygon(getShape().getVertices());
}
public void resize(boolean reposition, int width, int height) {
if (reposition) {
moveTo(width / 2, height / 2);
}
}
}
Sector code:
public class Sector extends GameObject {
private Polyline direction;
private float[] vertices;
private Platform platform;
private int sectorId;
public Sector(float[] vertices, Color color, Platform platform, int sectorId) throws Exception {
setSectorId(sectorId);
setVertices(vertices);
initialize(vertices, color, platform);
}
private void createDirection() {
float[] vertices = getShape().getPolygon().getVertices();
float x1 = vertices[0];
float y1 = vertices[1];
float x2 = (vertices[2]+vertices[4])/2;
float y2 = (vertices[3]+vertices[5])/2;
setDirection(new Polyline(new float[]{ x1, y1, x2, y2 }));
}
public Sector(float[] vertices, Color color, boolean isEmpty) throws Exception {
initialize(vertices, color);
setEmpty(isEmpty);
}
private void initialize(float[] vertices, Color color) throws Exception {
if (vertices.length != 6) {
throw new Exception("Sector constructor expects 6 vertices");
}
setShape(ShapeType.TRIANGLE.getInstance(vertices, color));
createDirection();
}
private void initialize(float[] vertices, Color color, Platform platform) throws Exception {
if (vertices.length != 6) {
throw new IllegalArgumentException("Sector constructor expects 6 vertices");
}
setShape(ShapeType.TRIANGLE.getInstance(vertices, color));
setPlatform(platform);
createDirection();
}
public void rotate(float degrees) {
getShape().rotate(degrees);
getDirection().rotate(degrees);
}
#Override
public void moveTo(float x, float y) {
super.moveTo(x, y);
getShape().moveTo(x, y);
getDirection().setPosition(x, y);
}
#Override
public void render(SpriteBatch spriteBatch, ShapeRenderer shapeRenderer) {
render(spriteBatch);
if (Config.DEBUG_LAYOUTS) render(shapeRenderer);
}
private void render(ShapeRenderer shapeRenderer) {
shapeRenderer.setColor(getShape().getColor());
shapeRenderer.polygon(getShape().getVertices());
shapeRenderer.setColor(Color.GREEN);
shapeRenderer.line(getDirection().getTransformedVertices()[0], getDirection().getTransformedVertices()[1], getDirection().getTransformedVertices()[2], getDirection().getTransformedVertices()[3]);
}
}
Figure code:
public class Figure extends GameObject {
private GameWorld world;
public void init(GameWorld world, float cx, float cy, float radius, float sides, Color color) {
super.init();
setWorld(world);
initialize(cx, cy, radius, sides, color);
}
private void initialize(float cx, float cy, float radius, float sides, Color color) {
super.moveTo(cx, cy);
setRadius(radius);
setShape(ShapeType.POLYGON.getInstance(new float[] {
cx, cy, radius, sides
}, color));
}
#Override
public void moveTo(float x, float y) {
super.moveTo(x, y);
getShape().moveTo(x, y);
}
#Override
public void setPosition(float x, float y) {
if (!isAllowedToFlyFuther()) {
clearActions();
return;
}
moveTo(x, y);
}
private boolean isAllowedToFlyFuther() {
for (Figure figure: getWorld().getActiveFigures()) {
if (!figure.equals(this) && Intersector.overlapConvexPolygons(figure.getShape().getPolygon(), getShape().getPolygon())) {
return false;
}
}
return true;
}
#Override
public void reset() {
super.reset();
remove();
}
#Override
public void update(float delta) {}
private void render(SpriteBatch spriteBatch) {}
#Override
public void dispose() {}
#Override
public void render(SpriteBatch spriteBatch, ShapeRenderer shapeRenderer) {
render(spriteBatch);
if (Config.DEBUG_LAYOUTS) render(shapeRenderer);
}
private void render(ShapeRenderer shapeRenderer) {
shapeRenderer.setColor(getShape().getColor());
shapeRenderer.polygon(getShape().getVertices());
}
public void rotate(float degrees) {
setRotation(degrees);
getShape().rotate(degrees);
}
public void origin(float originX, float originY) {
setOrigin(originX, originY);
getShape().setOrigin(originX, originY);
}
public void resize(boolean reposition, int width, int height) {
if (reposition) {
//TODO: implement reposition for figures
}
}
}
GameObject code:
public abstract class GameObject extends Actor implements Poolable {
private int speed = 200;
private int baseSpeed = 200;
private boolean alive;
private float radius;
private GameWorld gameWorld;
private Shape shape;
private boolean empty = false;
public GameObject() {
setAlive(false);
}
public void init() {
setAlive(true);
}
public void reset() {
setAlive(false);
}
public abstract void update(float delta);
public abstract void render(SpriteBatch spriteBatch, ShapeRenderer shapeRenderer);
public abstract void dispose();
public void moveTo(float x, float y) {
super.setPosition(x, y);
}
}
Shape code:
public class Shape {
private Color color = new Color(Color.RED);
private float[] vertices;
private int sides;
private float radius;
private Polygon polygon = new Polygon();
public void rotate(float degrees) {
getPolygon().rotate(degrees);
setVertices(getPolygon().getTransformedVertices());
}
public void moveTo(float x, float y) {
getPolygon().setPosition(x, y);
setVertices(getPolygon().getTransformedVertices());
}
public void setOrigin(float originX, float originY) {
getPolygon().setOrigin(originX, originY);
setVertices(getPolygon().getTransformedVertices());
}
public void scale(float ratio) {
getPolygon().setScale(ratio, ratio);
setVertices(getPolygon().getTransformedVertices());
}
}
ShapeType code:
public enum ShapeType {
POLYGON {#Override
public Shape getInstance(float[] settings, Color color) {
try {
return new PolygonShape(settings, color);
} catch (Exception e) {
e.printStackTrace();
}
return new BaseShape();
}
},
TRIANGLE {#Override
public Shape getInstance(float[] settings, Color color) {
try {
return new TriangleShape(settings, color);
} catch (Exception e) {
e.printStackTrace();
}
return new BaseShape();
}
};
public abstract Shape getInstance(float[] settings, Color color);
}
PolygonShape code:
public class PolygonShape extends Shape {
public PolygonShape(float[] settings, Color color) throws Exception {
if (settings.length < 4) {
throw new IllegalArgumentException("Polygon shape constructor expects minimum 4 items in settings");
}
setSides((int) settings[3]);
setRadius(settings[2]);
setVertices(Utils.mergeCoordinates(Utils.getPolygonArrays(settings[0], settings[1], settings[2], (int) settings[3])));
getPolygon().setVertices(getVertices());
}
}
TriangleShape code:
public class TriangleShape extends Shape {
public TriangleShape(float[] settings, Color color) throws Exception {
if (settings.length < 6) {
throw new IllegalArgumentException("Triangle shape constructor expects minimum 6 items in settings");
}
setVertices(settings);
setColor(color);
getPolygon().setVertices(getVertices());
}
}
Utils code:
public class Utils {
public static float[] mergeCoordinates(float[][] vertices) throws Exception {
if (vertices.length != 2 || vertices[0].length != vertices[1].length) throw new Exception("No valid data");
ArrayList < Float > mergedArrayList = new ArrayList < Float > ();
float[] mergedArray = new float[vertices[0].length * 2];
for (int i = 0; i < vertices[0].length; i++) {
mergedArrayList.add(vertices[0][i]);
mergedArrayList.add(vertices[1][i]);
}
int i = 0;
for (Float f: mergedArrayList) {
mergedArray[i++] = (f != null ? f : Float.NaN);
}
return mergedArray;
}
public static float[][] getPolygonArrays(float cx, float cy, float R, int sides) {
float[] x = new float[sides];
float[] y = new float[sides];
double thetaInc = 2 * Math.PI / sides;
double theta = (sides % 2 == 0) ? thetaInc : -Math.PI / 2;
for (int j = 0; j < sides; j++) {
x[j] = (float)(cx + R * Math.cos(theta));
y[j] = (float)(cy + R * Math.sin(theta));
theta += thetaInc;
}
return new float[][] {
x, y
};
}
}
Config code:
public class Config {
public static final String LOG = TheGame.class.getSimpleName();
public static final boolean DEBUG_LAYOUTS = true;
public static final boolean SHOW_LOG = false;
public static final float PLATFORM_ROTATE_DEGREES = 36;
}
DesktopLauncher code:
public class DesktopLauncher {
public static void main(String[] arg) {
LwjglApplicationConfiguration config = new LwjglApplicationConfiguration();
config.title = "The Game!";
config.width = 1920 / 3;
config.height = 1080 / 3;
new LwjglApplication(new TheGame(), config);
}
}
Project structure:
Platform object structure and dependencies:
Render objects workflow
This is a grade 12 object oriented programming project.
I have a class called Ball to construct my ball object.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.util.*;
public class Ball{
private double xPos;
private double yPos;
private int direction;
public Ball(int ixPos, int iyPos, int idirection){
xPos = ixPos;
yPos = iyPos;
direction = idirection;
}
public int returnX(){
return (int)xPos;
}
public int returnY(){
return (int)yPos;
}
public int returnDirection(){
return direction;
}
public void move(){
xPos += 1*Math.cos(Math.toRadians(direction));
yPos -= 1*Math.sin(Math.toRadians(direction));
}
public void collide(int collideWith){
if(collideWith==1){//collide with left wall
if(90<direction && direction<180){
direction = 180-direction;
}
if(180<direction && direction<270){
direction = 540-direction;
}
}
if(collideWith==2){//collide with right wall
if(0<direction && direction<90){
direction = 180-direction;
}
if(270<direction && direction<360){
direction = 540-direction;
}
}
if(collideWith==3){//collide with up wall
if(0<direction && direction<90){
direction = 360-direction;
}
if(90<direction && direction<180){
direction = 360-direction;
}
}
if(collideWith==4){//collide with down wall
direction = 360-direction;
}
}
public void collidePaddle(int collidePos){
if(collidePos!=50 && collidePos!=0){
direction = (50-collidePos)*180/50;
}
}
}
As you can see in the "move" function, right now the ball is going at a very low speed. But i need the ball to go faster. If I change the 1 into something like, 5, there would be a problem. In my main class where it checks if the ball is hitting the wall or blocks to change direction, the ball would go into the wall or the blocks if the amount of pixels the ball can move each time is greater than 1.
To me it seems like there's no way to solve this problem, no idea where I would start thinking, is there a better way of checking collide or something?
Thank you.
Instead of using absolute checks in your collidePaddle check, you should allow for ranges.
For example...
public void collidePaddle(int collidePos){
if (collidePos >= 50) {
direction = (50-collidePos)*180/50;
// Correct the position of the ball to meet the minimum requirements
// of the collision...
} else if (collidePos <= 0) {
direction = (50-collidePos)*180/50;
// Correct the position of the ball to meet the minimum requirements
// of the collision...
}
}
(Sorry, I'm having fun working out your code ;))
This will allow the ball the "pass" beyond the these points within a virtual context, but if you correct the position to componsate, it should make no difference...when it's rendered...
Updated
Here's a REALLY SIMPLE example of what I'm talking about...
public class SimpleBouncyBall {
public static void main(String[] args) {
new SimpleBouncyBall();
}
public SimpleBouncyBall() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception ex) {
}
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new CourtPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class CourtPane extends JPanel {
private Ball ball;
private int speed = 5;
public CourtPane() {
Timer timer = new Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
Rectangle bounds = new Rectangle(new Point(0, 0), getSize());
if (ball == null) {
ball = new Ball(bounds);
}
speed = ball.move(speed, bounds);
repaint();
}
});
timer.setRepeats(true);
timer.setCoalesce(true);
timer.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(100, 100);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (ball != null) {
Graphics2D g2d = (Graphics2D) g.create();
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
Point p = ball.getPoint();
g2d.translate(p.x, p.y);
ball.paint(g2d);
g2d.dispose();
}
}
}
public class Ball {
private Point p;
private int radius = 12;
public Ball(Rectangle bounds) {
p = new Point();
p.x = 0;
p.y = bounds.y + (bounds.height - radius) / 2;
}
public Point getPoint() {
return p;
}
public int move(int speed, Rectangle bounds) {
p.x += speed;
if (p.x + radius >= (bounds.x + bounds.width)) {
speed *= -1;
p.x = ((bounds.x + bounds.width) - radius) + speed;
} else if (p.x <= bounds.x) {
speed *= -1;
p.x = bounds.x + speed;
}
p.y = bounds.y + (bounds.height - radius) / 2;
return speed;
}
public void paint(Graphics2D g) {
g.setColor(Color.RED);
g.fillOval(0, 0, radius, radius);
}
}
}
My move method doesn't care if you've past the boundaries as it will reposition the ball back to sit within side those boundaries
public int move(int speed, Rectangle bounds) {
// Apply the delta
p.x += speed;
// Have we passed beyond the right side??
if (p.x + radius >= (bounds.x + bounds.width)) {
speed *= -1;
p.x = ((bounds.x + bounds.width) - radius) + speed;
// Have we past beyond the left side??
} else if (p.x <= bounds.x) {
speed *= -1;
p.x = bounds.x + speed;
}
p.y = bounds.y + (bounds.height - radius) / 2;
return speed;
}
Play around with the speed and see what you get ;)