Related
Hi so i have here a 'falling' style game where i have homer simpsons head falling through the sky avoiding the salads and collecting burgers to increase score. I have implemented collision detection so when homer hits a salad he should re-spawn and have a life deducted which works fine. However if homers head hits the side of the salad and not directly in the centre of it the game will freeze for a moment and then carry on like nothing happened. I am not sure why this happens and wondering if there is a problem with how i am doing this. Here is my code below:
Are the collision detection not accurate enough or is there another issue that i am missing?
PImage background;
PImage MenuBackground;
int y=0;//global variable background location
final int End = 0;
final int Active = 1;
final int Menu = 2;
int gameMode = Menu;
int score = 0;
int lives = 3;
Boolean BurgerCollisionInProgress = false;
Boolean BurgerCollisionInProgress2 = false;
Salad salad1;
Salad salad2;
Salad salad3;
Homer user1;
Burger Burger;
public void settings()
{
size(500,1000); //setup size of canvas
}
void menu()
{
background = loadImage("spaceBackground.jpg"); //image used for background
background.resize(500,1000); //resizes the background
gameMode = Active;
float rand = random(25,475);
int intRand = int(rand);
float rand2 = random(25,475);
int intRand2 = int(rand2);
float rand3 = random(25,475);
int intRand3 = int(rand3);
float rand4 = random(25,475);
int intRand4 = int(rand4);
user1 = new Homer(250,100); //declares new defender as user1
Burger = new Burger(intRand,900,2);
salad1 = new Salad(intRand2,900,3);
salad2 = new Salad(intRand3,900,3);
salad3 = new Salad(intRand4,900,3); //3 aliens declared with their x and y position and their speed they move at
draw();
}
void setup()
{
if(gameMode == 2)
{
MenuBackground = loadImage("simpMenu.png");
MenuBackground.resize(540,1000);
image(MenuBackground, 0, y);
textAlign(CENTER);
textSize(40);
fill(252, 3, 3);
text("Press 'p' to play", 250,500);
}
}
void draw ()
{
if (gameMode == Active)
{
if(crash() == false)
{
drawBackground();//calls the drawBackground method
textSize(32);
fill(22,100,8);
text("Score: " + score,75,40);
text("Lives: " + lives,75,80);
salad1.update();//calls the update method which holds the move and render methods for alien
salad2.update();
salad3.update();
user1.render();//calls the update method which holds the move and render methods for user
Burger.update();//calls the update method which holds the move and render methods for burger
if(Bcrash() == true && BurgerCollisionInProgress == false)
{
score = score+1;
BurgerCollisionInProgress = true;
Burger.y = 900;
float rand = random(25,475);
int intRand = int(rand);
Burger.x = intRand;
}
if(Bcrash() == false)
{
BurgerCollisionInProgress = false;
}
if(crash() == true && BurgerCollisionInProgress2 == false)
{
if (lives < 1)
{ gameMode = End;
textSize(28);
fill(22,100,8);
text("Game Over, press 'r' to restart",200,200);
}
else
{
lives = lives - 1;
BurgerCollisionInProgress2 = true;
menu();
}
if(crash() == false)
{
BurgerCollisionInProgress2 = false;
}
}
}
}
}
void drawBackground()
{
image(background, 0, y); //draw background twice adjacent
image(background, 0, y-background.width);
y -=2;
if(y == -background.width)
y=0; //wrap background
}
boolean crash()
{
if(user1.crash(salad1))
{
return true;
}
if(user1.crash(salad2))
{
return true;
}
if(user1.crash(salad3))
{
return true;
}
return false;
}
boolean Bcrash()
{
if(user1.crash(Burger))
{
return true;
}
return false;
}
Homer class:
class Homer
{
PImage UserImage;
int x,y; //declaring variables
Homer(int x, int y)
{
this.x = x;
this.y = y;
UserImage = loadImage("homer.png");
UserImage.resize (60, 52);
} // end of Homer
void render()
{
//draw a Homer
image(UserImage,x,y);
} //end of void render
boolean crash(Salad A)
{
if((abs(x-A.x)<=30) && abs(y-A.y)<=30)
{
return true;
}
return false;
}// end of crash
boolean crash(Burger A)
{
if((abs(x-A.x)<=30) && abs(y-A.y)<=30)
{
return true;
}
return false;
}
} // end of class
Burger Class:
class Burger
{
PImage burgerImage;
int x,y, speedX;
int speedY = 0;
Burger(int x, int y, int speedY)
{
this.x = x;
this.y = y;
this.speedY= speedY;
burgerImage = loadImage("food.png");
burgerImage.resize (60, 52);
}
void render()
{
image(burgerImage,x,y);
}
void move()
{
y = y - speedY;
float rand = random(25,475);
int intRand = int(rand);
if(this.y < 0)
{
this.y = 900;
this.x = intRand;
}
}
void update()
{
move();
render();
}
}
Salad Class:
class Salad
{
float x,y;
float speedX, speedY; //declaring variables
PImage saladImage;
Salad(int x, int y, int speedY)
{
this.x = x;
this.y = y;
this.speedY = speedY;
saladImage = loadImage("salad.png");
saladImage.resize (60, 52);
} //end of salad
void move()
{
y=y-speedY;
float stepY = random(-5,5);
y = y + (int)stepY;
float rand = random(25,475);
int intRand = int(rand);
if(this.y < 0)
{
this.y = 900; // once the salads y is less than 0 they restart at 900
this.x = intRand;
speedY = speedY + 0.5;
}
} //end of void move
//draw a salad
void render()
{
image(saladImage,x,y);
} //end of void render
void update()
{
move();
render();
}
}// end of alien class
There are several little things which makes this harder than it could be. First, your intersect method isn't quite right. Then, the way you handle coordinates could be improved upon.
What I'm going to do first is to show you how to intersect rectangles. After that, I'll show you how I would deal with the drawable objets so they stay easy to manipulate. Then I'll show you some skeleton code for a short, easy game with stuff falling and colliding, and just for you I'll add some help so you can implement these suggestions into the context of your game.
1. Collisions
There are many ways to handle collisions. Most of them are applied mathematics, some of them are clever algorithms making use of colors or invisible sprites. There are probably methods I'm forgetting, too.
We'll only do collisions between rectangles, as your program seems quite rectangle-friendly and it's the easier method. So we'll write a intersection detection algorithm.
First thing to do when writing an algorithm is the pseudocode. I'm not joking. It's easy to go all clakety-clak with your keyboard and hit compile. It works most of the time... but it's more intuitive logic than applying your brain to the problem.
Being able to pseudocode is like a superpower for programmers. Never underestimate it.
Now, how do you know if two rectangles are intersecting? The answer is:
There are 4 ways that two rectangles can intersect, whether horizontally or vertically.
They must intersect both horizontally and vertically to overlap for real.
These are the possibilities you have to look for:
Red rectangle is bigger than black rectangle and black rectangle is completely inside it.
Both rectangles overlap on the left side (horizontally) or on the top side (vertically).
Red rectangle small enough to be inside black rectangle.
Both rectangles overlap on the right side (horizontally) or on the bottom side (vertically).
Because this code can be used in many places, I took it out of context and put it inside a fonction which takes coordinates and returns a boolean (true if there indeed is a collision):
// INTERSECT RECTs
boolean intersect(float x1, float y1, float w1, float h1, float x2, float y2, float w2, float h2)
{
boolean checkX = x1 < x2 && x1+w1 > x2 || x1 < x2+w2 && x1+w1 > x2+w2 || x1 > x2 && x1+w1 < x2+w2 || x1 < x2 && x1+w1 > x2+w2;
boolean checkY = y1 < y2 && y1+h1 > y2 || y1 < y2+h2 && y1+h1 > y2+h2 || y1 > y2 && y1+h1 < y2+h2 || y1 < y2 && y1+h1 > y2+h2;
return checkX && checkY;
}
This is one way of handling collisions between rectangles. You could take this information and apply it to your game, and it would rock.
This said, you could also improve on your code with Inheritance...
2. Inheritance (in this case: for graphical objects)
Inheritance in computer science is a way to make a class obtain the properties of another one. Most people explains it in term of family: there is a parent class and there are children class which inherits the parent class' properties.
Inheritance is especially useful when several of your class share the same properties or methods. Drawable objects are a great example, because they all need coordinates. They all need a method to be drawn.
As you'll see with the example game later, I noticed that all my rectangles needed these modal variables:
protected float x, y, w, h; // x and y coordinate, width and height of the square
protected color fill, stroke;
protected float strokeWeight = 1;
So I created a base class named 'Drawable'. In a bigger project, it could be the base class of a whole tree of classes, like this:
So in this example, Rat would be the child of Walker, which is the child of Enemy, which is the child of Actor, which is the child of Drawable.
The advantage is that every child inherits everything from it's parent. It both makes you write less code and let you fix your mistakes in only one place instead of everywhere. For an example, if there's a mistake in how you use the coordinates of your objects, you want to fix it in the class where this logic is written, not in every class.
There are many other advantages to Inheritance, but for now let's keep it simple, all right?
3. Example program
This one is very straightforward: this is an example which use both inheritance and collisions. You can copy and paste it into a Processing IDE and it'll run. Take some time to see how the 3 classes relate to one another, and how every child class has the modal variables and functions of it's parent.
Hero hero;
ArrayList<Bomb> bombs = new ArrayList<Bomb>();
int numberOfBombs = 20; // if you change this number the number of bombs will change too. Try it!
int hitCount = 0;
public void settings()
{
size(800, 600); //setup size of canvas
}
public void setup() {
hero = new Hero();
for (int i = 0; i < numberOfBombs; i++) {
bombs.add(new Bomb(random(20, width-20), random(1, 10)));
}
// This part serves no purpose but to demonstrate that you can gather objets which share a parent class together
ArrayList<Drawable> myDrawables = new ArrayList<Drawable>();
for (Bomb b : bombs) {
myDrawables.add(b);
}
myDrawables.add(hero);
for (Drawable d : myDrawables) {
d.Render();
// Even though hero and the bombs are different classes, they are in the same ArrayList because they share the Drawable parent class.
// Drawable has the Render() function, which may be called, but the child class will overshadow the Drawable's method.
// Proof is that the error message "Drawable child: Render() was not overshadowed." will not appear in the console.
}
}
public void draw() {
DrawBackground();
hero.Update();
hero.Render();
for (Bomb b : bombs) {
b.Update();
b.Render();
}
ShowHitCount();
}
public void DrawBackground() {
fill(0);
stroke(0);
rect(0, 0, width, height, 0); // dark background
}
public void ShowHitCount() {
textAlign (RIGHT);
textSize(height/20);
fill(color(200, 200, 0));
text(hitCount, width-20, height/20 + 20);
}
class Drawable {
protected float x, y, w, h; // 'protected' is like 'private', but child class retain access
protected color fill, stroke;
protected float strokeWeight = 1;
Drawable() {
this(0, 0, 0, 0);
}
Drawable(float x, float y, float w, float h) {
this.x = x;
this.y = y;
this.w = w;
this.h = h;
}
public void Render() { print("Drawable child: Render() was not overshadowed."); } // nothing to see here: this exists so we can overshadow it in the childs
public void Update() { print("Drawable child: Update() was not overshadowed."); } // same thing
}
class Hero extends Drawable { // 'extends' is keyword for 'will inherit from'
Hero() {
// 'super()' calls the parent's constructor
// in this example, I decided that the hero would be a red 40x60 rectangle that follows the mouse X position
super(mouseX - 20, height - 80, 40, 60);
fill = color(200, 0, 0);
stroke = color(250);
}
public void Update() { // when both parents and child have the same function (type and signature), the child's one prevail. That's overshadowing.
x = mouseX - w/2;
}
public void Render() {
fill(fill);
stroke(stroke);
strokeWeight(strokeWeight);
rect(x, y, w, h);
}
}
class Bomb extends Drawable {
protected float fallSpeed;
Bomb(float xPosition, float fallSpeed) {
// Bombs will be small blue squares falling from the sky
super(xPosition, -20, 20, 20);
this.fallSpeed = fallSpeed;
fill = color(0, 0, 200);
stroke = fill;
}
private void FallAgain() {
x = random(20, width-20);
fallSpeed = random(1, 10);
y = 0 - random(20, 100);
}
public void Update() {
y += fallSpeed;
// check for collision with the Hero
if (intersect(x, y, w, h, hero.x, hero.y, hero.w, hero.h)) {
hitCount++;
FallAgain();
}
// check if it fell lower than the screen
if (y > height) {
FallAgain();
}
}
public void Render() {
fill(fill);
stroke(stroke);
strokeWeight(strokeWeight);
rect(x, y, w, h);
}
}
// INTERSECT RECTs
boolean intersect(float x1, float y1, float w1, float h1, float x2, float y2, float w2, float h2)
{
boolean checkX = x1 < x2 && x1+w1 > x2 || x1 < x2+w2 && x1+w1 > x2+w2 || x1 > x2 && x1+w1 < x2+w2 || x1 < x2 && x1+w1 > x2+w2;
boolean checkY = y1 < y2 && y1+h1 > y2 || y1 < y2+h2 && y1+h1 > y2+h2 || y1 > y2 && y1+h1 < y2+h2 || x1 < y2 && y1+h1 > y2+h2;
return checkX && checkY;
}
4. Bonus: help with implementation
So... you're seeing this and it makes you want to improve on your program. That's good. Maybe you want to implement some inheritance, maybe just the collisions. Both can be tricky, and neither is supposed to impact the user.
This is what is called 'refactoring'.
Let's implement a Drawable class first. The rest will be easier then.
First step: find what's the common ground with Burger, Homer and Salad. From the code you posted, I can see that they need these things:
int x, y;
int speedX, speedY;
PImage img;
// To which I would add:
int w, h;
boolean isVisible;
I notice that you're using integers. That's fine, but I strongly suggest using float for coordinates. I did the same thing when I was learning to code and I ended up regretting not using float earlier. Both integer and float will probably do the trick for this project (with some cast when needed).
Also, here are a couple functions that they share:
void Render()
void Update()
void Move()
// To which I would add:
void SetPosition()
void SetIsVisible()
boolean Crash() // so we can check if it intersect with given coordinates
So far, your Drawable class could look like this:
class Drawable {
public float x, y, w, h; // Making variables public while you could avoid it is bad practice, I'm doing it to avoid writing Get functions. Avoid doing this as much as possible, but bear with me for now.
protected float speedX, speedY;
protected PImage img;
protected boolean isVisible = true;
Drawable(float x, float y, float w, float h, String imagePath) {
this.x = x; // starting x position
this.y = y; // starting y position
this.w = w; // width if the object (your image in this case)
this.h = h; // height of the object (height of your image)
if (imagePath.length() > 0) { // if there is nothing in the string it won't try to load an image
img = loadImage(imagePath);
}
}
public void Render() {
if (isVisible && img != null) {
image(img, x, y);
}
}
public void Update() {
Move(); // I kept Move() out of Update() so you can overshadow Update() without having to re-code Move() later
}
protected void Move() {
// The 'normal' behavior of a Drawable would then to move according to it's speed.
// You can then change how they move by changing their speed values.
// Or... you can overshadow this function in a child class and write your own!
x += speedX;
y += speedY;
}
public void SetPosition(float x, float y) {
this.x = x;
this.y = y;
}
public void SetIsVisible(boolean isVisible) {
this.isVisible = isVisible;
}
public boolean Crash(float x, float y, float w, float h) {
// this function uses the 'intersect' function I wrote earlier, so it would have to be included in the project
return intersect(this.x, this.y, this.w, this.h, x, y, w, h);
}
}
Not so bad so far, isn't it? This will make a strong base for all your objects. Now, let's see how to implement this into your existing class:
Homer:
class Homer extends Drawable // give Homer the power of the Drawable class!
{
Homer(float x, float y)
{
// I can read in the code that your image will be (60, 52), but you have to write the manipulation here
super(x, y, 60, 52, "homer.png");
img.resize (60, 52);
}
public void Update() {
// do Update stuff so Homer can move around
}
}
Notice how smaller this class is now that all the Drawable stuff is dealt elsewhere.
Now, here's for the Salad class:
First, you can drop the salad1, salad2, salad3 global variables. We'll put them in a list, and you'll be able to have more or less of them if you want (you can think of this as being able to change the difficulty setting):
int numberOfSalads = 3;
ArrayList<Salad> salads = new ArrayList<Salad>();
In the place where you innitialize the salads, you can initialize them in a loop:
for (int i=0; i<numberOfSalads; i++) {
salads.add(new Salad(random(25,475), 900, 3);
}
Sure, there will be some modifications to make to the Salad class, too:
class Salad extends Drawable {
Salad(float x, float y, float speedY)
{
super(x, y, 60, 52, "salad.png");
this.speedY = speedY; // Drawable will take it from here
img.resize (60, 52);
}
protected void Move() // I knew this would come in handy!
{
// I have no idea what's going on, just re-writing your stuff
y = y - speedY;
y = y + random(-5, 5);
if (this.y < 0)
{
this.y = 900; // once the salads y is less than 0 they restart at 900
this.x = random(25, 475);
speedY = speedY + 0.5;
}
}
}
So far, so good. There are MANY other places where you'll have to adapt the code, but you should notice that so far you've removed more lines that You've added. That's a good thing. As long as your code is easy to read, making it shorter means that there's less places to look for nasty bugs to fix.
Also, when you avoid repeating the same lines (like all those identical Render functions) by having them all in one place (the Drawable class in this case), you also avoid having to hunt down every iteration of your code if you want to make one change. This is called DRY code. DRY (for Dont Repeat Yourself) code is waaay easier to debug and maintain. As a rule of thumb, every time you copy and paste code without any change, you should ask yourself if you could just keep these line in one centralized place, whether it's a variable or a function or a class.
I'll let you code the Burger class. I think you'll manage it now that you have seen how to deal with the others.
Now, let's take a look at how to update your main loop, draw():
void draw ()
{
// As a general rule, all your game states should be dealt in the game loop.
// I like 'switch' statements for this kind of operations
// Also, try not to clutter the game loop. If you have a lot of code here, you should probably put them into functions
// it will make it easier to read and the game loop can very easily become a spaghetti nightmare if you're not careful.
switch(gameMode) {
case Menu:
// Do Menu stuff
break;
case Active:
drawBackground(); // Maybe this should be before the switch, I'm not sure how you want to deal with this
// Updates
user1.Update();
burger.Update();
for (Salad s : salads) {
s.Update();
}
// Check for collisions
// I may be mistaken but I think only the Homer can collide with stuff
if (burger.Crash(user1.x, user1.y, user1.w, user1.h)) {
// Do burger crash stuff
}
for (Salad s : salads) {
if (s.Crash(user1.x, user1.y, user1.w, user1.h)) {
// Do Salad crash stuff
}
}
// Render
user1.Render();
burger.Render();
for (Salad s : salads) {
s.Render();
}
break;
case End:
// Do End stuff
break;
}
}
This should put you on track.
If, for some reason you only want to use the intersect method: remember that the width and height of your objects are the one you use for their images.
You probably have questions, don't hesitate to ask away. And have fun!
the function
boolean intersect(float x1, float y1, float w1, float h1, float x2, float y2, float w2,
float h2)
{
boolean checkX = x1 < x2 && x1+w1 > x2 || x1 < x2+w2 && x1+w1 > x2+w2 || x1 > x2 &&
x1+w1 < x2+w2 || x1 < x2 && x1+w1 > x2+w2;
boolean checkY = y1 < y2 && y1+h1 > y2 || y1 < y2+h2 && y1+h1 > y2+h2 || y1 > y2 &&
y1+h1 < y2+h2 || y1 < y2 && y1+h1 > y2+h2;
return checkX && checkY;
}
only checks if rect1 is inside of rect2
in the function you dont neeed any or statements
here is the correct function
boolean intersect(float x1, float y1, float w1, float h1, float x2, float y2, float, w2, float h2)
{
boolean checkX = x1 < x2+w2 && x1+w1>x2;
boolean checkY = y1 < y2+h2 && y1+h1>y2;
return checkX && checkY;
}
I've been stuck on collision detection and how to handle it for a VERY long time. I need help understanding how to use collision detection with Tiled map editor. I have the TMX file parsed and displayed with a player, camera and keyboard movement. I'm not sure how to about doing to collision. My map has 2 layers, one for grass and tiles you can walk on, the other is objectLayer. I've seen people saying I should loop through all the tiles in an object layer and assign rectangles to them. I've got NO idea how to do that and I'm looking for some insight.
My main question is: how do I loop through my layer and get all the tiles and assign rectangles to them.
Game Class:
public class Game extends BasicGameState {
Player player;
Camera cam;
Map map = new Map();
public void init(GameContainer container, StateBasedGame sbg) throws SlickException {
map.init();
cam = new Camera(0, 0);
player = new Player(new Image("res/textures/player.png"), container.getWidth() / 2, container.getHeight() / 2, 32, 32);
}
public void update(GameContainer container, StateBasedGame sbg, int delta) throws SlickException {
cam.tick(player);
player.update(container, delta, map);
}
public void render(GameContainer container, StateBasedGame sbg, Graphics g) throws SlickException {
g.translate(-cam.getX(), -cam.getY());
map.render();
player.render(g);
g.translate(cam.getX(), cam.getY());
}
public int getID() {
return 1;
}
}
Player Class:
public class Player {
float x, y;
int width, height;
double velX = 0.4;
double velY = 0.4;
Image img;
public Player(Image img, float x, float y, int width, int height) {
this.img = img;
this.x = x;
this.y = y;
this.width = width;
this.height = height;
}
public void init() throws SlickException {
}
public void update(GameContainer container, int delta, Map map) {
Input input = container.getInput();
if (input.isKeyDown(Input.KEY_D)) x += velX;
if (input.isKeyDown(Input.KEY_A)) x -= velX;
if (input.isKeyDown(Input.KEY_W)) y -= velY;
if (input.isKeyDown(Input.KEY_S)) y += velY;
}
public void render(Graphics g) {
//g.scale(-2, -2);
g.drawImage(img, x, y);
}
public float getX() {
return x;
}
public float getY() {
return y;
}
}
Map class:
public class Map {
TiledMap map;
public void init() throws SlickException {
map = new TiledMap("res/map/zenith.tmx");
}
public void render() throws SlickException {
map.render(0, 0);
}
}
I'm not too familiar with the methods exactly for getting the tile, but it's definitely in the Javadoc. http://slick.ninjacave.com/javadoc/
For checking collisions against a player, you need to change your movement to check whether the new location of the player is going to have a collision, otherwise you'll get stuck on them.
public void update(GameContainer container, int delta, Map map) {
Input input = container.getInput();
float dx = 0, dy = 0;
// add to dx and dy instead of moving the player straight away.
if (input.isKeyDown(Input.KEY_D)) dx += velX;
if (input.isKeyDown(Input.KEY_A)) dx -= velX;
if (input.isKeyDown(Input.KEY_W)) dy -= velY;
if (input.isKeyDown(Input.KEY_S)) dy += velY;
// here we check the collisions, you can fiddle with this if statement and it'll behave differently.
if(dx != 0 || dy != 0){
if(map.checkCollision(x+dx,y+y){
x+=dx;
y+=dy;
}
}
}
As for checking the collision itself you need a new method in your map class that creates a rectangle for the player and checks against a 2D array inside the map class which holds your collisions. You can populate this array when the map is intialised by looping through your Tiled map through the layer you need and checking if the tile is a collision with getTile(x,y). If the tile has the correct properties for a collision you create a rectangle in that location in your 2D array with something like this:
collisionArray[x][y] = new Rectangle(x,y,TILEWIDTH,TILEHEIGHT);
You can now check for the collision inside
map.checkCollision(x,y)
and with a rectangle created for the player using
Rectangle player = new Rectangle(x,y,PLAYERWIDTH,PLAYERHEIGHT);
Putting it all together you can then check for an intersection with any tiles in the collision array with .intersect(r) as mentioned earlier and map.collision can return true to the player which should stop movement of the player.
I hope that wasn't too convoluted, the javadoc is very helpful and Kevin Glass wrote a thing about checking collisions with just 2D arrays, with varying collision box sizes.
http://www.cokeandcode.com/main/tutorials/tile-maps/
Ok, now im getting a null pointer exception in my checkCollision()
public void assignRectangles() {
int objectLayer = map.getLayerIndex("objectLayer");
for (int x = 0; x < map.getWidth(); x++) {
for (int y = 0; y < map.getHeight(); y++) {
if (map.getTileId(x, y, objectLayer) == 1){
collisionArray[x][y] = new Rectangle(x * 32, y * 32, 32, 32);
}
}
}
}
public boolean checkCollision(float x, float y) {
for (int j = 0; j < collisionArray.length; j++) {
for (int i = 0; i < collisionArray[j].length; i++) {
if (collisionArray[j][i].getX() == x || collisionArray[j][i].getY() == y) {
return false;
}
}
}
return true;
}
Im getting it on the line where it say if (collisionArray[j][i].getX() == x || collisionArray[j][i].getY() == y)
Not sure why im getting it though
I'm trying to create a program that creates an instance of circle which gets the x and y coordinates of the mouseclick. However when trying to get the x for the circle it seems to be equal to 0.
pane.setOnMouseClicked(new EventHandler<MouseEvent>() {
public void handle(MouseEvent event) {
final Circle newCircle = getCircle(event.getX(), event.getY(), 30);
pane.getChildren().addAll(newCircle);
final Timeline loop = new Timeline(new KeyFrame(Duration.millis(100), new EventHandler<ActionEvent>() {
double deltaX = 2;
double deltaY = 2;
public void handle(ActionEvent event) {
newCircle.setLayoutX(newCircle.getLayoutX() + deltaX);
newCircle.setLayoutY(newCircle.getLayoutY() + deltaY);
final Bounds bounds = pane.getBoundsInLocal();
final boolean atRightBorder = newCircle.getLayoutX() >= (bounds.getMaxX()-newCircle.getRadius());
final boolean atLeftBorder = newCircle.getLayoutX() <= (bounds.getMinX()+newCircle.getRadius());
final boolean atBottomBorder = newCircle.getLayoutY() >= (bounds.getMinY()+newCircle.getRadius());
final boolean atTopBorder = newCircle.getLayoutY() <= (bounds.getMinY()-newCircle.getRadius());
if(atRightBorder || atLeftBorder)
deltaX *= -1;
if(atBottomBorder ||atTopBorder)
deltaY *= -1;
}
}));
loop.setCycleCount(Timeline.INDEFINITE);
loop.play();
The line i'm focusing on is this one:
final Circle newCircle = getCircle(event.getX(), event.getY(), 30);
System.out.print(newCircle.getLayoutX());
Where i get the print out "0.0" even though the event.getX parameter is different.
Any ideas as to why this happens?
Edit: getCircle():
private Circle getCircle(double x, double y, double r){
final Circle newCircle = new Circle(x, y, r);
return newCircle;
}
The constructor you are calling is implemented as such:
public Circle(double centerX, double centerY, double radius) {
setCenterX(centerX);
setCenterY(centerY);
setRadius(radius);
}
Where setCenterX() has the implementation:
public final void setCenterX(double value) {
if (centerX != null || value != 0.0) {
centerXProperty().set(value);
}
}
getLayoutX() is accessing the layoutX property, which haven't been set by the constructor, hence it returns 0.0:
public final double getLayoutX() {
return layoutX == null ? 0.0 : layoutX.get();
}
You need to set the layoutX property, like you already do in your code:
newCircle.setLayoutX(newCircle.getLayoutX() + deltaX);
I'm having issues with the balls that I display in my JFrame window. Here's the main idea of my application:
Point object (with coordinates x and y) --> Vector object (a class I wrote that has the x and y components of a vector, as well as a few methods... gets its location and head/tail points from the Point object) --> Ball object (the position, velocity, and acceleration vectors are collected in an ArrayList, also has radius and color attributes) --> ContainerBox object (contains the balls, defines the min/max x and y for collision detection purposes).
I'm working toward having one ball centered in the window and the other orbiting the first, but right now I'm just trying to get my objects to play nicely. I'm very new to OOP principles and this is the first time I've written a program using classes in this way.
Everything works perfectly, the JFrame comes up and displays the balls... the problem is that the balls won't show up in the right places. No matter what I put in for the x and y coordinates of the balls (either explicitly or using the objects), they always show up in the upper left-hand corner of the screen. Here's a pic of what I get: Picture Here
I don't know whether it's the vectors or the collision detection or what... any ideas? Thanks a lot for reading and replying!
EDIT: Here's some of the code that I'm using (sorry to put so much, I have no idea where the problem is):
EDIT AGAIN: Added the ball class that I forgot.
package chaneyBouncingBall;
import java.util.*;
public class Point
{
float x;
float y;
public Point(float x, float y)
{
this.x = x;
this.y = y;
}
}
package chaneyBouncingBall;
import java.util.ArrayList;
public class Chaney2DVector
{
float x;
float y;
Point fromLocation;
public Chaney2DVector(float x,
float y)
{
this.x = x;
this.y = y;
}
public Chaney2DVector(Point point1,
Point point2)
{
fromLocation = new Point(point1.x, point1.y);
this.x = point2.x - point1.x;
this.y = point2.y - point1.y;
}
}
package chaneyBouncingBall;
import java.awt.*;
import java.util.*;
public class Ball
{
float x, y;
float velX, velY;
float accelX, accelY;
float radius;
private Color color;
public Ball(float x, float y, float velX,
float velY, float accelX,
float accelY, float radius,
Color color)
{
Chaney2DVector position = new Chaney2DVector(x, y);
Chaney2DVector velocity = new Chaney2DVector(velX, velY);
Chaney2DVector acceleration = new Chaney2DVector(accelX, accelY);
ArrayList posVelAcc = new ArrayList();
posVelAcc.add(position);
posVelAcc.add(velocity);
posVelAcc.add(acceleration);
this.radius = radius;
this.color = color;
}
public void draw(Graphics g)
{
g.setColor(color);
g.fillOval((int)(x - radius), (int)(y - radius),
(int)(2 * radius), (int)(2 * radius));
}
public void moveOneStepWithCollisionDetection( ContainerBox box)
{
float ballMinX = box.minX + radius;
float ballMinY = box.minY + radius;
float ballMaxX = box.maxX - radius;
float ballMaxY = box.maxY - radius;
x = x + velX;
y = y + velY;
if (x < ballMinX)
{
velX = -velX;
x = ballMinX;
}
else if (x > ballMaxX)
{
velX = -velX;
x = ballMaxX;
}
if (y < ballMinY)
{
velY = -velY;
y = ballMinY;
}
else if (y > ballMaxY)
{
velY = -velY;
y = ballMaxY;
}
}
}
package chaneyBouncingBall;
import java.awt.*;
/**
* A rectangular container box, containing the bouncing ball.
*/
public class ContainerBox {
int minX, maxX, minY, maxY; // Box's bounds (package access)
private Color colorFilled; // Box's filled color (background)
private Color colorBorder; // Box's border color
private static final Color DEFAULT_COLOR_FILLED = Color.BLACK;
private static final Color DEFAULT_COLOR_BORDER = Color.YELLOW;
/** Constructors */
public ContainerBox(int x, int y, int width, int height, Color colorFilled, Color colorBorder) {
minX = x;
minY = y;
maxX = x + width - 1;
maxY = y + height - 1;
this.colorFilled = colorFilled;
this.colorBorder = colorBorder;
}
/** Constructor with the default color */
public ContainerBox(int x, int y, int width, int height) {
this(x, y, width, height, DEFAULT_COLOR_FILLED, DEFAULT_COLOR_BORDER);
}
/** Set or reset the boundaries of the box. */
public void set(int x, int y, int width, int height) {
minX = x;
minY = y;
maxX = x + width - 1;
maxY = y + height - 1;
}
/** Draw itself using the given graphic context. */
public void draw(Graphics g) {
g.setColor(colorFilled);
g.fillRect(minX, minY, maxX - minX - 1, maxY - minY - 1);
g.setColor(colorBorder);
g.drawRect(minX, minY, maxX - minX - 1, maxY - minY - 1);
}
}
package chaneyBouncingBall;
import java.awt.*;
import java.awt.event.*;
import java.util.Random;
import javax.swing.*;
/**
* The control logic and main display panel for game.
*/
public class BallWorld extends JPanel {
private static final int UPDATE_RATE = 50; // Frames per second (fps)
private Ball ball; // A single bouncing Ball's instance
private Ball ball2;
private ContainerBox box; // The container rectangular box
private DrawCanvas canvas; // Custom canvas for drawing the box/ball
private int canvasWidth;
private int canvasHeight;
/**
* Constructor to create the UI components and init the game objects.
* Set the drawing canvas to fill the screen (given its width and height).
*
* #param width : screen width
* #param height : screen height
*/
public BallWorld(int width, int height) {
canvasWidth = width;
canvasHeight = height;
boolean stationary = true;
Random rand = new Random();
int angleInDegree = rand.nextInt(360);
int radius = 50;
int radius2 = 25;
// double accelAngle;
float x1 = rand.nextInt(canvasWidth - radius * 2 - 20) + radius + 10;
float y1 = rand.nextInt(canvasHeight - radius * 2 - 20) + radius + 10;
float x2 = rand.nextInt(canvasWidth - radius * 2 - 20) + radius + 10;
float y2 = rand.nextInt(canvasHeight - radius * 2 - 20) + radius + 10;
// float x = 100;
// float y = 100;
float velX1 = 0;
float velY1 = 0;
float accelX1 = 0;
float accelY1 = 0;
float velX2 = 0;
float velY2 = 0;
float accelX2 = 0;
float accelY2 = 0;
ball = new Ball(canvasWidth / 2, canvasHeight / 2, velX1, velY1, accelX1, accelY1,
radius, Color.BLUE);
ball2 = new Ball(x2, y2, velX2, velY2, accelX2, accelY2, radius / 5, Color.YELLOW);
// Init the Container Box to fill the screen
box = new ContainerBox(0, 0, canvasWidth, canvasHeight, Color.BLACK, Color.WHITE);
// Init the custom drawing panel for drawing the game
canvas = new DrawCanvas();
this.setLayout(new BorderLayout());
this.add(canvas, BorderLayout.CENTER);
// Handling window resize.
this.addComponentListener(new ComponentAdapter() {
#Override
public void componentResized(ComponentEvent e) {
Component c = (Component)e.getSource();
Dimension dim = c.getSize();
canvasWidth = dim.width;
canvasHeight = dim.height;
// Adjust the bounds of the container to fill the window
box.set(0, 0, canvasWidth, canvasHeight);
}
});
// Start the ball bouncing
gameStart();
}
/** Start the ball bouncing. */
public void gameStart() {
// Run the game logic in its own thread.
Thread gameThread = new Thread() {
public void run() {
while (true) {
// Execute one time-step for the game
gameUpdate();
// Refresh the display
repaint();
// Delay and give other thread a chance
try {
Thread.sleep(1000 / UPDATE_RATE);
} catch (InterruptedException ex) {}
}
}
};
gameThread.start(); // Invoke GaemThread.run()
}
/**
* One game time-step.
* Update the game objects, with proper collision detection and response.
*/
public void gameUpdate() {
ball.moveOneStepWithCollisionDetection(box);
ball2.moveOneStepWithCollisionDetection(box);
}
/** The custom drawing panel for the bouncing ball (inner class). */
class DrawCanvas extends JPanel {
/** Custom drawing codes */
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g); // Paint background
// Draw the box and the ball
box.draw(g);
ball.draw(g);
ball2.draw(g);
// Display ball's information
g.setColor(Color.WHITE);
g.setFont(new Font("Courier New", Font.PLAIN, 12));
// g.drawString("Ball " + ball.toString(), 20, 30);
}
/** Called back to get the preferred size of the component. */
#Override
public Dimension getPreferredSize() {
return (new Dimension(canvasWidth, canvasHeight));
}
}
}
package chaneyBouncingBall;
import javax.swing.JFrame;
public class Main
{
public static void main(String[] args)
{
javax.swing.SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
JFrame frame = new JFrame("Matt Chaney's Gravity App");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setContentPane(new BallWorld(550, 450));
frame.pack();
frame.setVisible(true);
}
});
}
}
In the constructor of your Ball class, you don't assign all the instance variables to their parameters, these need to be added.
public Ball(float x, float y, float velX, float velY, float accelX, float accelY, float radius, Color color) {
...
this.x = x;
this.y = y;
this.velX = velX;
this.velY = velY;
this.accelX = accelX;
this.accelY = accelY;
}
After a lot of trial and error I was able to draw a cube and a tile map for the cube to be infront of. I added input so that when you move your finger, the camera moves. But now I want to add the next step in my games progression. I want it to have my cube move to where i pressed my finger in 3d space. It sounds a bit difficult. What I wanted to do is have you use one finger to slide the camera around the map and the other to double tap and move. So far my cube jitters and wobbles in place but doesn't move to where I want it to go. Of course none of my code is perfect and my following of gluunproject might be far from correct. Here is my code so far
The cube creator and update code//its short
public shapemaker(int x, int y, int z,int width)
{
centerx =x;
centery =y;
centerz =z;
mysquare1= new square(centerx,centery,centerz-5,1,.0f,.0f,1f,"vert");
mysquare2= new square(centerx-1,centery,centerz-6f,1,.0f,1.0f,.0f,"side");
mysquare3= new square(centerx,centery,centerz-7,1,1.0f,.0f,.0f,"vert");
mysquare4= new square(centerx+1,centery,centerz-6f,1,.0f,.5f,.5f,"side");
mysquare5= new square(centerx,centery-1,centerz-6f,1,.5f,.5f,.0f,"horiz");
mysquare6= new square(centerx,centery+1,centerz-6f,1,.5f,.0f,.5f,"horiz");
}
public void updatecube(float x, float y, float z)
{
centerx =x;
centery =y;
centerz =z;
mysquare1= new square(centerx,centery,centerz-5,1,.0f,.0f,1f,"vert");
mysquare2= new square(centerx-1,centery,centerz-6f,1,.0f,1.0f,.0f,"side");
mysquare3= new square(centerx,centery,centerz-7,1,1.0f,.0f,.0f,"vert");
mysquare4= new square(centerx+1,centery,centerz-6f,1,.0f,.5f,.5f,"side");
mysquare5= new square(centerx,centery-1,centerz-6f,1,.5f,.5f,.0f,"horiz");
mysquare6= new square(centerx,centery+1,centerz-6f,1,.5f,.0f,.5f,"horiz");
}
public void drawcube(GL10 gl)
{
mysquare1.draw(gl);
mysquare2.draw(gl);
mysquare3.draw(gl);
mysquare4.draw(gl);
mysquare5.draw(gl);
mysquare6.draw(gl);
}
public float getcenterx()
{
return centerx;
}
public float getcentery()
{
return centery;
}
public float getcenterz()
{
return centerz;
}
Here is the actual implementation code , it is based off of googles intro to opengl
class ClearGLSurfaceView extends GLSurfaceView {
private static final String BufferUtils = null;
float x =0;
float y =0;
float z =0f;
float prevx=0;
float prevy=0;
GL10 mygl;
// GL gl;
float mywidth;
float myheight;
public ClearGLSurfaceView(Context context,float width, float height) {
super(context);
mRenderer = new ClearRenderer();
setRenderer(mRenderer);
mywidth = width;
myheight = height;
}
#Override
public boolean onTouchEvent(final MotionEvent event)
{
queueEvent(new Runnable() {
// Find the index of the active pointer and fetch its position
public void run()
{
float curx = event.getX();
float cury = event.getY();
final int action = event.getAction();
if(action == MotionEvent.ACTION_MOVE)
{
if(curx>prevx)
{
x-=.1f;
}
if(curx<prevx)
{
x+=.1f;
}
if(cury>prevy)
{
y+=.1f;
}
if(cury<prevy)
{
y-=.1f;
}
}
if(action == MotionEvent.ACTION_DOWN)
{
// GLU.gluUnProject(winX, winY, winZ, model,
// modelOffset, project, projectOffset, view, viewOffset, obj, objOffset)
vector2d moveto = new vector2d(0,0);
moveto = mRenderer.getworkcoords(curx,cury);
Log.i("move to ", "x "+moveto.x+" y "+ moveto.y+" z " + moveto.z);
mRenderer.updatemoveto(moveto.x, moveto.y);
}
prevx= curx;
prevy = cury;
mRenderer.updatecamera(x, y, z);
}
});
return true;
}
ClearRenderer mRenderer;
}
class ClearRenderer implements GLSurfaceView.Renderer {
GL10 mygl;
GL11 mygl11;
int viewport[] = new int[4];
float modelview[] = new float[16];
float projection[] = new float[16];
float wcoord[] = new float[4];
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
gl.glClearColor(0.0f, 0.0f, 0.0f, 0.5f); // OpenGL docs.
gl.glShadeModel(GL10.GL_SMOOTH);// OpenGL docs.
gl.glClearDepthf(1.0f);// OpenGL docs.
gl.glEnable(GL10.GL_DEPTH_TEST);// OpenGL docs.
gl.glDepthFunc(GL10.GL_LEQUAL);// OpenGL docs.
gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, // OpenGL docs.
GL10.GL_NICEST);
mygl = gl;
mygl11 = (GL11) gl;
int index;
int counter = 0;
float zcoord = -7.0f;
mygl11.glGetFloatv(GL11.GL_MODELVIEW_MATRIX, modelview, 0);
mygl11.glGetFloatv(GL11.GL_PROJECTION_MATRIX, projection, 0);
mygl11.glGetIntegerv(GL11.GL_VIEWPORT, viewport, 0);
for(int x=0;x<11;x++)
{
for(int y =0;y<10;y++)
{
index = tilemap1[y][x];
if(index==0)
{
tiles[counter]=new square(x,y,zcoord,0.5f,1.0f,0.0f,0f,"vert");
}
if(index==1)
{
tiles[counter]=new square(x,y,zcoord,0.5f,0f,1.0f,0f,"vert");
}
if(index==2)
{
tiles[counter]=new square(x,y,zcoord,0.5f,0.0f,0.0f,1f,"vert");
}
counter++;
}
}
}
public vector2d getworkcoords(float width,float height)
{
float[] depth = new float[1];
float winx = width;
float winy =viewport[3]-height;
//vector2d position = new vector2d(0,0);
int test = GLU.gluUnProject(winx, winy,0.0f, modelview, 0, projection,
0, viewport, 0, wcoord, 0);
vector2d v = new vector2d(0,0);
v.x = wcoord[0];
v.y = wcoord[1];
v.z = wcoord[2];
return v ;
}
public void onSurfaceChanged(GL10 gl, int w, int h) {
gl.glViewport(0, 0, w, h);
gl.glMatrixMode(GL10.GL_PROJECTION);// OpenGL docs.
gl.glLoadIdentity();// OpenGL docs.
GLU.gluPerspective(gl, 45.0f,
(float) w / (float) h,
0.1f, 100.0f);
gl.glMatrixMode(GL10.GL_MODELVIEW);// OpenGL docs.
gl.glLoadIdentity();// OpenGL docs.
}
public float movetox;
public float movetoy;
float currentposx;
float currentposy;
public void updatemoveto(float x , float y)
{
movetox = x;
movetoy = y;
}
public void onDrawFrame(GL10 gl) {
gl.glClearColor(mRed, mGreen, mBlue, 1.0f);
gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
currentposx = mycube.getcenterx();
currentposy = mycube.getcentery();
float x = movetox -currentposx;
float y = movetoy - currentposy;
double angle = Math.atan2(y, x);
double mx =.5f* Math.cos(angle);
double my = .5f* Math.sin(angle);
double mmx = mx+ currentposx;
double mmy = my+ currentposy;
mycube.updatecube((float)(mmx), (float)(mmy),0);
mycube.drawcube(gl);
for(int i = 0;i<110;i++)
{
tiles[i].draw(gl);
}
}
public void setColor(float r, float g, float b) {
mRed = r;
mGreen = g;
mBlue = b;
}
public void updatecamera(float myx, float myy, float myz)
{
mygl.glLoadIdentity();// OpenGL docs.
GLU.gluLookAt(mygl, myx, myy, myz, myx, myy, myz-10, 0, 1, 0);
}
private float mRed;
private float mGreen;
private float mBlue;
int tilemap1[][] = {
{0,0,0,0,0,0,0,0,0,0,0},
{0,1,2,1,1,1,1,1,1,1,0},
{0,1,2,1,1,1,1,1,1,1,0},
{0,1,2,1,1,1,1,1,1,1,0},
{0,1,2,2,2,1,1,1,1,1,0},
{0,1,1,1,2,1,1,1,1,1,0},
{0,1,1,1,2,1,1,1,1,1,0},
{0,1,1,1,2,1,1,1,1,1,0},
{0,1,1,1,2,2,1,1,1,1,0},
{0,0,0,0,0,0,0,0,0,0,0},
};
square[] tiles = new square[110];
shapemaker mycube = new shapemaker(0,0,0,1);
}
I did not include the on create method and the on pause ones. I would also like advice on designing the code structure because my implementation is far from perfect.
So in short. I would like help figuring out how to modify the code I made to have a cube move to where i press. And how you would improve the code
Thanks
First of all,
you don't need to recreate vertices on every moving. It is very complex task and it will low the framerate (FPS). In you code you have model matrix. Usually it is responsible for translating, scaling rotating of your model(mesh, cube in your example). In common case you have to translate the model matrix and then multiple model, projection and view matrixes, you will get the matrix for your device screen. Then by multiplication of vertex postion and this matrix you'll get the right position of vertex.
you can look at this question/answer