Hi I want to draw new circles when I enter new radius for them and click button. I am supposed to position it with mouse. However after I draw circle I either can't draw new one or i can't position it mouse too.
import interfascia.*;
int numCircles = 500;
Circle[] circles = new Circle[numCircles]; // define the array
int k=0;
GUIController c;
IFButton b1;
IFTextField tn;
float bx;
float by;
boolean overBox = false;
boolean locked = false;
float xOffset = 0.0;
float yOffset = 0.0;
float r;
void setup() {
size(1439,800);
smooth();
noStroke();
bx=width/2;
by=height/2;
c = new GUIController (this);
b1 = new IFButton ("Click to draw", 600, 220, 100);
tn=new IFTextField("", 100,20,50);
c.add(b1);
c.add(tn);
}
void draw() {
background(205);
if (mouseX > bx-r && mouseX < bx+r &&
mouseY > by-r && mouseY < by+r) {
overBox = true;
if(!locked) {
}
} else {
overBox = false;
}
circles[k] = new Circle(bx,by,r);
circles[k].display(); // display all the circles
}
void actionPerformed (GUIEvent e) {
if (e.getSource() == b1) {
r=float(tn.getValue());
}
}
class Circle {
float x,y,r; // location
color c; // color
Circle(float x, float y, float r) {
this.x = x;
this.y = y;
this.r = r;
c = color(random(255));
}
void display() {
ellipse(x,y,10,10); // a circle at position xy
}
}
void mousePressed() {
if(overBox) {
locked = true;
} else {
locked = false;
}
xOffset = mouseX-bx;
yOffset = mouseY-by;
}
void mouseDragged() {
if(locked) {
bx = mouseX-xOffset;
by = mouseY-yOffset;
}
}
void mouseReleased() {
locked = false;
overBox=false;
}
You have two main options:
Option 1: Stop calling the background() function from your draw() function. This is what's clearing out old frames. If you remove that (or move it to setup(), then your old frames will never be cleared out.
Option 2: Store your state in a set of data structures. You can think of using arrays to store the position and radius of each of your circles. (Or better yet, create a Circle class and store it instances of that in an ArrayList.) Then to draw your scene, just iterate over your data structure and draw all of your circles.
Another option is to use an off-screen PGraphics buffer.
Related
My game is a shooting game for school. I need help on the collision between my bullet and my enemy. I have placed the bullet class with my player but for some reason it keeps on passing through it and not disappearing. The first example below shows the weapon class which is for the bullet and the one underneath that is the main.
class Weapons{
PImage bullet;
int speed;
int imageSize = 20;
float x = 20;
float y = 20;
float m;
int damage = 5;
int[] src = new int[2];
float[] dest = new float[2];
Weapons(int x, int y){
speed = 12;
dest[0] = mouseX;
dest[1] = mouseY;
src[0] = x;
src[1] = y;
m = (dest[1]-src[1]) / (dest[0]-src[0]);
this.x = x;
this.y = y;
bullet = loadImage("bullet.png");
bullet.resize(imageSize,imageSize);
}
boolean shooting(){
image(bullet, x, y); // if attack is true then renfer the image
x += speed;
y = (m * (x - dest[0])) + dest[1];
return (x > width || y > height);
}
boolean crash(Enemy enemies) {
//// return the result of checking if the plane has crashed into the bird
return abs(this.x-enemies.x) < bullet.width && abs(this.y - enemies.y) < bullet.height;
}
}
Maincharacter MC;
Goal G;
ArrayList<Enemy> enemies = new ArrayList<>();
ArrayList<Weapons> bullets = new ArrayList<>();
final int Playing = 0;
final int Finish = 1;
int gameMode = Playing;
//Enemy[] enemies = new Enemy[10];
PImage background; // creating background
int bgX = 0;
void setup(){
size(800,500);
MC = new Maincharacter(100,3);
//E1 = new Enemy();
G = new Goal(100,20);
background = loadImage("Battleground1.png");
background.resize(width*2,height); //set image to be same size as the canvas
for(int i = 0; i<2; i++){ // i represent how many enemy I want.
enemies.add(new Enemy((int)random(200,850), (int)random(0, height- 150) ) );
}
//for(int i=0 ; i < enemies.length ; i++){
//enemies[i] = new Enemy(); }
}
void draw(){
if(gameMode == Playing){
background();
MC.display();
G.display();
for(Enemy E : enemies){
E.update();
if (MC.crash(E)){
gameMode = Finish;
}
}
for(Weapons W: bullets){
W.update();
if(enemies.crash(W)){ // doesnt exist
gameMode = Finish;
}
}
// for(int i=0 ; i < enemies.length ; i++){
//enemies[i].update(); }
}}
void keyPressed(){
// creating the movemnt for my main charcter
MC.move();
if (keyCode == RIGHT || keyCode == 68 ){
MC.x += 10;
} else if (keyCode == UP || keyCode == 87) {
MC.y -=10;
} else if (keyCode == LEFT || keyCode == 65){
MC.x -= 10;
} else if(keyCode == DOWN || keyCode == 83 ){
MC.y += 10;
} else if (keyCode == 32) { // space bar
MC.attack_function(); // call attack function to create bullet object
}// make an attack
}
void mousePressed(){ // when mouse is pressed call attack function to create bullet object
MC.attack_function();
}
void background(){
//scrolling background image
image(background, bgX, height/2); //draw image to fill the canvas
//draw image again off the right of the canvas
image(background, bgX+background.width, height/2);
bgX -= 4;
if(bgX == -background.width ) //if first image completely off the canvas
{
bgX=0; //reset back to initial value background
}
}
What I want is to have bullet colliding with the enemies and then disappearing.
You are trying to call the method enemies.crash(W) but enemies is some collection of Enemy instances. Enemy does not provide the method crash as well.
You have to loop for each of your enemies all of your weapons and call the method crash on the Weapon instance giving the current enemy, something like:
for (Enemy enemy: enemies){
enemy.update();
if (mainCharacter.crash(enemy)) {
gameMode = Finish;
}
for (Weapons weapon: bullets) {
weapon.update();
if (weapon.crash(enemy)) {
gameMode = Finish;
// TODO: are you sure that killing an enemy finishes the game?
}
}
}
(Please note that I used mainCharacter instead of MC here for better readability.)
Hi this is my code the code should work like after clicking the new spawned cirlce another one should apear and after cliking the new one another one should spawn but it has some problem , for example i dont know what type of logic i should write for it? should i write
recursion if yes then how?
float xpaikka =random(640);
float ypaikka =random(640) ;
int value_y = 0 ;
int value_x = 0 ;
void setup(){
size(640,640);
frameRate(500);
background(34,45,323);
}
void draw(){
ellipse(xpaikka,ypaikka,60,60);
float randomi_x = random(630);
float randomi_y = random(630);
var d= dist(mouseX,mouseY,randomi_x,ypaikka);
if (d < 30 && value_x != 0){
ellipse(randomi_x,randomi_y,60,60);
value_x = 0;
}
}
void mousePressed() {
if (value_x == 0) {
value_x = mouseX;
print("mouseX",value_x);
} else {
value_x = 0;
}
}
void mousePressed2() {
if (value_y == 0) {
value_y = mouseY;
print("mouseY",mouseY);
} else {
value_y = 0;
}
}
Use an PVector to represent a the center point of the circle
PVector centerPoint = new PVector(random(640), random(640));
When the mouse button is pressed, calculate the distance from the mouse to the center of the circle. If the distance is less than the radius of the circle, create a new random center point:
PVector mousePoint = new PVector(mouseX, mouseY);
if (mousePoint.dist(centerPoint) <= 30.0) {
centerPoint = new PVector(random(640), random(640));
}
Complete example:
PVector centerPoint = new PVector(random(640), random(640));
void setup() {
size(640, 640);
}
void draw() {
background(34, 45, 323);
ellipse(centerPoint.x, centerPoint.y, 60, 60);
}
void mousePressed() {
PVector mousePoint = new PVector(mouseX, mouseY);
if (mousePoint.dist(centerPoint) <= 30.0) {
centerPoint = new PVector(random(640), random(640));
}
}
I want to create an ArrayList of ball objects, which should be in a loop until there are 100 pieces.
Now my problem: I must implement a function hitTest, so that when you click on a ball it gets removed. In the same position, there should appear two balls then, which go into a different direction.
Can someone help me? I am so lost...
Here's my code so far:
Ball b;
ArrayList<Ball> balls;
void setup() {
size(800, 800);
balls = new ArrayList<Ball>();
for (int i = 0; i<100; i++) {
drawBall();
}
}
void draw() {
background(255);
//b.update();
for (int i= 0; i<balls.size(); i++) {
balls.get(i).update();
}
}
void drawBall() {
Ball b = new Ball();
balls.add(b);
}
class Ball {
private float x;
private float y;
private float ballSize;
private float dirX;
private float dirY;
private boolean moving = true;
Ball() {
this.x = width/2;
this.y = height/2;
this.ballSize = random(10.0, 30.0);
this.dirX = random(-3.0, 3.0);
this.dirY = random(-3.0, 3.0);
if (this.dirX<1.0 && this.dirX>1.0) //1 statt -1 macht zufälliger { this.dirX = 1.0; }
if (this.dirY<1.0 && this.dirY>1.0) {
this.dirY = 1.0;
}
}
public void update() {
stroke(255);
fill(random(255), random(255), random(255), random(255));
ellipse( this.x, this.y, this.ballSize, this.ballSize);
if (this.moving == true) {
this.x += this.dirX;
this.y += this.dirY;
}
if (this.x+ this.ballSize/2> width ||this.x- this.ballSize/2<0) {
this.dirX= dirX*-1;
}
if (this.y+ this.ballSize/2> height ||this.y- this.ballSize/2<0) {
this.dirY= dirY*-1;
}
}
}
Break your problem down into smaller, simpler steps.
e.g.
when you click on a ball, it gets removed. In the same position, there should appear two balls then, which go into a different direction.
when you click on a ball: you can mix the dist() function (to check if the distance between the mouse and a ball is smaller then the radius) with mouseClicked() (or mousePressed() / mouseReleased())
it gets removed: you already called balls.add(). Similarly you can call balls.remove() passing the ball object or index to remove (depending on the case)
same position: you need to remember (store the coordinates) of the ball that was clicked to add two balls at the same position
different direction: you already do that in the Ball() constructor: you can apply the same logic on each new ball.
Here's a basic sketch to illustrate point 1, using dist():
void draw(){
background(255);
int ballX = 50;
int ballY = 50;
int ballRadius = 35;
if(dist(ballX, ballY, mouseX, mouseY) < ballRadius){
fill(0,192,0);
}else{
fill(192,0,0);
}
ellipse(ballX,ballY, ballRadius * 2, ballRadius * 2);
}
Paste this in a new sketch, run it and you should get the hang of using dist() in the context of your problem.
Regarding points 2,3,4 here's a modified version of your sketch with comments and a slightly different approach: instead of removing a ball to add a new one at the exact location with a different direction, simply randomise the direction. Visually it will look similar to a new ball (except the random size/colour). With the clicked ball being re-used, only a second one is added:
Ball b;
ArrayList<Ball> balls;
void setup() {
size(800, 800);
balls = new ArrayList<Ball>();
for (int i = 0; i<100; i++) {
drawBall();
}
}
void draw() {
background(255);
//b.update();
for (int i= 0; i<balls.size(); i++) {
// pass the mouse coordinates to each ball to check if it's hovered or not
balls.get(i).update(mouseX, mouseY);
}
}
// on mouse pressed
void mousePressed(){
for (int i= 0; i<balls.size(); i++) {
// make current ball reusable in this loop
Ball ball = balls.get(i);
// if ball is hovered
if(ball.isHovered){
// randomize direction of current ball
ball.setRandomDirection();
// add a new ball from the current location
balls.add(ball.copy());
}
}
}
void drawBall() {
Ball b = new Ball();
balls.add(b);
}
class Ball {
private float x;
private float y;
private float ballSize;
private float dirX;
private float dirY;
private boolean moving = true;
private color fillColor;
// property to keep track if the ball is hovered or not
private boolean isHovered;
Ball() {
this.x = width/2;
this.y = height/2;
this.ballSize = random(10.0, 30.0);
this.setRandomDirection();
this.fillColor = color(random(255), random(255), random(255), random(255));
}
// extract random direction calls into a re-usable function (handy for click / collision)
void setRandomDirection(){
this.dirX = random(-3.0, 3.0);
this.dirY = random(-3.0, 3.0);
if (this.dirX<1.0 && this.dirX>1.0) { //1 statt -1 macht zufälliger { this.dirX = 1.0; }
if (this.dirY<1.0 && this.dirY>1.0) {
this.dirY = 1.0;
}
}
}
public void update(int x, int y) {
// euclidean distance between this ball's coordinates a given x y position (e.g. mouse)
isHovered = dist(this.x, this.y, x, y) < this.ballSize / 2;
// optional: use stroke color to visually display highlighted ball
if(isHovered){
stroke(0);
}else{
stroke(255);
}
fill(fillColor);
ellipse( this.x, this.y, this.ballSize, this.ballSize);
if (this.moving == true) {
this.x += this.dirX;
this.y += this.dirY;
}
if (this.x + this.ballSize / 2 > width ||
this.x - this.ballSize / 2 < 0) {
this.dirX= dirX*-1;
}
if (this.y + this.ballSize / 2 > height ||
this.y - this.ballSize / 2 < 0) {
this.dirY= dirY*-1;
}
}
// utility function: simply copies this ball's x,y position to the new one
Ball copy(){
Ball clone = new Ball();
clone.x = this.x;
clone.y = this.y;
return clone;
}
}
The copy() method is flexible enough that it's ease to remove one ball to add two more if that is an absolute must. For example:
// on mouse pressed
void mousePressed(){
for (int i= 0; i<balls.size(); i++) {
// make current ball reusable in this loop
Ball ball = balls.get(i);
// if ball is hovered
if(ball.isHovered){
// add two new balls from the current location
balls.add(ball.copy());
balls.add(ball.copy());
// remove ball
balls.remove(i);
}
}
}
I'm begginer in java game programming and I have a small actually big problem (for me) with game.
I'm trying making collision between enemy and blocks it doesnt work and i dont know why. It should worked but it just slow game on one fps per second and dont do anything.
I have main class called Game
with this main Init() function
public void init(){
WIDTH = getWidth();
HEIGHT = getHeight();
tex = new Texture();
BufferImageLoader loader = new BufferImageLoader();
level = loader.loadImage("/level.png"); // loading level
cloud = loader.loadImage("/cloud.png"); // loading clouds
handler = new Handler();
cam = new Camera(0,0);
LoadImageLevel(level);
this.addKeyListener(new KeyInput(handler));
}
and than LoadImageLevel function where I read level.png pixel by pixel and by the differents color I'm setting position of every objects.
private void LoadImageLevel (BufferedImage image){
int w = image.getWidth();
int h = image.getHeight();
//System.out.println(w + " , " + h);
for(int xx = 0; xx < h; xx++){
for(int yy = 0; yy < w ; yy++){
int pixel = image.getRGB(xx, yy);
int red = (pixel >> 16) & 0xff;
int green = (pixel >> 8) & 0xff;
int blue = (pixel) & 0xff;
if(red == 255 && green == 255 && blue == 255)
handler.addObject(new Block(xx*32,yy*32,1,ObjectId.Block));
if(red == 0 && green == 0 && blue == 255)
handler.addObject(new Player(xx*32,yy*32,1,handler,ObjectId.Player));
if(red == 0 && green == 255 && blue == 0)
handler.addObject(new Enemy(xx*32,yy*32,handler,ObjectId.Enemy));
}
}
}
In class Player is two important functions tick and collision where in tick is collison called.
public class Player extends GameObject{
private float width = 32, // 48
height = 64; // 96
private float gravity = 0.5f;
private final float MAX_SPEED = 10;
private int facing = 1;
private int last = 0; // last position left or right
private Handler handler;
Texture tex = Game.getInstance();
private int type;
private Animation playerWalk, playerWalkLeft,jump;
public Player(float x, float y,int type , Handler handler ,ObjectId id) {
super(x, y, id);
this.handler = handler;
this.type = type;
playerWalk = new Animation(2,tex.player[2],tex.player[3],
tex.player[4],tex.player[5]);
playerWalkLeft = new Animation(2,tex.player[7],tex.player[8],
tex.player[9],tex.player[10]);
jump = new Animation(2,tex.player[11],tex.player[12]);
}
public void tick(LinkedList<GameObject> object) {
x += velX;
y += velY;
if(velX < 0) facing = -1;
else if(velX > 0) facing = 1;
if(falling || jumping){
velY += gravity;
if(velY > MAX_SPEED){
velY = MAX_SPEED;
}
}
Collision(object);
//System.out.println(velX + " " + velY);
playerWalk.runAnimation();
playerWalkLeft.runAnimation();
jump.runAnimation();
}
private void Collision(LinkedList<GameObject> object){
for(int i = 0; i < handler.object.size();i++){
GameObject tempObject = handler.object.get(i);
if(tempObject.getId() == ObjectId.Block ){
if(getBoundsTop().intersects(tempObject.getBounds())){
y = tempObject.getY() + 32;
velY = 0;
}
if(getBounds().intersects(tempObject.getBounds())){
y = tempObject.getY() - height;
velY = 0;
falling = false;
jumping = false;
}else
falling = true;
if(getBoundsRight().intersects(tempObject.getBounds())){
x = tempObject.getX() - width;
}
if(getBoundsLeft().intersects(tempObject.getBounds())){
x = tempObject.getX() + 35;
}
}
/* new */
}
}
public void render(Graphics g) {
/*
g.setColor(Color.blue);
g.fillRect((int)x,(int)y,(int)width,(int)height);
Graphics2D g2d = (Graphics2D) g;
g.setColor(Color.red);
g2d.draw(getBounds());
g2d.draw(getBoundsRight());
g2d.draw(getBoundsLeft());
g2d.draw(getBoundsTop());
*/
if(velX != 0){
if (facing == 1){
playerWalk.drawAnimation(g,(int) x, (int)y,32,64);
last = 1;
}
else{
playerWalkLeft.drawAnimation(g,(int) x, (int)y,32,64);
last = 0;
}
}
else
if (last == 1)
g.drawImage(tex.player[1], (int)x,(int) y,32,64,null);
else
g.drawImage(tex.player[6], (int)x,(int) y,32,64,null); // 6 ,32,64
//System.out.println("Y: " + y); // 513 je max
if (y >= 513){
g.setColor(Color.red);
g.drawString("Game Over", (int) x, 200);
}
}
public Rectangle getBounds() {
return new Rectangle((int) ((int)x+(width/2)-((width/2)/2)),(int) ((int)y+(height/2)),(int)width/2,(int)height/2);
}
public Rectangle getBoundsTop() {
return new Rectangle((int) ((int)x+(width/2)-((width/2)/2)),(int)y,(int)width/2,(int)height/2);
}
public Rectangle getBoundsRight() {
return new Rectangle((int) ((int)x+width-5),(int)y+5,(int)5,(int)height-10);
}
public Rectangle getBoundsLeft() {
return new Rectangle((int)x,(int)y+5,(int)5,(int)height-10);
}
Player dont have any problem with block collision.
public class Block extends GameObject {
Texture tex = Game.getInstance();
private int type;
public Block(float x, float y,int type,ObjectId id) {
super(x, y, id);
this.type = type;
}
public void tick(LinkedList<GameObject> object) {
}
public void render(Graphics g) {
if(type == 0)
g.drawImage(tex.block[0], (int) x, (int) y ,null);
if(type == 1)
g.drawImage(tex.block[1], (int) x, (int) y ,null);
}
public Rectangle getBounds() {
return new Rectangle((int)x,(int)y,32,32);
}
}
But when i tried created Enemy class and make it same like in Player class I mean collision it just make game slower and nothing else.
public class Enemy extends GameObject{
private Handler handler;
public Enemy(float x, float y,Handler handler, ObjectId id) {
super(x, y, id);
this.handler = handler;
}
public void tick(LinkedList<GameObject> object) {
for(int i = 0; i < handler.object.size();i++){
GameObject tempObject = handler.object.get(i);
if(tempObject.getId() == ObjectId.Block ){
if(getBoundsTop().intersects(tempObject.getBounds())){
}
if(getBounds().intersects(tempObject.getBounds())){
}
if(getBoundsRight().intersects(tempObject.getBounds())){
}
if(getBoundsLeft().intersects(tempObject.getBounds())){
}
}
}
}
public void render(Graphics g) {
g.setColor(Color.red);
g.fillRect((int)x,(int) y, 32, 32);
}
public Rectangle getBoundsTop() {
return new Rectangle((int)x,(int)y,32,32);
}
public Rectangle getBoundsLeft() {
return new Rectangle((int)x,(int)y,32,32);
}
public Rectangle getBoundsRight() {
return new Rectangle((int)x,(int)y,32,32);
}
public Rectangle getBounds() {
return new Rectangle((int)x,(int)y,32,32);
}}
I know that bounds should not have return same "new Rectangle" and that theres no any movements of enemy anyway when i set in tick method x--; for just trying if enemy stop when it come to the edge of block but it doesnt work i dont know whats wrong with it i spend more than two days with fixing this. If it can help you there is a link for whole project (Eclipse) You can download it from dropbox
I just wanted to have an enemy which move left and right and have collison with block it means when he "touch" by his left side to the block he "turn around" and move to right side until he "touch" by his right side etc... other collisions between Player and Enemy is not problem for me. But just this.
I'm so thankful for every advice :)
The problem is with your getBounds() method.
You are saying in getBounds() method to return a rectangle with width=32 and height=32. And since your rectangle is 32 by 32 (as mentioned in fillrect(x,y,32,32) ) so getBounds() returns with the intersection in height and width.
In other words, try not to collide the returned Top left bottom or right bounds with themselves.
And in enemy(), you are declaring set.color = red while in loading you are using green. Try red==255, green==0, blue==0 instead of if(red == 0 && green == 255 && blue == 0)
I am making a 2d rpg game in java and I have run into a problem. I can make the player move around the stage and I have rocks, trees, walls, etc. on the stage as well. I don't know how to detect the collision and make it to where the player can't move through the object. The code that reads map file and draws image on the canvas is as follows:
public void loadLevel(BufferedImage levelImage){
tiles = new int[levelImage.getWidth()][levelImage.getHeight()];
for (int y = 0; y < h; y++) {
for (int x = 0; x < w; x++) {
Color c = new Color(levelImage.getRGB(x, y));
String h = String.format("%02x%02x%02x", c.getRed(),c.getGreen(),c.getBlue());
switch(h){
case "00ff00"://GRASS Tile - 1
tiles[x][y] = 1;
break;
case "808080"://Stone -2
tiles[x][y] = 2;
break;
case "894627"://Dirt -3
tiles[x][y] = 3;
break;
case "404040"://Rock on Grass -4
tiles[x][y] = 4;
break;
case "00b700"://Tree -5
tiles[x][y] = 5;
break;
case"000000"://Wall -6
tiles[x][y] = 6;
break;
case "cccccc"://Rock on stone -7
tiles[x][y] = 7;
break;
default:
tiles[x][y] = 1;
System.out.println(h);
break;
}
}
}
}
And the player class is as follows:
public class Player {
private int x,y;
public int locx,locy;
private Rectangle playerR;
private ImageManager im;
public boolean up =false,dn = false,lt=false,rt=false,moving = false,canMove = true;
private final int SPEED =2;
public Player(int x, int y, ImageManager im){
this.x = x;
this.y = y;
this.im = im;
locx = x;
locy = y;
playerR = new Rectangle(x,y,16,16);
}
public void tick(){
if (up) {
if(canMove){
y -= SPEED;
locx = x;
locy = y;
playerR.setLocation(locx, locy);
moving = true;
}
else{
y += 1;
canMove=true;
}
}
if (dn) {
y +=SPEED;
locx = x;
locy = y;
moving = true;
}
}
if (lt) {
x -= SPEED;
locx = x;
locy = y;
moving = true;
}
if (rt) {
x+=SPEED;
locx = x;
locy = y;
moving = true;
}
}
if(moving){
System.out.println("PLAYER\tX:"+locx+" Y:"+locy);
moving = false;
}
}
public void render(Graphics g){
g.drawImage(im.player, x, y, Game.TILESIZE*Game.SCALE, Game.TILESIZE*Game.SCALE, null);
}
}
I don't really know how to do collision, but i googled it and people said to make a rectangle for the player and all the objects that the player should collide with, and every time the player moves, move the player's rectangle. Is this the right way to do this?
EDIT EDIT EDIT EDIT
code for when collision is true:
if (rt) {
x+=SPEED;
locx = x;
locy = y;
playerR.setLocation(locx, locy);
for(int i = 0;i<Level.collisions.size();i++){
if(intersects(playerR,Level.collisions.get(i))==true){
x-=SPEED;
locx = x;
playerR.setLocation(locx, locy);
}
}
moving = true;
}
And the intersects method is as follows:
private boolean intersects(Rectangle r1, Rectangle r2){
return r1.intersects(r2);
}
I'm going to focus on your tick method since that is where most of this logic is going. There are a couple changes here. Most notably, we only move the rectangle before checking for collisions. Then loop through all the collideable objects in your level. Once one is found, we reset our x and y and break out of the loop (no sense in looking at any of the other objects since we already found the one we collided with). Then we update our player position. By doing it this way, I centralized the code so it is not being repeated. If you ever see yourself repeating code, there is a pretty good chance that it can be pulled out to a common place, or to a method.
public void tick() {
if (up) {
y -= SPEED;
} else if (dn) {
y += SPEED;
} else if (lt) {
x -= SPEED;
} else if (rt) {
x += SPEED;
}
playerR.setLocation(x, y);
for (Rectangle collideable : Level.collisions) {
if (intersects(playerR, collideable)) {
x = locx;
y = locy;
playerR.setLocation(x, y);
break;
}
}
locx = x;
locy = y;
}
There are different ways to do that. As you talk about a "rpg" i think your view is Isometric (45° top down).
I would do the collision detection in pure 90° top down, as it is easier and, imho, more realistic.
We have 2 possibilities:
Move your Player to the next position. If there is a collision, reset his position.
Calculate the next position, if there would be a collision don't move.
If you want to have a "gliding" collision response, you have to check in which axis the collision will happen, and stop / reset movement for this axis only.
To have a more efficient collision detection only check near objects, which will possibly collide.
Do this by comparing a squared "dangerRadius" with the squared distance between your player and the object:
if ((player.x - object.x)² + (player.y - object.y)² <= dangerRadius²)
// Check for intersection
This will sort out most of the objects by using a simple calculation of:
2 subtractions
1 addition
3 multiplications (the ²)
1 compare (<=)
In your game you should sepparate the logic and the view. So basicly you don't detect, if the two images overlapp, but you check, if the objects in your logic overlap. Then you draw the images on the right position.
Hope this helps.
EDIT: Important: If you update your character, depending on the time between the last and this frame (1/FPS) you have to limit the max timestep. Why? Because if for some reason (maybe slow device?) the FPS are really low, it is possible, that the character moves verry far between 2 frames and for that he could go through an object in 1 frame.
Also if you simply reset the movement on collision or just don't move the distance between you and the object could be big for low FPS. For normal FPS and not to high movementspeed this won't happen/ be noticeable.
I personally am fairly new to Java, though I have worked with C# in the past. I am making a similar game, and for collision detection I just check the locations of the player and objects:
if (z.gettileX() == p.gettileX()){
if (z.gettileY() == p.gettileY()){
System.out.println("Collision!");
}
}
If the player (p) has equal X coordinates and Y coordinates to z(the bad guy), it will send this message and confirm that the two have, in fact, collided. If you can make it inherent in the actual class behind z to check if the coordinates a equal, you can create an unlimited number of in-game objects that detect collision and react in the same way, i.e. walls.
This is probably what your looking for. I've made this class spicificly for collision of multiple objects and for individual side collisions.
abstract class Entity {
private Line2D topLine;
private Line2D bottomLine;
private Line2D leftLine;
private Line2D rightLine;
private Rectangle rectangle;
private Entity entity;
protected boolean top;
protected boolean bottom;
protected boolean left;
protected boolean right;
protected int x;
protected int y;
protected int width;
protected int height;
public Entity(int x, int y, int width, int height) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
updateLinesAndRects();
}
public void updateLinesAndRects() {
topLine = new Line(x + 1, y, width - 2, 0);
bottomLine = new Line(x + 1, y + height, width - 2, height);
leftLine = new Line(x, y + 1, 0, height - 2);
rightLine = new Line(x + width, y + 1, 0, height - 2);
rectangle = new Rectangle(x, y, width, height)
}
public void setCollision(Entity entity) {
this.entity = entity;
top = isColliding(new Line2D[]{topLine, bottomLine, leftLine, rightLine});
bottom = isColliding(new Line2D[]{bottomLine, topLine, leftLine, rightLine});
left = isColliding(new Line2D[]{leftLine, topLine, bottomLine, rightLine});
right = isColliding(new Line2D[]{rightLine, topLine, bottomLine, leftLine});
}
public void updateBounds() {
if(top) y = entity.y + entity.height;
if(bottom) y = entity.y - height;
if(left) x = entity.x + entity.width;
if(right) x = entity.x - width;
}
public boolean isColliding() {
return rectangle.intersects(entity.rect);
}
private boolean isLinesColliding(Line2D[] lines) {
Rectangle rect = entity.getRectangle();
return lines[0].intersects(rect) && !lines[1].intersects(rect) && !lines[2].intersects(rect) && !lines[3].intersects(rect);
}
private Line2D line(float x, float y, float width, float height) {
return new Line2D(new Point2D.Float(x, y), new Point2D.Float(x + width, x + height));
}
public Rectangle getRectangle() {
return rectangle;
}
}
Example:
class Player extends Entity{
Entity[] entities;
public Player(int x, int y, int width, int height) {
super(x, y, width, height);
}
public void update() {
updateLinesAndRects();
for(Entity entity : entities) {
setCollision(entity);
if(top) system.out.println("player is colliding from the top!");
if(isColliding()) system.out.println("player is colliding!");
updateBounds(); // updates the collision bounds for the player from the entities when colliding.
}
}
public void setEntities(Entity[] entities) {
this.entities = entities;
}
}